Skip to main content

cfg_rs/
value.rs

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/// Config value, [ConfigSource](source/trait.ConfigSource.html) use this value to store config properties.
16///
17/// # Placeholder expression
18///
19/// Placeholder expression use normalized string representation of [`crate::ConfigKey`], with extra brackets.
20/// For example: `${cfg.k1}`.
21///
22/// Placeholder expression is powerful in realworld application, it has following benifits:
23///
24/// ## Placeholder can reduce duplicated configs, use one key config to affect multiple keys.
25///
26/// * `app.name` = `cfg`
27/// * `app.version` = `1.0.0`
28/// * `app.desc` = `Application ${app.name}, version ${app.version}`
29///
30/// ## Placeholder can generate configs, we can use placeholder to generate random values.
31///
32/// * `app.id` = `${random.u64}`
33/// * `app.instance` = `${app.name}-${app.id}`
34///
35#[derive(Debug)]
36pub enum ConfigValue<'a> {
37    /// String reference, supports placeholder expression.
38    StrRef(&'a str),
39    /// String, supports placeholder expression.
40    Str(String),
41    /// Integer.
42    Int(i64),
43    /// Float value.
44    Float(f64),
45    /// Bool value.
46    Bool(bool),
47    #[cfg(feature = "rand")]
48    /// Random value.
49    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    // Accept forms like `HashMap::new` or `HashMap::with_hasher(Default::default())`
203    ($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    // Accept forms like `HashMap::new` or `HashMap::with_hasher(Default::default())`
225    ($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
297/// Get from string.
298pub trait FromStringValue: Sized + Any {
299    /// Convert from string value.
300    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/// Wrapper for all FromStr type.
365#[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/// Implement [`FromConfig`] for enums.
486///
487/// ```ignore,rust
488/// impl_enum!(Ordering{
489///     "lt" | "less" => Ordering::Less
490///     "eq" | "equal" => Ordering::Equal
491///     "gt" | "greater" => Ordering::Greater
492/// });
493/// ```
494#[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            // println!("{}", $val);
603            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        // None value should return Ok(())
891        let v: Result<(), ConfigError> =
892            <()>::from_config(&mut context.0.source.new_context(&mut context.1), None);
893        assert!(v.is_ok());
894        // Some value should also return Ok(())
895        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        // StrRef
922        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        // Str
929        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        // 非字符串类型应报错
936        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        // StrRef true/false
948        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        // Str true/false
960        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        // Bool
972        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        // 非法类型
979        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        // 正常解析
992        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        // 错误解析
997        let r = Holder::from_str_value(&mut context.0.source.new_context(&mut context.1), "abc");
998        assert!(r.is_err());
999        // 错误类型为 ConfigError::ConfigCause(ParseIntError)
1000        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        // 错误类型
1049        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        // 秒
1097        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        // 分钟
1103        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        // 小时
1109        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        // 毫秒
1115        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        // 微秒
1121        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        // 纳秒
1127        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        // 没有单位,默认为秒
1133        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        // 错误格式
1139        assert!(
1140            parse_duration_from_str(&mut context.0.source.new_context(&mut context.1), "abc")
1141                .is_err()
1142        );
1143        // 不支持的单位
1144        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        // String (seconds)
1155        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        // String (with unit)
1162        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        // StrRef
1169        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        // Int
1176        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        // Float
1183        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        // Invalid type
1190        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        // Lowercase matches
1214        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        // Uppercase matches (case-insensitive)
1240        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        // Unknown value returns error
1250        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}