1use std::borrow::Cow;
9use std::collections::HashSet;
10use std::hash::Hash;
11use std::path::PathBuf;
12use std::time::Duration;
13
14use minibytes::Text;
15use util::path::expand_path;
16
17use crate::Config;
18use crate::Error;
19use crate::Result;
20
21pub trait FromConfig: Sized {
22 fn try_from_str_with_config(c: &dyn Config, s: &str) -> Result<Self>;
23}
24
25pub trait FromConfigValue: Sized {
26 fn try_from_str(s: &str) -> Result<Self>;
27}
28
29impl<T: FromConfigValue> FromConfig for T {
30 fn try_from_str_with_config(_c: &dyn Config, s: &str) -> Result<Self> {
31 Self::try_from_str(s)
32 }
33}
34
35impl FromConfigValue for bool {
36 fn try_from_str(s: &str) -> Result<Self> {
37 let value = s.to_lowercase();
38 match value.as_ref() {
39 "1" | "yes" | "true" | "on" | "always" => Ok(true),
40 "0" | "no" | "false" | "off" | "never" => Ok(false),
41 _ => Err(Error::Convert(format!("invalid bool: {}", value))),
42 }
43 }
44}
45
46impl FromConfigValue for i8 {
47 fn try_from_str(s: &str) -> Result<Self> {
48 let value = s.parse()?;
49 Ok(value)
50 }
51}
52
53impl FromConfigValue for i16 {
54 fn try_from_str(s: &str) -> Result<Self> {
55 let value = s.parse()?;
56 Ok(value)
57 }
58}
59
60impl FromConfigValue for i32 {
61 fn try_from_str(s: &str) -> Result<Self> {
62 let value = s.parse()?;
63 Ok(value)
64 }
65}
66
67impl FromConfigValue for i64 {
68 fn try_from_str(s: &str) -> Result<Self> {
69 let value = s.parse()?;
70 Ok(value)
71 }
72}
73
74impl FromConfigValue for isize {
75 fn try_from_str(s: &str) -> Result<Self> {
76 let value = s.parse()?;
77 Ok(value)
78 }
79}
80
81impl FromConfigValue for u8 {
82 fn try_from_str(s: &str) -> Result<Self> {
83 let value = s.parse()?;
84 Ok(value)
85 }
86}
87
88impl FromConfigValue for u16 {
89 fn try_from_str(s: &str) -> Result<Self> {
90 let value = s.parse()?;
91 Ok(value)
92 }
93}
94
95impl FromConfigValue for u32 {
96 fn try_from_str(s: &str) -> Result<Self> {
97 let value = s.parse()?;
98 Ok(value)
99 }
100}
101
102impl FromConfigValue for u64 {
103 fn try_from_str(s: &str) -> Result<Self> {
104 let value = s.parse()?;
105 Ok(value)
106 }
107}
108
109impl FromConfigValue for usize {
110 fn try_from_str(s: &str) -> Result<Self> {
111 let value = s.parse()?;
112 Ok(value)
113 }
114}
115
116impl FromConfigValue for f32 {
117 fn try_from_str(s: &str) -> Result<Self> {
118 let value = s.parse()?;
119 Ok(value)
120 }
121}
122
123impl FromConfigValue for f64 {
124 fn try_from_str(s: &str) -> Result<Self> {
125 let value = s.parse()?;
126 Ok(value)
127 }
128}
129
130impl FromConfigValue for String {
131 fn try_from_str(s: &str) -> Result<Self> {
132 Ok(s.to_string())
133 }
134}
135
136impl FromConfigValue for Cow<'_, str> {
137 fn try_from_str(s: &str) -> Result<Self> {
138 Ok(Cow::Owned(s.to_string()))
139 }
140}
141
142#[derive(Copy, Clone, Default)]
144pub struct ByteCount(u64);
145
146impl ByteCount {
147 pub fn value(self) -> u64 {
149 self.0
150 }
151}
152
153impl From<u64> for ByteCount {
154 fn from(value: u64) -> ByteCount {
155 ByteCount(value)
156 }
157}
158
159impl FromConfigValue for ByteCount {
160 fn try_from_str(s: &str) -> Result<Self> {
161 let sizeunits = [
163 ("kb", 1u64 << 10),
164 ("mb", 1 << 20),
165 ("gb", 1 << 30),
166 ("tb", 1 << 40),
167 ("k", 1 << 10),
168 ("m", 1 << 20),
169 ("g", 1 << 30),
170 ("t", 1 << 40),
171 ("b", 1),
172 ("", 1),
173 ];
174
175 let value = s.to_lowercase();
176 for (suffix, unit) in sizeunits.iter() {
177 if value.ends_with(suffix) {
178 let number_str: &str = value[..value.len() - suffix.len()].trim();
179 let number: f64 = number_str.parse()?;
180 if number < 0.0 {
181 return Err(Error::Convert(format!(
182 "byte size '{:?}' cannot be negative",
183 value
184 )));
185 }
186 let unit = *unit as f64;
187 return Ok(ByteCount((number * unit) as u64));
188 }
189 }
190
191 Err(Error::Convert(format!(
192 "'{:?}' cannot be parsed as a byte size",
193 value
194 )))
195 }
196}
197
198impl FromConfigValue for PathBuf {
199 fn try_from_str(s: &str) -> Result<Self> {
200 Ok(expand_path(s))
201 }
202}
203
204impl FromConfigValue for Duration {
205 fn try_from_str(s: &str) -> Result<Self> {
206 Ok(Duration::from_secs_f64(s.parse()?))
207 }
208}
209
210impl<T: FromConfigValue> FromConfigValue for Vec<T> {
211 fn try_from_str(s: &str) -> Result<Self> {
212 let items = parse_list(s);
213 items.into_iter().map(|s| T::try_from_str(&s)).collect()
214 }
215}
216
217impl<T: FromConfigValue + Eq + Hash> FromConfigValue for HashSet<T> {
218 fn try_from_str(s: &str) -> Result<Self> {
219 let items = parse_list(s);
220 items.into_iter().map(|s| T::try_from_str(&s)).collect()
221 }
222}
223
224impl FromConfigValue for Vec<Text> {
225 fn try_from_str(s: &str) -> Result<Self> {
226 Ok(parse_list(s))
227 }
228}
229
230impl<T: FromConfigValue> FromConfigValue for Option<T> {
231 fn try_from_str(s: &str) -> Result<Self> {
232 T::try_from_str(s).map(Option::Some)
233 }
234}
235
236pub fn parse_list<B: AsRef<str>>(value: B) -> Vec<Text> {
257 let mut value = value.as_ref();
258
259 while [" ", ",", "\n"].iter().any(|b| value.starts_with(b)) {
260 value = &value[1..]
261 }
262
263 parse_list_internal(value)
264 .into_iter()
265 .map(Text::from)
266 .collect()
267}
268
269fn parse_list_internal(value: &str) -> Vec<String> {
270 let mut value = value;
274
275 value = value.trim_end_matches(|c| " ,\n".contains(c));
276
277 if value.is_empty() {
278 return Vec::new();
279 }
280
281 #[derive(Copy, Clone)]
282 enum State {
283 Plain,
284 Quote,
285 }
286
287 let mut offset = 0;
288 let mut parts: Vec<String> = vec![String::new()];
289 let mut state = State::Plain;
290 let value: Vec<char> = value.chars().collect();
291
292 loop {
293 match state {
294 State::Plain => {
295 let mut whitespace = false;
296 while offset < value.len() && " \n\r\t,".contains(value[offset]) {
297 whitespace = true;
298 offset += 1;
299 }
300 if offset >= value.len() {
301 break;
302 }
303 if whitespace {
304 parts.push(Default::default());
305 }
306 if value[offset] == '"' {
307 let branch = {
308 match parts.last() {
309 None => 1,
310 Some(last) => {
311 if last.is_empty() {
312 1
313 } else if last.ends_with('\\') {
314 2
315 } else {
316 3
317 }
318 }
319 }
320 }; if branch == 1 {
322 state = State::Quote;
324 offset += 1;
325 continue;
326 } else if branch == 2 {
327 let last = parts.last_mut().unwrap();
329 last.pop();
330 last.push(value[offset]);
331 offset += 1;
332 continue;
333 }
334 }
335 let last = parts.last_mut().unwrap();
336 last.push(value[offset]);
337 offset += 1;
338 }
339
340 State::Quote => {
341 if offset < value.len() && value[offset] == '"' {
342 parts.push(Default::default());
343 offset += 1;
344 while offset < value.len() && " \n\r\t,".contains(value[offset]) {
345 offset += 1;
346 }
347 state = State::Plain;
348 continue;
349 }
350 while offset < value.len() && value[offset] != '"' {
351 if value[offset] == '\\' && offset + 1 < value.len() && value[offset + 1] == '"'
352 {
353 offset += 1;
354 parts.last_mut().unwrap().push('"');
355 } else {
356 parts.last_mut().unwrap().push(value[offset]);
357 }
358 offset += 1;
359 }
360 if offset >= value.len() {
361 let mut real_parts: Vec<String> = parse_list_internal(parts.last().unwrap());
362 if real_parts.is_empty() {
363 parts.pop();
364 parts.push("\"".to_string());
365 } else {
366 real_parts[0].insert(0, '"');
367 parts.pop();
368 parts.append(&mut real_parts);
369 }
370 break;
371 }
372 offset += 1;
373 while offset < value.len() && " ,".contains(value[offset]) {
374 offset += 1;
375 }
376 if offset < value.len() {
377 if offset + 1 == value.len() && value[offset] == '"' {
378 parts.last_mut().unwrap().push('"');
379 offset += 1;
380 } else {
381 parts.push(Default::default());
382 }
383 } else {
384 break;
385 }
386 state = State::Plain;
387 }
388 }
389 }
390
391 parts
392}
393
394#[cfg(test)]
395mod tests {
396 use super::*;
397
398 #[test]
399 fn test_parse_list() {
400 fn b<B: AsRef<str>>(bytes: B) -> Text {
401 Text::copy_from_slice(bytes.as_ref())
402 }
403
404 assert_eq!(parse_list("foo"), vec![b("foo")]);
406 assert_eq!(
407 parse_list("foo bar baz"),
408 vec![b("foo"), b("bar"), b("baz")]
409 );
410 assert_eq!(parse_list("alice, bob"), vec![b("alice"), b("bob")]);
411 assert_eq!(
412 parse_list("foo bar baz alice, bob"),
413 vec![b("foo"), b("bar"), b("baz"), b("alice"), b("bob")]
414 );
415 assert_eq!(
416 parse_list("abc d\"ef\"g \"hij def\""),
417 vec![b("abc"), b("d\"ef\"g"), b("hij def")]
418 );
419 assert_eq!(
420 parse_list("\"hello world\", \"how are you?\""),
421 vec![b("hello world"), b("how are you?")]
422 );
423 assert_eq!(
424 parse_list("Do\"Not\"Separate"),
425 vec![b("Do\"Not\"Separate")]
426 );
427 assert_eq!(parse_list("\"Do\"Separate"), vec![b("Do"), b("Separate")]);
428 assert_eq!(
429 parse_list("\"Do\\\"NotSeparate\""),
430 vec![b("Do\"NotSeparate")]
431 );
432 assert_eq!(
433 parse_list("string \"with extraneous\" quotation mark\""),
434 vec![
435 b("string"),
436 b("with extraneous"),
437 b("quotation"),
438 b("mark\""),
439 ]
440 );
441 assert_eq!(parse_list("x, y"), vec![b("x"), b("y")]);
442 assert_eq!(parse_list("\"x\", \"y\""), vec![b("x"), b("y")]);
443 assert_eq!(
444 parse_list("\"\"\" key = \"x\", \"y\" \"\"\""),
445 vec![b(""), b(" key = "), b("x\""), b("y"), b(""), b("\"")]
446 );
447 assert_eq!(parse_list(",,,, "), Vec::<Text>::new());
448 assert_eq!(
449 parse_list("\" just with starting quotation"),
450 vec![b("\""), b("just"), b("with"), b("starting"), b("quotation")]
451 );
452 assert_eq!(
453 parse_list("\"longer quotation\" with \"no ending quotation"),
454 vec![
455 b("longer quotation"),
456 b("with"),
457 b("\"no"),
458 b("ending"),
459 b("quotation"),
460 ]
461 );
462 assert_eq!(
463 parse_list("this is \\\" \"not a quotation mark\""),
464 vec![b("this"), b("is"), b("\""), b("not a quotation mark")]
465 );
466 assert_eq!(parse_list("\n \n\nding\ndong"), vec![b("ding"), b("dong")]);
467
468 assert_eq!(parse_list("a,b,,c"), vec![b("a"), b("b"), b("c")]);
470 assert_eq!(parse_list("a b c"), vec![b("a"), b("b"), b("c")]);
471 assert_eq!(
472 parse_list(" , a , , b, , c , "),
473 vec![b("a"), b("b"), b("c")]
474 );
475 assert_eq!(parse_list("a,\"b,c\" d"), vec![b("a"), b("b,c"), b("d")]);
476 assert_eq!(parse_list("a,\",c"), vec![b("a"), b("\""), b("c")]);
477 assert_eq!(parse_list("a,\" c\" \""), vec![b("a"), b(" c\"")]);
478 assert_eq!(
479 parse_list("a,\" c\" \" d"),
480 vec![b("a"), b(" c"), b("\""), b("d")]
481 );
482 }
483}