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
15mod byte;
16pub use self::byte::ByteSize;
17
18/// Config value, [ConfigSource](source/trait.ConfigSource.html) use this value to store config properties.
19///
20/// # Placeholder expression
21///
22/// Placeholder expression use normalized string representation of [`crate::ConfigKey`], with extra brackets.
23/// For example: `${cfg.k1}`.
24///
25/// Placeholder expression is powerful in realworld application, it has following benifits:
26///
27/// ## Placeholder can reduce duplicated configs, use one key config to affect multiple keys.
28///
29/// * `app.name` = `cfg`
30/// * `app.version` = `1.0.0`
31/// * `app.desc` = `Application ${app.name}, version ${app.version}`
32///
33/// ## Placeholder can generate configs, we can use placeholder to generate random values.
34///
35/// * `app.id` = `${random.u64}`
36/// * `app.instance` = `${app.name}-${app.id}`
37///
38#[derive(Debug)]
39pub enum ConfigValue<'a> {
40    /// String reference, supports placeholder expression.
41    StrRef(&'a str),
42    /// String, supports placeholder expression.
43    Str(String),
44    /// Integer.
45    Int(i64),
46    /// Float value.
47    Float(f64),
48    /// Bool value.
49    Bool(bool),
50    #[cfg(feature = "rand")]
51    /// Random value.
52    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    // Accept forms like `HashMap::new` or `HashMap::with_hasher(Default::default())`
206    ($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    // Accept forms like `HashMap::new` or `HashMap::with_hasher(Default::default())`
228    ($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
300/// Get from string.
301pub trait FromStringValue: Sized + Any {
302    /// Convert from string value.
303    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/// Wrapper for all FromStr type.
368#[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/// Implement [`FromConfig`] for enums.
489///
490/// ```ignore,rust
491/// impl_enum!(Ordering{
492///     "lt" | "less" => Ordering::Less
493///     "eq" | "equal" => Ordering::Equal
494///     "gt" | "greater" => Ordering::Greater
495/// });
496/// ```
497#[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            // println!("{}", $val);
606            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        // None value should return Ok(())
894        let v: Result<(), ConfigError> =
895            <()>::from_config(&mut context.0.source.new_context(&mut context.1), None);
896        assert!(v.is_ok());
897        // Some value should also return Ok(())
898        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        // StrRef
925        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        // Str
932        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        // 非字符串类型应报错
939        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        // StrRef true/false
951        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        // Str true/false
963        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        // Bool
975        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        // 非法类型
982        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        // 正常解析
995        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        // 错误解析
1000        let r = Holder::from_str_value(&mut context.0.source.new_context(&mut context.1), "abc");
1001        assert!(r.is_err());
1002        // 错误类型为 ConfigError::ConfigCause(ParseIntError)
1003        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        // 错误类型
1052        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        // 秒
1100        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        // 分钟
1106        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        // 小时
1112        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        // 毫秒
1118        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        // 微秒
1124        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        // 纳秒
1130        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        // 没有单位,默认为秒
1136        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        // 错误格式
1142        assert!(
1143            parse_duration_from_str(&mut context.0.source.new_context(&mut context.1), "abc")
1144                .is_err()
1145        );
1146        // 不支持的单位
1147        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        // String (seconds)
1158        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        // String (with unit)
1165        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        // StrRef
1172        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        // Int
1179        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        // Float
1186        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        // Invalid type
1193        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        // Lowercase matches
1217        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        // Uppercase matches (case-insensitive)
1243        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        // Unknown value returns error
1253        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}