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    use zng_ext_fs_watcher::FsWatcherManager;
80
81    use super::*;
82
83    #[test]
84    fn swap_config_in_memory() {
85        let mut app = APP
86            .minimal()
87            .extend(FsWatcherManager::default())
88            .extend(ConfigManager::default())
89            .run_headless(false);
90
91        let mut cfg = SwapConfig::new();
92
93        let raw_true = RawConfigValue::serialize(true).unwrap();
94        let raw_false = RawConfigValue::serialize(false).unwrap();
95
96        let v = cfg.get_raw("key".into(), raw_true.clone(), false);
97        assert_eq!(v.get(), raw_true);
98        v.set(raw_false.clone());
99        app.update(false).assert_wait();
100
101        let v2 = cfg.get_raw("key".into(), raw_true, false);
102        assert!(v2.get() == raw_false && v.get() == raw_false);
103        assert!(v.var_eq(&v2));
104    }
105
106    #[test]
107    fn swap_config_swap() {
108        let mut app = APP
109            .minimal()
110            .extend(FsWatcherManager::default())
111            .extend(ConfigManager::default())
112            .run_headless(false);
113
114        let mut inner1 = MemoryConfig::default();
115        let c1 = inner1.get_raw("key".into(), RawConfigValue::serialize(0).unwrap(), false);
116        c1.set(RawConfigValue::serialize(32).unwrap());
117        app.update(false).assert_wait();
118
119        let mut test = SwapConfig::new();
120        test.replace_source(Box::new(inner1));
121
122        let c1 = test.get("key", 0, false);
123
124        assert_eq!(32, c1.get());
125    }
126
127    #[test]
128    fn swap_config_swap_load() {
129        let mut app = APP
130            .minimal()
131            .extend(FsWatcherManager::default())
132            .extend(ConfigManager::default())
133            .run_headless(false);
134
135        let mut inner1 = MemoryConfig::default();
136        let inner_v1 = inner1.get_raw("key".into(), RawConfigValue::serialize(0).unwrap(), false);
137        inner_v1.set(RawConfigValue::serialize(32).unwrap());
138        app.update(false).assert_wait();
139
140        let mut test = SwapConfig::new();
141        test.replace_source(Box::new(inner1));
142
143        let cfg = test.get("key", 0, false);
144
145        assert_eq!(32, cfg.get());
146
147        let mut inner2 = MemoryConfig::default();
148        let inner_v2 = inner2.get_raw("key".into(), RawConfigValue::serialize(0).unwrap(), false);
149        inner_v2.set(RawConfigValue::serialize(42).unwrap());
150        app.update(false).assert_wait();
151
152        test.replace_source(Box::new(inner2));
153        app.update(false).assert_wait();
154
155        assert_eq!(42, cfg.get());
156    }
157
158    #[test]
159    fn swap_config_swap_load_delayed() {
160        let mut app = APP
161            .minimal()
162            .extend(FsWatcherManager::default())
163            .extend(ConfigManager::default())
164            .run_headless(false);
165
166        let mut inner1 = MemoryConfig::default();
167        let inner_v1 = inner1.get_raw("key".into(), RawConfigValue::serialize(0).unwrap(), false);
168        inner_v1.set(RawConfigValue::serialize(32).unwrap());
169        app.update(false).assert_wait();
170
171        let mut test = SwapConfig::new();
172        test.replace_source(Box::new(inner1));
173
174        let cfg = test.get("key", 0, false);
175
176        assert_eq!(32, cfg.get());
177
178        let mut inner2 = MemoryConfig::default();
179        let inner_v2 = inner2.get_raw("key".into(), RawConfigValue::serialize(0).unwrap(), false);
180        app.update(false).assert_wait();
181
182        test.replace_source(Box::new(inner2));
183        app.update(false).assert_wait();
184
185        assert_eq!(0, cfg.get());
186
187        inner_v2.set(RawConfigValue::serialize(42).unwrap());
188        app.update(false).assert_wait();
189        assert_eq!(42, cfg.get());
190    }
191
192    #[test]
193    fn swap_config_swap_fallback_delayed() {
194        let mut app = APP
195            .minimal()
196            .extend(FsWatcherManager::default())
197            .extend(ConfigManager::default())
198            .run_headless(false);
199
200        let mut fallback = MemoryConfig::default();
201        fallback
202            .get_raw("key".into(), RawConfigValue::serialize(0).unwrap(), false)
203            .set(RawConfigValue::serialize(100).unwrap());
204
205        let mut inner1 = MemoryConfig::default();
206        let inner_v1 = inner1.get_raw("key".into(), RawConfigValue::serialize(0).unwrap(), false);
207        inner_v1.set(RawConfigValue::serialize(32).unwrap());
208        app.update(false).assert_wait();
209
210        let mut test = SwapConfig::new();
211        test.replace_source(Box::new(FallbackConfig::new(inner1, fallback)));
212
213        let cfg = test.get("key", -1, false);
214
215        assert_eq!(32, cfg.get());
216
217        let mut fallback = MemoryConfig::default();
218        fallback
219            .get_raw("key".into(), RawConfigValue::serialize(0).unwrap(), false)
220            .set(RawConfigValue::serialize(100).unwrap());
221        let mut inner2 = MemoryConfig::default();
222        let inner_v2 = inner2.get_raw("key".into(), RawConfigValue::serialize(0).unwrap(), false);
223        app.update(false).assert_wait();
224
225        test.replace_source(Box::new(FallbackConfig::new(inner2, fallback)));
226        app.update(false).assert_wait();
227
228        assert_eq!(0, cfg.get());
229
230        inner_v2.set(RawConfigValue::serialize(42).unwrap());
231        app.update(false).assert_wait();
232        assert_eq!(42, cfg.get());
233    }
234}