salak/
raw.rs

1#[cfg(feature = "derive")]
2use crate::{DescFromEnvironment, PrefixedFromEnvironment, SalakDescContext};
3use crate::{FromEnvironment, PropertyError, Res, SalakContext};
4use std::{
5    collections::HashSet,
6    ffi::OsString,
7    net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
8    path::PathBuf,
9    time::Duration,
10};
11
12/// Raw property, it is a temprory representation of property, which can be either [`&str`] or [`String`], or other values.
13#[derive(Clone, Debug)]
14pub enum Property<'a> {
15    /// [`&str`] holder.
16    S(&'a str),
17    /// [`String`] holder.
18    O(String),
19    /// Number holder.
20    I(i64),
21    /// Float holder.
22    F(f64),
23    /// Bool holder.
24    B(bool),
25}
26
27/// Any object implements this trait is automatically implmenting [`crate::FromEnvironment`].
28///
29/// This trait defines how to parse value from property, and defines specific behaviors such as
30/// how empty string being parsed.
31pub trait IsProperty: Sized {
32    /// Check if empty string means property does not exist.
33    /// In most case this is true, except String.
34    #[inline]
35    fn is_empty(p: &Property<'_>) -> bool {
36        match p {
37            Property::S(s) => s.is_empty(),
38            Property::O(s) => s.is_empty(),
39            _ => false,
40        }
41    }
42
43    /// Parse value from property.
44    fn from_property(_: Property<'_>) -> Res<Self>;
45}
46
47impl<T: IsProperty> FromEnvironment for T {
48    #[inline]
49    fn from_env(val: Option<Property<'_>>, env: &mut SalakContext<'_>) -> Res<Self> {
50        if let Some(v) = val {
51            if !Self::is_empty(&v) {
52                return Self::from_property(v);
53            }
54        }
55        Err(PropertyError::NotFound(env.current_key().to_string()))
56    }
57}
58
59#[cfg(feature = "derive")]
60#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
61impl<T: IsProperty> DescFromEnvironment for T {
62    #[inline]
63    fn key_desc(env: &mut SalakDescContext<'_>) {
64        env.current.ignore = false;
65        env.current.set_required(true);
66    }
67}
68
69impl FromEnvironment for () {
70    fn from_env(_: Option<Property<'_>>, _: &mut SalakContext<'_>) -> Res<Self> {
71        Ok(())
72    }
73}
74#[cfg(feature = "derive")]
75#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
76impl DescFromEnvironment for () {
77    fn key_desc(_: &mut SalakDescContext<'_>) {}
78}
79
80#[cfg(feature = "derive")]
81#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
82impl PrefixedFromEnvironment for () {
83    fn prefix() -> &'static str {
84        ""
85    }
86}
87
88#[inline]
89fn check_f64(f: f64) -> Result<f64, PropertyError> {
90    if f.is_finite() {
91        Ok(f)
92    } else {
93        Err(PropertyError::parse_fail("f64 value is infinite"))
94    }
95}
96
97impl IsProperty for String {
98    #[inline]
99    fn is_empty(_: &Property<'_>) -> bool {
100        false
101    }
102    #[inline]
103    fn from_property(p: Property<'_>) -> Res<Self> {
104        Ok(match p {
105            Property::S(v) => v.to_string(),
106            Property::O(v) => v,
107            Property::I(v) => v.to_string(),
108            Property::F(v) => check_f64(v)?.to_string(),
109            Property::B(v) => v.to_string(),
110        })
111    }
112}
113impl IsProperty for bool {
114    #[inline]
115    fn from_property(p: Property<'_>) -> Res<Self> {
116        fn str_to_bool(v: &str) -> Res<bool> {
117            match v {
118                "yes" | "true" => Ok(true),
119                "no" | "false" => Ok(false),
120                _ => Err(PropertyError::parse_fail("invalid bool value")),
121            }
122        }
123        match p {
124            Property::B(v) => Ok(v),
125            Property::S(v) => str_to_bool(v),
126            Property::O(v) => str_to_bool(&v),
127            _ => Err(PropertyError::parse_fail("can not num to bool")),
128        }
129    }
130}
131
132macro_rules! impl_property_num {
133    ($($x:ident),+) => {$(
134            impl IsProperty for $x {
135                #[inline]
136                fn from_property(p: Property<'_>) -> Res<Self> {
137                    use std::convert::TryFrom;
138                    Ok(match p {
139                    Property::S(s) => s.parse::<$x>()?,
140                    Property::O(s) => s.parse::<$x>()?,
141                    Property::I(s) => $x::try_from(s)?,
142                    Property::F(s) => check_f64(s)? as $x,
143                    _ => return Err(PropertyError::parse_fail("can not convert bool to num")),
144                    })
145                }
146
147            }
148
149            )+}
150}
151
152impl_property_num!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, isize, usize);
153
154macro_rules! impl_property_float {
155    ($($x:ident),+) => {$(
156            #[allow(trivial_numeric_casts)]
157            impl IsProperty for $x {
158                #[inline]
159                fn from_property(p: Property<'_>) -> Res<Self> {
160                    Ok(match p {
161                    Property::S(s) => s.parse::<$x>()?,
162                    Property::O(s) => s.parse::<$x>()?,
163                    Property::I(s) => s as $x,
164                    Property::F(s) => check_f64(s)? as $x,
165                    _ => return Err(PropertyError::parse_fail("can not convert bool to num")),
166                    })
167                }
168
169            }
170
171            )+}
172}
173
174impl_property_float!(f32, f64);
175
176#[inline]
177fn parse_duration_from_str(du: &str) -> Res<Duration> {
178    let mut i = 0;
179    let mut multi = 1;
180    let mut last = None;
181    for c in du.chars().rev() {
182        match c {
183            'h' | 'm' | 's' if last.is_none() => {
184                if c == 'm' {
185                    last = Some('M');
186                } else {
187                    last = Some(c);
188                }
189            }
190            'm' | 'u' | 'n' if last == Some('s') => {
191                last = Some(c);
192            }
193            c if ('0'..='9').contains(&c) => {
194                if last.is_none() {
195                    last = Some('s');
196                }
197                i += multi * (c as u64 - '0' as u64);
198                multi *= 10;
199            }
200            _ => return Err(PropertyError::parse_fail("Invalid duration")),
201        }
202    }
203    Ok(match last.unwrap_or('s') {
204        'h' => Duration::new(i * 3600, 0),
205        'M' => Duration::new(i * 60, 0),
206        's' => Duration::from_secs(i),
207        'm' => Duration::from_millis(i),
208        'u' => Duration::from_micros(i),
209        'n' => Duration::from_nanos(i),
210        _ => return Err(PropertyError::parse_fail("Invalid duration")),
211    })
212}
213
214impl IsProperty for Duration {
215    fn from_property(p: Property<'_>) -> Res<Self> {
216        match p {
217            Property::O(du) => parse_duration_from_str(&du),
218            Property::S(du) => parse_duration_from_str(du),
219            Property::I(seconds) => Ok(Duration::from_secs(seconds as u64)),
220            Property::F(sec) => Ok(Duration::new(0, 0).mul_f64(sec)),
221            Property::B(_) => Err(PropertyError::parse_fail("bool cannot convert to duration")),
222        }
223    }
224}
225
226/// Sub key is partial [`Key`] having values with either `[a-z][_a-z0-9]*` or [`usize`].
227#[derive(Debug)]
228pub(crate) enum SubKey<'a> {
229    /// Str sub key.
230    S(&'a str),
231    /// Index sub key.
232    I(usize),
233}
234
235impl SubKey<'_> {
236    pub(crate) fn is_empty(&self) -> bool {
237        if let SubKey::S(v) = self {
238            return v.is_empty();
239        }
240        false
241    }
242}
243
244lazy_static::lazy_static! {
245    static ref P: &'static [char] = &['.', '[', ']'];
246}
247/// Key with a string buffer, can be avoid allocating memory when parsing configuration.
248#[derive(Debug)]
249pub struct Key<'a> {
250    buf: String,
251    key: Vec<SubKey<'a>>,
252}
253
254impl<'a> Key<'a> {
255    #[inline]
256    pub(crate) fn new() -> Self {
257        Self {
258            buf: String::new(),
259            key: vec![],
260        }
261    }
262
263    pub(crate) fn from_str(key: &'a str) -> Self {
264        let mut k = Self::new();
265        for n in key.split(&P[..]) {
266            if let Some(c) = n.chars().next() {
267                if c.is_ascii_digit() {
268                    if let Ok(v) = n.parse() {
269                        k.push(SubKey::I(v));
270                        continue;
271                    }
272                }
273                k.push(SubKey::S(n));
274            }
275        }
276        k
277    }
278
279    #[allow(dead_code)]
280    pub(crate) fn as_generic(&self) -> String {
281        self.as_str().replace("[0]", "[*]")
282    }
283
284    #[allow(dead_code)]
285    pub(crate) fn iter(&self) -> std::slice::Iter<'_, SubKey<'_>> {
286        self.key.iter()
287    }
288
289    pub(crate) fn as_str(&self) -> &str {
290        if self.buf.starts_with('.') {
291            return &self.buf.as_str()[1..];
292        }
293        self.buf.as_str()
294    }
295
296    pub(crate) fn push(&mut self, k: SubKey<'a>) {
297        match &k {
298            SubKey::S(v) => {
299                self.buf.push('.');
300                self.buf.push_str(*v);
301            }
302            SubKey::I(v) => {
303                self.buf.push_str(&format!("[{}]", *v));
304            }
305        }
306        self.key.push(k);
307    }
308
309    pub(crate) fn pop(&mut self) {
310        if let Some(v) = self.key.pop() {
311            match v {
312                SubKey::S(n) => self.buf.truncate(self.buf.len() - n.len() - 1),
313                SubKey::I(n) => self.buf.truncate(self.buf.len() - n.to_string().len() - 2),
314            }
315        }
316    }
317}
318
319impl<'a> From<&'a str> for SubKey<'a> {
320    fn from(mut u: &'a str) -> Self {
321        if u.starts_with('[') {
322            u = &u[1..];
323            let mut x = 0;
324            for i in u.chars() {
325                if ('0'..='9').contains(&i) {
326                    x = x * 10 + (i as usize) - ('0' as usize);
327                } else {
328                    break;
329                }
330            }
331            return SubKey::I(x);
332        }
333        SubKey::S(u)
334    }
335}
336
337impl From<usize> for SubKey<'_> {
338    #[inline]
339    fn from(u: usize) -> Self {
340        SubKey::I(u)
341    }
342}
343/// Sub key collection, which stands for lists of sub keys with same prefix.
344#[derive(Debug)]
345pub struct SubKeys<'a> {
346    keys: HashSet<&'a str>,
347    upper: Option<usize>,
348}
349
350impl<'a> SubKeys<'a> {
351    /// Insert a sub key.
352    pub(crate) fn insert<K: Into<SubKey<'a>>>(&mut self, key: K) {
353        match key.into() {
354            SubKey::S(s) => {
355                self.keys.insert(s);
356            }
357            SubKey::I(i) => {
358                if let Some(max) = self.upper {
359                    if i <= max {
360                        return;
361                    }
362                }
363                self.upper = Some(i);
364            }
365        }
366    }
367
368    pub(crate) fn str_keys(&self) -> Vec<&'a str> {
369        self.keys
370            .iter()
371            .filter(|a| {
372                if let Some(c) = a.chars().next() {
373                    c < '0' && c > '9'
374                } else {
375                    false
376                }
377            })
378            .copied()
379            .collect()
380    }
381
382    #[inline]
383    pub(crate) fn new() -> Self {
384        Self {
385            keys: HashSet::new(),
386            upper: None,
387        }
388    }
389
390    #[inline]
391    pub(crate) fn max(&self) -> Option<usize> {
392        self.upper
393    }
394}
395
396macro_rules! impl_property_from_str {
397    ($($x:ident),+) => {$(
398            impl IsProperty for $x {
399                #[inline]
400                fn from_property(p: Property<'_>) -> Res<Self> {
401                    use std::str::FromStr;
402                    Ok(match p {
403                    Property::S(s) => <$x>::from_str(s)?,
404                    Property::O(s) => <$x>::from_str(&s)?,
405                    _ => return Err(PropertyError::parse_fail("can not convert")),
406                    })
407                }
408
409            }
410            )+}
411}
412
413impl_property_from_str!(
414    Ipv4Addr,
415    Ipv6Addr,
416    IpAddr,
417    SocketAddrV4,
418    SocketAddrV6,
419    SocketAddr,
420    PathBuf,
421    OsString
422);
423
424#[cfg(feature = "ipnet")]
425mod ipnet {
426    use crate::*;
427    use ipnet::*;
428    impl_property_from_str!(IpNet, Ipv4Net, Ipv6Net);
429}
430
431#[cfg(test)]
432mod tests {
433    use crate::*;
434
435    #[test]
436    fn property_test() {
437        let env = Salak::builder()
438            .set("a", "0")
439            .set("b", "${b}")
440            .set("c", "${a}")
441            .set("d", "${z}")
442            .set("e", "${z:}")
443            .set("f", "${z:${a}}")
444            .set("g", "a")
445            .set("h", "${${g}}")
446            .set("i", "\\$\\{a\\}")
447            .set("j", "${${g}:a}")
448            .set("k", "${a} ${a}")
449            .set("l", "${c}")
450            .set("m", "${no_found:${no_found_2:hello}}")
451            .build()
452            .unwrap();
453
454        fn validate<T: std::fmt::Debug + FromEnvironment>(env: &Salak, key: &str, val: &str) {
455            println!("{} key: {}", std::any::type_name::<T>(), key);
456            assert_eq!(val, &format!("{:?}", env.require::<T>(key)));
457        }
458
459        validate::<String>(&env, "a", "Ok(\"0\")");
460        validate::<String>(&env, "b", "Err(RecursiveFail(\"b\"))");
461        validate::<String>(&env, "c", "Ok(\"0\")");
462        validate::<String>(&env, "d", "Err(ResolveNotFound(\"z\"))");
463        validate::<String>(&env, "e", "Ok(\"\")");
464        validate::<String>(&env, "f", "Ok(\"0\")");
465        validate::<String>(&env, "g", "Ok(\"a\")");
466        validate::<String>(&env, "h", "Ok(\"0\")");
467        validate::<String>(&env, "i", "Ok(\"${a}\")");
468        validate::<String>(&env, "j", "Ok(\"0\")");
469        validate::<String>(&env, "k", "Ok(\"0 0\")");
470        validate::<String>(&env, "l", "Ok(\"0\")");
471        validate::<String>(&env, "m", "Ok(\"hello\")");
472
473        validate::<bool>(
474            &env,
475            "a",
476            "Err(ParseFail(None, SalakParseError(\"invalid bool value\")))",
477        );
478        validate::<bool>(&env, "b", "Err(RecursiveFail(\"b\"))");
479        validate::<bool>(
480            &env,
481            "c",
482            "Err(ParseFail(None, SalakParseError(\"invalid bool value\")))",
483        );
484        validate::<bool>(&env, "d", "Err(ResolveNotFound(\"z\"))");
485        validate::<bool>(&env, "e", "Err(NotFound(\"e\"))");
486        validate::<bool>(
487            &env,
488            "f",
489            "Err(ParseFail(None, SalakParseError(\"invalid bool value\")))",
490        );
491        validate::<bool>(
492            &env,
493            "g",
494            "Err(ParseFail(None, SalakParseError(\"invalid bool value\")))",
495        );
496        validate::<bool>(
497            &env,
498            "h",
499            "Err(ParseFail(None, SalakParseError(\"invalid bool value\")))",
500        );
501        validate::<bool>(
502            &env,
503            "i",
504            "Err(ParseFail(None, SalakParseError(\"invalid bool value\")))",
505        );
506        validate::<bool>(
507            &env,
508            "j",
509            "Err(ParseFail(None, SalakParseError(\"invalid bool value\")))",
510        );
511        validate::<bool>(
512            &env,
513            "k",
514            "Err(ParseFail(None, SalakParseError(\"invalid bool value\")))",
515        );
516        validate::<bool>(
517            &env,
518            "l",
519            "Err(ParseFail(None, SalakParseError(\"invalid bool value\")))",
520        );
521        validate::<bool>(
522            &env,
523            "m",
524            "Err(ParseFail(None, SalakParseError(\"invalid bool value\")))",
525        );
526
527        validate::<u8>(&env, "a", "Ok(0)");
528        validate::<u8>(&env, "b", "Err(RecursiveFail(\"b\"))");
529        validate::<u8>(&env, "c", "Ok(0)");
530        validate::<u8>(&env, "d", "Err(ResolveNotFound(\"z\"))");
531        validate::<u8>(&env, "e", "Err(NotFound(\"e\"))");
532        validate::<u8>(&env, "f", "Ok(0)");
533        validate::<u8>(
534            &env,
535            "g",
536            "Err(ParseFail(None, ParseIntError { kind: InvalidDigit }))",
537        );
538        validate::<u8>(&env, "h", "Ok(0)");
539        validate::<u8>(
540            &env,
541            "i",
542            "Err(ParseFail(None, ParseIntError { kind: InvalidDigit }))",
543        );
544        validate::<u8>(&env, "j", "Ok(0)");
545        validate::<u8>(
546            &env,
547            "k",
548            "Err(ParseFail(None, ParseIntError { kind: InvalidDigit }))",
549        );
550        validate::<u8>(&env, "l", "Ok(0)");
551        validate::<u8>(
552            &env,
553            "m",
554            "Err(ParseFail(None, ParseIntError { kind: InvalidDigit }))",
555        );
556
557        validate::<Option<u8>>(&env, "a", "Ok(Some(0))");
558        validate::<Option<u8>>(&env, "b", "Err(RecursiveFail(\"b\"))");
559        validate::<Option<u8>>(&env, "c", "Ok(Some(0))");
560        validate::<Option<u8>>(&env, "d", "Err(ResolveNotFound(\"z\"))");
561        validate::<Option<u8>>(&env, "e", "Ok(None)");
562        validate::<Option<u8>>(&env, "f", "Ok(Some(0))");
563        validate::<Option<u8>>(
564            &env,
565            "g",
566            "Err(ParseFail(None, ParseIntError { kind: InvalidDigit }))",
567        );
568        validate::<Option<u8>>(&env, "h", "Ok(Some(0))");
569        validate::<Option<u8>>(
570            &env,
571            "i",
572            "Err(ParseFail(None, ParseIntError { kind: InvalidDigit }))",
573        );
574        validate::<Option<u8>>(&env, "j", "Ok(Some(0))");
575        validate::<Option<u8>>(
576            &env,
577            "k",
578            "Err(ParseFail(None, ParseIntError { kind: InvalidDigit }))",
579        );
580        validate::<Option<u8>>(&env, "l", "Ok(Some(0))");
581        validate::<Option<u8>>(
582            &env,
583            "m",
584            "Err(ParseFail(None, ParseIntError { kind: InvalidDigit }))",
585        );
586    }
587
588    #[test]
589    fn bool_tests() {
590        assert_eq!(true, bool::from_property(Property::S("yes")).unwrap());
591        assert_eq!(true, bool::from_property(Property::S("true")).unwrap());
592        assert_eq!(false, bool::from_property(Property::S("no")).unwrap());
593        assert_eq!(false, bool::from_property(Property::S("false")).unwrap());
594
595        assert_eq!(true, bool::from_property(Property::S("x")).is_err());
596        assert_eq!(true, bool::from_property(Property::S("n")).is_err());
597        assert_eq!(true, bool::from_property(Property::S("f")).is_err());
598        assert_eq!(true, bool::from_property(Property::S("y")).is_err());
599        assert_eq!(true, bool::from_property(Property::S("t")).is_err());
600        assert_eq!(true, bool::from_property(Property::I(0)).is_err());
601        assert_eq!(true, bool::from_property(Property::I(1)).is_err());
602        assert_eq!(true, bool::from_property(Property::F(0.0)).is_err());
603        assert_eq!(true, bool::from_property(Property::F(1.0)).is_err());
604    }
605
606    #[quickcheck]
607    fn num_tests(i: i64) {
608        assert_eq!(
609            i,
610            i64::from_property(Property::O(format!("{}", i))).unwrap()
611        );
612        assert_eq!(i, i64::from_property(Property::I(i)).unwrap());
613        assert_eq!(true, i64::from_property(Property::B(true)).is_err());
614    }
615
616    #[quickcheck]
617    fn i64_convert_tests(i: i64) -> bool {
618        let u8: Result<u8, PropertyError> = IsProperty::from_property(Property::I(i));
619        let u16: Result<u16, PropertyError> = IsProperty::from_property(Property::I(i));
620        let u32: Result<u32, PropertyError> = IsProperty::from_property(Property::I(i));
621        let u64: Result<u64, PropertyError> = IsProperty::from_property(Property::I(i));
622        let u128: Result<u128, PropertyError> = IsProperty::from_property(Property::I(i));
623        let i8: Result<i8, PropertyError> = IsProperty::from_property(Property::I(i));
624        let i16: Result<i16, PropertyError> = IsProperty::from_property(Property::I(i));
625        let i32: Result<i32, PropertyError> = IsProperty::from_property(Property::I(i));
626        let i64: Result<i64, PropertyError> = IsProperty::from_property(Property::I(i));
627        let i128: Result<i128, PropertyError> = IsProperty::from_property(Property::I(i));
628        let f32: Result<f32, PropertyError> = IsProperty::from_property(Property::I(i));
629        let f64: Result<f64, PropertyError> = IsProperty::from_property(Property::I(i));
630        vec![
631            i >= 0 && i <= (u8::MAX as i64) && u8.is_ok() || u8.is_err(),
632            i >= 0 && i <= (u16::MAX as i64) && u16.is_ok() || u16.is_err(),
633            i >= 0 && i <= (u32::MAX as i64) && u32.is_ok() || u32.is_err(),
634            i >= 0 && u64.is_ok() || u64.is_err(),
635            i >= 0 && u128.is_ok() || u128.is_err(),
636            i >= (i8::MIN as i64) && i <= (i8::MAX as i64) && i8.is_ok() || i8.is_err(),
637            i >= (i16::MIN as i64) && i <= (i16::MAX as i64) && i16.is_ok() || i16.is_err(),
638            i >= (i32::MIN as i64) && i <= (i32::MAX as i64) && i32.is_ok() || i32.is_err(),
639            i64.is_ok(),
640            i128.is_ok(),
641            f32.is_ok() && f32.unwrap_or(0.0).is_finite(),
642            f64.is_ok() && f64.unwrap_or(0.0).is_finite(),
643        ]
644        .iter()
645        .all(|a| *a)
646    }
647
648    #[quickcheck]
649    fn f64_convert_tests(i: f64) -> bool {
650        let u8: Result<u8, PropertyError> = IsProperty::from_property(Property::F(i));
651        let u16: Result<u16, PropertyError> = IsProperty::from_property(Property::F(i));
652        let u32: Result<u32, PropertyError> = IsProperty::from_property(Property::F(i));
653        let u64: Result<u64, PropertyError> = IsProperty::from_property(Property::F(i));
654        let u128: Result<u128, PropertyError> = IsProperty::from_property(Property::F(i));
655        let i8: Result<i8, PropertyError> = IsProperty::from_property(Property::F(i));
656        let i16: Result<i16, PropertyError> = IsProperty::from_property(Property::F(i));
657        let i32: Result<i32, PropertyError> = IsProperty::from_property(Property::F(i));
658        let i64: Result<i64, PropertyError> = IsProperty::from_property(Property::F(i));
659        let i128: Result<i128, PropertyError> = IsProperty::from_property(Property::F(i));
660        let f32: Result<f32, PropertyError> = IsProperty::from_property(Property::F(i));
661        let f64: Result<f64, PropertyError> = IsProperty::from_property(Property::F(i));
662
663        vec![
664            i.is_finite() && u8.is_ok() || u8.is_err(),
665            i.is_finite() && u16.is_ok() || u16.is_err(),
666            i.is_finite() && u32.is_ok() || u32.is_err(),
667            i.is_finite() && u64.is_ok() || u64.is_err(),
668            i.is_finite() && u128.is_ok() || u128.is_err(),
669            i.is_finite() && i8.is_ok() || i8.is_err(),
670            i.is_finite() && i16.is_ok() || i16.is_err(),
671            i.is_finite() && i32.is_ok() || i32.is_err(),
672            i.is_finite() && i64.is_ok() || i64.is_err(),
673            i.is_finite() && i128.is_ok() || i128.is_err(),
674            i.is_finite() && f32.is_ok() || f32.is_err(),
675            i.is_finite() && f64.is_ok() || f64.is_err(),
676        ]
677        .iter()
678        .all(|a| *a)
679    }
680
681    #[test]
682    fn duration_test() {
683        use super::*;
684        assert_eq!(
685            Duration::new(123, 0),
686            parse_duration_from_str("123").unwrap()
687        );
688        assert_eq!(
689            Duration::new(123, 0),
690            parse_duration_from_str("123s").unwrap()
691        );
692        assert_eq!(
693            Duration::new(10 * 60, 0),
694            parse_duration_from_str("10m").unwrap()
695        );
696        assert_eq!(
697            Duration::new(123 * 3600, 0),
698            parse_duration_from_str("123h").unwrap()
699        );
700        assert_eq!(
701            Duration::new(0, 123 * 1000_000),
702            parse_duration_from_str("123ms").unwrap()
703        );
704        assert_eq!(
705            Duration::new(0, 123 * 1000),
706            parse_duration_from_str("123us").unwrap()
707        );
708        assert_eq!(
709            Duration::new(0, 123),
710            parse_duration_from_str("123ns").unwrap()
711        );
712        assert_eq!(
713            Duration::new(1, 0),
714            parse_duration_from_str("1000ms").unwrap()
715        );
716    }
717
718    #[derive(Debug)]
719    struct Config {
720        i8: i8,
721    }
722
723    impl FromEnvironment for Config {
724        fn from_env(_: Option<Property<'_>>, env: &mut SalakContext<'_>) -> Res<Self> {
725            Ok(Config {
726                i8: env.require_def("i8", None)?,
727            })
728        }
729    }
730
731    #[cfg(feature = "derive")]
732    #[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
733    impl DescFromEnvironment for Config {
734        fn key_desc(env: &mut SalakDescContext<'_>) {
735            env.add_key_desc::<i8>("i8", None, None, None);
736        }
737    }
738    #[test]
739    fn config_test() {
740        let env = Salak::builder()
741            .set("a", "0")
742            .set("b", "${b}")
743            .set("c", "${a}")
744            .set("d", "${z}")
745            .set("e", "${z:}")
746            .set("f", "${z:${a}}")
747            .set("g", "a")
748            .set("h", "${${g}}")
749            .set("i", "\\$\\{a\\}")
750            .set("j", "${${g}:a}")
751            .set("k", "${a} ${a}")
752            .set("l", "${c}")
753            .build()
754            .unwrap();
755        println!("{:?}", env.require::<Config>(""));
756        println!("{:?}", env.require::<Option<Config>>(""));
757    }
758
759    #[test]
760    fn key_test() {
761        fn assert_key(prefix: &str, target: &str) {
762            assert_eq!(Key::from_str(prefix).as_str(), target);
763        }
764
765        assert_key("salak.prop", "salak.prop");
766        assert_key(".salak.prop", "salak.prop");
767        assert_key("[]salak.prop", "salak.prop");
768        assert_key("[0]salak.prop", "[0].salak.prop");
769        assert_key("salak[0].prop", "salak[0].prop");
770        assert_key("salak.0.prop", "salak[0].prop");
771        assert_key("", "");
772        assert_key("hello", "hello");
773        assert_key(".", "");
774        assert_key("[0]", "[0]");
775        assert_key("0", "[0]");
776    }
777
778    #[test]
779    fn key_modification_test() {
780        fn assert_key<'a>(key: &mut Key<'a>, target: &'a str) {
781            let prefix = key.as_str().to_string();
782            let p = key.as_str().to_string();
783            key.push(SubKey::S(target));
784            assert_eq!(key.as_str(), &format!("{}.{}", p, target));
785            key.pop();
786            assert_eq!(prefix, key.as_str());
787        }
788
789        fn assert_keys(key: &str, targets: Vec<&str>) {
790            let mut key = Key::from_str(key);
791            for target in targets {
792                assert_key(&mut key, target);
793            }
794        }
795
796        assert_keys("redis", vec!["port", "host", "ssl", "pool"]);
797        assert_keys("hello.hey", vec!["world"]);
798        assert_keys("hello[0].hey", vec!["world"]);
799    }
800}