platform_core/runtime_config/
provider.rs1use crate::runtime_config::descriptor::RuntimeConfigRegistry;
2use crate::runtime_config::snapshot::RuntimeConfigSnapshot;
3use arc_swap::ArcSwap;
4use std::collections::BTreeMap;
5use std::fmt::Debug;
6use std::sync::Arc;
7
8pub trait RuntimeConfigProvider: Debug + Send + Sync {
13 fn snapshot(&self) -> Arc<RuntimeConfigSnapshot>;
15}
16
17#[derive(Debug)]
20pub struct StaticRuntimeConfigProvider {
21 snapshot: Arc<RuntimeConfigSnapshot>,
22}
23
24impl StaticRuntimeConfigProvider {
25 #[must_use]
27 pub fn new(registry: &RuntimeConfigRegistry, service_key: &str) -> Self {
28 let snapshot = RuntimeConfigSnapshot::resolve(registry, service_key, &BTreeMap::new());
29 Self {
30 snapshot: Arc::new(snapshot),
31 }
32 }
33
34 #[must_use]
36 pub fn empty() -> Self {
37 Self {
38 snapshot: Arc::new(RuntimeConfigSnapshot::default()),
39 }
40 }
41}
42
43impl RuntimeConfigProvider for StaticRuntimeConfigProvider {
44 fn snapshot(&self) -> Arc<RuntimeConfigSnapshot> {
45 Arc::clone(&self.snapshot)
46 }
47}
48
49#[derive(Debug)]
52pub struct RuntimeConfigCell {
53 inner: ArcSwap<RuntimeConfigSnapshot>,
54}
55
56impl RuntimeConfigCell {
57 #[must_use]
58 pub fn new(initial: RuntimeConfigSnapshot) -> Self {
59 Self {
60 inner: ArcSwap::from_pointee(initial),
61 }
62 }
63
64 #[must_use]
65 pub fn load(&self) -> Arc<RuntimeConfigSnapshot> {
66 self.inner.load_full()
67 }
68
69 pub fn store(&self, snapshot: RuntimeConfigSnapshot) {
70 self.inner.store(Arc::new(snapshot));
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77 use crate::runtime_config::descriptor::{
78 RuntimeConfigDescriptor, RuntimeConfigScope, RuntimeConfigType,
79 };
80 use serde_json::json;
81
82 #[test]
83 fn static_provider_serves_defaults() {
84 let registry = RuntimeConfigRegistry::try_new(vec![RuntimeConfigDescriptor {
85 key: "demo.enabled".to_owned(),
86 scope: RuntimeConfigScope::Shared,
87 group: None,
88 section: None,
89 order: 0,
90 visible_when: None,
91 generated: None,
92 value_type: RuntimeConfigType::Bool,
93 default: json!(true),
94 editable: true,
95 restart_only: false,
96 description: "flag",
97 }])
98 .unwrap();
99 let provider = StaticRuntimeConfigProvider::new(®istry, "api");
100 assert_eq!(provider.snapshot().raw("demo.enabled"), Some(&json!(true)));
101 }
102
103 #[test]
104 fn snapshot_cell_swaps() {
105 let cell = RuntimeConfigCell::new(RuntimeConfigSnapshot::default());
106 assert!(cell.load().raw("x").is_none());
107 let registry = RuntimeConfigRegistry::try_new(vec![RuntimeConfigDescriptor {
108 key: "x".to_owned(),
109 scope: RuntimeConfigScope::Shared,
110 group: None,
111 section: None,
112 order: 0,
113 visible_when: None,
114 generated: None,
115 value_type: RuntimeConfigType::Bool,
116 default: json!(false),
117 editable: true,
118 restart_only: false,
119 description: "x",
120 }])
121 .unwrap();
122 cell.store(RuntimeConfigSnapshot::resolve(
123 ®istry,
124 "api",
125 &BTreeMap::new(),
126 ));
127 assert_eq!(cell.load().raw("x"), Some(&json!(false)));
128 }
129}