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 tracing::error;
66
67use mz_proto::{ProtoType, RustType};
68
69include!(concat!(env!("OUT_DIR"), "/mz_dyncfg.rs"));
70
71#[derive(Clone, Debug)]
80pub struct Config<D: ConfigDefault> {
81 name: &'static str,
82 desc: &'static str,
83 default: D,
84}
85
86impl<D: ConfigDefault> Config<D> {
87 pub const fn new(name: &'static str, default: D, desc: &'static str) -> Self {
102 Config {
103 name,
104 default,
105 desc,
106 }
107 }
108
109 pub fn name(&self) -> &str {
111 self.name
112 }
113
114 pub fn desc(&self) -> &str {
116 self.desc
117 }
118
119 pub fn default(&self) -> &D {
121 &self.default
122 }
123
124 pub fn get(&self, set: &ConfigSet) -> D::ConfigType {
134 D::ConfigType::from_val(self.shared(set).load())
135 }
136
137 pub fn handle(&self, set: &ConfigSet) -> ConfigValHandle<D::ConfigType> {
141 ConfigValHandle {
142 val: self.shared(set).clone(),
143 _type: PhantomData,
144 }
145 }
146
147 fn shared<'a>(&self, set: &'a ConfigSet) -> &'a ConfigValAtomic {
149 &set.configs
150 .get(self.name)
151 .unwrap_or_else(|| panic!("config {} should be registered to set", self.name))
152 .val
153 }
154
155 pub fn parse_val(&self, val: &str) -> Result<ConfigVal, String> {
157 let val = D::ConfigType::parse(val)?;
158 let val = Into::<ConfigVal>::into(val);
159 Ok(val)
160 }
161}
162
163pub trait ConfigType: Into<ConfigVal> + Clone + Sized {
165 fn from_val(val: ConfigVal) -> Self;
169
170 fn parse(s: &str) -> Result<Self, String>;
172}
173
174pub trait ConfigDefault: Clone {
176 type ConfigType: ConfigType;
177
178 fn into_config_type(self) -> Self::ConfigType;
180}
181
182impl<T: ConfigType> ConfigDefault for T {
183 type ConfigType = T;
184
185 fn into_config_type(self) -> T {
186 self
187 }
188}
189
190impl<T: ConfigType> ConfigDefault for fn() -> T {
191 type ConfigType = T;
192
193 fn into_config_type(self) -> T {
194 (self)()
195 }
196}
197
198#[derive(Clone, Default)]
212pub struct ConfigSet {
213 configs: BTreeMap<String, ConfigEntry>,
214}
215
216impl ConfigSet {
217 pub fn add<D: ConfigDefault>(mut self, config: &Config<D>) -> Self {
227 let default = config.default.clone().into_config_type();
228 let default = Into::<ConfigVal>::into(default);
229 let config = ConfigEntry {
230 name: config.name,
231 desc: config.desc,
232 default: default.clone(),
233 val: ConfigValAtomic::from(default),
234 };
235 if let Some(prev) = self.configs.insert(config.name.to_owned(), config) {
236 panic!("{} registered twice", prev.name);
237 }
238 self
239 }
240
241 pub fn entries(&self) -> impl Iterator<Item = &ConfigEntry> {
243 self.configs.values()
244 }
245
246 pub fn entry(&self, name: &str) -> Option<&ConfigEntry> {
248 self.configs.get(name)
249 }
250}
251
252#[derive(Clone, Debug)]
254pub struct ConfigEntry {
255 name: &'static str,
256 desc: &'static str,
257 default: ConfigVal,
258 val: ConfigValAtomic,
259}
260
261impl ConfigEntry {
262 pub fn name(&self) -> &'static str {
264 self.name
265 }
266
267 pub fn desc(&self) -> &'static str {
269 self.desc
270 }
271
272 pub fn default(&self) -> &ConfigVal {
276 &self.default
277 }
278
279 pub fn val(&self) -> ConfigVal {
281 self.val.load()
282 }
283}
284
285#[derive(Debug, Clone)]
291pub struct ConfigValHandle<T> {
292 val: ConfigValAtomic,
293 _type: PhantomData<T>,
294}
295
296impl<T: ConfigType> ConfigValHandle<T> {
297 pub fn get(&self) -> T {
300 T::from_val(self.val.load())
301 }
302
303 pub fn disconnected<X>(value: X) -> Self
306 where
307 X: ConfigDefault<ConfigType = T>,
308 {
309 let config_val: ConfigVal = value.into_config_type().into();
310 Self {
311 val: config_val.into(),
312 _type: Default::default(),
313 }
314 }
315}
316
317#[derive(Clone, Debug, PartialEq)]
320pub enum ConfigVal {
321 Bool(bool),
323 U32(u32),
325 Usize(usize),
327 OptUsize(Option<usize>),
329 F64(f64),
331 String(String),
333 Duration(Duration),
335 Json(serde_json::Value),
337}
338
339#[derive(Clone, Debug)]
347enum ConfigValAtomic {
348 Bool(Arc<AtomicBool>),
349 U32(Arc<AtomicU32>),
350 Usize(Arc<AtomicUsize>),
351 OptUsize(Arc<RwLock<Option<usize>>>),
352 F64(Arc<AtomicU64>),
354 String(Arc<RwLock<String>>),
355 Duration(Arc<RwLock<Duration>>),
356 Json(Arc<RwLock<serde_json::Value>>),
357}
358
359impl From<ConfigVal> for ConfigValAtomic {
360 fn from(val: ConfigVal) -> ConfigValAtomic {
361 match val {
362 ConfigVal::Bool(x) => ConfigValAtomic::Bool(Arc::new(AtomicBool::new(x))),
363 ConfigVal::U32(x) => ConfigValAtomic::U32(Arc::new(AtomicU32::new(x))),
364 ConfigVal::Usize(x) => ConfigValAtomic::Usize(Arc::new(AtomicUsize::new(x))),
365 ConfigVal::OptUsize(x) => ConfigValAtomic::OptUsize(Arc::new(RwLock::new(x))),
366 ConfigVal::F64(x) => ConfigValAtomic::F64(Arc::new(AtomicU64::new(x.to_bits()))),
367 ConfigVal::String(x) => ConfigValAtomic::String(Arc::new(RwLock::new(x))),
368 ConfigVal::Duration(x) => ConfigValAtomic::Duration(Arc::new(RwLock::new(x))),
369 ConfigVal::Json(x) => ConfigValAtomic::Json(Arc::new(RwLock::new(x))),
370 }
371 }
372}
373
374impl ConfigValAtomic {
375 fn load(&self) -> ConfigVal {
376 match self {
377 ConfigValAtomic::Bool(x) => ConfigVal::Bool(x.load(SeqCst)),
378 ConfigValAtomic::U32(x) => ConfigVal::U32(x.load(SeqCst)),
379 ConfigValAtomic::Usize(x) => ConfigVal::Usize(x.load(SeqCst)),
380 ConfigValAtomic::OptUsize(x) => ConfigVal::OptUsize(*x.read().expect("lock poisoned")),
381 ConfigValAtomic::F64(x) => ConfigVal::F64(f64::from_bits(x.load(SeqCst))),
382 ConfigValAtomic::String(x) => {
383 ConfigVal::String(x.read().expect("lock poisoned").clone())
384 }
385 ConfigValAtomic::Duration(x) => ConfigVal::Duration(*x.read().expect("lock poisoned")),
386 ConfigValAtomic::Json(x) => ConfigVal::Json(x.read().expect("lock poisoned").clone()),
387 }
388 }
389
390 fn store(&self, val: ConfigVal) {
391 match (self, val) {
392 (ConfigValAtomic::Bool(x), ConfigVal::Bool(val)) => x.store(val, SeqCst),
393 (ConfigValAtomic::U32(x), ConfigVal::U32(val)) => x.store(val, SeqCst),
394 (ConfigValAtomic::Usize(x), ConfigVal::Usize(val)) => x.store(val, SeqCst),
395 (ConfigValAtomic::OptUsize(x), ConfigVal::OptUsize(val)) => {
396 *x.write().expect("lock poisoned") = val
397 }
398 (ConfigValAtomic::F64(x), ConfigVal::F64(val)) => x.store(val.to_bits(), SeqCst),
399 (ConfigValAtomic::String(x), ConfigVal::String(val)) => {
400 *x.write().expect("lock poisoned") = val
401 }
402 (ConfigValAtomic::Duration(x), ConfigVal::Duration(val)) => {
403 *x.write().expect("lock poisoned") = val
404 }
405 (ConfigValAtomic::Json(x), ConfigVal::Json(val)) => {
406 *x.write().expect("lock poisoned") = val
407 }
408 (ConfigValAtomic::Bool(_), val)
409 | (ConfigValAtomic::U32(_), val)
410 | (ConfigValAtomic::Usize(_), val)
411 | (ConfigValAtomic::OptUsize(_), val)
412 | (ConfigValAtomic::F64(_), val)
413 | (ConfigValAtomic::String(_), val)
414 | (ConfigValAtomic::Duration(_), val)
415 | (ConfigValAtomic::Json(_), val) => {
416 panic!("attempted to store {val:?} value in {self:?} parameter")
417 }
418 }
419 }
420}
421
422impl ConfigUpdates {
423 pub fn add<T, U>(&mut self, config: &Config<T>, val: U)
428 where
429 T: ConfigDefault,
430 U: ConfigDefault<ConfigType = T::ConfigType>,
431 {
432 self.add_dynamic(config.name, val.into_config_type().into());
433 }
434
435 pub fn add_dynamic(&mut self, name: &str, val: ConfigVal) {
443 self.updates.insert(
444 name.to_owned(),
445 ProtoConfigVal {
446 val: val.into_proto(),
447 },
448 );
449 }
450
451 pub fn extend(&mut self, mut other: Self) {
453 self.updates.append(&mut other.updates)
454 }
455
456 pub fn apply(&self, set: &ConfigSet) {
466 for (name, ProtoConfigVal { val }) in self.updates.iter() {
467 let Some(config) = set.configs.get(name) else {
468 error!("config update {} {:?} not known set: {:?}", name, val, set);
469 continue;
470 };
471 let val = match (val.clone()).into_rust() {
472 Ok(x) => x,
473 Err(err) => {
474 error!("config update {} decode error: {}", name, err);
475 continue;
476 }
477 };
478 config.val.store(val);
479 }
480 }
481}
482
483mod impls {
484 use std::num::{ParseFloatError, ParseIntError};
485 use std::str::ParseBoolError;
486 use std::time::Duration;
487
488 use mz_ore::cast::CastFrom;
489 use mz_proto::{ProtoType, RustType, TryFromProtoError};
490
491 use crate::{
492 ConfigDefault, ConfigSet, ConfigType, ConfigVal, ProtoOptionU64, proto_config_val,
493 };
494
495 impl ConfigType for bool {
496 fn from_val(val: ConfigVal) -> Self {
497 match val {
498 ConfigVal::Bool(x) => x,
499 x => panic!("expected bool value got {:?}", x),
500 }
501 }
502
503 fn parse(s: &str) -> Result<Self, String> {
504 match s {
505 "on" => return Ok(true),
506 "off" => return Ok(false),
507 _ => {}
508 }
509 s.parse().map_err(|e: ParseBoolError| e.to_string())
510 }
511 }
512
513 impl From<bool> for ConfigVal {
514 fn from(val: bool) -> ConfigVal {
515 ConfigVal::Bool(val)
516 }
517 }
518
519 impl ConfigType for u32 {
520 fn from_val(val: ConfigVal) -> Self {
521 match val {
522 ConfigVal::U32(x) => x,
523 x => panic!("expected u32 value got {:?}", x),
524 }
525 }
526
527 fn parse(s: &str) -> Result<Self, String> {
528 s.parse().map_err(|e: ParseIntError| e.to_string())
529 }
530 }
531
532 impl From<u32> for ConfigVal {
533 fn from(val: u32) -> ConfigVal {
534 ConfigVal::U32(val)
535 }
536 }
537
538 impl ConfigType for usize {
539 fn from_val(val: ConfigVal) -> Self {
540 match val {
541 ConfigVal::Usize(x) => x,
542 x => panic!("expected usize value got {:?}", x),
543 }
544 }
545
546 fn parse(s: &str) -> Result<Self, String> {
547 s.parse().map_err(|e: ParseIntError| e.to_string())
548 }
549 }
550
551 impl From<usize> for ConfigVal {
552 fn from(val: usize) -> ConfigVal {
553 ConfigVal::Usize(val)
554 }
555 }
556
557 impl ConfigType for Option<usize> {
558 fn from_val(val: ConfigVal) -> Self {
559 match val {
560 ConfigVal::OptUsize(x) => x,
561 x => panic!("expected usize value got {:?}", x),
562 }
563 }
564
565 fn parse(s: &str) -> Result<Self, String> {
566 if s.is_empty() {
567 Ok(None)
568 } else {
569 let val = s.parse().map_err(|e: ParseIntError| e.to_string())?;
570 Ok(Some(val))
571 }
572 }
573 }
574
575 impl From<Option<usize>> for ConfigVal {
576 fn from(val: Option<usize>) -> ConfigVal {
577 ConfigVal::OptUsize(val)
578 }
579 }
580
581 impl ConfigType for f64 {
582 fn from_val(val: ConfigVal) -> Self {
583 match val {
584 ConfigVal::F64(x) => x,
585 x => panic!("expected f64 value got {:?}", x),
586 }
587 }
588
589 fn parse(s: &str) -> Result<Self, String> {
590 s.parse().map_err(|e: ParseFloatError| e.to_string())
591 }
592 }
593
594 impl From<f64> for ConfigVal {
595 fn from(val: f64) -> ConfigVal {
596 ConfigVal::F64(val)
597 }
598 }
599
600 impl ConfigType for String {
601 fn from_val(val: ConfigVal) -> Self {
602 match val {
603 ConfigVal::String(x) => x,
604 x => panic!("expected String value got {:?}", x),
605 }
606 }
607
608 fn parse(s: &str) -> Result<Self, String> {
609 Ok(s.to_string())
610 }
611 }
612
613 impl From<String> for ConfigVal {
614 fn from(val: String) -> ConfigVal {
615 ConfigVal::String(val)
616 }
617 }
618
619 impl ConfigDefault for &str {
620 type ConfigType = String;
621
622 fn into_config_type(self) -> String {
623 self.into()
624 }
625 }
626
627 impl ConfigType for Duration {
628 fn from_val(val: ConfigVal) -> Self {
629 match val {
630 ConfigVal::Duration(x) => x,
631 x => panic!("expected Duration value got {:?}", x),
632 }
633 }
634
635 fn parse(s: &str) -> Result<Self, String> {
636 humantime::parse_duration(s).map_err(|e| e.to_string())
637 }
638 }
639
640 impl From<Duration> for ConfigVal {
641 fn from(val: Duration) -> ConfigVal {
642 ConfigVal::Duration(val)
643 }
644 }
645
646 impl ConfigType for serde_json::Value {
647 fn from_val(val: ConfigVal) -> Self {
648 match val {
649 ConfigVal::Json(x) => x,
650 x => panic!("expected JSON value got {:?}", x),
651 }
652 }
653
654 fn parse(s: &str) -> Result<Self, String> {
655 serde_json::from_str(s).map_err(|e| e.to_string())
656 }
657 }
658
659 impl From<serde_json::Value> for ConfigVal {
660 fn from(val: serde_json::Value) -> ConfigVal {
661 ConfigVal::Json(val)
662 }
663 }
664
665 impl RustType<Option<proto_config_val::Val>> for ConfigVal {
666 fn into_proto(&self) -> Option<proto_config_val::Val> {
667 use crate::proto_config_val::Val;
668 let val = match self {
669 ConfigVal::Bool(x) => Val::Bool(*x),
670 ConfigVal::U32(x) => Val::U32(*x),
671 ConfigVal::Usize(x) => Val::Usize(u64::cast_from(*x)),
672 ConfigVal::OptUsize(x) => Val::OptUsize(ProtoOptionU64 {
673 val: x.map(u64::cast_from),
674 }),
675 ConfigVal::F64(x) => Val::F64(*x),
676 ConfigVal::String(x) => Val::String(x.into_proto()),
677 ConfigVal::Duration(x) => Val::Duration(x.into_proto()),
678 ConfigVal::Json(x) => Val::Json(x.to_string()),
679 };
680 Some(val)
681 }
682
683 fn from_proto(proto: Option<proto_config_val::Val>) -> Result<Self, TryFromProtoError> {
684 let val = match proto {
685 Some(proto_config_val::Val::Bool(x)) => ConfigVal::Bool(x),
686 Some(proto_config_val::Val::U32(x)) => ConfigVal::U32(x),
687 Some(proto_config_val::Val::Usize(x)) => ConfigVal::Usize(usize::cast_from(x)),
688 Some(proto_config_val::Val::OptUsize(ProtoOptionU64 { val })) => {
689 ConfigVal::OptUsize(val.map(usize::cast_from))
690 }
691 Some(proto_config_val::Val::F64(x)) => ConfigVal::F64(x),
692 Some(proto_config_val::Val::String(x)) => ConfigVal::String(x),
693 Some(proto_config_val::Val::Duration(x)) => ConfigVal::Duration(x.into_rust()?),
694 Some(proto_config_val::Val::Json(x)) => ConfigVal::Json(serde_json::from_str(&x)?),
695 None => {
696 return Err(TryFromProtoError::unknown_enum_variant(
697 "ProtoConfigVal::Val",
698 ));
699 }
700 };
701 Ok(val)
702 }
703 }
704
705 impl std::fmt::Debug for ConfigSet {
706 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
707 let ConfigSet { configs } = self;
708 f.debug_map()
709 .entries(configs.iter().map(|(name, val)| (name, val.val())))
710 .finish()
711 }
712 }
713}
714
715#[cfg(test)]
716mod tests {
717 use super::*;
718
719 use mz_ore::assert_err;
720
721 const BOOL: Config<bool> = Config::new("bool", true, "");
722 const U32: Config<u32> = Config::new("u32", 4, "");
723 const USIZE: Config<usize> = Config::new("usize", 1, "");
724 const OPT_USIZE: Config<Option<usize>> = Config::new("opt_usize", Some(2), "");
725 const F64: Config<f64> = Config::new("f64", 5.0, "");
726 const STRING: Config<&str> = Config::new("string", "a", "");
727 const DURATION: Config<Duration> = Config::new("duration", Duration::from_nanos(3), "");
728 const JSON: Config<fn() -> serde_json::Value> =
729 Config::new("json", || serde_json::json!({}), "");
730
731 #[mz_ore::test]
732 fn all_types() {
733 let configs = ConfigSet::default()
734 .add(&BOOL)
735 .add(&USIZE)
736 .add(&U32)
737 .add(&OPT_USIZE)
738 .add(&F64)
739 .add(&STRING)
740 .add(&DURATION)
741 .add(&JSON);
742 assert_eq!(BOOL.get(&configs), true);
743 assert_eq!(U32.get(&configs), 4);
744 assert_eq!(USIZE.get(&configs), 1);
745 assert_eq!(OPT_USIZE.get(&configs), Some(2));
746 assert_eq!(F64.get(&configs), 5.0);
747 assert_eq!(STRING.get(&configs), "a");
748 assert_eq!(DURATION.get(&configs), Duration::from_nanos(3));
749 assert_eq!(JSON.get(&configs), serde_json::json!({}));
750
751 let mut updates = ConfigUpdates::default();
752 updates.add(&BOOL, false);
753 updates.add(&U32, 7);
754 updates.add(&USIZE, 2);
755 updates.add(&OPT_USIZE, None);
756 updates.add(&F64, 8.0);
757 updates.add(&STRING, "b");
758 updates.add(&DURATION, Duration::from_nanos(4));
759 updates.add(&JSON, serde_json::json!({"a": 1}));
760 updates.apply(&configs);
761
762 assert_eq!(BOOL.get(&configs), false);
763 assert_eq!(U32.get(&configs), 7);
764 assert_eq!(USIZE.get(&configs), 2);
765 assert_eq!(OPT_USIZE.get(&configs), None);
766 assert_eq!(F64.get(&configs), 8.0);
767 assert_eq!(STRING.get(&configs), "b");
768 assert_eq!(DURATION.get(&configs), Duration::from_nanos(4));
769 assert_eq!(JSON.get(&configs), serde_json::json!({"a": 1}));
770 }
771
772 #[mz_ore::test]
773 fn fn_default() {
774 const BOOL_FN_DEFAULT: Config<fn() -> bool> = Config::new("bool", || !true, "");
775 const STRING_FN_DEFAULT: Config<fn() -> String> =
776 Config::new("string", || "x".repeat(3), "");
777
778 let configs = ConfigSet::default()
779 .add(&BOOL_FN_DEFAULT)
780 .add(&STRING_FN_DEFAULT);
781 assert_eq!(BOOL_FN_DEFAULT.get(&configs), false);
782 assert_eq!(STRING_FN_DEFAULT.get(&configs), "xxx");
783 }
784
785 #[mz_ore::test]
786 fn config_set() {
787 let c0 = ConfigSet::default().add(&USIZE);
788 assert_eq!(USIZE.get(&c0), 1);
789 let mut updates = ConfigUpdates::default();
790 updates.add(&USIZE, 2);
791 updates.apply(&c0);
792 assert_eq!(USIZE.get(&c0), 2);
793
794 let c1 = ConfigSet::default().add(&USIZE);
797 assert_eq!(USIZE.get(&c1), 1);
798 let mut updates = ConfigUpdates::default();
799 updates.add(&USIZE, 3);
800 updates.apply(&c1);
801 assert_eq!(USIZE.get(&c1), 3);
802 assert_eq!(USIZE.get(&c0), 2);
803
804 let mut updates = ConfigUpdates::default();
806 for e in c0.entries() {
807 updates.add_dynamic(e.name, e.val());
808 }
809 assert_eq!(USIZE.get(&c1), 3);
810 updates.apply(&c1);
811 assert_eq!(USIZE.get(&c1), 2);
812 }
813
814 #[mz_ore::test]
815 fn config_updates_extend() {
816 let mut u1 = {
822 let c = ConfigSet::default().add(&USIZE).add(&STRING);
823 let mut x = ConfigUpdates::default();
824 for e in c.entries() {
825 x.add_dynamic(e.name(), e.val());
826 }
827 x
828 };
829 let u2 = {
830 let c = ConfigSet::default().add(&USIZE).add(&DURATION);
831 let mut updates = ConfigUpdates::default();
832 updates.add(&USIZE, 2);
833 updates.apply(&c);
834 let mut x = ConfigUpdates::default();
835 for e in c.entries() {
836 x.add_dynamic(e.name(), e.val());
837 }
838 x
839 };
840 assert_eq!(u1.updates.len(), 2);
841 assert_eq!(u2.updates.len(), 2);
842 u1.extend(u2);
843 assert_eq!(u1.updates.len(), 3);
844
845 let c = ConfigSet::default().add(&USIZE);
848 u1.apply(&c);
849 assert_eq!(USIZE.get(&c), 2);
850 }
851
852 #[mz_ore::test]
853 fn config_parse() {
854 assert_eq!(BOOL.parse_val("true"), Ok(ConfigVal::Bool(true)));
855 assert_eq!(BOOL.parse_val("on"), Ok(ConfigVal::Bool(true)));
856 assert_eq!(BOOL.parse_val("false"), Ok(ConfigVal::Bool(false)));
857 assert_eq!(BOOL.parse_val("off"), Ok(ConfigVal::Bool(false)));
858 assert_err!(BOOL.parse_val("42"));
859 assert_err!(BOOL.parse_val("66.6"));
860 assert_err!(BOOL.parse_val("farragut"));
861 assert_err!(BOOL.parse_val(""));
862 assert_err!(BOOL.parse_val("5 s"));
863
864 assert_err!(U32.parse_val("true"));
865 assert_err!(U32.parse_val("false"));
866 assert_eq!(U32.parse_val("42"), Ok(ConfigVal::U32(42)));
867 assert_err!(U32.parse_val("66.6"));
868 assert_err!(U32.parse_val("farragut"));
869 assert_err!(U32.parse_val(""));
870 assert_err!(U32.parse_val("5 s"));
871
872 assert_err!(USIZE.parse_val("true"));
873 assert_err!(USIZE.parse_val("false"));
874 assert_eq!(USIZE.parse_val("42"), Ok(ConfigVal::Usize(42)));
875 assert_err!(USIZE.parse_val("66.6"));
876 assert_err!(USIZE.parse_val("farragut"));
877 assert_err!(USIZE.parse_val(""));
878 assert_err!(USIZE.parse_val("5 s"));
879
880 assert_err!(OPT_USIZE.parse_val("true"));
881 assert_err!(OPT_USIZE.parse_val("false"));
882 assert_eq!(OPT_USIZE.parse_val("42"), Ok(ConfigVal::OptUsize(Some(42))));
883 assert_err!(OPT_USIZE.parse_val("66.6"));
884 assert_err!(OPT_USIZE.parse_val("farragut"));
885 assert_eq!(OPT_USIZE.parse_val(""), Ok(ConfigVal::OptUsize(None)));
886 assert_err!(OPT_USIZE.parse_val("5 s"));
887
888 assert_err!(F64.parse_val("true"));
889 assert_err!(F64.parse_val("false"));
890 assert_eq!(F64.parse_val("42"), Ok(ConfigVal::F64(42.0)));
891 assert_eq!(F64.parse_val("66.6"), Ok(ConfigVal::F64(66.6)));
892 assert_err!(F64.parse_val("farragut"));
893 assert_err!(F64.parse_val(""));
894 assert_err!(F64.parse_val("5 s"));
895
896 assert_eq!(
897 STRING.parse_val("true"),
898 Ok(ConfigVal::String("true".to_string()))
899 );
900 assert_eq!(
901 STRING.parse_val("false"),
902 Ok(ConfigVal::String("false".to_string()))
903 );
904 assert_eq!(
905 STRING.parse_val("66.6"),
906 Ok(ConfigVal::String("66.6".to_string()))
907 );
908 assert_eq!(
909 STRING.parse_val("42"),
910 Ok(ConfigVal::String("42".to_string()))
911 );
912 assert_eq!(
913 STRING.parse_val("farragut"),
914 Ok(ConfigVal::String("farragut".to_string()))
915 );
916 assert_eq!(STRING.parse_val(""), Ok(ConfigVal::String("".to_string())));
917 assert_eq!(
918 STRING.parse_val("5 s"),
919 Ok(ConfigVal::String("5 s".to_string()))
920 );
921
922 assert_err!(DURATION.parse_val("true"));
923 assert_err!(DURATION.parse_val("false"));
924 assert_err!(DURATION.parse_val("42"));
925 assert_err!(DURATION.parse_val("66.6"));
926 assert_err!(DURATION.parse_val("farragut"));
927 assert_err!(DURATION.parse_val(""));
928 assert_eq!(
929 DURATION.parse_val("5 s"),
930 Ok(ConfigVal::Duration(Duration::from_secs(5)))
931 );
932
933 assert_eq!(
934 JSON.parse_val("true"),
935 Ok(ConfigVal::Json(serde_json::json!(true)))
936 );
937 assert_eq!(
938 JSON.parse_val("false"),
939 Ok(ConfigVal::Json(serde_json::json!(false)))
940 );
941 assert_eq!(
942 JSON.parse_val("42"),
943 Ok(ConfigVal::Json(serde_json::json!(42)))
944 );
945 assert_eq!(
946 JSON.parse_val("66.6"),
947 Ok(ConfigVal::Json(serde_json::json!(66.6)))
948 );
949 assert_err!(JSON.parse_val("farragut"));
950 assert_err!(JSON.parse_val(""));
951 assert_err!(JSON.parse_val("5 s"));
952 assert_eq!(
953 JSON.parse_val("{\"joe\": \"developer\"}"),
954 Ok(ConfigVal::Json(serde_json::json!({"joe": "developer"})))
955 );
956 }
957}