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
599mod impls {
600 use std::num::{ParseFloatError, ParseIntError};
601 use std::str::ParseBoolError;
602 use std::time::Duration;
603
604 use crate::{ConfigDefault, ConfigSet, ConfigType, ConfigVal};
605
606 impl ConfigType for bool {
607 fn from_val(val: ConfigVal) -> Self {
608 match val {
609 ConfigVal::Bool(x) => x,
610 x => panic!("expected bool value got {:?}", x),
611 }
612 }
613
614 fn parse(s: &str) -> Result<Self, String> {
615 match s {
616 "on" => return Ok(true),
617 "off" => return Ok(false),
618 _ => {}
619 }
620 s.parse().map_err(|e: ParseBoolError| e.to_string())
621 }
622 }
623
624 impl From<bool> for ConfigVal {
625 fn from(val: bool) -> ConfigVal {
626 ConfigVal::Bool(val)
627 }
628 }
629
630 impl ConfigType for u32 {
631 fn from_val(val: ConfigVal) -> Self {
632 match val {
633 ConfigVal::U32(x) => x,
634 x => panic!("expected u32 value got {:?}", x),
635 }
636 }
637
638 fn parse(s: &str) -> Result<Self, String> {
639 s.parse().map_err(|e: ParseIntError| e.to_string())
640 }
641 }
642
643 impl From<u32> for ConfigVal {
644 fn from(val: u32) -> ConfigVal {
645 ConfigVal::U32(val)
646 }
647 }
648
649 impl ConfigType for usize {
650 fn from_val(val: ConfigVal) -> Self {
651 match val {
652 ConfigVal::Usize(x) => x,
653 x => panic!("expected usize value got {:?}", x),
654 }
655 }
656
657 fn parse(s: &str) -> Result<Self, String> {
658 s.parse().map_err(|e: ParseIntError| e.to_string())
659 }
660 }
661
662 impl From<usize> for ConfigVal {
663 fn from(val: usize) -> ConfigVal {
664 ConfigVal::Usize(val)
665 }
666 }
667
668 impl ConfigType for Option<usize> {
669 fn from_val(val: ConfigVal) -> Self {
670 match val {
671 ConfigVal::OptUsize(x) => x,
672 x => panic!("expected usize value got {:?}", x),
673 }
674 }
675
676 fn parse(s: &str) -> Result<Self, String> {
677 if s.is_empty() {
678 Ok(None)
679 } else {
680 let val = s.parse().map_err(|e: ParseIntError| e.to_string())?;
681 Ok(Some(val))
682 }
683 }
684 }
685
686 impl From<Option<usize>> for ConfigVal {
687 fn from(val: Option<usize>) -> ConfigVal {
688 ConfigVal::OptUsize(val)
689 }
690 }
691
692 impl ConfigType for f64 {
693 fn from_val(val: ConfigVal) -> Self {
694 match val {
695 ConfigVal::F64(x) => x,
696 x => panic!("expected f64 value got {:?}", x),
697 }
698 }
699
700 fn parse(s: &str) -> Result<Self, String> {
701 s.parse().map_err(|e: ParseFloatError| e.to_string())
702 }
703 }
704
705 impl From<f64> for ConfigVal {
706 fn from(val: f64) -> ConfigVal {
707 ConfigVal::F64(val)
708 }
709 }
710
711 impl ConfigType for String {
712 fn from_val(val: ConfigVal) -> Self {
713 match val {
714 ConfigVal::String(x) => x,
715 x => panic!("expected String value got {:?}", x),
716 }
717 }
718
719 fn parse(s: &str) -> Result<Self, String> {
720 Ok(s.to_string())
721 }
722 }
723
724 impl From<String> for ConfigVal {
725 fn from(val: String) -> ConfigVal {
726 ConfigVal::String(val)
727 }
728 }
729
730 impl ConfigDefault for &str {
731 type ConfigType = String;
732
733 fn into_config_type(self) -> String {
734 self.into()
735 }
736 }
737
738 impl ConfigType for Option<String> {
739 fn from_val(val: ConfigVal) -> Self {
740 match val {
741 ConfigVal::OptString(x) => x,
742 x => panic!("expected String value got {:?}", x),
743 }
744 }
745
746 fn parse(s: &str) -> Result<Self, String> {
747 Ok(Some(s.to_string()))
748 }
749 }
750
751 impl From<Option<String>> for ConfigVal {
752 fn from(val: Option<String>) -> ConfigVal {
753 ConfigVal::OptString(val)
754 }
755 }
756
757 impl ConfigDefault for Option<&str> {
758 type ConfigType = Option<String>;
759
760 fn into_config_type(self) -> Option<String> {
761 self.map(|s| s.to_string())
762 }
763 }
764
765 impl ConfigType for Duration {
766 fn from_val(val: ConfigVal) -> Self {
767 match val {
768 ConfigVal::Duration(x) => x,
769 x => panic!("expected Duration value got {:?}", x),
770 }
771 }
772
773 fn parse(s: &str) -> Result<Self, String> {
774 humantime::parse_duration(s).map_err(|e| e.to_string())
775 }
776 }
777
778 impl From<Duration> for ConfigVal {
779 fn from(val: Duration) -> ConfigVal {
780 ConfigVal::Duration(val)
781 }
782 }
783
784 impl ConfigType for serde_json::Value {
785 fn from_val(val: ConfigVal) -> Self {
786 match val {
787 ConfigVal::Json(x) => x,
788 x => panic!("expected JSON value got {:?}", x),
789 }
790 }
791
792 fn parse(s: &str) -> Result<Self, String> {
793 serde_json::from_str(s).map_err(|e| e.to_string())
794 }
795 }
796
797 impl From<serde_json::Value> for ConfigVal {
798 fn from(val: serde_json::Value) -> ConfigVal {
799 ConfigVal::Json(val)
800 }
801 }
802
803 impl std::fmt::Debug for ConfigSet {
804 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
805 let ConfigSet { configs } = self;
806 f.debug_map()
807 .entries(configs.iter().map(|(name, val)| (name, val.val())))
808 .finish()
809 }
810 }
811}
812
813#[cfg(test)]
814mod tests {
815 use super::*;
816
817 use mz_ore::assert_err;
818
819 const BOOL: Config<bool> = Config::new("bool", true, "");
820 const U32: Config<u32> = Config::new("u32", 4, "");
821 const USIZE: Config<usize> = Config::new("usize", 1, "");
822 const OPT_USIZE: Config<Option<usize>> = Config::new("opt_usize", Some(2), "");
823 const F64: Config<f64> = Config::new("f64", 5.0, "");
824 const STRING: Config<&str> = Config::new("string", "a", "");
825 const OPT_STRING: Config<Option<&str>> = Config::new("opt_string", Some("a"), "");
826 const DURATION: Config<Duration> = Config::new("duration", Duration::from_nanos(3), "");
827 const JSON: Config<fn() -> serde_json::Value> =
828 Config::new("json", || serde_json::json!({}), "");
829
830 #[mz_ore::test]
831 fn all_types() {
832 let configs = ConfigSet::default()
833 .add(&BOOL)
834 .add(&USIZE)
835 .add(&U32)
836 .add(&OPT_USIZE)
837 .add(&F64)
838 .add(&STRING)
839 .add(&OPT_STRING)
840 .add(&DURATION)
841 .add(&JSON);
842 assert_eq!(BOOL.get(&configs), true);
843 assert_eq!(U32.get(&configs), 4);
844 assert_eq!(USIZE.get(&configs), 1);
845 assert_eq!(OPT_USIZE.get(&configs), Some(2));
846 assert_eq!(F64.get(&configs), 5.0);
847 assert_eq!(STRING.get(&configs), "a");
848 assert_eq!(OPT_STRING.get(&configs), Some("a".to_string()));
849 assert_eq!(DURATION.get(&configs), Duration::from_nanos(3));
850 assert_eq!(JSON.get(&configs), serde_json::json!({}));
851
852 let mut updates = ConfigUpdates::default();
853 updates.add(&BOOL, false);
854 updates.add(&U32, 7);
855 updates.add(&USIZE, 2);
856 updates.add(&OPT_USIZE, None::<usize>);
857 updates.add(&F64, 8.0);
858 updates.add(&STRING, "b");
859 updates.add(&OPT_STRING, None::<String>);
860 updates.add(&DURATION, Duration::from_nanos(4));
861 updates.add(&JSON, serde_json::json!({"a": 1}));
862 updates.apply(&configs);
863
864 assert_eq!(BOOL.get(&configs), false);
865 assert_eq!(U32.get(&configs), 7);
866 assert_eq!(USIZE.get(&configs), 2);
867 assert_eq!(OPT_USIZE.get(&configs), None);
868 assert_eq!(F64.get(&configs), 8.0);
869 assert_eq!(STRING.get(&configs), "b");
870 assert_eq!(OPT_STRING.get(&configs), None);
871 assert_eq!(DURATION.get(&configs), Duration::from_nanos(4));
872 assert_eq!(JSON.get(&configs), serde_json::json!({"a": 1}));
873 }
874
875 #[mz_ore::test]
876 fn fn_default() {
877 const BOOL_FN_DEFAULT: Config<fn() -> bool> = Config::new("bool", || !true, "");
878 const STRING_FN_DEFAULT: Config<fn() -> String> =
879 Config::new("string", || "x".repeat(3), "");
880
881 const OPT_STRING_FN_DEFAULT: Config<fn() -> Option<String>> =
882 Config::new("opt_string", || Some("x".repeat(3)), "");
883
884 let configs = ConfigSet::default()
885 .add(&BOOL_FN_DEFAULT)
886 .add(&STRING_FN_DEFAULT)
887 .add(&OPT_STRING_FN_DEFAULT);
888 assert_eq!(BOOL_FN_DEFAULT.get(&configs), false);
889 assert_eq!(STRING_FN_DEFAULT.get(&configs), "xxx");
890 assert_eq!(OPT_STRING_FN_DEFAULT.get(&configs), Some("xxx".to_string()));
891 }
892
893 #[mz_ore::test]
894 fn config_set() {
895 let c0 = ConfigSet::default().add(&USIZE);
896 assert_eq!(USIZE.get(&c0), 1);
897 let mut updates = ConfigUpdates::default();
898 updates.add(&USIZE, 2);
899 updates.apply(&c0);
900 assert_eq!(USIZE.get(&c0), 2);
901
902 let c1 = ConfigSet::default().add(&USIZE);
905 assert_eq!(USIZE.get(&c1), 1);
906 let mut updates = ConfigUpdates::default();
907 updates.add(&USIZE, 3);
908 updates.apply(&c1);
909 assert_eq!(USIZE.get(&c1), 3);
910 assert_eq!(USIZE.get(&c0), 2);
911
912 let mut updates = ConfigUpdates::default();
914 for e in c0.entries() {
915 updates.add_dynamic(e.name, e.val());
916 }
917 assert_eq!(USIZE.get(&c1), 3);
918 updates.apply(&c1);
919 assert_eq!(USIZE.get(&c1), 2);
920 }
921
922 #[mz_ore::test]
923 fn config_updates_extend() {
924 let mut u1 = {
930 let c = ConfigSet::default().add(&USIZE).add(&STRING);
931 let mut x = ConfigUpdates::default();
932 for e in c.entries() {
933 x.add_dynamic(e.name(), e.val());
934 }
935 x
936 };
937 let u2 = {
938 let c = ConfigSet::default().add(&USIZE).add(&DURATION);
939 let mut updates = ConfigUpdates::default();
940 updates.add(&USIZE, 2);
941 updates.apply(&c);
942 let mut x = ConfigUpdates::default();
943 for e in c.entries() {
944 x.add_dynamic(e.name(), e.val());
945 }
946 x
947 };
948 assert_eq!(u1.updates.len(), 2);
949 assert_eq!(u2.updates.len(), 2);
950 u1.extend(u2);
951 assert_eq!(u1.updates.len(), 3);
952
953 let c = ConfigSet::default().add(&USIZE);
956 u1.apply(&c);
957 assert_eq!(USIZE.get(&c), 2);
958 }
959
960 #[mz_ore::test]
961 fn config_parse() {
962 assert_eq!(BOOL.parse_val("true"), Ok(ConfigVal::Bool(true)));
963 assert_eq!(BOOL.parse_val("on"), Ok(ConfigVal::Bool(true)));
964 assert_eq!(BOOL.parse_val("false"), Ok(ConfigVal::Bool(false)));
965 assert_eq!(BOOL.parse_val("off"), Ok(ConfigVal::Bool(false)));
966 assert_err!(BOOL.parse_val("42"));
967 assert_err!(BOOL.parse_val("66.6"));
968 assert_err!(BOOL.parse_val("farragut"));
969 assert_err!(BOOL.parse_val(""));
970 assert_err!(BOOL.parse_val("5 s"));
971
972 assert_err!(U32.parse_val("true"));
973 assert_err!(U32.parse_val("false"));
974 assert_eq!(U32.parse_val("42"), Ok(ConfigVal::U32(42)));
975 assert_err!(U32.parse_val("66.6"));
976 assert_err!(U32.parse_val("farragut"));
977 assert_err!(U32.parse_val(""));
978 assert_err!(U32.parse_val("5 s"));
979
980 assert_err!(USIZE.parse_val("true"));
981 assert_err!(USIZE.parse_val("false"));
982 assert_eq!(USIZE.parse_val("42"), Ok(ConfigVal::Usize(42)));
983 assert_err!(USIZE.parse_val("66.6"));
984 assert_err!(USIZE.parse_val("farragut"));
985 assert_err!(USIZE.parse_val(""));
986 assert_err!(USIZE.parse_val("5 s"));
987
988 assert_err!(OPT_USIZE.parse_val("true"));
989 assert_err!(OPT_USIZE.parse_val("false"));
990 assert_eq!(OPT_USIZE.parse_val("42"), Ok(ConfigVal::OptUsize(Some(42))));
991 assert_err!(OPT_USIZE.parse_val("66.6"));
992 assert_err!(OPT_USIZE.parse_val("farragut"));
993 assert_eq!(OPT_USIZE.parse_val(""), Ok(ConfigVal::OptUsize(None)));
994 assert_err!(OPT_USIZE.parse_val("5 s"));
995
996 assert_err!(F64.parse_val("true"));
997 assert_err!(F64.parse_val("false"));
998 assert_eq!(F64.parse_val("42"), Ok(ConfigVal::F64(42.0)));
999 assert_eq!(F64.parse_val("66.6"), Ok(ConfigVal::F64(66.6)));
1000 assert_err!(F64.parse_val("farragut"));
1001 assert_err!(F64.parse_val(""));
1002 assert_err!(F64.parse_val("5 s"));
1003
1004 assert_eq!(
1005 STRING.parse_val("true"),
1006 Ok(ConfigVal::String("true".to_string()))
1007 );
1008 assert_eq!(
1009 STRING.parse_val("false"),
1010 Ok(ConfigVal::String("false".to_string()))
1011 );
1012 assert_eq!(
1013 STRING.parse_val("66.6"),
1014 Ok(ConfigVal::String("66.6".to_string()))
1015 );
1016 assert_eq!(
1017 STRING.parse_val("42"),
1018 Ok(ConfigVal::String("42".to_string()))
1019 );
1020 assert_eq!(
1021 STRING.parse_val("farragut"),
1022 Ok(ConfigVal::String("farragut".to_string()))
1023 );
1024 assert_eq!(STRING.parse_val(""), Ok(ConfigVal::String("".to_string())));
1025 assert_eq!(
1026 STRING.parse_val("5 s"),
1027 Ok(ConfigVal::String("5 s".to_string()))
1028 );
1029
1030 assert_eq!(
1031 OPT_STRING.parse_val("true"),
1032 Ok(ConfigVal::OptString(Some("true".to_string())))
1033 );
1034 assert_eq!(
1035 OPT_STRING.parse_val("false"),
1036 Ok(ConfigVal::OptString(Some("false".to_string())))
1037 );
1038 assert_eq!(
1039 OPT_STRING.parse_val("66.6"),
1040 Ok(ConfigVal::OptString(Some("66.6".to_string())))
1041 );
1042 assert_eq!(
1043 OPT_STRING.parse_val("42"),
1044 Ok(ConfigVal::OptString(Some("42".to_string())))
1045 );
1046 assert_eq!(
1047 OPT_STRING.parse_val("farragut"),
1048 Ok(ConfigVal::OptString(Some("farragut".to_string())))
1049 );
1050 assert_eq!(
1051 OPT_STRING.parse_val(""),
1052 Ok(ConfigVal::OptString(Some("".to_string())))
1053 );
1054 assert_eq!(
1055 OPT_STRING.parse_val("5 s"),
1056 Ok(ConfigVal::OptString(Some("5 s".to_string())))
1057 );
1058
1059 assert_err!(DURATION.parse_val("true"));
1060 assert_err!(DURATION.parse_val("false"));
1061 assert_err!(DURATION.parse_val("42"));
1062 assert_err!(DURATION.parse_val("66.6"));
1063 assert_err!(DURATION.parse_val("farragut"));
1064 assert_err!(DURATION.parse_val(""));
1065 assert_eq!(
1066 DURATION.parse_val("5 s"),
1067 Ok(ConfigVal::Duration(Duration::from_secs(5)))
1068 );
1069
1070 assert_eq!(
1071 JSON.parse_val("true"),
1072 Ok(ConfigVal::Json(serde_json::json!(true)))
1073 );
1074 assert_eq!(
1075 JSON.parse_val("false"),
1076 Ok(ConfigVal::Json(serde_json::json!(false)))
1077 );
1078 assert_eq!(
1079 JSON.parse_val("42"),
1080 Ok(ConfigVal::Json(serde_json::json!(42)))
1081 );
1082 assert_eq!(
1083 JSON.parse_val("66.6"),
1084 Ok(ConfigVal::Json(serde_json::json!(66.6)))
1085 );
1086 assert_err!(JSON.parse_val("farragut"));
1087 assert_err!(JSON.parse_val(""));
1088 assert_err!(JSON.parse_val("5 s"));
1089 assert_eq!(
1090 JSON.parse_val("{\"joe\": \"developer\"}"),
1091 Ok(ConfigVal::Json(serde_json::json!({"joe": "developer"})))
1092 );
1093 }
1094}