1use std::collections::BTreeMap;
60use std::marker::PhantomData;
61use std::sync::atomic::Ordering::SeqCst;
62use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, AtomicUsize};
63use std::sync::{Arc, RwLock};
64use std::time::Duration;
65
66use serde::{Deserialize, Serialize};
67use tracing::error;
68
69#[derive(Clone, Copy, Debug, PartialEq, Eq)]
135pub enum ParameterScope {
136 Environment,
143 Cluster,
147 Replica,
151}
152
153impl Default for ParameterScope {
154 fn default() -> Self {
155 Self::DEFAULT
156 }
157}
158
159impl ParameterScope {
160 pub const DEFAULT: ParameterScope = ParameterScope::Environment;
165
166 pub const fn as_str(&self) -> &'static str {
169 match self {
170 ParameterScope::Environment => "environment",
171 ParameterScope::Cluster => "cluster",
172 ParameterScope::Replica => "replica",
173 }
174 }
175}
176
177#[derive(Clone, Debug)]
186pub struct Config<D: ConfigDefault> {
187 name: &'static str,
188 desc: &'static str,
189 default: D,
190 scope: ParameterScope,
191}
192
193impl<D: ConfigDefault> Config<D> {
194 pub const fn new(
214 name: &'static str,
215 default: D,
216 desc: &'static str,
217 scope: ParameterScope,
218 ) -> Self {
219 Config {
220 name,
221 default,
222 desc,
223 scope,
224 }
225 }
226
227 pub fn name(&self) -> &str {
229 self.name
230 }
231
232 pub fn desc(&self) -> &str {
234 self.desc
235 }
236
237 pub fn scope(&self) -> ParameterScope {
239 self.scope
240 }
241
242 pub fn default(&self) -> &D {
244 &self.default
245 }
246
247 pub fn get(&self, set: &ConfigSet) -> D::ConfigType {
257 D::ConfigType::from_val(self.shared(set).load())
258 }
259
260 pub fn get_with_overrides(
273 &self,
274 set: &ConfigSet,
275 overrides: Option<&ConfigUpdates>,
276 ) -> D::ConfigType {
277 let val = self.shared(set).load();
278 let val = match overrides.and_then(|o| o.updates.get(self.name)) {
279 None => val,
280 Some(o) if std::mem::discriminant(o) == std::mem::discriminant(&val) => o.clone(),
281 Some(o) => {
282 error!(
283 "override {:?} for config {} does not match its type {:?}",
284 o, self.name, val
285 );
286 val
287 }
288 };
289 D::ConfigType::from_val(val)
290 }
291
292 pub fn handle(&self, set: &ConfigSet) -> ConfigValHandle<D::ConfigType> {
296 ConfigValHandle {
297 val: self.shared(set).clone(),
298 _type: PhantomData,
299 }
300 }
301
302 fn shared<'a>(&self, set: &'a ConfigSet) -> &'a ConfigValAtomic {
304 &set.configs
305 .get(self.name)
306 .unwrap_or_else(|| panic!("config {} should be registered to set", self.name))
307 .val
308 }
309
310 pub fn parse_val(&self, val: &str) -> Result<ConfigVal, String> {
312 let val = D::ConfigType::parse(val)?;
313 let val = Into::<ConfigVal>::into(val);
314 Ok(val)
315 }
316}
317
318pub trait ConfigType: Into<ConfigVal> + Clone + Sized {
320 fn from_val(val: ConfigVal) -> Self;
324
325 fn parse(s: &str) -> Result<Self, String>;
327}
328
329pub trait ConfigDefault: Clone {
331 type ConfigType: ConfigType;
332
333 fn into_config_type(self) -> Self::ConfigType;
335}
336
337impl<T: ConfigType> ConfigDefault for T {
338 type ConfigType = T;
339
340 fn into_config_type(self) -> T {
341 self
342 }
343}
344
345impl<T: ConfigType> ConfigDefault for fn() -> T {
346 type ConfigType = T;
347
348 fn into_config_type(self) -> T {
349 (self)()
350 }
351}
352
353#[derive(Clone, Default)]
367pub struct ConfigSet {
368 configs: BTreeMap<String, ConfigEntry>,
369}
370
371impl ConfigSet {
372 pub fn add<D: ConfigDefault>(mut self, config: &Config<D>) -> Self {
382 let default = config.default.clone().into_config_type();
383 let default = Into::<ConfigVal>::into(default);
384 let config = ConfigEntry {
385 name: config.name,
386 desc: config.desc,
387 scope: config.scope,
388 default: default.clone(),
389 val: ConfigValAtomic::from(default),
390 };
391 if let Some(prev) = self.configs.insert(config.name.to_owned(), config) {
392 panic!("{} registered twice", prev.name);
393 }
394 self
395 }
396
397 pub fn entries(&self) -> impl Iterator<Item = &ConfigEntry> {
399 self.configs.values()
400 }
401
402 pub fn entry(&self, name: &str) -> Option<&ConfigEntry> {
404 self.configs.get(name)
405 }
406}
407
408#[derive(Clone, Debug)]
410pub struct ConfigEntry {
411 name: &'static str,
412 desc: &'static str,
413 scope: ParameterScope,
414 default: ConfigVal,
415 val: ConfigValAtomic,
416}
417
418impl ConfigEntry {
419 pub fn name(&self) -> &'static str {
421 self.name
422 }
423
424 pub fn desc(&self) -> &'static str {
426 self.desc
427 }
428
429 pub fn scope(&self) -> ParameterScope {
431 self.scope
432 }
433
434 pub fn default(&self) -> &ConfigVal {
438 &self.default
439 }
440
441 pub fn parse_val(&self, val: &str) -> Result<ConfigVal, String> {
446 match self.default {
447 ConfigVal::Bool(_) => <bool as ConfigType>::parse(val).map(Into::into),
448 ConfigVal::U32(_) => <u32 as ConfigType>::parse(val).map(Into::into),
449 ConfigVal::Usize(_) => <usize as ConfigType>::parse(val).map(Into::into),
450 ConfigVal::OptUsize(_) => <Option<usize> as ConfigType>::parse(val).map(Into::into),
451 ConfigVal::F64(_) => <f64 as ConfigType>::parse(val).map(Into::into),
452 ConfigVal::String(_) => <String as ConfigType>::parse(val).map(Into::into),
453 ConfigVal::OptString(_) => <Option<String> as ConfigType>::parse(val).map(Into::into),
454 ConfigVal::Duration(_) => <Duration as ConfigType>::parse(val).map(Into::into),
455 ConfigVal::Json(_) => <serde_json::Value as ConfigType>::parse(val).map(Into::into),
456 }
457 }
458
459 pub fn val(&self) -> ConfigVal {
461 self.val.load()
462 }
463}
464
465#[derive(Debug, Clone)]
471pub struct ConfigValHandle<T> {
472 val: ConfigValAtomic,
473 _type: PhantomData<T>,
474}
475
476impl<T: ConfigType> ConfigValHandle<T> {
477 pub fn get(&self) -> T {
480 T::from_val(self.val.load())
481 }
482
483 pub fn disconnected<X>(value: X) -> Self
486 where
487 X: ConfigDefault<ConfigType = T>,
488 {
489 let config_val: ConfigVal = value.into_config_type().into();
490 Self {
491 val: config_val.into(),
492 _type: Default::default(),
493 }
494 }
495}
496
497#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
500pub enum ConfigVal {
501 Bool(bool),
503 U32(u32),
505 Usize(usize),
507 OptUsize(Option<usize>),
509 F64(f64),
511 String(String),
513 OptString(Option<String>),
515 Duration(Duration),
517 #[serde(with = "serde_json_string")]
519 Json(serde_json::Value),
520}
521
522mod serde_json_string {
525 use serde::de::{Deserialize, Deserializer, Error};
526 use serde::ser::Serializer;
527
528 pub fn serialize<S>(value: &serde_json::Value, serializer: S) -> Result<S::Ok, S::Error>
529 where
530 S: Serializer,
531 {
532 serializer.serialize_str(&value.to_string())
533 }
534
535 pub fn deserialize<'de, D>(deserializer: D) -> Result<serde_json::Value, D::Error>
536 where
537 D: Deserializer<'de>,
538 {
539 let s = String::deserialize(deserializer)?;
540 serde_json::from_str(&s).map_err(D::Error::custom)
541 }
542}
543
544#[derive(Clone, Debug)]
552enum ConfigValAtomic {
553 Bool(Arc<AtomicBool>),
554 U32(Arc<AtomicU32>),
555 Usize(Arc<AtomicUsize>),
556 OptUsize(Arc<RwLock<Option<usize>>>),
557 F64(Arc<AtomicU64>),
559 String(Arc<RwLock<String>>),
560 OptString(Arc<RwLock<Option<String>>>),
561 Duration(Arc<RwLock<Duration>>),
562 Json(Arc<RwLock<serde_json::Value>>),
563}
564
565impl From<ConfigVal> for ConfigValAtomic {
566 fn from(val: ConfigVal) -> ConfigValAtomic {
567 match val {
568 ConfigVal::Bool(x) => ConfigValAtomic::Bool(Arc::new(AtomicBool::new(x))),
569 ConfigVal::U32(x) => ConfigValAtomic::U32(Arc::new(AtomicU32::new(x))),
570 ConfigVal::Usize(x) => ConfigValAtomic::Usize(Arc::new(AtomicUsize::new(x))),
571 ConfigVal::OptUsize(x) => ConfigValAtomic::OptUsize(Arc::new(RwLock::new(x))),
572 ConfigVal::F64(x) => ConfigValAtomic::F64(Arc::new(AtomicU64::new(x.to_bits()))),
573 ConfigVal::String(x) => ConfigValAtomic::String(Arc::new(RwLock::new(x))),
574 ConfigVal::OptString(x) => ConfigValAtomic::OptString(Arc::new(RwLock::new(x))),
575 ConfigVal::Duration(x) => ConfigValAtomic::Duration(Arc::new(RwLock::new(x))),
576 ConfigVal::Json(x) => ConfigValAtomic::Json(Arc::new(RwLock::new(x))),
577 }
578 }
579}
580
581impl ConfigValAtomic {
582 fn load(&self) -> ConfigVal {
583 match self {
584 ConfigValAtomic::Bool(x) => ConfigVal::Bool(x.load(SeqCst)),
585 ConfigValAtomic::U32(x) => ConfigVal::U32(x.load(SeqCst)),
586 ConfigValAtomic::Usize(x) => ConfigVal::Usize(x.load(SeqCst)),
587 ConfigValAtomic::OptUsize(x) => ConfigVal::OptUsize(*x.read().expect("lock poisoned")),
588 ConfigValAtomic::F64(x) => ConfigVal::F64(f64::from_bits(x.load(SeqCst))),
589 ConfigValAtomic::String(x) => {
590 ConfigVal::String(x.read().expect("lock poisoned").clone())
591 }
592 ConfigValAtomic::OptString(x) => {
593 ConfigVal::OptString(x.read().expect("lock poisoned").clone())
594 }
595 ConfigValAtomic::Duration(x) => ConfigVal::Duration(*x.read().expect("lock poisoned")),
596 ConfigValAtomic::Json(x) => ConfigVal::Json(x.read().expect("lock poisoned").clone()),
597 }
598 }
599
600 fn store(&self, val: ConfigVal) {
601 match (self, val) {
602 (ConfigValAtomic::Bool(x), ConfigVal::Bool(val)) => x.store(val, SeqCst),
603 (ConfigValAtomic::U32(x), ConfigVal::U32(val)) => x.store(val, SeqCst),
604 (ConfigValAtomic::Usize(x), ConfigVal::Usize(val)) => x.store(val, SeqCst),
605 (ConfigValAtomic::OptUsize(x), ConfigVal::OptUsize(val)) => {
606 *x.write().expect("lock poisoned") = val
607 }
608 (ConfigValAtomic::F64(x), ConfigVal::F64(val)) => x.store(val.to_bits(), SeqCst),
609 (ConfigValAtomic::String(x), ConfigVal::String(val)) => {
610 *x.write().expect("lock poisoned") = val
611 }
612 (ConfigValAtomic::OptString(x), ConfigVal::OptString(val)) => {
613 *x.write().expect("lock poisoned") = val
614 }
615 (ConfigValAtomic::Duration(x), ConfigVal::Duration(val)) => {
616 *x.write().expect("lock poisoned") = val
617 }
618 (ConfigValAtomic::Json(x), ConfigVal::Json(val)) => {
619 *x.write().expect("lock poisoned") = val
620 }
621 (ConfigValAtomic::Bool(_), val)
622 | (ConfigValAtomic::U32(_), val)
623 | (ConfigValAtomic::Usize(_), val)
624 | (ConfigValAtomic::OptUsize(_), val)
625 | (ConfigValAtomic::F64(_), val)
626 | (ConfigValAtomic::String(_), val)
627 | (ConfigValAtomic::OptString(_), val)
628 | (ConfigValAtomic::Duration(_), val)
629 | (ConfigValAtomic::Json(_), val) => {
630 panic!("attempted to store {val:?} value in {self:?} parameter")
631 }
632 }
633 }
634}
635
636#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
641pub struct ConfigUpdates {
642 pub updates: BTreeMap<String, ConfigVal>,
643}
644
645impl ConfigUpdates {
646 pub fn add<T, U>(&mut self, config: &Config<T>, val: U)
651 where
652 T: ConfigDefault,
653 U: ConfigDefault<ConfigType = T::ConfigType>,
654 {
655 self.add_dynamic(config.name, val.into_config_type().into());
656 }
657
658 pub fn add_dynamic(&mut self, name: &str, val: ConfigVal) {
666 self.updates.insert(name.to_owned(), val);
667 }
668
669 pub fn extend(&mut self, mut other: Self) {
671 self.updates.append(&mut other.updates)
672 }
673
674 pub fn apply(&self, set: &ConfigSet) {
684 for (name, val) in self.updates.iter() {
685 let Some(config) = set.configs.get(name) else {
686 error!("config update {} {:?} not known set: {:?}", name, val, set);
687 continue;
688 };
689 config.val.store(val.clone());
690 }
691 }
692}
693
694impl From<&ConfigSet> for ConfigUpdates {
695 fn from(set: &ConfigSet) -> Self {
700 let mut updates = ConfigUpdates::default();
701 for entry in set.entries() {
702 updates.add_dynamic(entry.name(), entry.val());
703 }
704 updates
705 }
706}
707
708mod impls {
709 use std::num::{ParseFloatError, ParseIntError};
710 use std::str::ParseBoolError;
711 use std::time::Duration;
712
713 use crate::{ConfigDefault, ConfigSet, ConfigType, ConfigVal};
714
715 impl ConfigType for bool {
716 fn from_val(val: ConfigVal) -> Self {
717 match val {
718 ConfigVal::Bool(x) => x,
719 x => panic!("expected bool value got {:?}", x),
720 }
721 }
722
723 fn parse(s: &str) -> Result<Self, String> {
724 match s {
725 "on" => return Ok(true),
726 "off" => return Ok(false),
727 _ => {}
728 }
729 s.parse().map_err(|e: ParseBoolError| e.to_string())
730 }
731 }
732
733 impl From<bool> for ConfigVal {
734 fn from(val: bool) -> ConfigVal {
735 ConfigVal::Bool(val)
736 }
737 }
738
739 impl ConfigType for u32 {
740 fn from_val(val: ConfigVal) -> Self {
741 match val {
742 ConfigVal::U32(x) => x,
743 x => panic!("expected u32 value got {:?}", x),
744 }
745 }
746
747 fn parse(s: &str) -> Result<Self, String> {
748 s.parse().map_err(|e: ParseIntError| e.to_string())
749 }
750 }
751
752 impl From<u32> for ConfigVal {
753 fn from(val: u32) -> ConfigVal {
754 ConfigVal::U32(val)
755 }
756 }
757
758 impl ConfigType for usize {
759 fn from_val(val: ConfigVal) -> Self {
760 match val {
761 ConfigVal::Usize(x) => x,
762 x => panic!("expected usize value got {:?}", x),
763 }
764 }
765
766 fn parse(s: &str) -> Result<Self, String> {
767 s.parse().map_err(|e: ParseIntError| e.to_string())
768 }
769 }
770
771 impl From<usize> for ConfigVal {
772 fn from(val: usize) -> ConfigVal {
773 ConfigVal::Usize(val)
774 }
775 }
776
777 impl ConfigType for Option<usize> {
778 fn from_val(val: ConfigVal) -> Self {
779 match val {
780 ConfigVal::OptUsize(x) => x,
781 x => panic!("expected usize value got {:?}", x),
782 }
783 }
784
785 fn parse(s: &str) -> Result<Self, String> {
786 if s.is_empty() {
787 Ok(None)
788 } else {
789 let val = s.parse().map_err(|e: ParseIntError| e.to_string())?;
790 Ok(Some(val))
791 }
792 }
793 }
794
795 impl From<Option<usize>> for ConfigVal {
796 fn from(val: Option<usize>) -> ConfigVal {
797 ConfigVal::OptUsize(val)
798 }
799 }
800
801 impl ConfigType for f64 {
802 fn from_val(val: ConfigVal) -> Self {
803 match val {
804 ConfigVal::F64(x) => x,
805 x => panic!("expected f64 value got {:?}", x),
806 }
807 }
808
809 fn parse(s: &str) -> Result<Self, String> {
810 s.parse().map_err(|e: ParseFloatError| e.to_string())
811 }
812 }
813
814 impl From<f64> for ConfigVal {
815 fn from(val: f64) -> ConfigVal {
816 ConfigVal::F64(val)
817 }
818 }
819
820 impl ConfigType for String {
821 fn from_val(val: ConfigVal) -> Self {
822 match val {
823 ConfigVal::String(x) => x,
824 x => panic!("expected String value got {:?}", x),
825 }
826 }
827
828 fn parse(s: &str) -> Result<Self, String> {
829 Ok(s.to_string())
830 }
831 }
832
833 impl From<String> for ConfigVal {
834 fn from(val: String) -> ConfigVal {
835 ConfigVal::String(val)
836 }
837 }
838
839 impl ConfigDefault for &str {
840 type ConfigType = String;
841
842 fn into_config_type(self) -> String {
843 self.into()
844 }
845 }
846
847 impl ConfigType for Option<String> {
848 fn from_val(val: ConfigVal) -> Self {
849 match val {
850 ConfigVal::OptString(x) => x,
851 x => panic!("expected String value got {:?}", x),
852 }
853 }
854
855 fn parse(s: &str) -> Result<Self, String> {
856 Ok(Some(s.to_string()))
857 }
858 }
859
860 impl From<Option<String>> for ConfigVal {
861 fn from(val: Option<String>) -> ConfigVal {
862 ConfigVal::OptString(val)
863 }
864 }
865
866 impl ConfigDefault for Option<&str> {
867 type ConfigType = Option<String>;
868
869 fn into_config_type(self) -> Option<String> {
870 self.map(|s| s.to_string())
871 }
872 }
873
874 impl ConfigType for Duration {
875 fn from_val(val: ConfigVal) -> Self {
876 match val {
877 ConfigVal::Duration(x) => x,
878 x => panic!("expected Duration value got {:?}", x),
879 }
880 }
881
882 fn parse(s: &str) -> Result<Self, String> {
883 humantime::parse_duration(s).map_err(|e| e.to_string())
884 }
885 }
886
887 impl From<Duration> for ConfigVal {
888 fn from(val: Duration) -> ConfigVal {
889 ConfigVal::Duration(val)
890 }
891 }
892
893 impl ConfigType for serde_json::Value {
894 fn from_val(val: ConfigVal) -> Self {
895 match val {
896 ConfigVal::Json(x) => x,
897 x => panic!("expected JSON value got {:?}", x),
898 }
899 }
900
901 fn parse(s: &str) -> Result<Self, String> {
902 serde_json::from_str(s).map_err(|e| e.to_string())
903 }
904 }
905
906 impl From<serde_json::Value> for ConfigVal {
907 fn from(val: serde_json::Value) -> ConfigVal {
908 ConfigVal::Json(val)
909 }
910 }
911
912 impl std::fmt::Debug for ConfigSet {
913 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
914 let ConfigSet { configs } = self;
915 f.debug_map()
916 .entries(configs.iter().map(|(name, val)| (name, val.val())))
917 .finish()
918 }
919 }
920}
921
922#[cfg(test)]
923mod tests {
924 use super::*;
925
926 use mz_ore::assert_err;
927
928 const BOOL: Config<bool> = Config::new("bool", true, "", ParameterScope::Environment);
929 const U32: Config<u32> = Config::new("u32", 4, "", ParameterScope::Environment);
930 const USIZE: Config<usize> = Config::new("usize", 1, "", ParameterScope::Environment);
931 const OPT_USIZE: Config<Option<usize>> =
932 Config::new("opt_usize", Some(2), "", ParameterScope::Environment);
933 const F64: Config<f64> = Config::new("f64", 5.0, "", ParameterScope::Environment);
934 const STRING: Config<&str> = Config::new("string", "a", "", ParameterScope::Environment);
935 const OPT_STRING: Config<Option<&str>> =
936 Config::new("opt_string", Some("a"), "", ParameterScope::Environment);
937 const DURATION: Config<Duration> = Config::new(
938 "duration",
939 Duration::from_nanos(3),
940 "",
941 ParameterScope::Environment,
942 );
943 const JSON: Config<fn() -> serde_json::Value> = Config::new(
944 "json",
945 || serde_json::json!({}),
946 "",
947 ParameterScope::Environment,
948 );
949
950 #[mz_ore::test]
951 fn all_types() {
952 let configs = ConfigSet::default()
953 .add(&BOOL)
954 .add(&USIZE)
955 .add(&U32)
956 .add(&OPT_USIZE)
957 .add(&F64)
958 .add(&STRING)
959 .add(&OPT_STRING)
960 .add(&DURATION)
961 .add(&JSON);
962 assert_eq!(BOOL.get(&configs), true);
963 assert_eq!(U32.get(&configs), 4);
964 assert_eq!(USIZE.get(&configs), 1);
965 assert_eq!(OPT_USIZE.get(&configs), Some(2));
966 assert_eq!(F64.get(&configs), 5.0);
967 assert_eq!(STRING.get(&configs), "a");
968 assert_eq!(OPT_STRING.get(&configs), Some("a".to_string()));
969 assert_eq!(DURATION.get(&configs), Duration::from_nanos(3));
970 assert_eq!(JSON.get(&configs), serde_json::json!({}));
971
972 let mut updates = ConfigUpdates::default();
973 updates.add(&BOOL, false);
974 updates.add(&U32, 7);
975 updates.add(&USIZE, 2);
976 updates.add(&OPT_USIZE, None::<usize>);
977 updates.add(&F64, 8.0);
978 updates.add(&STRING, "b");
979 updates.add(&OPT_STRING, None::<String>);
980 updates.add(&DURATION, Duration::from_nanos(4));
981 updates.add(&JSON, serde_json::json!({"a": 1}));
982 updates.apply(&configs);
983
984 assert_eq!(BOOL.get(&configs), false);
985 assert_eq!(U32.get(&configs), 7);
986 assert_eq!(USIZE.get(&configs), 2);
987 assert_eq!(OPT_USIZE.get(&configs), None);
988 assert_eq!(F64.get(&configs), 8.0);
989 assert_eq!(STRING.get(&configs), "b");
990 assert_eq!(OPT_STRING.get(&configs), None);
991 assert_eq!(DURATION.get(&configs), Duration::from_nanos(4));
992 assert_eq!(JSON.get(&configs), serde_json::json!({"a": 1}));
993 }
994
995 #[mz_ore::test]
996 fn fn_default() {
997 const BOOL_FN_DEFAULT: Config<fn() -> bool> =
998 Config::new("bool", || !true, "", ParameterScope::Environment);
999 const STRING_FN_DEFAULT: Config<fn() -> String> =
1000 Config::new("string", || "x".repeat(3), "", ParameterScope::Environment);
1001
1002 const OPT_STRING_FN_DEFAULT: Config<fn() -> Option<String>> = Config::new(
1003 "opt_string",
1004 || Some("x".repeat(3)),
1005 "",
1006 ParameterScope::Environment,
1007 );
1008
1009 let configs = ConfigSet::default()
1010 .add(&BOOL_FN_DEFAULT)
1011 .add(&STRING_FN_DEFAULT)
1012 .add(&OPT_STRING_FN_DEFAULT);
1013 assert_eq!(BOOL_FN_DEFAULT.get(&configs), false);
1014 assert_eq!(STRING_FN_DEFAULT.get(&configs), "xxx");
1015 assert_eq!(OPT_STRING_FN_DEFAULT.get(&configs), Some("xxx".to_string()));
1016 }
1017
1018 #[mz_ore::test]
1019 fn config_set() {
1020 let c0 = ConfigSet::default().add(&USIZE);
1021 assert_eq!(USIZE.get(&c0), 1);
1022 let mut updates = ConfigUpdates::default();
1023 updates.add(&USIZE, 2);
1024 updates.apply(&c0);
1025 assert_eq!(USIZE.get(&c0), 2);
1026
1027 let c1 = ConfigSet::default().add(&USIZE);
1030 assert_eq!(USIZE.get(&c1), 1);
1031 let mut updates = ConfigUpdates::default();
1032 updates.add(&USIZE, 3);
1033 updates.apply(&c1);
1034 assert_eq!(USIZE.get(&c1), 3);
1035 assert_eq!(USIZE.get(&c0), 2);
1036
1037 let mut updates = ConfigUpdates::default();
1039 for e in c0.entries() {
1040 updates.add_dynamic(e.name, e.val());
1041 }
1042 assert_eq!(USIZE.get(&c1), 3);
1043 updates.apply(&c1);
1044 assert_eq!(USIZE.get(&c1), 2);
1045 }
1046
1047 #[mz_ore::test]
1048 fn get_with_overrides() {
1049 let configs = ConfigSet::default().add(&USIZE).add(&STRING);
1050
1051 assert_eq!(USIZE.get_with_overrides(&configs, None), 1);
1054 let mut overrides = ConfigUpdates::default();
1055 overrides.add(&STRING, "b");
1056 assert_eq!(USIZE.get_with_overrides(&configs, Some(&overrides)), 1);
1057
1058 overrides.add(&USIZE, 2);
1060 assert_eq!(USIZE.get_with_overrides(&configs, Some(&overrides)), 2);
1061 assert_eq!(USIZE.get(&configs), 1);
1062
1063 let mut mistyped = ConfigUpdates::default();
1065 mistyped.add_dynamic(USIZE.name(), ConfigVal::Bool(true));
1066 assert_eq!(USIZE.get_with_overrides(&configs, Some(&mistyped)), 1);
1067 }
1068
1069 #[mz_ore::test]
1070 fn config_updates_extend() {
1071 let mut u1 = {
1077 let c = ConfigSet::default().add(&USIZE).add(&STRING);
1078 let mut x = ConfigUpdates::default();
1079 for e in c.entries() {
1080 x.add_dynamic(e.name(), e.val());
1081 }
1082 x
1083 };
1084 let u2 = {
1085 let c = ConfigSet::default().add(&USIZE).add(&DURATION);
1086 let mut updates = ConfigUpdates::default();
1087 updates.add(&USIZE, 2);
1088 updates.apply(&c);
1089 let mut x = ConfigUpdates::default();
1090 for e in c.entries() {
1091 x.add_dynamic(e.name(), e.val());
1092 }
1093 x
1094 };
1095 assert_eq!(u1.updates.len(), 2);
1096 assert_eq!(u2.updates.len(), 2);
1097 u1.extend(u2);
1098 assert_eq!(u1.updates.len(), 3);
1099
1100 let c = ConfigSet::default().add(&USIZE);
1103 u1.apply(&c);
1104 assert_eq!(USIZE.get(&c), 2);
1105 }
1106
1107 #[mz_ore::test]
1108 fn config_parse() {
1109 assert_eq!(BOOL.parse_val("true"), Ok(ConfigVal::Bool(true)));
1110 assert_eq!(BOOL.parse_val("on"), Ok(ConfigVal::Bool(true)));
1111 assert_eq!(BOOL.parse_val("false"), Ok(ConfigVal::Bool(false)));
1112 assert_eq!(BOOL.parse_val("off"), Ok(ConfigVal::Bool(false)));
1113 assert_err!(BOOL.parse_val("42"));
1114 assert_err!(BOOL.parse_val("66.6"));
1115 assert_err!(BOOL.parse_val("farragut"));
1116 assert_err!(BOOL.parse_val(""));
1117 assert_err!(BOOL.parse_val("5 s"));
1118
1119 assert_err!(U32.parse_val("true"));
1120 assert_err!(U32.parse_val("false"));
1121 assert_eq!(U32.parse_val("42"), Ok(ConfigVal::U32(42)));
1122 assert_err!(U32.parse_val("66.6"));
1123 assert_err!(U32.parse_val("farragut"));
1124 assert_err!(U32.parse_val(""));
1125 assert_err!(U32.parse_val("5 s"));
1126
1127 assert_err!(USIZE.parse_val("true"));
1128 assert_err!(USIZE.parse_val("false"));
1129 assert_eq!(USIZE.parse_val("42"), Ok(ConfigVal::Usize(42)));
1130 assert_err!(USIZE.parse_val("66.6"));
1131 assert_err!(USIZE.parse_val("farragut"));
1132 assert_err!(USIZE.parse_val(""));
1133 assert_err!(USIZE.parse_val("5 s"));
1134
1135 assert_err!(OPT_USIZE.parse_val("true"));
1136 assert_err!(OPT_USIZE.parse_val("false"));
1137 assert_eq!(OPT_USIZE.parse_val("42"), Ok(ConfigVal::OptUsize(Some(42))));
1138 assert_err!(OPT_USIZE.parse_val("66.6"));
1139 assert_err!(OPT_USIZE.parse_val("farragut"));
1140 assert_eq!(OPT_USIZE.parse_val(""), Ok(ConfigVal::OptUsize(None)));
1141 assert_err!(OPT_USIZE.parse_val("5 s"));
1142
1143 assert_err!(F64.parse_val("true"));
1144 assert_err!(F64.parse_val("false"));
1145 assert_eq!(F64.parse_val("42"), Ok(ConfigVal::F64(42.0)));
1146 assert_eq!(F64.parse_val("66.6"), Ok(ConfigVal::F64(66.6)));
1147 assert_err!(F64.parse_val("farragut"));
1148 assert_err!(F64.parse_val(""));
1149 assert_err!(F64.parse_val("5 s"));
1150
1151 assert_eq!(
1152 STRING.parse_val("true"),
1153 Ok(ConfigVal::String("true".to_string()))
1154 );
1155 assert_eq!(
1156 STRING.parse_val("false"),
1157 Ok(ConfigVal::String("false".to_string()))
1158 );
1159 assert_eq!(
1160 STRING.parse_val("66.6"),
1161 Ok(ConfigVal::String("66.6".to_string()))
1162 );
1163 assert_eq!(
1164 STRING.parse_val("42"),
1165 Ok(ConfigVal::String("42".to_string()))
1166 );
1167 assert_eq!(
1168 STRING.parse_val("farragut"),
1169 Ok(ConfigVal::String("farragut".to_string()))
1170 );
1171 assert_eq!(STRING.parse_val(""), Ok(ConfigVal::String("".to_string())));
1172 assert_eq!(
1173 STRING.parse_val("5 s"),
1174 Ok(ConfigVal::String("5 s".to_string()))
1175 );
1176
1177 assert_eq!(
1178 OPT_STRING.parse_val("true"),
1179 Ok(ConfigVal::OptString(Some("true".to_string())))
1180 );
1181 assert_eq!(
1182 OPT_STRING.parse_val("false"),
1183 Ok(ConfigVal::OptString(Some("false".to_string())))
1184 );
1185 assert_eq!(
1186 OPT_STRING.parse_val("66.6"),
1187 Ok(ConfigVal::OptString(Some("66.6".to_string())))
1188 );
1189 assert_eq!(
1190 OPT_STRING.parse_val("42"),
1191 Ok(ConfigVal::OptString(Some("42".to_string())))
1192 );
1193 assert_eq!(
1194 OPT_STRING.parse_val("farragut"),
1195 Ok(ConfigVal::OptString(Some("farragut".to_string())))
1196 );
1197 assert_eq!(
1198 OPT_STRING.parse_val(""),
1199 Ok(ConfigVal::OptString(Some("".to_string())))
1200 );
1201 assert_eq!(
1202 OPT_STRING.parse_val("5 s"),
1203 Ok(ConfigVal::OptString(Some("5 s".to_string())))
1204 );
1205
1206 assert_err!(DURATION.parse_val("true"));
1207 assert_err!(DURATION.parse_val("false"));
1208 assert_err!(DURATION.parse_val("42"));
1209 assert_err!(DURATION.parse_val("66.6"));
1210 assert_err!(DURATION.parse_val("farragut"));
1211 assert_err!(DURATION.parse_val(""));
1212 assert_eq!(
1213 DURATION.parse_val("5 s"),
1214 Ok(ConfigVal::Duration(Duration::from_secs(5)))
1215 );
1216
1217 assert_eq!(
1218 JSON.parse_val("true"),
1219 Ok(ConfigVal::Json(serde_json::json!(true)))
1220 );
1221 assert_eq!(
1222 JSON.parse_val("false"),
1223 Ok(ConfigVal::Json(serde_json::json!(false)))
1224 );
1225 assert_eq!(
1226 JSON.parse_val("42"),
1227 Ok(ConfigVal::Json(serde_json::json!(42)))
1228 );
1229 assert_eq!(
1230 JSON.parse_val("66.6"),
1231 Ok(ConfigVal::Json(serde_json::json!(66.6)))
1232 );
1233 assert_err!(JSON.parse_val("farragut"));
1234 assert_err!(JSON.parse_val(""));
1235 assert_err!(JSON.parse_val("5 s"));
1236 assert_eq!(
1237 JSON.parse_val("{\"joe\": \"developer\"}"),
1238 Ok(ConfigVal::Json(serde_json::json!({"joe": "developer"})))
1239 );
1240 }
1241}