zng_ext_config/
swap.rs

1use super::*;
2
3use crate::task::parking_lot::Mutex;
4use zng_var::VarHandle;
5
6/// Represents a config source that can swap its backing config source without disconnecting any bound keys.
7///
8/// Note that the [`CONFIG`] service already uses this type internally.
9pub struct SwapConfig {
10    cfg: Mutex<Box<dyn AnyConfig>>,
11    shared: ConfigVars,
12
13    source_status: Var<ConfigStatus>,
14    status: Var<ConfigStatus>,
15    status_binding: VarHandle,
16}
17impl AnyConfig for SwapConfig {
18    fn get_raw(&mut self, key: ConfigKey, default: RawConfigValue, insert: bool) -> Var<RawConfigValue> {
19        self.shared
20            .get_or_bind(key, |key| self.cfg.get_mut().get_raw(key.clone(), default, insert))
21    }
22
23    fn contains_key(&mut self, key: ConfigKey) -> Var<bool> {
24        self.shared
25            .get_or_bind_contains(key, |key| self.cfg.get_mut().contains_key(key.clone()))
26    }
27
28    fn status(&self) -> Var<ConfigStatus> {
29        self.status.read_only()
30    }
31
32    fn remove(&mut self, key: &ConfigKey) -> bool {
33        self.cfg.get_mut().remove(key)
34    }
35
36    fn low_memory(&mut self) {
37        self.cfg.get_mut().low_memory();
38        self.shared.low_memory();
39    }
40}
41impl SwapConfig {
42    /// New with [`MemoryConfig`] backend.
43    pub fn new() -> Self {
44        Self {
45            cfg: Mutex::new(Box::<MemoryConfig>::default()),
46            shared: ConfigVars::default(),
47            source_status: const_var(ConfigStatus::Loaded),
48            status: var(ConfigStatus::Loaded),
49            status_binding: VarHandle::dummy(),
50        }
51    }
52
53    /// Load the config.
54    ///
55    /// The previous source will be dropped and all active config variables are set and rebound to the new config.
56    pub fn load(&mut self, cfg: impl AnyConfig) {
57        self.replace_source(Box::new(cfg))
58    }
59
60    fn replace_source(&mut self, source: Box<dyn AnyConfig>) {
61        self.source_status = source.status();
62        self.status.set_from(&self.source_status);
63        self.status_binding = self.source_status.bind(&self.status);
64
65        *self.cfg.get_mut() = source; // drop previous source first
66
67        self.shared.rebind(&mut **self.cfg.get_mut());
68    }
69}
70impl Default for SwapConfig {
71    fn default() -> Self {
72        Self::new()
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use zng_app::APP;
79
80    use super::*;
81
82    #[test]
83    fn swap_config_in_memory() {
84        let mut app = APP.minimal().run_headless(false);
85
86        let mut cfg = SwapConfig::new();
87
88        let raw_true = RawConfigValue::serialize(true).unwrap();
89        let raw_false = RawConfigValue::serialize(false).unwrap();
90
91        let v = cfg.get_raw("key".into(), raw_true.clone(), false);
92        assert_eq!(v.get(), raw_true);
93        v.set(raw_false.clone());
94        app.update(false).assert_wait();
95
96        let v2 = cfg.get_raw("key".into(), raw_true, false);
97        assert!(v2.get() == raw_false && v.get() == raw_false);
98        assert!(v.var_eq(&v2));
99    }
100
101    #[test]
102    fn swap_config_swap() {
103        let mut app = APP.minimal().run_headless(false);
104
105        let mut inner1 = MemoryConfig::default();
106        let c1 = inner1.get_raw("key".into(), RawConfigValue::serialize(0).unwrap(), false);
107        c1.set(RawConfigValue::serialize(32).unwrap());
108        app.update(false).assert_wait();
109
110        let mut test = SwapConfig::new();
111        test.replace_source(Box::new(inner1));
112
113        let c1 = test.get("key", 0, false);
114
115        assert_eq!(32, c1.get());
116    }
117
118    #[test]
119    fn swap_config_swap_load() {
120        let mut app = APP.minimal().run_headless(false);
121
122        let mut inner1 = MemoryConfig::default();
123        let inner_v1 = inner1.get_raw("key".into(), RawConfigValue::serialize(0).unwrap(), false);
124        inner_v1.set(RawConfigValue::serialize(32).unwrap());
125        app.update(false).assert_wait();
126
127        let mut test = SwapConfig::new();
128        test.replace_source(Box::new(inner1));
129
130        let cfg = test.get("key", 0, false);
131
132        assert_eq!(32, cfg.get());
133
134        let mut inner2 = MemoryConfig::default();
135        let inner_v2 = inner2.get_raw("key".into(), RawConfigValue::serialize(0).unwrap(), false);
136        inner_v2.set(RawConfigValue::serialize(42).unwrap());
137        app.update(false).assert_wait();
138
139        test.replace_source(Box::new(inner2));
140        app.update(false).assert_wait();
141
142        assert_eq!(42, cfg.get());
143    }
144
145    #[test]
146    fn swap_config_swap_load_delayed() {
147        let mut app = APP.minimal().run_headless(false);
148
149        let mut inner1 = MemoryConfig::default();
150        let inner_v1 = inner1.get_raw("key".into(), RawConfigValue::serialize(0).unwrap(), false);
151        inner_v1.set(RawConfigValue::serialize(32).unwrap());
152        app.update(false).assert_wait();
153
154        let mut test = SwapConfig::new();
155        test.replace_source(Box::new(inner1));
156
157        let cfg = test.get("key", 0, false);
158
159        assert_eq!(32, cfg.get());
160
161        let mut inner2 = MemoryConfig::default();
162        let inner_v2 = inner2.get_raw("key".into(), RawConfigValue::serialize(0).unwrap(), false);
163        app.update(false).assert_wait();
164
165        test.replace_source(Box::new(inner2));
166        app.update(false).assert_wait();
167
168        assert_eq!(0, cfg.get());
169
170        inner_v2.set(RawConfigValue::serialize(42).unwrap());
171        app.update(false).assert_wait();
172        assert_eq!(42, cfg.get());
173    }
174
175    #[test]
176    fn swap_config_swap_fallback_delayed() {
177        let mut app = APP.minimal().run_headless(false);
178
179        let mut fallback = MemoryConfig::default();
180        fallback
181            .get_raw("key".into(), RawConfigValue::serialize(0).unwrap(), false)
182            .set(RawConfigValue::serialize(100).unwrap());
183
184        let mut inner1 = MemoryConfig::default();
185        let inner_v1 = inner1.get_raw("key".into(), RawConfigValue::serialize(0).unwrap(), false);
186        inner_v1.set(RawConfigValue::serialize(32).unwrap());
187        app.update(false).assert_wait();
188
189        let mut test = SwapConfig::new();
190        test.replace_source(Box::new(FallbackConfig::new(inner1, fallback)));
191
192        let cfg = test.get("key", -1, false);
193
194        assert_eq!(32, cfg.get());
195
196        let mut fallback = MemoryConfig::default();
197        fallback
198            .get_raw("key".into(), RawConfigValue::serialize(0).unwrap(), false)
199            .set(RawConfigValue::serialize(100).unwrap());
200        let mut inner2 = MemoryConfig::default();
201        let inner_v2 = inner2.get_raw("key".into(), RawConfigValue::serialize(0).unwrap(), false);
202        app.update(false).assert_wait();
203
204        test.replace_source(Box::new(FallbackConfig::new(inner2, fallback)));
205        app.update(false).assert_wait();
206
207        assert_eq!(0, cfg.get());
208
209        inner_v2.set(RawConfigValue::serialize(42).unwrap());
210        app.update(false).assert_wait();
211        assert_eq!(42, cfg.get());
212    }
213}