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, Copy, Debug, PartialEq, Eq)]
75pub enum ParameterScope {
76 Environment,
79 Cluster,
83 Replica,
87}
88
89impl Default for ParameterScope {
90 fn default() -> Self {
91 Self::DEFAULT
92 }
93}
94
95impl ParameterScope {
96 pub const DEFAULT: ParameterScope = ParameterScope::Environment;
101
102 pub const fn as_str(&self) -> &'static str {
105 match self {
106 ParameterScope::Environment => "environment",
107 ParameterScope::Cluster => "cluster",
108 ParameterScope::Replica => "replica",
109 }
110 }
111}
112
113#[derive(Clone, Debug)]
122pub struct Config<D: ConfigDefault> {
123 name: &'static str,
124 desc: &'static str,
125 default: D,
126 scope: ParameterScope,
127}
128
129impl<D: ConfigDefault> Config<D> {
130 pub const fn new(name: &'static str, default: D, desc: &'static str) -> Self {
145 Config {
146 name,
147 default,
148 desc,
149 scope: ParameterScope::DEFAULT,
150 }
151 }
152
153 pub const fn scoped(mut self, scope: ParameterScope) -> Self {
160 self.scope = scope;
161 self
162 }
163
164 pub fn name(&self) -> &str {
166 self.name
167 }
168
169 pub fn desc(&self) -> &str {
171 self.desc
172 }
173
174 pub fn scope(&self) -> ParameterScope {
176 self.scope
177 }
178
179 pub fn default(&self) -> &D {
181 &self.default
182 }
183
184 pub fn get(&self, set: &ConfigSet) -> D::ConfigType {
194 D::ConfigType::from_val(self.shared(set).load())
195 }
196
197 pub fn handle(&self, set: &ConfigSet) -> ConfigValHandle<D::ConfigType> {
201 ConfigValHandle {
202 val: self.shared(set).clone(),
203 _type: PhantomData,
204 }
205 }
206
207 fn shared<'a>(&self, set: &'a ConfigSet) -> &'a ConfigValAtomic {
209 &set.configs
210 .get(self.name)
211 .unwrap_or_else(|| panic!("config {} should be registered to set", self.name))
212 .val
213 }
214
215 pub fn parse_val(&self, val: &str) -> Result<ConfigVal, String> {
217 let val = D::ConfigType::parse(val)?;
218 let val = Into::<ConfigVal>::into(val);
219 Ok(val)
220 }
221}
222
223pub trait ConfigType: Into<ConfigVal> + Clone + Sized {
225 fn from_val(val: ConfigVal) -> Self;
229
230 fn parse(s: &str) -> Result<Self, String>;
232}
233
234pub trait ConfigDefault: Clone {
236 type ConfigType: ConfigType;
237
238 fn into_config_type(self) -> Self::ConfigType;
240}
241
242impl<T: ConfigType> ConfigDefault for T {
243 type ConfigType = T;
244
245 fn into_config_type(self) -> T {
246 self
247 }
248}
249
250impl<T: ConfigType> ConfigDefault for fn() -> T {
251 type ConfigType = T;
252
253 fn into_config_type(self) -> T {
254 (self)()
255 }
256}
257
258#[derive(Clone, Default)]
272pub struct ConfigSet {
273 configs: BTreeMap<String, ConfigEntry>,
274}
275
276impl ConfigSet {
277 pub fn add<D: ConfigDefault>(mut self, config: &Config<D>) -> Self {
287 let default = config.default.clone().into_config_type();
288 let default = Into::<ConfigVal>::into(default);
289 let config = ConfigEntry {
290 name: config.name,
291 desc: config.desc,
292 scope: config.scope,
293 default: default.clone(),
294 val: ConfigValAtomic::from(default),
295 };
296 if let Some(prev) = self.configs.insert(config.name.to_owned(), config) {
297 panic!("{} registered twice", prev.name);
298 }
299 self
300 }
301
302 pub fn entries(&self) -> impl Iterator<Item = &ConfigEntry> {
304 self.configs.values()
305 }
306
307 pub fn entry(&self, name: &str) -> Option<&ConfigEntry> {
309 self.configs.get(name)
310 }
311}
312
313#[derive(Clone, Debug)]
315pub struct ConfigEntry {
316 name: &'static str,
317 desc: &'static str,
318 scope: ParameterScope,
319 default: ConfigVal,
320 val: ConfigValAtomic,
321}
322
323impl ConfigEntry {
324 pub fn name(&self) -> &'static str {
326 self.name
327 }
328
329 pub fn desc(&self) -> &'static str {
331 self.desc
332 }
333
334 pub fn scope(&self) -> ParameterScope {
336 self.scope
337 }
338
339 pub fn default(&self) -> &ConfigVal {
343 &self.default
344 }
345
346 pub fn parse_val(&self, val: &str) -> Result<ConfigVal, String> {
351 match self.default {
352 ConfigVal::Bool(_) => <bool as ConfigType>::parse(val).map(Into::into),
353 ConfigVal::U32(_) => <u32 as ConfigType>::parse(val).map(Into::into),
354 ConfigVal::Usize(_) => <usize as ConfigType>::parse(val).map(Into::into),
355 ConfigVal::OptUsize(_) => <Option<usize> as ConfigType>::parse(val).map(Into::into),
356 ConfigVal::F64(_) => <f64 as ConfigType>::parse(val).map(Into::into),
357 ConfigVal::String(_) => <String as ConfigType>::parse(val).map(Into::into),
358 ConfigVal::OptString(_) => <Option<String> as ConfigType>::parse(val).map(Into::into),
359 ConfigVal::Duration(_) => <Duration as ConfigType>::parse(val).map(Into::into),
360 ConfigVal::Json(_) => <serde_json::Value as ConfigType>::parse(val).map(Into::into),
361 }
362 }
363
364 pub fn val(&self) -> ConfigVal {
366 self.val.load()
367 }
368}
369
370#[derive(Debug, Clone)]
376pub struct ConfigValHandle<T> {
377 val: ConfigValAtomic,
378 _type: PhantomData<T>,
379}
380
381impl<T: ConfigType> ConfigValHandle<T> {
382 pub fn get(&self) -> T {
385 T::from_val(self.val.load())
386 }
387
388 pub fn disconnected<X>(value: X) -> Self
391 where
392 X: ConfigDefault<ConfigType = T>,
393 {
394 let config_val: ConfigVal = value.into_config_type().into();
395 Self {
396 val: config_val.into(),
397 _type: Default::default(),
398 }
399 }
400}
401
402#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
405pub enum ConfigVal {
406 Bool(bool),
408 U32(u32),
410 Usize(usize),
412 OptUsize(Option<usize>),
414 F64(f64),
416 String(String),
418 OptString(Option<String>),
420 Duration(Duration),
422 #[serde(with = "serde_json_string")]
424 Json(serde_json::Value),
425}
426
427mod serde_json_string {
430 use serde::de::{Deserialize, Deserializer, Error};
431 use serde::ser::Serializer;
432
433 pub fn serialize<S>(value: &serde_json::Value, serializer: S) -> Result<S::Ok, S::Error>
434 where
435 S: Serializer,
436 {
437 serializer.serialize_str(&value.to_string())
438 }
439
440 pub fn deserialize<'de, D>(deserializer: D) -> Result<serde_json::Value, D::Error>
441 where
442 D: Deserializer<'de>,
443 {
444 let s = String::deserialize(deserializer)?;
445 serde_json::from_str(&s).map_err(D::Error::custom)
446 }
447}
448
449#[derive(Clone, Debug)]
457enum ConfigValAtomic {
458 Bool(Arc<AtomicBool>),
459 U32(Arc<AtomicU32>),
460 Usize(Arc<AtomicUsize>),
461 OptUsize(Arc<RwLock<Option<usize>>>),
462 F64(Arc<AtomicU64>),
464 String(Arc<RwLock<String>>),
465 OptString(Arc<RwLock<Option<String>>>),
466 Duration(Arc<RwLock<Duration>>),
467 Json(Arc<RwLock<serde_json::Value>>),
468}
469
470impl From<ConfigVal> for ConfigValAtomic {
471 fn from(val: ConfigVal) -> ConfigValAtomic {
472 match val {
473 ConfigVal::Bool(x) => ConfigValAtomic::Bool(Arc::new(AtomicBool::new(x))),
474 ConfigVal::U32(x) => ConfigValAtomic::U32(Arc::new(AtomicU32::new(x))),
475 ConfigVal::Usize(x) => ConfigValAtomic::Usize(Arc::new(AtomicUsize::new(x))),
476 ConfigVal::OptUsize(x) => ConfigValAtomic::OptUsize(Arc::new(RwLock::new(x))),
477 ConfigVal::F64(x) => ConfigValAtomic::F64(Arc::new(AtomicU64::new(x.to_bits()))),
478 ConfigVal::String(x) => ConfigValAtomic::String(Arc::new(RwLock::new(x))),
479 ConfigVal::OptString(x) => ConfigValAtomic::OptString(Arc::new(RwLock::new(x))),
480 ConfigVal::Duration(x) => ConfigValAtomic::Duration(Arc::new(RwLock::new(x))),
481 ConfigVal::Json(x) => ConfigValAtomic::Json(Arc::new(RwLock::new(x))),
482 }
483 }
484}
485
486impl ConfigValAtomic {
487 fn load(&self) -> ConfigVal {
488 match self {
489 ConfigValAtomic::Bool(x) => ConfigVal::Bool(x.load(SeqCst)),
490 ConfigValAtomic::U32(x) => ConfigVal::U32(x.load(SeqCst)),
491 ConfigValAtomic::Usize(x) => ConfigVal::Usize(x.load(SeqCst)),
492 ConfigValAtomic::OptUsize(x) => ConfigVal::OptUsize(*x.read().expect("lock poisoned")),
493 ConfigValAtomic::F64(x) => ConfigVal::F64(f64::from_bits(x.load(SeqCst))),
494 ConfigValAtomic::String(x) => {
495 ConfigVal::String(x.read().expect("lock poisoned").clone())
496 }
497 ConfigValAtomic::OptString(x) => {
498 ConfigVal::OptString(x.read().expect("lock poisoned").clone())
499 }
500 ConfigValAtomic::Duration(x) => ConfigVal::Duration(*x.read().expect("lock poisoned")),
501 ConfigValAtomic::Json(x) => ConfigVal::Json(x.read().expect("lock poisoned").clone()),
502 }
503 }
504
505 fn store(&self, val: ConfigVal) {
506 match (self, val) {
507 (ConfigValAtomic::Bool(x), ConfigVal::Bool(val)) => x.store(val, SeqCst),
508 (ConfigValAtomic::U32(x), ConfigVal::U32(val)) => x.store(val, SeqCst),
509 (ConfigValAtomic::Usize(x), ConfigVal::Usize(val)) => x.store(val, SeqCst),
510 (ConfigValAtomic::OptUsize(x), ConfigVal::OptUsize(val)) => {
511 *x.write().expect("lock poisoned") = val
512 }
513 (ConfigValAtomic::F64(x), ConfigVal::F64(val)) => x.store(val.to_bits(), SeqCst),
514 (ConfigValAtomic::String(x), ConfigVal::String(val)) => {
515 *x.write().expect("lock poisoned") = val
516 }
517 (ConfigValAtomic::OptString(x), ConfigVal::OptString(val)) => {
518 *x.write().expect("lock poisoned") = val
519 }
520 (ConfigValAtomic::Duration(x), ConfigVal::Duration(val)) => {
521 *x.write().expect("lock poisoned") = val
522 }
523 (ConfigValAtomic::Json(x), ConfigVal::Json(val)) => {
524 *x.write().expect("lock poisoned") = val
525 }
526 (ConfigValAtomic::Bool(_), val)
527 | (ConfigValAtomic::U32(_), val)
528 | (ConfigValAtomic::Usize(_), val)
529 | (ConfigValAtomic::OptUsize(_), val)
530 | (ConfigValAtomic::F64(_), val)
531 | (ConfigValAtomic::String(_), val)
532 | (ConfigValAtomic::OptString(_), val)
533 | (ConfigValAtomic::Duration(_), val)
534 | (ConfigValAtomic::Json(_), val) => {
535 panic!("attempted to store {val:?} value in {self:?} parameter")
536 }
537 }
538 }
539}
540
541#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
546pub struct ConfigUpdates {
547 pub updates: BTreeMap<String, ConfigVal>,
548}
549
550impl ConfigUpdates {
551 pub fn add<T, U>(&mut self, config: &Config<T>, val: U)
556 where
557 T: ConfigDefault,
558 U: ConfigDefault<ConfigType = T::ConfigType>,
559 {
560 self.add_dynamic(config.name, val.into_config_type().into());
561 }
562
563 pub fn add_dynamic(&mut self, name: &str, val: ConfigVal) {
571 self.updates.insert(name.to_owned(), val);
572 }
573
574 pub fn extend(&mut self, mut other: Self) {
576 self.updates.append(&mut other.updates)
577 }
578
579 pub fn apply(&self, set: &ConfigSet) {
589 for (name, val) in self.updates.iter() {
590 let Some(config) = set.configs.get(name) else {
591 error!("config update {} {:?} not known set: {:?}", name, val, set);
592 continue;
593 };
594 config.val.store(val.clone());
595 }
596 }
597}
598
599impl From<&ConfigSet> for ConfigUpdates {
600 fn from(set: &ConfigSet) -> Self {
605 let mut updates = ConfigUpdates::default();
606 for entry in set.entries() {
607 updates.add_dynamic(entry.name(), entry.val());
608 }
609 updates
610 }
611}
612
613mod impls {
614 use std::num::{ParseFloatError, ParseIntError};
615 use std::str::ParseBoolError;
616 use std::time::Duration;
617
618 use crate::{ConfigDefault, ConfigSet, ConfigType, ConfigVal};
619
620 impl ConfigType for bool {
621 fn from_val(val: ConfigVal) -> Self {
622 match val {
623 ConfigVal::Bool(x) => x,
624 x => panic!("expected bool value got {:?}", x),
625 }
626 }
627
628 fn parse(s: &str) -> Result<Self, String> {
629 match s {
630 "on" => return Ok(true),
631 "off" => return Ok(false),
632 _ => {}
633 }
634 s.parse().map_err(|e: ParseBoolError| e.to_string())
635 }
636 }
637
638 impl From<bool> for ConfigVal {
639 fn from(val: bool) -> ConfigVal {
640 ConfigVal::Bool(val)
641 }
642 }
643
644 impl ConfigType for u32 {
645 fn from_val(val: ConfigVal) -> Self {
646 match val {
647 ConfigVal::U32(x) => x,
648 x => panic!("expected u32 value got {:?}", x),
649 }
650 }
651
652 fn parse(s: &str) -> Result<Self, String> {
653 s.parse().map_err(|e: ParseIntError| e.to_string())
654 }
655 }
656
657 impl From<u32> for ConfigVal {
658 fn from(val: u32) -> ConfigVal {
659 ConfigVal::U32(val)
660 }
661 }
662
663 impl ConfigType for usize {
664 fn from_val(val: ConfigVal) -> Self {
665 match val {
666 ConfigVal::Usize(x) => x,
667 x => panic!("expected usize value got {:?}", x),
668 }
669 }
670
671 fn parse(s: &str) -> Result<Self, String> {
672 s.parse().map_err(|e: ParseIntError| e.to_string())
673 }
674 }
675
676 impl From<usize> for ConfigVal {
677 fn from(val: usize) -> ConfigVal {
678 ConfigVal::Usize(val)
679 }
680 }
681
682 impl ConfigType for Option<usize> {
683 fn from_val(val: ConfigVal) -> Self {
684 match val {
685 ConfigVal::OptUsize(x) => x,
686 x => panic!("expected usize value got {:?}", x),
687 }
688 }
689
690 fn parse(s: &str) -> Result<Self, String> {
691 if s.is_empty() {
692 Ok(None)
693 } else {
694 let val = s.parse().map_err(|e: ParseIntError| e.to_string())?;
695 Ok(Some(val))
696 }
697 }
698 }
699
700 impl From<Option<usize>> for ConfigVal {
701 fn from(val: Option<usize>) -> ConfigVal {
702 ConfigVal::OptUsize(val)
703 }
704 }
705
706 impl ConfigType for f64 {
707 fn from_val(val: ConfigVal) -> Self {
708 match val {
709 ConfigVal::F64(x) => x,
710 x => panic!("expected f64 value got {:?}", x),
711 }
712 }
713
714 fn parse(s: &str) -> Result<Self, String> {
715 s.parse().map_err(|e: ParseFloatError| e.to_string())
716 }
717 }
718
719 impl From<f64> for ConfigVal {
720 fn from(val: f64) -> ConfigVal {
721 ConfigVal::F64(val)
722 }
723 }
724
725 impl ConfigType for String {
726 fn from_val(val: ConfigVal) -> Self {
727 match val {
728 ConfigVal::String(x) => x,
729 x => panic!("expected String value got {:?}", x),
730 }
731 }
732
733 fn parse(s: &str) -> Result<Self, String> {
734 Ok(s.to_string())
735 }
736 }
737
738 impl From<String> for ConfigVal {
739 fn from(val: String) -> ConfigVal {
740 ConfigVal::String(val)
741 }
742 }
743
744 impl ConfigDefault for &str {
745 type ConfigType = String;
746
747 fn into_config_type(self) -> String {
748 self.into()
749 }
750 }
751
752 impl ConfigType for Option<String> {
753 fn from_val(val: ConfigVal) -> Self {
754 match val {
755 ConfigVal::OptString(x) => x,
756 x => panic!("expected String value got {:?}", x),
757 }
758 }
759
760 fn parse(s: &str) -> Result<Self, String> {
761 Ok(Some(s.to_string()))
762 }
763 }
764
765 impl From<Option<String>> for ConfigVal {
766 fn from(val: Option<String>) -> ConfigVal {
767 ConfigVal::OptString(val)
768 }
769 }
770
771 impl ConfigDefault for Option<&str> {
772 type ConfigType = Option<String>;
773
774 fn into_config_type(self) -> Option<String> {
775 self.map(|s| s.to_string())
776 }
777 }
778
779 impl ConfigType for Duration {
780 fn from_val(val: ConfigVal) -> Self {
781 match val {
782 ConfigVal::Duration(x) => x,
783 x => panic!("expected Duration value got {:?}", x),
784 }
785 }
786
787 fn parse(s: &str) -> Result<Self, String> {
788 humantime::parse_duration(s).map_err(|e| e.to_string())
789 }
790 }
791
792 impl From<Duration> for ConfigVal {
793 fn from(val: Duration) -> ConfigVal {
794 ConfigVal::Duration(val)
795 }
796 }
797
798 impl ConfigType for serde_json::Value {
799 fn from_val(val: ConfigVal) -> Self {
800 match val {
801 ConfigVal::Json(x) => x,
802 x => panic!("expected JSON value got {:?}", x),
803 }
804 }
805
806 fn parse(s: &str) -> Result<Self, String> {
807 serde_json::from_str(s).map_err(|e| e.to_string())
808 }
809 }
810
811 impl From<serde_json::Value> for ConfigVal {
812 fn from(val: serde_json::Value) -> ConfigVal {
813 ConfigVal::Json(val)
814 }
815 }
816
817 impl std::fmt::Debug for ConfigSet {
818 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
819 let ConfigSet { configs } = self;
820 f.debug_map()
821 .entries(configs.iter().map(|(name, val)| (name, val.val())))
822 .finish()
823 }
824 }
825}
826
827#[cfg(test)]
828mod tests {
829 use super::*;
830
831 use mz_ore::assert_err;
832
833 const BOOL: Config<bool> = Config::new("bool", true, "");
834 const U32: Config<u32> = Config::new("u32", 4, "");
835 const USIZE: Config<usize> = Config::new("usize", 1, "");
836 const OPT_USIZE: Config<Option<usize>> = Config::new("opt_usize", Some(2), "");
837 const F64: Config<f64> = Config::new("f64", 5.0, "");
838 const STRING: Config<&str> = Config::new("string", "a", "");
839 const OPT_STRING: Config<Option<&str>> = Config::new("opt_string", Some("a"), "");
840 const DURATION: Config<Duration> = Config::new("duration", Duration::from_nanos(3), "");
841 const JSON: Config<fn() -> serde_json::Value> =
842 Config::new("json", || serde_json::json!({}), "");
843
844 #[mz_ore::test]
845 fn all_types() {
846 let configs = ConfigSet::default()
847 .add(&BOOL)
848 .add(&USIZE)
849 .add(&U32)
850 .add(&OPT_USIZE)
851 .add(&F64)
852 .add(&STRING)
853 .add(&OPT_STRING)
854 .add(&DURATION)
855 .add(&JSON);
856 assert_eq!(BOOL.get(&configs), true);
857 assert_eq!(U32.get(&configs), 4);
858 assert_eq!(USIZE.get(&configs), 1);
859 assert_eq!(OPT_USIZE.get(&configs), Some(2));
860 assert_eq!(F64.get(&configs), 5.0);
861 assert_eq!(STRING.get(&configs), "a");
862 assert_eq!(OPT_STRING.get(&configs), Some("a".to_string()));
863 assert_eq!(DURATION.get(&configs), Duration::from_nanos(3));
864 assert_eq!(JSON.get(&configs), serde_json::json!({}));
865
866 let mut updates = ConfigUpdates::default();
867 updates.add(&BOOL, false);
868 updates.add(&U32, 7);
869 updates.add(&USIZE, 2);
870 updates.add(&OPT_USIZE, None::<usize>);
871 updates.add(&F64, 8.0);
872 updates.add(&STRING, "b");
873 updates.add(&OPT_STRING, None::<String>);
874 updates.add(&DURATION, Duration::from_nanos(4));
875 updates.add(&JSON, serde_json::json!({"a": 1}));
876 updates.apply(&configs);
877
878 assert_eq!(BOOL.get(&configs), false);
879 assert_eq!(U32.get(&configs), 7);
880 assert_eq!(USIZE.get(&configs), 2);
881 assert_eq!(OPT_USIZE.get(&configs), None);
882 assert_eq!(F64.get(&configs), 8.0);
883 assert_eq!(STRING.get(&configs), "b");
884 assert_eq!(OPT_STRING.get(&configs), None);
885 assert_eq!(DURATION.get(&configs), Duration::from_nanos(4));
886 assert_eq!(JSON.get(&configs), serde_json::json!({"a": 1}));
887 }
888
889 #[mz_ore::test]
890 fn fn_default() {
891 const BOOL_FN_DEFAULT: Config<fn() -> bool> = Config::new("bool", || !true, "");
892 const STRING_FN_DEFAULT: Config<fn() -> String> =
893 Config::new("string", || "x".repeat(3), "");
894
895 const OPT_STRING_FN_DEFAULT: Config<fn() -> Option<String>> =
896 Config::new("opt_string", || Some("x".repeat(3)), "");
897
898 let configs = ConfigSet::default()
899 .add(&BOOL_FN_DEFAULT)
900 .add(&STRING_FN_DEFAULT)
901 .add(&OPT_STRING_FN_DEFAULT);
902 assert_eq!(BOOL_FN_DEFAULT.get(&configs), false);
903 assert_eq!(STRING_FN_DEFAULT.get(&configs), "xxx");
904 assert_eq!(OPT_STRING_FN_DEFAULT.get(&configs), Some("xxx".to_string()));
905 }
906
907 #[mz_ore::test]
908 fn config_set() {
909 let c0 = ConfigSet::default().add(&USIZE);
910 assert_eq!(USIZE.get(&c0), 1);
911 let mut updates = ConfigUpdates::default();
912 updates.add(&USIZE, 2);
913 updates.apply(&c0);
914 assert_eq!(USIZE.get(&c0), 2);
915
916 let c1 = ConfigSet::default().add(&USIZE);
919 assert_eq!(USIZE.get(&c1), 1);
920 let mut updates = ConfigUpdates::default();
921 updates.add(&USIZE, 3);
922 updates.apply(&c1);
923 assert_eq!(USIZE.get(&c1), 3);
924 assert_eq!(USIZE.get(&c0), 2);
925
926 let mut updates = ConfigUpdates::default();
928 for e in c0.entries() {
929 updates.add_dynamic(e.name, e.val());
930 }
931 assert_eq!(USIZE.get(&c1), 3);
932 updates.apply(&c1);
933 assert_eq!(USIZE.get(&c1), 2);
934 }
935
936 #[mz_ore::test]
937 fn config_updates_extend() {
938 let mut u1 = {
944 let c = ConfigSet::default().add(&USIZE).add(&STRING);
945 let mut x = ConfigUpdates::default();
946 for e in c.entries() {
947 x.add_dynamic(e.name(), e.val());
948 }
949 x
950 };
951 let u2 = {
952 let c = ConfigSet::default().add(&USIZE).add(&DURATION);
953 let mut updates = ConfigUpdates::default();
954 updates.add(&USIZE, 2);
955 updates.apply(&c);
956 let mut x = ConfigUpdates::default();
957 for e in c.entries() {
958 x.add_dynamic(e.name(), e.val());
959 }
960 x
961 };
962 assert_eq!(u1.updates.len(), 2);
963 assert_eq!(u2.updates.len(), 2);
964 u1.extend(u2);
965 assert_eq!(u1.updates.len(), 3);
966
967 let c = ConfigSet::default().add(&USIZE);
970 u1.apply(&c);
971 assert_eq!(USIZE.get(&c), 2);
972 }
973
974 #[mz_ore::test]
975 fn config_parse() {
976 assert_eq!(BOOL.parse_val("true"), Ok(ConfigVal::Bool(true)));
977 assert_eq!(BOOL.parse_val("on"), Ok(ConfigVal::Bool(true)));
978 assert_eq!(BOOL.parse_val("false"), Ok(ConfigVal::Bool(false)));
979 assert_eq!(BOOL.parse_val("off"), Ok(ConfigVal::Bool(false)));
980 assert_err!(BOOL.parse_val("42"));
981 assert_err!(BOOL.parse_val("66.6"));
982 assert_err!(BOOL.parse_val("farragut"));
983 assert_err!(BOOL.parse_val(""));
984 assert_err!(BOOL.parse_val("5 s"));
985
986 assert_err!(U32.parse_val("true"));
987 assert_err!(U32.parse_val("false"));
988 assert_eq!(U32.parse_val("42"), Ok(ConfigVal::U32(42)));
989 assert_err!(U32.parse_val("66.6"));
990 assert_err!(U32.parse_val("farragut"));
991 assert_err!(U32.parse_val(""));
992 assert_err!(U32.parse_val("5 s"));
993
994 assert_err!(USIZE.parse_val("true"));
995 assert_err!(USIZE.parse_val("false"));
996 assert_eq!(USIZE.parse_val("42"), Ok(ConfigVal::Usize(42)));
997 assert_err!(USIZE.parse_val("66.6"));
998 assert_err!(USIZE.parse_val("farragut"));
999 assert_err!(USIZE.parse_val(""));
1000 assert_err!(USIZE.parse_val("5 s"));
1001
1002 assert_err!(OPT_USIZE.parse_val("true"));
1003 assert_err!(OPT_USIZE.parse_val("false"));
1004 assert_eq!(OPT_USIZE.parse_val("42"), Ok(ConfigVal::OptUsize(Some(42))));
1005 assert_err!(OPT_USIZE.parse_val("66.6"));
1006 assert_err!(OPT_USIZE.parse_val("farragut"));
1007 assert_eq!(OPT_USIZE.parse_val(""), Ok(ConfigVal::OptUsize(None)));
1008 assert_err!(OPT_USIZE.parse_val("5 s"));
1009
1010 assert_err!(F64.parse_val("true"));
1011 assert_err!(F64.parse_val("false"));
1012 assert_eq!(F64.parse_val("42"), Ok(ConfigVal::F64(42.0)));
1013 assert_eq!(F64.parse_val("66.6"), Ok(ConfigVal::F64(66.6)));
1014 assert_err!(F64.parse_val("farragut"));
1015 assert_err!(F64.parse_val(""));
1016 assert_err!(F64.parse_val("5 s"));
1017
1018 assert_eq!(
1019 STRING.parse_val("true"),
1020 Ok(ConfigVal::String("true".to_string()))
1021 );
1022 assert_eq!(
1023 STRING.parse_val("false"),
1024 Ok(ConfigVal::String("false".to_string()))
1025 );
1026 assert_eq!(
1027 STRING.parse_val("66.6"),
1028 Ok(ConfigVal::String("66.6".to_string()))
1029 );
1030 assert_eq!(
1031 STRING.parse_val("42"),
1032 Ok(ConfigVal::String("42".to_string()))
1033 );
1034 assert_eq!(
1035 STRING.parse_val("farragut"),
1036 Ok(ConfigVal::String("farragut".to_string()))
1037 );
1038 assert_eq!(STRING.parse_val(""), Ok(ConfigVal::String("".to_string())));
1039 assert_eq!(
1040 STRING.parse_val("5 s"),
1041 Ok(ConfigVal::String("5 s".to_string()))
1042 );
1043
1044 assert_eq!(
1045 OPT_STRING.parse_val("true"),
1046 Ok(ConfigVal::OptString(Some("true".to_string())))
1047 );
1048 assert_eq!(
1049 OPT_STRING.parse_val("false"),
1050 Ok(ConfigVal::OptString(Some("false".to_string())))
1051 );
1052 assert_eq!(
1053 OPT_STRING.parse_val("66.6"),
1054 Ok(ConfigVal::OptString(Some("66.6".to_string())))
1055 );
1056 assert_eq!(
1057 OPT_STRING.parse_val("42"),
1058 Ok(ConfigVal::OptString(Some("42".to_string())))
1059 );
1060 assert_eq!(
1061 OPT_STRING.parse_val("farragut"),
1062 Ok(ConfigVal::OptString(Some("farragut".to_string())))
1063 );
1064 assert_eq!(
1065 OPT_STRING.parse_val(""),
1066 Ok(ConfigVal::OptString(Some("".to_string())))
1067 );
1068 assert_eq!(
1069 OPT_STRING.parse_val("5 s"),
1070 Ok(ConfigVal::OptString(Some("5 s".to_string())))
1071 );
1072
1073 assert_err!(DURATION.parse_val("true"));
1074 assert_err!(DURATION.parse_val("false"));
1075 assert_err!(DURATION.parse_val("42"));
1076 assert_err!(DURATION.parse_val("66.6"));
1077 assert_err!(DURATION.parse_val("farragut"));
1078 assert_err!(DURATION.parse_val(""));
1079 assert_eq!(
1080 DURATION.parse_val("5 s"),
1081 Ok(ConfigVal::Duration(Duration::from_secs(5)))
1082 );
1083
1084 assert_eq!(
1085 JSON.parse_val("true"),
1086 Ok(ConfigVal::Json(serde_json::json!(true)))
1087 );
1088 assert_eq!(
1089 JSON.parse_val("false"),
1090 Ok(ConfigVal::Json(serde_json::json!(false)))
1091 );
1092 assert_eq!(
1093 JSON.parse_val("42"),
1094 Ok(ConfigVal::Json(serde_json::json!(42)))
1095 );
1096 assert_eq!(
1097 JSON.parse_val("66.6"),
1098 Ok(ConfigVal::Json(serde_json::json!(66.6)))
1099 );
1100 assert_err!(JSON.parse_val("farragut"));
1101 assert_err!(JSON.parse_val(""));
1102 assert_err!(JSON.parse_val("5 s"));
1103 assert_eq!(
1104 JSON.parse_val("{\"joe\": \"developer\"}"),
1105 Ok(ConfigVal::Json(serde_json::json!({"joe": "developer"})))
1106 );
1107 }
1108}