1use crate::{Error, OptionValue, Options, ParseError, Source, SourceBuilder};
4use std::borrow::Cow;
5use std::collections::HashMap;
6
7impl From<Source> for SourceBuilder {
8 fn from(value: Source) -> Self {
9 Self {
10 source: Some(value.source().to_string()),
11 options: value.options().clone(),
12 resource: value.resource().to_string(),
13 resource_colon: value.resource_colon(),
14 }
15 }
16}
17
18impl TryFrom<&str> for SourceBuilder {
19 type Error = Error;
20
21 fn try_from(value: &str) -> Result<Self, Self::Error> {
22 Ok(Source::parse(value)?.into())
23 }
24}
25
26impl TryFrom<String> for SourceBuilder {
27 type Error = Error;
28
29 fn try_from(value: String) -> Result<Self, Self::Error> {
30 Self::try_from(value.as_str())
31 }
32}
33
34impl TryFrom<&String> for SourceBuilder {
35 type Error = Error;
36
37 fn try_from(value: &String) -> Result<Self, Self::Error> {
38 Self::try_from(value.as_str())
39 }
40}
41
42impl TryFrom<Cow<'_, str>> for SourceBuilder {
43 type Error = Error;
44
45 fn try_from(value: Cow<'_, str>) -> Result<Self, Self::Error> {
46 Self::try_from(value.as_ref())
47 }
48}
49
50impl From<bool> for OptionValue {
51 fn from(value: bool) -> Self {
52 Self::Bool(value)
53 }
54}
55
56impl From<i64> for OptionValue {
57 fn from(value: i64) -> Self {
58 Self::Integer(value)
59 }
60}
61
62impl From<i32> for OptionValue {
63 fn from(value: i32) -> Self {
64 Self::Integer(value as i64)
65 }
66}
67
68impl From<i16> for OptionValue {
69 fn from(value: i16) -> Self {
70 Self::Integer(value as i64)
71 }
72}
73
74impl From<i8> for OptionValue {
75 fn from(value: i8) -> Self {
76 Self::Integer(value as i64)
77 }
78}
79
80impl From<u64> for OptionValue {
81 fn from(value: u64) -> Self {
82 Self::Integer(value as i64)
83 }
84}
85
86impl From<u32> for OptionValue {
87 fn from(value: u32) -> Self {
88 Self::Integer(value as i64)
89 }
90}
91
92impl From<u16> for OptionValue {
93 fn from(value: u16) -> Self {
94 Self::Integer(value as i64)
95 }
96}
97
98impl From<u8> for OptionValue {
99 fn from(value: u8) -> Self {
100 Self::Integer(value as i64)
101 }
102}
103
104impl From<isize> for OptionValue {
105 fn from(value: isize) -> Self {
106 Self::Integer(value as i64)
107 }
108}
109
110impl From<usize> for OptionValue {
111 fn from(value: usize) -> Self {
112 Self::Integer(value as i64)
113 }
114}
115
116impl From<f64> for OptionValue {
117 fn from(value: f64) -> Self {
118 Self::Float(value)
119 }
120}
121
122impl From<f32> for OptionValue {
123 fn from(value: f32) -> Self {
124 Self::Float(value as f64)
125 }
126}
127
128impl From<&str> for OptionValue {
129 fn from(value: &str) -> Self {
130 Self::String(value.to_string())
131 }
132}
133
134impl From<String> for OptionValue {
135 fn from(value: String) -> Self {
136 Self::String(value)
137 }
138}
139
140impl From<&String> for OptionValue {
141 fn from(value: &String) -> Self {
142 Self::String(value.clone())
143 }
144}
145
146impl<T: Into<OptionValue>> From<Vec<T>> for OptionValue {
147 fn from(value: Vec<T>) -> Self {
148 Self::List(value.into_iter().map(Into::into).collect())
149 }
150}
151
152impl<T: Into<OptionValue> + Clone> From<&[T]> for OptionValue {
153 fn from(value: &[T]) -> Self {
154 Self::List(value.iter().cloned().map(Into::into).collect())
155 }
156}
157
158impl From<Options> for OptionValue {
159 fn from(value: Options) -> Self {
160 Self::Map(value)
161 }
162}
163
164impl<K: Into<String>, V: Into<OptionValue>> From<HashMap<K, V>> for OptionValue {
165 fn from(value: HashMap<K, V>) -> Self {
166 let mut options = Options::default();
167 for (key, value) in value {
168 options.insert(key, value);
169 }
170 Self::Map(options)
171 }
172}
173
174impl std::str::FromStr for Source {
175 type Err = ParseError;
176
177 fn from_str(value: &str) -> Result<Self, Self::Err> {
178 Self::parse(value)
179 }
180}
181
182impl TryFrom<&str> for Source {
183 type Error = ParseError;
184
185 fn try_from(value: &str) -> Result<Self, Self::Error> {
186 Self::parse(value)
187 }
188}
189
190impl TryFrom<String> for Source {
191 type Error = ParseError;
192
193 fn try_from(value: String) -> Result<Self, Self::Error> {
194 Self::parse(&value)
195 }
196}
197
198impl TryFrom<&String> for Source {
199 type Error = ParseError;
200
201 fn try_from(value: &String) -> Result<Self, Self::Error> {
202 Self::parse(value)
203 }
204}
205
206impl TryFrom<Cow<'_, str>> for Source {
207 type Error = ParseError;
208
209 fn try_from(value: Cow<'_, str>) -> Result<Self, Self::Error> {
210 Self::parse(&value)
211 }
212}
213
214#[cfg(test)]
215mod tests {
216 use super::*;
217 use std::convert::TryFrom;
218 use std::str::FromStr;
219
220 #[test]
221 fn try_from_str() {
222 let source = Source::try_from("file:/tmp/x").unwrap();
223 assert_eq!(source.resource(), "/tmp/x");
224 }
225
226 #[test]
227 fn builder_try_from_str() {
228 let builder = SourceBuilder::try_from("env(prefix=APP_)").unwrap();
229 let source = builder.build().unwrap();
230 assert_eq!(source.source(), "env");
231 assert_eq!(
232 source.options().get("prefix"),
233 Some(&OptionValue::String("APP_".into()))
234 );
235 }
236
237 #[test]
238 fn builder_try_from_invalid_is_parse_error() {
239 let error = SourceBuilder::try_from("env(prefix=)").unwrap_err();
240 assert!(matches!(error, Error::Parse(ParseError::EmptyValue { .. })));
241 }
242
243 #[test]
244 fn source_round_trips_through_builder_from() {
245 let original = Source::try_from("file:/tmp/x").unwrap();
246 let rebuilt = SourceBuilder::from(original.clone()).build().unwrap();
247 assert_eq!(rebuilt.resource(), original.resource());
248 assert_eq!(rebuilt.source(), original.source());
249 }
250
251 #[test]
252 fn source_from_str_matches_try_from() {
253 let from_str = Source::from_str("env(prefix=APP_)").unwrap();
254 let try_from = Source::try_from("env(prefix=APP_)").unwrap();
255 assert_eq!(from_str.source(), try_from.source());
256 assert_eq!(from_str.resource(), try_from.resource());
257 }
258
259 #[test]
260 fn source_try_from_string_and_cow() {
261 let owned = Source::try_from("file:/a".to_string()).unwrap();
262 let borrowed = Source::try_from(&"file:/a".to_string()).unwrap();
263 let cow = Source::try_from(Cow::Borrowed("file:/a")).unwrap();
264 assert_eq!(owned.resource(), "/a");
265 assert_eq!(borrowed.resource(), "/a");
266 assert_eq!(cow.resource(), "/a");
267 }
268
269 #[test]
270 fn builder_try_from_string_and_cow() {
271 let owned = SourceBuilder::try_from("env".to_string()).unwrap();
272 let borrowed = SourceBuilder::try_from(&"env".to_string()).unwrap();
273 let cow = SourceBuilder::try_from(Cow::Borrowed("env")).unwrap();
274 assert_eq!(owned.build().unwrap().source(), "env");
275 assert_eq!(borrowed.build().unwrap().source(), "env");
276 assert_eq!(cow.build().unwrap().source(), "env");
277 }
278
279 #[test]
280 fn option_value_from_scalars() {
281 assert_eq!(OptionValue::from(true), OptionValue::Bool(true));
282 assert_eq!(OptionValue::from(42_i8), OptionValue::Integer(42));
283 assert_eq!(OptionValue::from(42_i16), OptionValue::Integer(42));
284 assert_eq!(OptionValue::from(42_i32), OptionValue::Integer(42));
285 assert_eq!(OptionValue::from(42_i64), OptionValue::Integer(42));
286 assert_eq!(OptionValue::from(42_u8), OptionValue::Integer(42));
287 assert_eq!(OptionValue::from(42_u16), OptionValue::Integer(42));
288 assert_eq!(OptionValue::from(42_u32), OptionValue::Integer(42));
289 assert_eq!(OptionValue::from(42_u64), OptionValue::Integer(42));
290 assert_eq!(OptionValue::from(42_usize), OptionValue::Integer(42));
291 assert_eq!(OptionValue::from(42_isize), OptionValue::Integer(42));
292 assert_eq!(OptionValue::from(1.5_f32), OptionValue::Float(1.5));
293 assert_eq!(OptionValue::from(2.5_f64), OptionValue::Float(2.5));
294 }
295
296 #[test]
297 fn option_value_from_strings_and_collections() {
298 assert_eq!(
299 OptionValue::from("hello"),
300 OptionValue::String("hello".into())
301 );
302 assert_eq!(
303 OptionValue::from("hello".to_string()),
304 OptionValue::String("hello".into())
305 );
306 let text = "hello".to_string();
307 assert_eq!(
308 OptionValue::from(&text),
309 OptionValue::String("hello".into())
310 );
311 assert_eq!(
312 OptionValue::from(vec![1_i64, 2_i64]),
313 OptionValue::List(vec![OptionValue::Integer(1), OptionValue::Integer(2)])
314 );
315 assert_eq!(
316 OptionValue::from([1_i64, 2_i64].as_slice()),
317 OptionValue::List(vec![OptionValue::Integer(1), OptionValue::Integer(2)])
318 );
319 let options = Options::default();
320 assert_eq!(
321 OptionValue::from(options.clone()),
322 OptionValue::Map(options)
323 );
324 assert_eq!(
325 OptionValue::from(HashMap::from([("k", "v")])),
326 OptionValue::Map({
327 let mut map = Options::default();
328 map.insert("k", "v");
329 map
330 })
331 );
332 }
333}