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