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 #[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 let v: Result<(), ConfigError> =
858 <()>::from_config(&mut context.0.source.new_context(&mut context.1), None);
859 assert!(v.is_ok());
860 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 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 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 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 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 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 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 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 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 let r = Holder::from_str_value(&mut context.0.source.new_context(&mut context.1), "abc");
964 assert!(r.is_err());
965 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 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 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 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 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 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 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 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 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 assert!(
1106 parse_duration_from_str(&mut context.0.source.new_context(&mut context.1), "abc")
1107 .is_err()
1108 );
1109 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 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 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 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 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 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 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 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 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 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}