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#[derive(Debug)]
36pub enum ConfigValue<'a> {
37 StrRef(&'a str),
39 Str(String),
41 Int(i64),
43 Float(f64),
45 Bool(bool),
47 #[cfg(feature = "rand")]
48 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
275pub trait FromStringValue: Sized + Any {
277 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#[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#[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 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 fn config_value_clone_static_works() {
806 let v1 = ConfigValue::StrRef("abc");
807 let v2 = v1.clone_static();
808 match v2 {
809 ConfigValue::Str(ref s) => assert_eq!(s, "abc"),
810 _ => panic!("Expected Str variant"),
811 }
812
813 let v1 = ConfigValue::Str("def".to_string());
814 let v2 = v1.clone_static();
815 match v2 {
816 ConfigValue::Str(ref s) => assert_eq!(s, "def"),
817 _ => panic!("Expected Str variant"),
818 }
819
820 let v1 = ConfigValue::Int(42);
821 let v2 = v1.clone_static();
822 match v2 {
823 ConfigValue::Int(i) => assert_eq!(i, 42),
824 _ => panic!("Expected Int variant"),
825 }
826
827 let v1 = ConfigValue::Float(3.14);
828 let v2 = v1.clone_static();
829 match v2 {
830 ConfigValue::Float(f) => assert!((f - 3.14).abs() < 1e-6),
831 _ => panic!("Expected Float variant"),
832 }
833
834 let v1 = ConfigValue::Bool(true);
835 let v2 = v1.clone_static();
836 match v2 {
837 ConfigValue::Bool(b) => assert!(b),
838 _ => panic!("Expected Bool variant"),
839 }
840
841 #[cfg(feature = "rand")]
842 {
843 let v1 = ConfigValue::Rand(crate::value::RandValue::U8);
844 let v2 = v1.clone_static();
845 match v2 {
846 ConfigValue::Rand(crate::value::RandValue::U8) => {}
847 _ => panic!("Expected Rand(U8) variant"),
848 }
849 }
850 }
851
852 #[test]
853 fn from_config_for_unit_type() {
854 let mut context = TestContext::new();
855 let v: Result<(), ConfigError> =
857 <()>::from_config(&mut context.0.source.new_context(&mut context.1), None);
858 assert!(v.is_ok());
859 let v: Result<(), ConfigError> = <()>::from_config(
861 &mut context.0.source.new_context(&mut context.1),
862 Some(ConfigValue::Int(1)),
863 );
864 assert!(v.is_ok());
865 }
866
867 #[test]
868 fn from_value_for_from_string_value() {
869 struct Dummy;
870 impl FromStr for Dummy {
871 type Err = std::convert::Infallible;
872 fn from_str(_s: &str) -> Result<Self, Self::Err> {
873 Ok(Dummy)
874 }
875 }
876 impl FromStringValue for Dummy {
877 fn from_str_value(
878 _context: &mut ConfigContext<'_>,
879 _value: &str,
880 ) -> Result<Self, ConfigError> {
881 Ok(Dummy)
882 }
883 }
884
885 let mut context = TestContext::new();
886 let v = <Dummy as FromValue>::from_value(
888 &mut context.0.source.new_context(&mut context.1),
889 ConfigValue::StrRef("abc"),
890 );
891 assert!(v.is_ok());
892
893 let v = <Dummy as FromValue>::from_value(
895 &mut context.0.source.new_context(&mut context.1),
896 ConfigValue::Str("abc".to_string()),
897 );
898 assert!(v.is_ok());
899
900 let v = <Dummy as FromValue>::from_value(
902 &mut context.0.source.new_context(&mut context.1),
903 ConfigValue::Int(1),
904 );
905 assert!(v.is_err());
906 }
907
908 #[test]
909 fn from_value_for_bool() {
910 let mut context = TestContext::new();
911
912 let v = <bool as FromValue>::from_value(
914 &mut context.0.source.new_context(&mut context.1),
915 ConfigValue::StrRef("true"),
916 );
917 assert!(v.unwrap());
918 let v = <bool as FromValue>::from_value(
919 &mut context.0.source.new_context(&mut context.1),
920 ConfigValue::StrRef("no"),
921 );
922 assert_eq!(v.unwrap(), false);
923
924 let v = <bool as FromValue>::from_value(
926 &mut context.0.source.new_context(&mut context.1),
927 ConfigValue::Str("yes".to_string()),
928 );
929 assert_eq!(v.unwrap(), true);
930 let v = <bool as FromValue>::from_value(
931 &mut context.0.source.new_context(&mut context.1),
932 ConfigValue::Str("false".to_string()),
933 );
934 assert_eq!(v.unwrap(), false);
935
936 let v = <bool as FromValue>::from_value(
938 &mut context.0.source.new_context(&mut context.1),
939 ConfigValue::Bool(true),
940 );
941 assert_eq!(v.unwrap(), true);
942
943 let v = <bool as FromValue>::from_value(
945 &mut context.0.source.new_context(&mut context.1),
946 ConfigValue::Int(1),
947 );
948 assert!(v.is_err());
949 }
950
951 #[test]
952 fn from_str_holder_from_string_value() {
953 type Holder = FromStrHolder<u32>;
954
955 let mut context = TestContext::new();
956 let r = Holder::from_str_value(&mut context.0.source.new_context(&mut context.1), "123");
958 assert!(r.is_ok());
959 assert_eq!(r.unwrap().0, 123u32);
960
961 let r = Holder::from_str_value(&mut context.0.source.new_context(&mut context.1), "abc");
963 assert!(r.is_err());
964 match r {
966 Err(ConfigError::ConfigCause(_)) => {}
967 _ => panic!("Expected ConfigCause"),
968 }
969 }
970
971 #[test]
972 fn from_value_for_integer_types() {
973 let mut context = TestContext::new();
974
975 macro_rules! check_int {
976 ($ty:ty, $val:expr, $expect:expr) => {
977 let v = <$ty as FromValue>::from_value(
978 &mut context.0.source.new_context(&mut context.1),
979 ConfigValue::Int($val),
980 );
981 assert_eq!(v.unwrap(), $expect);
982
983 let v = <$ty as FromValue>::from_value(
984 &mut context.0.source.new_context(&mut context.1),
985 ConfigValue::StrRef(&$val.to_string()),
986 );
987 assert_eq!(v.unwrap(), $expect);
988
989 let v = <$ty as FromValue>::from_value(
990 &mut context.0.source.new_context(&mut context.1),
991 ConfigValue::Str($val.to_string()),
992 );
993 assert_eq!(v.unwrap(), $expect);
994
995 let v = <$ty as FromValue>::from_value(
996 &mut context.0.source.new_context(&mut context.1),
997 ConfigValue::Float($val as f64),
998 );
999 assert_eq!(v.unwrap(), $expect);
1000 };
1001 }
1002
1003 check_int!(i8, 7, 7i8);
1004 check_int!(i16, 8, 8i16);
1005 check_int!(i32, 9, 9i32);
1006 check_int!(i64, 10, 10i64);
1007 check_int!(u8, 11, 11u8);
1008 check_int!(u16, 12, 12u16);
1009 check_int!(u32, 13, 13u32);
1010 check_int!(u64, 14, 14u64);
1011 check_int!(usize, 15, 15usize);
1012
1013 let v = <i8 as FromValue>::from_value(
1015 &mut context.0.source.new_context(&mut context.1),
1016 ConfigValue::Bool(true),
1017 );
1018 assert!(v.is_err());
1019 }
1020
1021 #[test]
1022 fn from_value_for_float_types() {
1023 let mut context = TestContext::new();
1024
1025 macro_rules! check_float {
1026 ($ty:ty, $val:expr, $expect:expr) => {
1027 let v = <$ty as FromValue>::from_value(
1028 &mut context.0.source.new_context(&mut context.1),
1029 ConfigValue::Float($val),
1030 );
1031 assert!((v.unwrap() - $expect).abs() < 1e-6);
1032
1033 let v = <$ty as FromValue>::from_value(
1034 &mut context.0.source.new_context(&mut context.1),
1035 ConfigValue::StrRef(&$val.to_string()),
1036 );
1037 assert!((v.unwrap() - $expect).abs() < 1e-6);
1038
1039 let v = <$ty as FromValue>::from_value(
1040 &mut context.0.source.new_context(&mut context.1),
1041 ConfigValue::Str($val.to_string()),
1042 );
1043 assert!((v.unwrap() - $expect).abs() < 1e-6);
1044 };
1045 }
1046
1047 check_float!(f32, 1.23, 1.23f32);
1048 check_float!(f64, 4.56, 4.56f64);
1049
1050 let v = <f32 as FromValue>::from_value(
1051 &mut context.0.source.new_context(&mut context.1),
1052 ConfigValue::Bool(true),
1053 );
1054 assert!(v.is_err());
1055 }
1056
1057 #[test]
1058 fn parse_duration_from_str_cases() {
1059 let mut context = TestContext::new();
1060
1061 assert_eq!(
1063 parse_duration_from_str(&mut context.0.source.new_context(&mut context.1), "123s")
1064 .unwrap(),
1065 Duration::new(123, 0)
1066 );
1067 assert_eq!(
1069 parse_duration_from_str(&mut context.0.source.new_context(&mut context.1), "2m")
1070 .unwrap(),
1071 Duration::new(120, 0)
1072 );
1073 assert_eq!(
1075 parse_duration_from_str(&mut context.0.source.new_context(&mut context.1), "3h")
1076 .unwrap(),
1077 Duration::new(3 * 3600, 0)
1078 );
1079 assert_eq!(
1081 parse_duration_from_str(&mut context.0.source.new_context(&mut context.1), "5ms")
1082 .unwrap(),
1083 Duration::new(0, 5_000_000)
1084 );
1085 assert_eq!(
1087 parse_duration_from_str(&mut context.0.source.new_context(&mut context.1), "7us")
1088 .unwrap(),
1089 Duration::new(0, 7_000)
1090 );
1091 assert_eq!(
1093 parse_duration_from_str(&mut context.0.source.new_context(&mut context.1), "9ns")
1094 .unwrap(),
1095 Duration::new(0, 9)
1096 );
1097 assert_eq!(
1099 parse_duration_from_str(&mut context.0.source.new_context(&mut context.1), "11")
1100 .unwrap(),
1101 Duration::new(11, 0)
1102 );
1103 assert!(
1105 parse_duration_from_str(&mut context.0.source.new_context(&mut context.1), "abc")
1106 .is_err()
1107 );
1108 assert!(
1110 parse_duration_from_str(&mut context.0.source.new_context(&mut context.1), "1x")
1111 .is_err()
1112 );
1113 }
1114
1115 #[test]
1116 fn from_value_for_duration() {
1117 let mut context = TestContext::new();
1118
1119 let v = <Duration as FromValue>::from_value(
1121 &mut context.0.source.new_context(&mut context.1),
1122 ConfigValue::Str("123".to_string()),
1123 );
1124 assert_eq!(v.unwrap(), Duration::new(123, 0));
1125
1126 let v = <Duration as FromValue>::from_value(
1128 &mut context.0.source.new_context(&mut context.1),
1129 ConfigValue::Str("2m".to_string()),
1130 );
1131 assert_eq!(v.unwrap(), Duration::new(120, 0));
1132
1133 let v = <Duration as FromValue>::from_value(
1135 &mut context.0.source.new_context(&mut context.1),
1136 ConfigValue::StrRef("3h"),
1137 );
1138 assert_eq!(v.unwrap(), Duration::new(3 * 3600, 0));
1139
1140 let v = <Duration as FromValue>::from_value(
1142 &mut context.0.source.new_context(&mut context.1),
1143 ConfigValue::Int(7),
1144 );
1145 assert_eq!(v.unwrap(), Duration::new(7, 0));
1146
1147 let v = <Duration as FromValue>::from_value(
1149 &mut context.0.source.new_context(&mut context.1),
1150 ConfigValue::Float(1.5),
1151 );
1152 assert!((v.unwrap().as_secs_f64() - 1.5).abs() < 1e-6);
1153
1154 let v = <Duration as FromValue>::from_value(
1156 &mut context.0.source.new_context(&mut context.1),
1157 ConfigValue::Bool(true),
1158 );
1159 assert!(v.is_err());
1160 }
1161
1162 #[test]
1163 fn impl_enum_macro_test() {
1164 #[derive(Debug, PartialEq)]
1165 enum MyEnum {
1166 Foo,
1167 Bar,
1168 Baz,
1169 }
1170 impl_enum!(MyEnum {
1171 "foo" => MyEnum::Foo
1172 "bar" => MyEnum::Bar
1173 "baz" => MyEnum::Baz
1174 });
1175
1176 let mut context = TestContext::new();
1177
1178 assert_eq!(
1180 <MyEnum as FromStringValue>::from_str_value(
1181 &mut context.0.source.new_context(&mut context.1),
1182 "foo"
1183 )
1184 .unwrap(),
1185 MyEnum::Foo
1186 );
1187 assert_eq!(
1188 <MyEnum as FromStringValue>::from_str_value(
1189 &mut context.0.source.new_context(&mut context.1),
1190 "bar"
1191 )
1192 .unwrap(),
1193 MyEnum::Bar
1194 );
1195 assert_eq!(
1196 <MyEnum as FromStringValue>::from_str_value(
1197 &mut context.0.source.new_context(&mut context.1),
1198 "baz"
1199 )
1200 .unwrap(),
1201 MyEnum::Baz
1202 );
1203
1204 assert_eq!(
1206 <MyEnum as FromStringValue>::from_str_value(
1207 &mut context.0.source.new_context(&mut context.1),
1208 "FOO"
1209 )
1210 .unwrap(),
1211 MyEnum::Foo
1212 );
1213
1214 let err = <MyEnum as FromStringValue>::from_str_value(
1216 &mut context.0.source.new_context(&mut context.1),
1217 "unknown",
1218 );
1219 assert!(err.is_err());
1220 }
1221}