1#![cfg_attr(coverage_nightly, feature(coverage_attribute))]
2use std::{
3 any::Any,
4 cmp::Ordering,
5 collections::{BTreeMap, HashMap, HashSet},
6 ffi::OsString,
7 net::{IpAddr, Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, SocketAddrV4, SocketAddrV6},
8 path::PathBuf,
9 str::FromStr,
10 time::Duration,
11};
12
13use crate::{ConfigContext, FromConfig, err::ConfigError};
14
15#[derive(Debug)]
36pub enum ConfigValue<'a> {
37 StrRef(&'a str),
39 Str(String),
41 Int(i64),
43 Float(f64),
45 Bool(bool),
47 #[cfg(feature = "rand")]
48 Rand(RandValue),
50}
51
52#[doc(hidden)]
53#[cfg(feature = "rand")]
54#[derive(Debug, Clone, Copy)]
55pub enum RandValue {
56 U8,
57 U16,
58 U32,
59 U64,
60 U128,
61 Usize,
62 I8,
63 I16,
64 I32,
65 I64,
66 I128,
67 Isize,
68}
69
70impl ConfigValue<'_> {
71 pub(crate) fn clone_static(&self) -> ConfigValue<'static> {
72 match self {
73 ConfigValue::StrRef(v) => ConfigValue::Str(v.to_string()),
74 ConfigValue::Str(v) => ConfigValue::Str(v.to_string()),
75 ConfigValue::Int(v) => ConfigValue::Int(*v),
76 ConfigValue::Float(v) => ConfigValue::Float(*v),
77 ConfigValue::Bool(v) => ConfigValue::Bool(*v),
78 #[cfg(feature = "rand")]
79 ConfigValue::Rand(v) => ConfigValue::Rand(*v),
80 }
81 }
82}
83
84impl From<String> for ConfigValue<'_> {
85 fn from(v: String) -> Self {
86 ConfigValue::Str(v)
87 }
88}
89
90impl<'a> From<&'a str> for ConfigValue<'a> {
91 fn from(c: &'a str) -> Self {
92 ConfigValue::StrRef(c)
93 }
94}
95
96macro_rules! into_config_value_le {
97 ($f:ident=$t:ident: $($x:ident),*) => {$(
98 impl From<$x> for ConfigValue<'_> {
99 #[allow(trivial_numeric_casts)]
100 fn from(c: $x) -> Self {
101 ConfigValue::$f(c as $t)
102 }
103 })*
104 };
105}
106
107into_config_value_le!(Int = i64: u8, u16, u32, i8, i16, i32, i64);
108into_config_value_le!(Float = f64: f32, f64);
109
110macro_rules! into_config_value_u {
111 ($($x:ident),*) => {$(
112 impl From<$x> for ConfigValue<'_> {
113 fn from(c: $x) -> Self {
114 if c <= i64::MAX as $x {
115 return ConfigValue::Int(c as i64);
116 }
117 ConfigValue::Str(c.to_string())
118 }
119 })*
120 };
121}
122
123into_config_value_u!(u64, u128, usize);
124
125macro_rules! into_config_value {
126 ($($x:ident),*) => {$(
127 impl From<$x> for ConfigValue<'_> {
128 fn from(c: $x) -> Self {
129 if c <= i64::MAX as $x && c>= i64::MIN as $x {
130 return ConfigValue::Int(c as i64);
131 }
132 ConfigValue::Str(c.to_string())
133 }
134 })*
135 };
136}
137
138into_config_value!(i128, isize);
139
140impl From<bool> for ConfigValue<'_> {
141 fn from(v: bool) -> Self {
142 ConfigValue::Bool(v)
143 }
144}
145
146#[cfg(feature = "rand")]
147impl From<RandValue> for ConfigValue<'_> {
148 fn from(v: RandValue) -> Self {
149 ConfigValue::Rand(v)
150 }
151}
152
153impl FromConfig for () {
154 fn from_config(
155 _: &mut ConfigContext<'_>,
156 _: Option<ConfigValue<'_>>,
157 ) -> Result<Self, ConfigError> {
158 Ok(())
159 }
160}
161
162impl<V: FromConfig> FromConfig for Result<V, ConfigError> {
163 fn from_config(
164 context: &mut ConfigContext<'_>,
165 value: Option<ConfigValue<'_>>,
166 ) -> Result<Self, ConfigError> {
167 Ok(V::from_config(context, value))
168 }
169}
170
171impl<V: FromConfig> FromConfig for Option<V> {
172 fn from_config(
173 context: &mut ConfigContext<'_>,
174 value: Option<ConfigValue<'_>>,
175 ) -> Result<Self, ConfigError> {
176 match V::from_config(context, value) {
177 Err(ConfigError::ConfigNotFound(_)) => Ok(None),
178 Err(err) => Err(err),
179 Ok(v) => Ok(Some(v)),
180 }
181 }
182}
183
184impl<V: FromConfig> FromConfig for Vec<V> {
185 #[inline]
186 fn from_config(
187 context: &mut ConfigContext<'_>,
188 _: Option<ConfigValue<'_>>,
189 ) -> Result<Self, ConfigError> {
190 let mut vs = vec![];
191 let list = context.collect_keys();
192 if let Some(v) = list.int_key {
193 for i in 0..v {
194 vs.push(context.do_parse_config(i, None, &mut HashSet::new())?);
195 }
196 }
197 Ok(vs)
198 }
199}
200
201macro_rules! impl_map_hash {
202 ($name:ident :: $($ctor:tt)+) => {
204 impl<V: FromConfig, S: std::hash::BuildHasher + Default> FromConfig for $name<String, V, S> {
205 #[inline]
206 fn from_config(
207 context: &mut ConfigContext<'_>,
208 _: Option<ConfigValue<'_>>,
209 ) -> Result<Self, ConfigError> {
210 let mut vs = $name:: $($ctor)+;
211 let list = context.collect_keys();
212 for k in list.str_key {
213 vs.insert(k.to_string(), context.parse_config(k, None)?);
214 }
215 Ok(vs)
216 }
217 }
218 };
219}
220
221impl_map_hash!(HashMap::with_hasher(Default::default()));
222
223macro_rules! impl_map {
224 ($name:ident :: $($ctor:tt)+) => {
226 impl<V: FromConfig> FromConfig for $name<String, V> {
227 #[inline]
228 fn from_config(
229 context: &mut ConfigContext<'_>,
230 _: Option<ConfigValue<'_>>,
231 ) -> Result<Self, ConfigError> {
232 let mut vs = $name:: $($ctor)+;
233 let list = context.collect_keys();
234 for k in list.str_key {
235 vs.insert(k.to_string(), context.parse_config(k, None)?);
236 }
237 Ok(vs)
238 }
239 }
240 };
241}
242
243impl_map!(BTreeMap::new());
244
245#[doc(hidden)]
246pub trait FromValue: Sized {
247 fn from_value(
248 context: &mut ConfigContext<'_>,
249 value: ConfigValue<'_>,
250 ) -> Result<Self, ConfigError>;
251
252 #[inline]
253 fn empty_value(context: &mut ConfigContext<'_>) -> Result<Self, ConfigError> {
254 Err(context.not_found())
255 }
256}
257
258impl<V: FromValue> FromConfig for V {
259 #[inline]
260 fn from_config(
261 context: &mut ConfigContext<'_>,
262 value: Option<ConfigValue<'_>>,
263 ) -> Result<Self, ConfigError> {
264 match value {
265 None => Err(context.not_found()),
266 Some(ConfigValue::Str(v)) if v.is_empty() => Self::empty_value(context),
267 Some(ConfigValue::StrRef("")) => Self::empty_value(context),
268 Some(val) => V::from_value(context, val),
269 }
270 }
271}
272
273impl FromValue for String {
274 #[inline]
275 fn from_value(
276 context: &mut ConfigContext<'_>,
277 value: ConfigValue<'_>,
278 ) -> Result<Self, ConfigError> {
279 let v = match value {
280 ConfigValue::StrRef(s) => s.to_owned(),
281 ConfigValue::Str(s) => s,
282 ConfigValue::Int(s) => s.to_string(),
283 ConfigValue::Float(s) => check_f64(context, s)?.to_string(),
284 ConfigValue::Bool(s) => s.to_string(),
285 #[cfg(feature = "rand")]
286 _ => return Err(context.parse_error("ConfigValueError")),
287 };
288 Ok(v)
289 }
290
291 #[inline]
292 fn empty_value(_: &mut ConfigContext<'_>) -> Result<Self, ConfigError> {
293 Ok("".to_owned())
294 }
295}
296
297pub trait FromStringValue: Sized + Any {
299 fn from_str_value(context: &mut ConfigContext<'_>, value: &str) -> Result<Self, ConfigError>;
301}
302
303impl<V: FromStringValue> FromValue for V {
304 #[inline]
305 fn from_value(
306 context: &mut ConfigContext<'_>,
307 value: ConfigValue<'_>,
308 ) -> Result<Self, ConfigError> {
309 match value {
310 ConfigValue::StrRef(s) => V::from_str_value(context, s),
311 ConfigValue::Str(s) => V::from_str_value(context, &s),
312 value => Err(context.type_mismatch::<V>(&value)),
313 }
314 }
315}
316
317#[inline]
318fn bool_from_str_value(context: &mut ConfigContext<'_>, value: &str) -> Result<bool, ConfigError> {
319 match &value.to_lowercase()[..] {
320 "true" | "yes" => Ok(true),
321 "false" | "no" => Ok(false),
322 _ => Err(context.parse_error(value)),
323 }
324}
325
326impl FromValue for bool {
327 #[inline]
328 fn from_value(
329 context: &mut ConfigContext<'_>,
330 value: ConfigValue<'_>,
331 ) -> Result<Self, ConfigError> {
332 match value {
333 ConfigValue::StrRef(s) => bool_from_str_value(context, s),
334 ConfigValue::Str(s) => bool_from_str_value(context, &s),
335 ConfigValue::Bool(s) => Ok(s),
336 value => Err(context.type_mismatch::<bool>(&value)),
337 }
338 }
339}
340
341macro_rules! impl_str_value {
342 ($($x:ident),+) => {$(
343impl FromStringValue for $x {
344 #[inline]
345 fn from_str_value(_: &mut ConfigContext<'_>, value: &str) -> Result<Self, ConfigError> {
346 use std::str::FromStr;
347 <$x>::from_str(value).map_err(ConfigError::from_cause)
348 }
349}
350 )+}
351}
352
353impl_str_value!(
354 Ipv4Addr,
355 Ipv6Addr,
356 IpAddr,
357 SocketAddrV4,
358 SocketAddrV6,
359 SocketAddr,
360 PathBuf,
361 OsString
362);
363
364#[allow(missing_debug_implementations)]
366pub struct FromStrHolder<V>(pub V);
367
368impl<V: FromStr<Err = E> + 'static, E: std::error::Error + 'static> FromStringValue
369 for FromStrHolder<V>
370{
371 #[inline]
372 fn from_str_value(_: &mut ConfigContext<'_>, value: &str) -> Result<Self, ConfigError> {
373 Ok(FromStrHolder(
374 V::from_str(value).map_err(ConfigError::from_cause)?,
375 ))
376 }
377}
378
379macro_rules! impl_integer {
380 ($($x:ident),+) => {$(
381impl FromValue for $x {
382 #[inline]
383 fn from_value(context: &mut ConfigContext<'_>, value: ConfigValue<'_>) -> Result<Self, ConfigError> {
384 use std::convert::TryFrom;
385 match value {
386 ConfigValue::StrRef(s) => Ok(s.parse::<$x>().map_err(ConfigError::from_cause)?),
387 ConfigValue::Str(s) => Ok(s.parse::<$x>().map_err(ConfigError::from_cause)?),
388 ConfigValue::Int(s) => Ok($x::try_from(s).map_err(ConfigError::from_cause)?),
389 ConfigValue::Float(s) => Ok(check_f64(context, s)? as $x),
390 _ => Err(context.type_mismatch::<$x>(&value)),
391 }
392 }
393}
394 )+};
395}
396
397impl_integer!(
398 i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize
399);
400
401#[inline]
402fn check_f64(context: &mut ConfigContext<'_>, f: f64) -> Result<f64, ConfigError> {
403 if f.is_finite() {
404 Ok(f)
405 } else {
406 Err(context.parse_error("infinite"))
407 }
408}
409macro_rules! impl_float {
410 ($($x:ident),+) => {$(
411impl FromValue for $x {
412 #[inline]
413 #[allow(trivial_numeric_casts)]
414 fn from_value(context: &mut ConfigContext<'_>, value: ConfigValue<'_>) -> Result<Self, ConfigError> {
415 match value {
416 ConfigValue::StrRef(s) => Ok(s.parse::<$x>().map_err(ConfigError::from_cause)?),
417 ConfigValue::Str(s) => Ok(s.parse::<$x>().map_err(ConfigError::from_cause)?),
418 ConfigValue::Int(s) => Ok(s as $x),
419 ConfigValue::Float(s) => Ok(check_f64(context, s)? as $x),
420 _ => Err(context.type_mismatch::<$x>(&value)),
421 }
422 }
423}
424 )+};
425}
426
427impl_float!(f32, f64);
428
429#[inline]
430fn parse_duration_from_str(
431 context: &mut ConfigContext<'_>,
432 du: &str,
433) -> Result<Duration, ConfigError> {
434 let mut i = 0;
435 let mut multi = 1;
436 let mut last = None;
437 for c in du.chars().rev() {
438 match c {
439 'h' | 'm' | 's' if last.is_none() => {
440 if c == 'm' {
441 last = Some('M');
442 } else {
443 last = Some(c);
444 }
445 }
446 'm' | 'u' | 'n' if last == Some('s') => {
447 last = Some(c);
448 }
449 c if c.is_ascii_digit() => {
450 if last.is_none() {
451 last = Some('s');
452 }
453 i += multi * (c as u64 - '0' as u64);
454 multi *= 10;
455 }
456 _ => return Err(context.parse_error(du)),
457 }
458 }
459 Ok(match last.unwrap_or('s') {
460 'h' => Duration::new(i * 3600, 0),
461 'M' => Duration::new(i * 60, 0),
462 's' => Duration::from_secs(i),
463 'm' => Duration::from_millis(i),
464 'u' => Duration::from_micros(i),
465 'n' => Duration::from_nanos(i),
466 _ => return Err(context.parse_error(du)),
467 })
468}
469
470impl FromValue for Duration {
471 fn from_value(
472 context: &mut ConfigContext<'_>,
473 value: ConfigValue<'_>,
474 ) -> Result<Self, ConfigError> {
475 match value {
476 ConfigValue::Str(du) => parse_duration_from_str(context, &du),
477 ConfigValue::StrRef(du) => parse_duration_from_str(context, du),
478 ConfigValue::Int(seconds) => Ok(Duration::from_secs(seconds as u64)),
479 ConfigValue::Float(sec) => Ok(Duration::new(1, 0).mul_f64(sec)),
480 _ => Err(context.type_mismatch::<Self>(&value)),
481 }
482 }
483}
484
485#[macro_export]
495macro_rules! impl_enum {
496 ($x:path {$($($k:pat_param)|* => $v:expr)+ }) => {
497 impl $crate::FromStringValue for $x {
498 fn from_str_value(context: &mut $crate::ConfigContext<'_>, value: &str) -> Result<Self, $crate::ConfigError> {
499 match &value.to_lowercase()[..] {
500 $($($k)|* => Ok($v),)+
501 _ => Err(context.parse_error(value)),
502 }
503 }
504 }
505 }
506}
507
508impl_enum!(Shutdown{
509 "read" => Shutdown::Read
510 "write" => Shutdown::Write
511 "both" => Shutdown::Both
512});
513
514impl_enum!(Ordering{
515 "lt" | "less" => Ordering::Less
516 "eq" | "equal" => Ordering::Equal
517 "gt" | "greater" => Ordering::Greater
518});
519
520#[cfg(feature = "log")]
521#[doc(hidden)]
522pub mod log {
523
524 use log::*;
525
526 impl_enum!(LevelFilter {
527 "off" => LevelFilter::Off
528 "trace" => LevelFilter::Trace
529 "debug" => LevelFilter::Debug
530 "info" => LevelFilter::Info
531 "warn" => LevelFilter::Warn
532 "error" => LevelFilter::Error
533 });
534
535 impl_enum!(Level {
536 "trace" => Level::Trace
537 "debug" => Level::Debug
538 "info" => Level::Info
539 "warn" => Level::Warn
540 "error" => Level::Error
541 });
542}
543
544#[cfg(feature = "coarsetime")]
545#[doc(hidden)]
546pub mod time {
547
548 impl crate::FromValue for coarsetime::Duration {
549 fn from_value(
550 context: &mut crate::ConfigContext<'_>,
551 value: crate::ConfigValue<'_>,
552 ) -> Result<Self, crate::ConfigError> {
553 std::time::Duration::from_value(context, value).map(|d| d.into())
554 }
555 }
556}
557
558#[cfg_attr(coverage_nightly, coverage(off))]
559#[cfg(test)]
560mod test {
561 use crate::{Configuration, key::CacheString};
562
563 use super::*;
564
565 struct TestContext(Configuration, CacheString);
566
567 impl TestContext {
568 fn new() -> Self {
569 Self(Configuration::new(), CacheString::new())
570 }
571
572 #[allow(single_use_lifetimes)]
573 fn read<'a, T: FromConfig>(
574 &mut self,
575 val: impl Into<ConfigValue<'a>>,
576 ) -> Result<T, ConfigError> {
577 T::from_config(
578 &mut self.0.source.new_context(&mut self.1),
579 Some(val.into()),
580 )
581 }
582 }
583
584 macro_rules! should_eq {
585 ($context:ident: $val:literal as $y:ty => $x:expr ) => {
586 let value: Result<$y, ConfigError> = $context.read($val);
587 assert_eq!($x, value.unwrap());
588 };
589 ($context:ident: $val:ident as $y:ty => $x:expr) => {
590 let value: Result<$y, ConfigError> = $context.read($val);
591 assert_eq!($x, value.unwrap());
592 };
593 }
594 macro_rules! should_err {
595 ($context:ident: $val:literal as $x:ty) => {
596 let value: Result<$x, ConfigError> = $context.read($val);
597 assert_eq!(true, value.is_err());
598 };
599 }
600 macro_rules! should_valid {
601 ($context:ident: $val:ident as $x:ty => $expr:expr) => {
602 let value: Result<$x, ConfigError> = $context.read($val);
604 assert_eq!($expr, value.is_ok());
605 };
606 }
607
608 macro_rules! should_option {
609 ($context:ident: $val:literal as $x:ty => $expr:expr) => {
610 let v = $context.0.get::<Option<$x>>($val);
611 assert_eq!($expr, v.unwrap());
612 };
613 }
614
615 #[test]
616 fn option_tests() {
617 let context = TestContext::new();
618 should_option!(context: "key" as u8 => None);
619 should_option!(context: "key" as u16 => None);
620 should_option!(context: "key" as u32 => None);
621 should_option!(context: "key" as u64 => None);
622 should_option!(context: "key" as u128 => None);
623 should_option!(context: "key" as usize => None);
624 should_option!(context: "key" as i8 => None);
625 should_option!(context: "key" as i16 => None);
626 should_option!(context: "key" as i32 => None);
627 should_option!(context: "key" as i64 => None);
628 should_option!(context: "key" as i128 => None);
629 should_option!(context: "key" as isize => None);
630 should_option!(context: "key" as String => None);
631 should_option!(context: "key" as bool => None);
632 }
633
634 #[test]
635 fn bool_tests() {
636 let mut context = TestContext::new();
637
638 should_eq!(context: "yes" as bool => true);
639 should_eq!(context: "true" as bool => true);
640 should_eq!(context: "no" as bool => false);
641 should_eq!(context: "false" as bool => false);
642
643 should_err!(context: "x" as bool);
644 should_err!(context: "n" as bool);
645 should_err!(context: "f" as bool);
646 should_err!(context: "y" as bool);
647 should_err!(context: "t" as bool);
648 should_err!(context: 0u64 as bool);
649 should_err!(context: 1u64 as bool);
650 should_err!(context: 0.0f64 as bool);
651 should_err!(context: 1.0f64 as bool);
652 }
653
654 #[quickcheck]
655 fn num_tests(i: i64) {
656 let mut context = TestContext::new();
657 let y = format!("{}", i);
658 should_eq!(context: y as i64 => i);
659 should_eq!(context: i as i64 => i);
660 }
661
662 macro_rules! num_into_test {
663 ($($fun:ident. $t:ty,)+) => {
664 $(
665 #[quickcheck]
666 fn $fun(i: $t) {
667 let v: ConfigValue<'static> = i.into();
668 match v {
669 ConfigValue::Int(_) => {}
670 _ => assert_eq!(true, false),
671 }
672 }
673 )+
674 };
675 }
676
677 num_into_test!(
678 u8_test.u8,
679 u16_test.u16,
680 u32_test.u32,
681 i8_test.i8,
682 i16_test.i16,
683 i32_test.i32,
684 i64_test.i64,
685 );
686
687 macro_rules! num_into_test_u {
688 ($($fun:ident. $t:ty),+) => {
689 $(
690 #[quickcheck]
691 fn $fun(i: $t) {
692 let v: ConfigValue<'static> = i.into();
693 match v {
694 ConfigValue::Int(_) => assert_eq!(true, i <= i64::MAX as $t),
695 ConfigValue::Str(_) => assert_eq!(true, i > i64::MAX as $t),
696 _ => assert_eq!(true, false),
697 }
698 }
699 )+
700 };
701 }
702
703 num_into_test_u!(u64_test.u64, u128_test.u128, usize_test.usize);
704
705 macro_rules! num_into_test_i {
706 ($($fun:ident. $t:ty),+) => {
707 $(
708 #[quickcheck]
709 fn $fun(i: $t) {
710 let v: ConfigValue<'static> = i.into();
711 match v {
712 ConfigValue::Int(_) => assert_eq!(true, i <= i64::MAX as $t && i>= i64::MIN as $t),
713 ConfigValue::Str(_) => assert_eq!(true, i > i64::MAX as $t || i< i64::MIN as $t),
714 _ => assert_eq!(true, false),
715 }
716 }
717 )+
718 };
719 }
720
721 num_into_test_i!(i128_test.i128, isize_test.isize);
722
723 #[quickcheck]
724 fn i64_tests(i: i64) {
725 let mut context = TestContext::new();
726 should_valid!(context: i as u8 => i >= 0 && i <= (u8::MAX as i64));
727 should_valid!(context: i as u16 => i >= 0 && i <= (u16::MAX as i64));
728 should_valid!(context: i as u32 => i >= 0 && i <= (u32::MAX as i64));
729 should_valid!(context: i as u64 => i>=0);
730 should_valid!(context: i as u128 => i>=0);
731 should_valid!(context: i as i8 => i >= 0 && i <= (i8::MAX as i64));
732 should_valid!(context: i as i16 => i >= 0 && i <= (i16::MAX as i64));
733 should_valid!(context: i as i32 => i >= 0 && i <= (i32::MAX as i64));
734 should_valid!(context: i as i64 => true);
735 should_valid!(context: i as i128 => true);
736 should_valid!(context: i as f32 => true);
737 should_valid!(context: i as f64 => true);
738 }
739
740 #[quickcheck]
741 fn f64_tests(i: f64) {
742 let mut context = TestContext::new();
743 should_valid!(context: i as u8 => i.is_finite());
744 should_valid!(context: i as u16 => i.is_finite());
745 should_valid!(context: i as u32 => i.is_finite());
746 should_valid!(context: i as u64 => i.is_finite());
747 should_valid!(context: i as u128 => i.is_finite());
748 should_valid!(context: i as i8 => i.is_finite());
749 should_valid!(context: i as i16 => i.is_finite());
750 should_valid!(context: i as i32 => i.is_finite());
751 should_valid!(context: i as i64 => i.is_finite());
752 should_valid!(context: i as i128 => i.is_finite());
753 should_valid!(context: i as f32 => i.is_finite());
754 should_valid!(context: i as f64 => i.is_finite());
755 }
756
757 #[test]
758 fn duration_test() {
759 let mut context = TestContext::new();
760 should_eq!(context: "123" as Duration => Duration::new(123, 0));
761 should_eq!(context: "123s" as Duration => Duration::new(123, 0));
762 should_eq!(context: "10m" as Duration => Duration::new(10 * 60, 0));
763 should_eq!(context: "123h" as Duration => Duration::new(123 * 3600, 0));
764 should_eq!(context: "123ms" as Duration => Duration::new(0, 123 * 1_000_000));
765 should_eq!(context: "123us" as Duration => Duration::new(0, 123 * 1000));
766 should_eq!(context: "123ns" as Duration => Duration::new(0, 123));
767 should_eq!(context: "1000ms" as Duration => Duration::new(1, 0));
768
769 #[cfg(feature = "coarsetime")]
770 {
771 use coarsetime::Duration as CoarseDuration;
772 should_eq!(context: "123" as CoarseDuration => CoarseDuration::new(123, 0));
773 should_eq!(context: "123s" as CoarseDuration => CoarseDuration::new(123, 0));
774 should_eq!(context: "10m" as CoarseDuration => CoarseDuration::new(10 * 60, 0));
775 should_eq!(context: "123h" as CoarseDuration => CoarseDuration::new(123 * 3600, 0));
776 should_eq!(context: "123ms" as CoarseDuration => CoarseDuration::new(0, 123 * 1_000_000));
777 should_eq!(context: "123us" as CoarseDuration => CoarseDuration::new(0, 123 * 1000));
778 should_eq!(context: "123ns" as CoarseDuration => CoarseDuration::new(0, 123));
779 should_eq!(context: "1000ms" as CoarseDuration => CoarseDuration::new(1, 0));
780 }
781 }
782
783 #[test]
784 fn net_test() {
785 let mut context = TestContext::new();
786 should_eq!(context: "127.0.0.1" as Ipv4Addr => Ipv4Addr::new(127, 0, 0, 1));
787 should_eq!(context: "::1" as Ipv6Addr => Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
788 should_eq!(context: "127.0.0.1:80" as SocketAddrV4 => SocketAddrV4::new(Ipv4Addr::new(127,0,0,1), 80));
789 let mut buf = PathBuf::new();
790 buf.push("/var");
791 should_eq!(context: "/var" as PathBuf => buf);
792 }
793
794 #[test]
795 #[allow(unused_qualifications)]
796 fn option_test() {
797 let mut context = TestContext::new();
798 let x: Result<Option<Ordering>, ConfigError> = context.read("val");
799 assert!(x.is_err());
800 match x.unwrap_err() {
801 ConfigError::ConfigParseError(_, _) => {}
802 _ => assert_eq!(true, false),
803 }
804 }
805
806 #[test]
807 #[allow(unused_qualifications)]
808 fn into_string_test() {
809 let mut context = TestContext::new();
810 let v: String = context.read(1.1f64).unwrap();
811 assert_eq!("1.1", v);
812 let v: String = context.read(1u8).unwrap();
813 assert_eq!("1", v);
814 let v: String = context.read(true).unwrap();
815 assert_eq!("true", v);
816 }
817
818 #[test]
819 #[allow(unused_qualifications)]
820 fn map_test() {
821 let mut context = TestContext::new();
822 let x: Result<BTreeMap<String, bool>, ConfigError> = context.read("val");
823 assert!(x.is_ok());
824 assert!(x.unwrap().is_empty());
825 let x: Result<HashMap<String, bool>, ConfigError> = context.read("val");
826 assert!(x.is_ok());
827 assert!(x.unwrap().is_empty());
828 }
829
830 #[test]
831 fn hash_map_with_hasher_test() {
832 let mut context = TestContext::new();
833 let x: Result<HashMap<String, bool>, ConfigError> = context.read("val");
834 assert!(x.is_ok());
835 assert!(x.unwrap().is_empty());
836 }
837
838 #[test]
839 #[allow(unused_qualifications)]
840 fn config_value_clone_static_works() {
841 let v1 = ConfigValue::StrRef("abc");
842 let v2 = v1.clone_static();
843 match v2 {
844 ConfigValue::Str(ref s) => assert_eq!(s, "abc"),
845 _ => panic!("Expected Str variant"),
846 }
847
848 let v1 = ConfigValue::Str("def".to_string());
849 let v2 = v1.clone_static();
850 match v2 {
851 ConfigValue::Str(ref s) => assert_eq!(s, "def"),
852 _ => panic!("Expected Str variant"),
853 }
854
855 let v1 = ConfigValue::Int(42);
856 let v2 = v1.clone_static();
857 match v2 {
858 ConfigValue::Int(i) => assert_eq!(i, 42),
859 _ => panic!("Expected Int variant"),
860 }
861
862 let v1 = ConfigValue::Float(3.14);
863 let v2 = v1.clone_static();
864 match v2 {
865 ConfigValue::Float(f) => assert!((f - 3.14).abs() < 1e-6),
866 _ => panic!("Expected Float variant"),
867 }
868
869 let v1 = ConfigValue::Bool(true);
870 let v2 = v1.clone_static();
871 match v2 {
872 ConfigValue::Bool(b) => assert!(b),
873 _ => panic!("Expected Bool variant"),
874 }
875
876 #[cfg(feature = "rand")]
877 {
878 let v1 = ConfigValue::Rand(crate::value::RandValue::U8);
879 let v2 = v1.clone_static();
880 match v2 {
881 ConfigValue::Rand(crate::value::RandValue::U8) => {}
882 _ => panic!("Expected Rand(U8) variant"),
883 }
884 }
885 }
886
887 #[test]
888 fn from_config_for_unit_type() {
889 let mut context = TestContext::new();
890 let v: Result<(), ConfigError> =
892 <()>::from_config(&mut context.0.source.new_context(&mut context.1), None);
893 assert!(v.is_ok());
894 let v: Result<(), ConfigError> = <()>::from_config(
896 &mut context.0.source.new_context(&mut context.1),
897 Some(ConfigValue::Int(1)),
898 );
899 assert!(v.is_ok());
900 }
901
902 #[test]
903 fn from_value_for_from_string_value() {
904 struct Dummy;
905 impl FromStr for Dummy {
906 type Err = std::convert::Infallible;
907 fn from_str(_s: &str) -> Result<Self, Self::Err> {
908 Ok(Dummy)
909 }
910 }
911 impl FromStringValue for Dummy {
912 fn from_str_value(
913 _context: &mut ConfigContext<'_>,
914 _value: &str,
915 ) -> Result<Self, ConfigError> {
916 Ok(Dummy)
917 }
918 }
919
920 let mut context = TestContext::new();
921 let v = <Dummy as FromValue>::from_value(
923 &mut context.0.source.new_context(&mut context.1),
924 ConfigValue::StrRef("abc"),
925 );
926 assert!(v.is_ok());
927
928 let v = <Dummy as FromValue>::from_value(
930 &mut context.0.source.new_context(&mut context.1),
931 ConfigValue::Str("abc".to_string()),
932 );
933 assert!(v.is_ok());
934
935 let v = <Dummy as FromValue>::from_value(
937 &mut context.0.source.new_context(&mut context.1),
938 ConfigValue::Int(1),
939 );
940 assert!(v.is_err());
941 }
942
943 #[test]
944 fn from_value_for_bool() {
945 let mut context = TestContext::new();
946
947 let v = <bool as FromValue>::from_value(
949 &mut context.0.source.new_context(&mut context.1),
950 ConfigValue::StrRef("true"),
951 );
952 assert!(v.unwrap());
953 let v = <bool as FromValue>::from_value(
954 &mut context.0.source.new_context(&mut context.1),
955 ConfigValue::StrRef("no"),
956 );
957 assert_eq!(v.unwrap(), false);
958
959 let v = <bool as FromValue>::from_value(
961 &mut context.0.source.new_context(&mut context.1),
962 ConfigValue::Str("yes".to_string()),
963 );
964 assert_eq!(v.unwrap(), true);
965 let v = <bool as FromValue>::from_value(
966 &mut context.0.source.new_context(&mut context.1),
967 ConfigValue::Str("false".to_string()),
968 );
969 assert_eq!(v.unwrap(), false);
970
971 let v = <bool as FromValue>::from_value(
973 &mut context.0.source.new_context(&mut context.1),
974 ConfigValue::Bool(true),
975 );
976 assert_eq!(v.unwrap(), true);
977
978 let v = <bool as FromValue>::from_value(
980 &mut context.0.source.new_context(&mut context.1),
981 ConfigValue::Int(1),
982 );
983 assert!(v.is_err());
984 }
985
986 #[test]
987 fn from_str_holder_from_string_value() {
988 type Holder = FromStrHolder<u32>;
989
990 let mut context = TestContext::new();
991 let r = Holder::from_str_value(&mut context.0.source.new_context(&mut context.1), "123");
993 assert!(r.is_ok());
994 assert_eq!(r.unwrap().0, 123u32);
995
996 let r = Holder::from_str_value(&mut context.0.source.new_context(&mut context.1), "abc");
998 assert!(r.is_err());
999 match r {
1001 Err(ConfigError::ConfigCause(_)) => {}
1002 _ => panic!("Expected ConfigCause"),
1003 }
1004 }
1005
1006 #[test]
1007 fn from_value_for_integer_types() {
1008 let mut context = TestContext::new();
1009
1010 macro_rules! check_int {
1011 ($ty:ty, $val:expr, $expect:expr) => {
1012 let v = <$ty as FromValue>::from_value(
1013 &mut context.0.source.new_context(&mut context.1),
1014 ConfigValue::Int($val),
1015 );
1016 assert_eq!(v.unwrap(), $expect);
1017
1018 let v = <$ty as FromValue>::from_value(
1019 &mut context.0.source.new_context(&mut context.1),
1020 ConfigValue::StrRef(&$val.to_string()),
1021 );
1022 assert_eq!(v.unwrap(), $expect);
1023
1024 let v = <$ty as FromValue>::from_value(
1025 &mut context.0.source.new_context(&mut context.1),
1026 ConfigValue::Str($val.to_string()),
1027 );
1028 assert_eq!(v.unwrap(), $expect);
1029
1030 let v = <$ty as FromValue>::from_value(
1031 &mut context.0.source.new_context(&mut context.1),
1032 ConfigValue::Float($val as f64),
1033 );
1034 assert_eq!(v.unwrap(), $expect);
1035 };
1036 }
1037
1038 check_int!(i8, 7, 7i8);
1039 check_int!(i16, 8, 8i16);
1040 check_int!(i32, 9, 9i32);
1041 check_int!(i64, 10, 10i64);
1042 check_int!(u8, 11, 11u8);
1043 check_int!(u16, 12, 12u16);
1044 check_int!(u32, 13, 13u32);
1045 check_int!(u64, 14, 14u64);
1046 check_int!(usize, 15, 15usize);
1047
1048 let v = <i8 as FromValue>::from_value(
1050 &mut context.0.source.new_context(&mut context.1),
1051 ConfigValue::Bool(true),
1052 );
1053 assert!(v.is_err());
1054 }
1055
1056 #[test]
1057 fn from_value_for_float_types() {
1058 let mut context = TestContext::new();
1059
1060 macro_rules! check_float {
1061 ($ty:ty, $val:expr, $expect:expr) => {
1062 let v = <$ty as FromValue>::from_value(
1063 &mut context.0.source.new_context(&mut context.1),
1064 ConfigValue::Float($val),
1065 );
1066 assert!((v.unwrap() - $expect).abs() < 1e-6);
1067
1068 let v = <$ty as FromValue>::from_value(
1069 &mut context.0.source.new_context(&mut context.1),
1070 ConfigValue::StrRef(&$val.to_string()),
1071 );
1072 assert!((v.unwrap() - $expect).abs() < 1e-6);
1073
1074 let v = <$ty as FromValue>::from_value(
1075 &mut context.0.source.new_context(&mut context.1),
1076 ConfigValue::Str($val.to_string()),
1077 );
1078 assert!((v.unwrap() - $expect).abs() < 1e-6);
1079 };
1080 }
1081
1082 check_float!(f32, 1.23, 1.23f32);
1083 check_float!(f64, 4.56, 4.56f64);
1084
1085 let v = <f32 as FromValue>::from_value(
1086 &mut context.0.source.new_context(&mut context.1),
1087 ConfigValue::Bool(true),
1088 );
1089 assert!(v.is_err());
1090 }
1091
1092 #[test]
1093 fn parse_duration_from_str_cases() {
1094 let mut context = TestContext::new();
1095
1096 assert_eq!(
1098 parse_duration_from_str(&mut context.0.source.new_context(&mut context.1), "123s")
1099 .unwrap(),
1100 Duration::new(123, 0)
1101 );
1102 assert_eq!(
1104 parse_duration_from_str(&mut context.0.source.new_context(&mut context.1), "2m")
1105 .unwrap(),
1106 Duration::new(120, 0)
1107 );
1108 assert_eq!(
1110 parse_duration_from_str(&mut context.0.source.new_context(&mut context.1), "3h")
1111 .unwrap(),
1112 Duration::new(3 * 3600, 0)
1113 );
1114 assert_eq!(
1116 parse_duration_from_str(&mut context.0.source.new_context(&mut context.1), "5ms")
1117 .unwrap(),
1118 Duration::new(0, 5_000_000)
1119 );
1120 assert_eq!(
1122 parse_duration_from_str(&mut context.0.source.new_context(&mut context.1), "7us")
1123 .unwrap(),
1124 Duration::new(0, 7_000)
1125 );
1126 assert_eq!(
1128 parse_duration_from_str(&mut context.0.source.new_context(&mut context.1), "9ns")
1129 .unwrap(),
1130 Duration::new(0, 9)
1131 );
1132 assert_eq!(
1134 parse_duration_from_str(&mut context.0.source.new_context(&mut context.1), "11")
1135 .unwrap(),
1136 Duration::new(11, 0)
1137 );
1138 assert!(
1140 parse_duration_from_str(&mut context.0.source.new_context(&mut context.1), "abc")
1141 .is_err()
1142 );
1143 assert!(
1145 parse_duration_from_str(&mut context.0.source.new_context(&mut context.1), "1x")
1146 .is_err()
1147 );
1148 }
1149
1150 #[test]
1151 fn from_value_for_duration() {
1152 let mut context = TestContext::new();
1153
1154 let v = <Duration as FromValue>::from_value(
1156 &mut context.0.source.new_context(&mut context.1),
1157 ConfigValue::Str("123".to_string()),
1158 );
1159 assert_eq!(v.unwrap(), Duration::new(123, 0));
1160
1161 let v = <Duration as FromValue>::from_value(
1163 &mut context.0.source.new_context(&mut context.1),
1164 ConfigValue::Str("2m".to_string()),
1165 );
1166 assert_eq!(v.unwrap(), Duration::new(120, 0));
1167
1168 let v = <Duration as FromValue>::from_value(
1170 &mut context.0.source.new_context(&mut context.1),
1171 ConfigValue::StrRef("3h"),
1172 );
1173 assert_eq!(v.unwrap(), Duration::new(3 * 3600, 0));
1174
1175 let v = <Duration as FromValue>::from_value(
1177 &mut context.0.source.new_context(&mut context.1),
1178 ConfigValue::Int(7),
1179 );
1180 assert_eq!(v.unwrap(), Duration::new(7, 0));
1181
1182 let v = <Duration as FromValue>::from_value(
1184 &mut context.0.source.new_context(&mut context.1),
1185 ConfigValue::Float(1.5),
1186 );
1187 assert!((v.unwrap().as_secs_f64() - 1.5).abs() < 1e-6);
1188
1189 let v = <Duration as FromValue>::from_value(
1191 &mut context.0.source.new_context(&mut context.1),
1192 ConfigValue::Bool(true),
1193 );
1194 assert!(v.is_err());
1195 }
1196
1197 #[test]
1198 fn impl_enum_macro_test() {
1199 #[derive(Debug, PartialEq)]
1200 enum MyEnum {
1201 Foo,
1202 Bar,
1203 Baz,
1204 }
1205 impl_enum!(MyEnum {
1206 "foo" => MyEnum::Foo
1207 "bar" => MyEnum::Bar
1208 "baz" => MyEnum::Baz
1209 });
1210
1211 let mut context = TestContext::new();
1212
1213 assert_eq!(
1215 <MyEnum as FromStringValue>::from_str_value(
1216 &mut context.0.source.new_context(&mut context.1),
1217 "foo"
1218 )
1219 .unwrap(),
1220 MyEnum::Foo
1221 );
1222 assert_eq!(
1223 <MyEnum as FromStringValue>::from_str_value(
1224 &mut context.0.source.new_context(&mut context.1),
1225 "bar"
1226 )
1227 .unwrap(),
1228 MyEnum::Bar
1229 );
1230 assert_eq!(
1231 <MyEnum as FromStringValue>::from_str_value(
1232 &mut context.0.source.new_context(&mut context.1),
1233 "baz"
1234 )
1235 .unwrap(),
1236 MyEnum::Baz
1237 );
1238
1239 assert_eq!(
1241 <MyEnum as FromStringValue>::from_str_value(
1242 &mut context.0.source.new_context(&mut context.1),
1243 "FOO"
1244 )
1245 .unwrap(),
1246 MyEnum::Foo
1247 );
1248
1249 let err = <MyEnum as FromStringValue>::from_str_value(
1251 &mut context.0.source.new_context(&mut context.1),
1252 "unknown",
1253 );
1254 assert!(err.is_err());
1255 }
1256}