1use std::collections::BTreeMap;
59use std::marker::PhantomData;
60use std::sync::atomic::Ordering::SeqCst;
61use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, AtomicUsize};
62use std::sync::{Arc, RwLock};
63use std::time::Duration;
64
65use serde::{Deserialize, Serialize};
66use tracing::error;
67
68#[derive(Clone, Debug)]
77pub struct Config<D: ConfigDefault> {
78 name: &'static str,
79 desc: &'static str,
80 default: D,
81}
82
83impl<D: ConfigDefault> Config<D> {
84 pub const fn new(name: &'static str, default: D, desc: &'static str) -> Self {
99 Config {
100 name,
101 default,
102 desc,
103 }
104 }
105
106 pub fn name(&self) -> &str {
108 self.name
109 }
110
111 pub fn desc(&self) -> &str {
113 self.desc
114 }
115
116 pub fn default(&self) -> &D {
118 &self.default
119 }
120
121 pub fn get(&self, set: &ConfigSet) -> D::ConfigType {
131 D::ConfigType::from_val(self.shared(set).load())
132 }
133
134 pub fn handle(&self, set: &ConfigSet) -> ConfigValHandle<D::ConfigType> {
138 ConfigValHandle {
139 val: self.shared(set).clone(),
140 _type: PhantomData,
141 }
142 }
143
144 fn shared<'a>(&self, set: &'a ConfigSet) -> &'a ConfigValAtomic {
146 &set.configs
147 .get(self.name)
148 .unwrap_or_else(|| panic!("config {} should be registered to set", self.name))
149 .val
150 }
151
152 pub fn parse_val(&self, val: &str) -> Result<ConfigVal, String> {
154 let val = D::ConfigType::parse(val)?;
155 let val = Into::<ConfigVal>::into(val);
156 Ok(val)
157 }
158}
159
160pub trait ConfigType: Into<ConfigVal> + Clone + Sized {
162 fn from_val(val: ConfigVal) -> Self;
166
167 fn parse(s: &str) -> Result<Self, String>;
169}
170
171pub trait ConfigDefault: Clone {
173 type ConfigType: ConfigType;
174
175 fn into_config_type(self) -> Self::ConfigType;
177}
178
179impl<T: ConfigType> ConfigDefault for T {
180 type ConfigType = T;
181
182 fn into_config_type(self) -> T {
183 self
184 }
185}
186
187impl<T: ConfigType> ConfigDefault for fn() -> T {
188 type ConfigType = T;
189
190 fn into_config_type(self) -> T {
191 (self)()
192 }
193}
194
195#[derive(Clone, Default)]
209pub struct ConfigSet {
210 configs: BTreeMap<String, ConfigEntry>,
211}
212
213impl ConfigSet {
214 pub fn add<D: ConfigDefault>(mut self, config: &Config<D>) -> Self {
224 let default = config.default.clone().into_config_type();
225 let default = Into::<ConfigVal>::into(default);
226 let config = ConfigEntry {
227 name: config.name,
228 desc: config.desc,
229 default: default.clone(),
230 val: ConfigValAtomic::from(default),
231 };
232 if let Some(prev) = self.configs.insert(config.name.to_owned(), config) {
233 panic!("{} registered twice", prev.name);
234 }
235 self
236 }
237
238 pub fn entries(&self) -> impl Iterator<Item = &ConfigEntry> {
240 self.configs.values()
241 }
242
243 pub fn entry(&self, name: &str) -> Option<&ConfigEntry> {
245 self.configs.get(name)
246 }
247}
248
249#[derive(Clone, Debug)]
251pub struct ConfigEntry {
252 name: &'static str,
253 desc: &'static str,
254 default: ConfigVal,
255 val: ConfigValAtomic,
256}
257
258impl ConfigEntry {
259 pub fn name(&self) -> &'static str {
261 self.name
262 }
263
264 pub fn desc(&self) -> &'static str {
266 self.desc
267 }
268
269 pub fn default(&self) -> &ConfigVal {
273 &self.default
274 }
275
276 pub fn val(&self) -> ConfigVal {
278 self.val.load()
279 }
280}
281
282#[derive(Debug, Clone)]
288pub struct ConfigValHandle<T> {
289 val: ConfigValAtomic,
290 _type: PhantomData<T>,
291}
292
293impl<T: ConfigType> ConfigValHandle<T> {
294 pub fn get(&self) -> T {
297 T::from_val(self.val.load())
298 }
299
300 pub fn disconnected<X>(value: X) -> Self
303 where
304 X: ConfigDefault<ConfigType = T>,
305 {
306 let config_val: ConfigVal = value.into_config_type().into();
307 Self {
308 val: config_val.into(),
309 _type: Default::default(),
310 }
311 }
312}
313
314#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
317pub enum ConfigVal {
318 Bool(bool),
320 U32(u32),
322 Usize(usize),
324 OptUsize(Option<usize>),
326 F64(f64),
328 String(String),
330 Duration(Duration),
332 #[serde(with = "serde_json_string")]
334 Json(serde_json::Value),
335}
336
337mod serde_json_string {
340 use serde::de::{Deserialize, Deserializer, Error};
341 use serde::ser::Serializer;
342
343 pub fn serialize<S>(value: &serde_json::Value, serializer: S) -> Result<S::Ok, S::Error>
344 where
345 S: Serializer,
346 {
347 serializer.serialize_str(&value.to_string())
348 }
349
350 pub fn deserialize<'de, D>(deserializer: D) -> Result<serde_json::Value, D::Error>
351 where
352 D: Deserializer<'de>,
353 {
354 let s = String::deserialize(deserializer)?;
355 serde_json::from_str(&s).map_err(D::Error::custom)
356 }
357}
358
359#[derive(Clone, Debug)]
367enum ConfigValAtomic {
368 Bool(Arc<AtomicBool>),
369 U32(Arc<AtomicU32>),
370 Usize(Arc<AtomicUsize>),
371 OptUsize(Arc<RwLock<Option<usize>>>),
372 F64(Arc<AtomicU64>),
374 String(Arc<RwLock<String>>),
375 Duration(Arc<RwLock<Duration>>),
376 Json(Arc<RwLock<serde_json::Value>>),
377}
378
379impl From<ConfigVal> for ConfigValAtomic {
380 fn from(val: ConfigVal) -> ConfigValAtomic {
381 match val {
382 ConfigVal::Bool(x) => ConfigValAtomic::Bool(Arc::new(AtomicBool::new(x))),
383 ConfigVal::U32(x) => ConfigValAtomic::U32(Arc::new(AtomicU32::new(x))),
384 ConfigVal::Usize(x) => ConfigValAtomic::Usize(Arc::new(AtomicUsize::new(x))),
385 ConfigVal::OptUsize(x) => ConfigValAtomic::OptUsize(Arc::new(RwLock::new(x))),
386 ConfigVal::F64(x) => ConfigValAtomic::F64(Arc::new(AtomicU64::new(x.to_bits()))),
387 ConfigVal::String(x) => ConfigValAtomic::String(Arc::new(RwLock::new(x))),
388 ConfigVal::Duration(x) => ConfigValAtomic::Duration(Arc::new(RwLock::new(x))),
389 ConfigVal::Json(x) => ConfigValAtomic::Json(Arc::new(RwLock::new(x))),
390 }
391 }
392}
393
394impl ConfigValAtomic {
395 fn load(&self) -> ConfigVal {
396 match self {
397 ConfigValAtomic::Bool(x) => ConfigVal::Bool(x.load(SeqCst)),
398 ConfigValAtomic::U32(x) => ConfigVal::U32(x.load(SeqCst)),
399 ConfigValAtomic::Usize(x) => ConfigVal::Usize(x.load(SeqCst)),
400 ConfigValAtomic::OptUsize(x) => ConfigVal::OptUsize(*x.read().expect("lock poisoned")),
401 ConfigValAtomic::F64(x) => ConfigVal::F64(f64::from_bits(x.load(SeqCst))),
402 ConfigValAtomic::String(x) => {
403 ConfigVal::String(x.read().expect("lock poisoned").clone())
404 }
405 ConfigValAtomic::Duration(x) => ConfigVal::Duration(*x.read().expect("lock poisoned")),
406 ConfigValAtomic::Json(x) => ConfigVal::Json(x.read().expect("lock poisoned").clone()),
407 }
408 }
409
410 fn store(&self, val: ConfigVal) {
411 match (self, val) {
412 (ConfigValAtomic::Bool(x), ConfigVal::Bool(val)) => x.store(val, SeqCst),
413 (ConfigValAtomic::U32(x), ConfigVal::U32(val)) => x.store(val, SeqCst),
414 (ConfigValAtomic::Usize(x), ConfigVal::Usize(val)) => x.store(val, SeqCst),
415 (ConfigValAtomic::OptUsize(x), ConfigVal::OptUsize(val)) => {
416 *x.write().expect("lock poisoned") = val
417 }
418 (ConfigValAtomic::F64(x), ConfigVal::F64(val)) => x.store(val.to_bits(), SeqCst),
419 (ConfigValAtomic::String(x), ConfigVal::String(val)) => {
420 *x.write().expect("lock poisoned") = val
421 }
422 (ConfigValAtomic::Duration(x), ConfigVal::Duration(val)) => {
423 *x.write().expect("lock poisoned") = val
424 }
425 (ConfigValAtomic::Json(x), ConfigVal::Json(val)) => {
426 *x.write().expect("lock poisoned") = val
427 }
428 (ConfigValAtomic::Bool(_), val)
429 | (ConfigValAtomic::U32(_), val)
430 | (ConfigValAtomic::Usize(_), val)
431 | (ConfigValAtomic::OptUsize(_), val)
432 | (ConfigValAtomic::F64(_), val)
433 | (ConfigValAtomic::String(_), val)
434 | (ConfigValAtomic::Duration(_), val)
435 | (ConfigValAtomic::Json(_), val) => {
436 panic!("attempted to store {val:?} value in {self:?} parameter")
437 }
438 }
439 }
440}
441
442#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
447pub struct ConfigUpdates {
448 pub updates: BTreeMap<String, ConfigVal>,
449}
450
451impl ConfigUpdates {
452 pub fn add<T, U>(&mut self, config: &Config<T>, val: U)
457 where
458 T: ConfigDefault,
459 U: ConfigDefault<ConfigType = T::ConfigType>,
460 {
461 self.add_dynamic(config.name, val.into_config_type().into());
462 }
463
464 pub fn add_dynamic(&mut self, name: &str, val: ConfigVal) {
472 self.updates.insert(name.to_owned(), val);
473 }
474
475 pub fn extend(&mut self, mut other: Self) {
477 self.updates.append(&mut other.updates)
478 }
479
480 pub fn apply(&self, set: &ConfigSet) {
490 for (name, val) in self.updates.iter() {
491 let Some(config) = set.configs.get(name) else {
492 error!("config update {} {:?} not known set: {:?}", name, val, set);
493 continue;
494 };
495 config.val.store(val.clone());
496 }
497 }
498}
499
500mod impls {
501 use std::num::{ParseFloatError, ParseIntError};
502 use std::str::ParseBoolError;
503 use std::time::Duration;
504
505 use crate::{ConfigDefault, ConfigSet, ConfigType, ConfigVal};
506
507 impl ConfigType for bool {
508 fn from_val(val: ConfigVal) -> Self {
509 match val {
510 ConfigVal::Bool(x) => x,
511 x => panic!("expected bool value got {:?}", x),
512 }
513 }
514
515 fn parse(s: &str) -> Result<Self, String> {
516 match s {
517 "on" => return Ok(true),
518 "off" => return Ok(false),
519 _ => {}
520 }
521 s.parse().map_err(|e: ParseBoolError| e.to_string())
522 }
523 }
524
525 impl From<bool> for ConfigVal {
526 fn from(val: bool) -> ConfigVal {
527 ConfigVal::Bool(val)
528 }
529 }
530
531 impl ConfigType for u32 {
532 fn from_val(val: ConfigVal) -> Self {
533 match val {
534 ConfigVal::U32(x) => x,
535 x => panic!("expected u32 value got {:?}", x),
536 }
537 }
538
539 fn parse(s: &str) -> Result<Self, String> {
540 s.parse().map_err(|e: ParseIntError| e.to_string())
541 }
542 }
543
544 impl From<u32> for ConfigVal {
545 fn from(val: u32) -> ConfigVal {
546 ConfigVal::U32(val)
547 }
548 }
549
550 impl ConfigType for usize {
551 fn from_val(val: ConfigVal) -> Self {
552 match val {
553 ConfigVal::Usize(x) => x,
554 x => panic!("expected usize value got {:?}", x),
555 }
556 }
557
558 fn parse(s: &str) -> Result<Self, String> {
559 s.parse().map_err(|e: ParseIntError| e.to_string())
560 }
561 }
562
563 impl From<usize> for ConfigVal {
564 fn from(val: usize) -> ConfigVal {
565 ConfigVal::Usize(val)
566 }
567 }
568
569 impl ConfigType for Option<usize> {
570 fn from_val(val: ConfigVal) -> Self {
571 match val {
572 ConfigVal::OptUsize(x) => x,
573 x => panic!("expected usize value got {:?}", x),
574 }
575 }
576
577 fn parse(s: &str) -> Result<Self, String> {
578 if s.is_empty() {
579 Ok(None)
580 } else {
581 let val = s.parse().map_err(|e: ParseIntError| e.to_string())?;
582 Ok(Some(val))
583 }
584 }
585 }
586
587 impl From<Option<usize>> for ConfigVal {
588 fn from(val: Option<usize>) -> ConfigVal {
589 ConfigVal::OptUsize(val)
590 }
591 }
592
593 impl ConfigType for f64 {
594 fn from_val(val: ConfigVal) -> Self {
595 match val {
596 ConfigVal::F64(x) => x,
597 x => panic!("expected f64 value got {:?}", x),
598 }
599 }
600
601 fn parse(s: &str) -> Result<Self, String> {
602 s.parse().map_err(|e: ParseFloatError| e.to_string())
603 }
604 }
605
606 impl From<f64> for ConfigVal {
607 fn from(val: f64) -> ConfigVal {
608 ConfigVal::F64(val)
609 }
610 }
611
612 impl ConfigType for String {
613 fn from_val(val: ConfigVal) -> Self {
614 match val {
615 ConfigVal::String(x) => x,
616 x => panic!("expected String value got {:?}", x),
617 }
618 }
619
620 fn parse(s: &str) -> Result<Self, String> {
621 Ok(s.to_string())
622 }
623 }
624
625 impl From<String> for ConfigVal {
626 fn from(val: String) -> ConfigVal {
627 ConfigVal::String(val)
628 }
629 }
630
631 impl ConfigDefault for &str {
632 type ConfigType = String;
633
634 fn into_config_type(self) -> String {
635 self.into()
636 }
637 }
638
639 impl ConfigType for Duration {
640 fn from_val(val: ConfigVal) -> Self {
641 match val {
642 ConfigVal::Duration(x) => x,
643 x => panic!("expected Duration value got {:?}", x),
644 }
645 }
646
647 fn parse(s: &str) -> Result<Self, String> {
648 humantime::parse_duration(s).map_err(|e| e.to_string())
649 }
650 }
651
652 impl From<Duration> for ConfigVal {
653 fn from(val: Duration) -> ConfigVal {
654 ConfigVal::Duration(val)
655 }
656 }
657
658 impl ConfigType for serde_json::Value {
659 fn from_val(val: ConfigVal) -> Self {
660 match val {
661 ConfigVal::Json(x) => x,
662 x => panic!("expected JSON value got {:?}", x),
663 }
664 }
665
666 fn parse(s: &str) -> Result<Self, String> {
667 serde_json::from_str(s).map_err(|e| e.to_string())
668 }
669 }
670
671 impl From<serde_json::Value> for ConfigVal {
672 fn from(val: serde_json::Value) -> ConfigVal {
673 ConfigVal::Json(val)
674 }
675 }
676
677 impl std::fmt::Debug for ConfigSet {
678 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
679 let ConfigSet { configs } = self;
680 f.debug_map()
681 .entries(configs.iter().map(|(name, val)| (name, val.val())))
682 .finish()
683 }
684 }
685}
686
687#[cfg(test)]
688mod tests {
689 use super::*;
690
691 use mz_ore::assert_err;
692
693 const BOOL: Config<bool> = Config::new("bool", true, "");
694 const U32: Config<u32> = Config::new("u32", 4, "");
695 const USIZE: Config<usize> = Config::new("usize", 1, "");
696 const OPT_USIZE: Config<Option<usize>> = Config::new("opt_usize", Some(2), "");
697 const F64: Config<f64> = Config::new("f64", 5.0, "");
698 const STRING: Config<&str> = Config::new("string", "a", "");
699 const DURATION: Config<Duration> = Config::new("duration", Duration::from_nanos(3), "");
700 const JSON: Config<fn() -> serde_json::Value> =
701 Config::new("json", || serde_json::json!({}), "");
702
703 #[mz_ore::test]
704 fn all_types() {
705 let configs = ConfigSet::default()
706 .add(&BOOL)
707 .add(&USIZE)
708 .add(&U32)
709 .add(&OPT_USIZE)
710 .add(&F64)
711 .add(&STRING)
712 .add(&DURATION)
713 .add(&JSON);
714 assert_eq!(BOOL.get(&configs), true);
715 assert_eq!(U32.get(&configs), 4);
716 assert_eq!(USIZE.get(&configs), 1);
717 assert_eq!(OPT_USIZE.get(&configs), Some(2));
718 assert_eq!(F64.get(&configs), 5.0);
719 assert_eq!(STRING.get(&configs), "a");
720 assert_eq!(DURATION.get(&configs), Duration::from_nanos(3));
721 assert_eq!(JSON.get(&configs), serde_json::json!({}));
722
723 let mut updates = ConfigUpdates::default();
724 updates.add(&BOOL, false);
725 updates.add(&U32, 7);
726 updates.add(&USIZE, 2);
727 updates.add(&OPT_USIZE, None);
728 updates.add(&F64, 8.0);
729 updates.add(&STRING, "b");
730 updates.add(&DURATION, Duration::from_nanos(4));
731 updates.add(&JSON, serde_json::json!({"a": 1}));
732 updates.apply(&configs);
733
734 assert_eq!(BOOL.get(&configs), false);
735 assert_eq!(U32.get(&configs), 7);
736 assert_eq!(USIZE.get(&configs), 2);
737 assert_eq!(OPT_USIZE.get(&configs), None);
738 assert_eq!(F64.get(&configs), 8.0);
739 assert_eq!(STRING.get(&configs), "b");
740 assert_eq!(DURATION.get(&configs), Duration::from_nanos(4));
741 assert_eq!(JSON.get(&configs), serde_json::json!({"a": 1}));
742 }
743
744 #[mz_ore::test]
745 fn fn_default() {
746 const BOOL_FN_DEFAULT: Config<fn() -> bool> = Config::new("bool", || !true, "");
747 const STRING_FN_DEFAULT: Config<fn() -> String> =
748 Config::new("string", || "x".repeat(3), "");
749
750 let configs = ConfigSet::default()
751 .add(&BOOL_FN_DEFAULT)
752 .add(&STRING_FN_DEFAULT);
753 assert_eq!(BOOL_FN_DEFAULT.get(&configs), false);
754 assert_eq!(STRING_FN_DEFAULT.get(&configs), "xxx");
755 }
756
757 #[mz_ore::test]
758 fn config_set() {
759 let c0 = ConfigSet::default().add(&USIZE);
760 assert_eq!(USIZE.get(&c0), 1);
761 let mut updates = ConfigUpdates::default();
762 updates.add(&USIZE, 2);
763 updates.apply(&c0);
764 assert_eq!(USIZE.get(&c0), 2);
765
766 let c1 = ConfigSet::default().add(&USIZE);
769 assert_eq!(USIZE.get(&c1), 1);
770 let mut updates = ConfigUpdates::default();
771 updates.add(&USIZE, 3);
772 updates.apply(&c1);
773 assert_eq!(USIZE.get(&c1), 3);
774 assert_eq!(USIZE.get(&c0), 2);
775
776 let mut updates = ConfigUpdates::default();
778 for e in c0.entries() {
779 updates.add_dynamic(e.name, e.val());
780 }
781 assert_eq!(USIZE.get(&c1), 3);
782 updates.apply(&c1);
783 assert_eq!(USIZE.get(&c1), 2);
784 }
785
786 #[mz_ore::test]
787 fn config_updates_extend() {
788 let mut u1 = {
794 let c = ConfigSet::default().add(&USIZE).add(&STRING);
795 let mut x = ConfigUpdates::default();
796 for e in c.entries() {
797 x.add_dynamic(e.name(), e.val());
798 }
799 x
800 };
801 let u2 = {
802 let c = ConfigSet::default().add(&USIZE).add(&DURATION);
803 let mut updates = ConfigUpdates::default();
804 updates.add(&USIZE, 2);
805 updates.apply(&c);
806 let mut x = ConfigUpdates::default();
807 for e in c.entries() {
808 x.add_dynamic(e.name(), e.val());
809 }
810 x
811 };
812 assert_eq!(u1.updates.len(), 2);
813 assert_eq!(u2.updates.len(), 2);
814 u1.extend(u2);
815 assert_eq!(u1.updates.len(), 3);
816
817 let c = ConfigSet::default().add(&USIZE);
820 u1.apply(&c);
821 assert_eq!(USIZE.get(&c), 2);
822 }
823
824 #[mz_ore::test]
825 fn config_parse() {
826 assert_eq!(BOOL.parse_val("true"), Ok(ConfigVal::Bool(true)));
827 assert_eq!(BOOL.parse_val("on"), Ok(ConfigVal::Bool(true)));
828 assert_eq!(BOOL.parse_val("false"), Ok(ConfigVal::Bool(false)));
829 assert_eq!(BOOL.parse_val("off"), Ok(ConfigVal::Bool(false)));
830 assert_err!(BOOL.parse_val("42"));
831 assert_err!(BOOL.parse_val("66.6"));
832 assert_err!(BOOL.parse_val("farragut"));
833 assert_err!(BOOL.parse_val(""));
834 assert_err!(BOOL.parse_val("5 s"));
835
836 assert_err!(U32.parse_val("true"));
837 assert_err!(U32.parse_val("false"));
838 assert_eq!(U32.parse_val("42"), Ok(ConfigVal::U32(42)));
839 assert_err!(U32.parse_val("66.6"));
840 assert_err!(U32.parse_val("farragut"));
841 assert_err!(U32.parse_val(""));
842 assert_err!(U32.parse_val("5 s"));
843
844 assert_err!(USIZE.parse_val("true"));
845 assert_err!(USIZE.parse_val("false"));
846 assert_eq!(USIZE.parse_val("42"), Ok(ConfigVal::Usize(42)));
847 assert_err!(USIZE.parse_val("66.6"));
848 assert_err!(USIZE.parse_val("farragut"));
849 assert_err!(USIZE.parse_val(""));
850 assert_err!(USIZE.parse_val("5 s"));
851
852 assert_err!(OPT_USIZE.parse_val("true"));
853 assert_err!(OPT_USIZE.parse_val("false"));
854 assert_eq!(OPT_USIZE.parse_val("42"), Ok(ConfigVal::OptUsize(Some(42))));
855 assert_err!(OPT_USIZE.parse_val("66.6"));
856 assert_err!(OPT_USIZE.parse_val("farragut"));
857 assert_eq!(OPT_USIZE.parse_val(""), Ok(ConfigVal::OptUsize(None)));
858 assert_err!(OPT_USIZE.parse_val("5 s"));
859
860 assert_err!(F64.parse_val("true"));
861 assert_err!(F64.parse_val("false"));
862 assert_eq!(F64.parse_val("42"), Ok(ConfigVal::F64(42.0)));
863 assert_eq!(F64.parse_val("66.6"), Ok(ConfigVal::F64(66.6)));
864 assert_err!(F64.parse_val("farragut"));
865 assert_err!(F64.parse_val(""));
866 assert_err!(F64.parse_val("5 s"));
867
868 assert_eq!(
869 STRING.parse_val("true"),
870 Ok(ConfigVal::String("true".to_string()))
871 );
872 assert_eq!(
873 STRING.parse_val("false"),
874 Ok(ConfigVal::String("false".to_string()))
875 );
876 assert_eq!(
877 STRING.parse_val("66.6"),
878 Ok(ConfigVal::String("66.6".to_string()))
879 );
880 assert_eq!(
881 STRING.parse_val("42"),
882 Ok(ConfigVal::String("42".to_string()))
883 );
884 assert_eq!(
885 STRING.parse_val("farragut"),
886 Ok(ConfigVal::String("farragut".to_string()))
887 );
888 assert_eq!(STRING.parse_val(""), Ok(ConfigVal::String("".to_string())));
889 assert_eq!(
890 STRING.parse_val("5 s"),
891 Ok(ConfigVal::String("5 s".to_string()))
892 );
893
894 assert_err!(DURATION.parse_val("true"));
895 assert_err!(DURATION.parse_val("false"));
896 assert_err!(DURATION.parse_val("42"));
897 assert_err!(DURATION.parse_val("66.6"));
898 assert_err!(DURATION.parse_val("farragut"));
899 assert_err!(DURATION.parse_val(""));
900 assert_eq!(
901 DURATION.parse_val("5 s"),
902 Ok(ConfigVal::Duration(Duration::from_secs(5)))
903 );
904
905 assert_eq!(
906 JSON.parse_val("true"),
907 Ok(ConfigVal::Json(serde_json::json!(true)))
908 );
909 assert_eq!(
910 JSON.parse_val("false"),
911 Ok(ConfigVal::Json(serde_json::json!(false)))
912 );
913 assert_eq!(
914 JSON.parse_val("42"),
915 Ok(ConfigVal::Json(serde_json::json!(42)))
916 );
917 assert_eq!(
918 JSON.parse_val("66.6"),
919 Ok(ConfigVal::Json(serde_json::json!(66.6)))
920 );
921 assert_err!(JSON.parse_val("farragut"));
922 assert_err!(JSON.parse_val(""));
923 assert_err!(JSON.parse_val("5 s"));
924 assert_eq!(
925 JSON.parse_val("{\"joe\": \"developer\"}"),
926 Ok(ConfigVal::Json(serde_json::json!({"joe": "developer"})))
927 );
928 }
929}