2
2
//! TOML settings files, in the same format as user data, or the JSON equivalent. The inputs are
3
3
//! pulled and applied to the API server in a single transaction.
4
4
5
+ use crate :: uri_resolver:: { StdinUri , FileUri , HttpUri , UriResolver } ;
5
6
use crate :: rando;
6
7
use futures:: future:: { join, ready, TryFutureExt } ;
7
8
use futures:: stream:: { self , StreamExt } ;
8
9
use reqwest:: Url ;
9
10
use serde:: de:: { Deserialize , IntoDeserializer } ;
10
11
use snafu:: { futures:: try_future:: TryFutureExt as SnafuTryFutureExt , OptionExt , ResultExt } ;
12
+ use std:: convert:: TryFrom ;
11
13
use std:: path:: Path ;
12
14
use tokio:: io:: AsyncReadExt ;
13
15
16
+
14
17
/// Reads settings in TOML or JSON format from files at the requested URIs (or from stdin, if given
15
18
/// "-"), then commits them in a single transaction and applies them to the system.
16
19
pub async fn apply < P > ( socket_path : P , input_sources : Vec < String > ) -> Result < ( ) >
@@ -67,46 +70,36 @@ where
67
70
}
68
71
69
72
/// Retrieves the given source location and returns the result in a String.
70
- async fn get < S > ( input_source : S ) -> Result < String >
71
- where
72
- S : Into < String > ,
73
- {
74
- let input_source = input_source. into ( ) ;
75
-
76
- // Read from stdin if "-" was given.
77
- if input_source == "-" {
78
- let mut output = String :: new ( ) ;
79
- tokio:: io:: stdin ( )
80
- . read_to_string ( & mut output)
81
- . context ( error:: StdinReadSnafu )
82
- . await ?;
83
- return Ok ( output) ;
73
+ pub async fn get ( input : & str ) -> Result < String > {
74
+ let resolver = select_resolver ( input) ?;
75
+ resolver. resolve ( ) . await
76
+ }
77
+
78
+ /// Choose which UriResolver applies to `input` (stdin, file://, http(s)://).
79
+ fn select_resolver ( input : & str ) -> Result < Box < dyn UriResolver > > {
80
+ // 1) "-" → stdin
81
+ if let Ok ( r) = StdinUri :: try_from ( input) {
82
+ return Ok ( Box :: new ( r) ) ;
84
83
}
85
84
86
- // Otherwise, the input should be a URI; parse it to know what kind.
87
- // Until reqwest handles file:// URIs: https://github.com/seanmonstar/reqwest/issues/178
88
- let uri = Url :: parse ( & input_source) . context ( error:: UriSnafu {
89
- input_source : & input_source,
90
- } ) ?;
91
- if uri. scheme ( ) == "file" {
92
- // Turn the URI to a file path, and return a future that reads it.
93
- let path = uri. to_file_path ( ) . ok ( ) . context ( error:: FileUriSnafu {
94
- input_source : & input_source,
95
- } ) ?;
96
- tokio:: fs:: read_to_string ( path)
97
- . context ( error:: FileReadSnafu { input_source } )
98
- . await
99
- } else {
100
- // Return a future that contains the text of the (non-file) URI.
101
- reqwest:: get ( uri)
102
- . and_then ( |response| ready ( response. error_for_status ( ) ) )
103
- . and_then ( |response| response. text ( ) )
104
- . context ( error:: ReqwestSnafu {
105
- uri : input_source,
106
- method : "GET" ,
107
- } )
108
- . await
85
+ // 2) parse as a URL
86
+ let url = Url :: parse ( input) . context ( error:: UriSnafu { input_source : input. to_string ( ) } ) ?;
87
+
88
+ // 3) file://
89
+ if let Ok ( r) = FileUri :: try_from ( url. clone ( ) ) {
90
+ return Ok ( Box :: new ( r) ) ;
109
91
}
92
+
93
+ // 4) http(s)://
94
+ if let Ok ( r) = HttpUri :: try_from ( url. clone ( ) ) {
95
+ return Ok ( Box :: new ( r) ) ;
96
+ }
97
+
98
+
99
+ error:: NoResolverSnafu {
100
+ input_source : input. to_string ( ) ,
101
+ }
102
+ . fail ( )
110
103
}
111
104
112
105
/// Takes a string of TOML or JSON settings data and reserializes
@@ -142,11 +135,11 @@ fn format_change(input: &str, input_source: &str) -> Result<String> {
142
135
serde_json:: to_string ( & json_inner) . context ( error:: JsonSerializeSnafu { input_source } )
143
136
}
144
137
145
- mod error {
138
+ pub ( crate ) mod error {
146
139
use snafu:: Snafu ;
147
140
148
141
#[ derive( Debug , Snafu ) ]
149
- #[ snafu( visibility( pub ( super ) ) ) ]
142
+ #[ snafu( visibility( pub ( crate ) ) ) ]
150
143
pub enum Error {
151
144
#[ snafu( display( "Failed to commit combined settings to '{}': {}" , uri, source) ) ]
152
145
CommitApply {
@@ -164,6 +157,9 @@ mod error {
164
157
#[ snafu( display( "Given invalid file URI '{}'" , input_source) ) ]
165
158
FileUri { input_source : String } ,
166
159
160
+ #[ snafu( display( "No URI resolver found for '{}'" , input_source) ) ]
161
+ NoResolver { input_source : String } ,
162
+
167
163
#[ snafu( display(
168
164
"Input '{}' is not valid TOML or JSON. (TOML error: {}) (JSON error: {})" ,
169
165
input_source,
@@ -218,6 +214,12 @@ mod error {
218
214
source : reqwest:: Error ,
219
215
} ,
220
216
217
+ #[ snafu( display( "Given invalid file URI '{}'" , input_source) ) ]
218
+ InvalidFileUri { input_source : String } ,
219
+
220
+ #[ snafu( display( "Given HTTP(S) URI '{}'" , input_source) ) ]
221
+ InvalidHTTPUri { input_source : String } ,
222
+
221
223
#[ snafu( display( "Failed to read standard input: {}" , source) ) ]
222
224
StdinRead { source : std:: io:: Error } ,
223
225
0 commit comments