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