routing/bedrock/
structured_dict.rs1use crate::DictExt;
6use cm_types::{BorrowedName, IterablePath, Name};
7use runtime_capabilities::{Capability, Data, Dictionary};
8use std::fmt;
9use std::marker::PhantomData;
10use std::sync::{Arc, LazyLock};
11
12trait StructuredDict: Into<Arc<Dictionary>> + Default + Clone + fmt::Debug {
19 fn from_dict(dict: Arc<Dictionary>) -> Self;
26}
27
28#[derive(Clone, Debug, Default)]
37#[allow(private_bounds)]
38pub struct StructuredDictMap<T: StructuredDict> {
39 inner: Arc<Dictionary>,
40 phantom: PhantomData<T>,
41}
42
43impl<T: StructuredDict> StructuredDict for StructuredDictMap<T> {
44 fn from_dict(dict: Arc<Dictionary>) -> Self {
45 Self { inner: dict, phantom: Default::default() }
46 }
47}
48
49#[allow(private_bounds)]
50impl<T: StructuredDict> StructuredDictMap<T> {
51 pub fn insert(&self, key: Name, value: T) -> Option<Capability> {
52 self.inner.insert(key, Capability::Dictionary(value.into()))
53 }
54
55 pub fn get(&self, key: &BorrowedName) -> Option<T> {
56 self.inner.get(key).map(|cap| {
57 let Capability::Dictionary(dict) = cap else {
58 unreachable!("structured map entry must be a dict: {cap:?}");
59 };
60 T::from_dict(dict)
61 })
62 }
63
64 pub fn remove(&self, key: &Name) -> Option<T> {
65 self.inner.remove(&*key).map(|cap| {
66 let Capability::Dictionary(dict) = cap else {
67 unreachable!("structured map entry must be a dict: {cap:?}");
68 };
69 T::from_dict(dict)
70 })
71 }
72
73 pub fn append(&self, other: &Self) -> Result<(), ()> {
74 self.inner.append(&other.inner)
75 }
76
77 pub fn enumerate(&self) -> impl Iterator<Item = (Name, T)> {
78 self.inner.enumerate().map(|(key, capability_res)| match capability_res {
79 Capability::Dictionary(dict) => (key, T::from_dict(dict)),
80 cap => unreachable!("structured map entry must be a dict: {cap:?}"),
81 })
82 }
83}
84
85impl<T: StructuredDict> From<StructuredDictMap<T>> for Arc<Dictionary> {
86 fn from(m: StructuredDictMap<T>) -> Self {
87 m.inner
88 }
89}
90
91static PARENT: LazyLock<Name> = LazyLock::new(|| "parent".parse().unwrap());
94
95static ENVIRONMENT: LazyLock<Name> = LazyLock::new(|| "environment".parse().unwrap());
97
98static DEBUG: LazyLock<Name> = LazyLock::new(|| "debug".parse().unwrap());
100
101static RUNNERS: LazyLock<Name> = LazyLock::new(|| "runners".parse().unwrap());
103
104static RESOLVERS: LazyLock<Name> = LazyLock::new(|| "resolvers".parse().unwrap());
106
107static FRAMEWORK: LazyLock<Name> = LazyLock::new(|| "framework".parse().unwrap());
109
110static STOP_TIMEOUT: LazyLock<Name> = LazyLock::new(|| "stop_timeout".parse().unwrap());
112
113static NAME: LazyLock<Name> = LazyLock::new(|| "name".parse().unwrap());
115
116#[derive(Clone, Debug)]
119pub struct ComponentInput(Arc<Dictionary>);
120
121impl Default for ComponentInput {
122 fn default() -> Self {
123 Self::new(ComponentEnvironment::new())
124 }
125}
126
127impl StructuredDict for ComponentInput {
128 fn from_dict(dict: Arc<Dictionary>) -> Self {
129 Self(dict)
130 }
131}
132
133impl ComponentInput {
134 pub fn new(environment: ComponentEnvironment) -> Self {
135 let dict = Dictionary::new();
136
137 if !environment.0.is_empty() {
138 dict.insert(ENVIRONMENT.clone(), Capability::Dictionary(environment.into()));
139 }
140 Self(dict)
141 }
142
143 pub fn shallow_copy(&self) -> Self {
148 let dest = Dictionary::new();
152 shallow_copy(&self.0, &dest, &*PARENT);
153 shallow_copy(&self.0, &dest, &*ENVIRONMENT);
154 Self(dest)
155 }
156
157 pub fn capabilities(&self) -> Arc<Dictionary> {
159 get_or_insert(&self.0, &*PARENT)
160 }
161
162 pub fn environment(&self) -> ComponentEnvironment {
164 ComponentEnvironment(get_or_insert(&self.0, &*ENVIRONMENT))
165 }
166
167 pub fn insert_capability(
168 &self,
169 path: &impl IterablePath,
170 capability: Capability,
171 ) -> Option<Capability> {
172 self.capabilities().insert_capability(path, capability.into())
173 }
174}
175
176impl From<ComponentInput> for Arc<Dictionary> {
177 fn from(e: ComponentInput) -> Self {
178 e.0
179 }
180}
181
182#[derive(Clone, Debug)]
185pub struct ComponentEnvironment(Arc<Dictionary>);
186
187impl Default for ComponentEnvironment {
188 fn default() -> Self {
189 Self(Dictionary::new())
190 }
191}
192
193impl StructuredDict for ComponentEnvironment {
194 fn from_dict(dict: Arc<Dictionary>) -> Self {
195 Self(dict)
196 }
197}
198
199impl ComponentEnvironment {
200 pub fn new() -> Self {
201 Self::default()
202 }
203
204 pub fn debug(&self) -> Arc<Dictionary> {
206 get_or_insert(&self.0, &*DEBUG)
207 }
208
209 pub fn runners(&self) -> Arc<Dictionary> {
211 get_or_insert(&self.0, &*RUNNERS)
212 }
213
214 pub fn resolvers(&self) -> Arc<Dictionary> {
216 get_or_insert(&self.0, &*RESOLVERS)
217 }
218
219 pub fn set_stop_timeout(&self, timeout: i64) {
221 let _ =
222 self.0.insert(STOP_TIMEOUT.clone(), Capability::Data(Arc::new(Data::Int64(timeout))));
223 }
224
225 pub fn stop_timeout(&self) -> Option<i64> {
227 let Some(Capability::Data(data_cap)) = self.0.get(&*STOP_TIMEOUT) else {
228 return None;
229 };
230 let Data::Int64(timeout) = &*data_cap else {
231 return None;
232 };
233 Some(*timeout)
234 }
235
236 pub fn set_name(&self, name: &Name) {
238 let _ = self
239 .0
240 .insert(NAME.clone(), Capability::Data(Arc::new(Data::String(name.as_str().into()))));
241 }
242
243 pub fn name(&self) -> Option<Name> {
245 let Some(Capability::Data(data_cap)) = self.0.get(&*NAME) else {
246 return None;
247 };
248 let Data::String(name) = &*data_cap else {
249 return None;
250 };
251 Some(Name::new(name).unwrap())
252 }
253
254 pub fn shallow_copy(&self) -> Self {
255 let dest = Dictionary::new();
259 shallow_copy(&self.0, &dest, &*DEBUG);
260 shallow_copy(&self.0, &dest, &*RUNNERS);
261 shallow_copy(&self.0, &dest, &*RESOLVERS);
262 Self(dest)
263 }
264}
265
266impl From<ComponentEnvironment> for Arc<Dictionary> {
267 fn from(e: ComponentEnvironment) -> Self {
268 e.0
269 }
270}
271
272#[derive(Clone, Debug)]
276pub struct ComponentOutput(Arc<Dictionary>);
277
278impl Default for ComponentOutput {
279 fn default() -> Self {
280 Self::new()
281 }
282}
283
284impl StructuredDict for ComponentOutput {
285 fn from_dict(dict: Arc<Dictionary>) -> Self {
286 Self(dict)
287 }
288}
289
290impl ComponentOutput {
291 pub fn new() -> Self {
292 Self(Dictionary::new())
293 }
294
295 pub fn shallow_copy(&self) -> Self {
300 let dest = Dictionary::new();
304 shallow_copy(&self.0, &dest, &*PARENT);
305 shallow_copy(&self.0, &dest, &*FRAMEWORK);
306 Self(dest)
307 }
308
309 pub fn capabilities(&self) -> Arc<Dictionary> {
312 get_or_insert(&self.0, &*PARENT)
313 }
314
315 pub fn framework(&self) -> Arc<Dictionary> {
318 get_or_insert(&self.0, &*FRAMEWORK)
319 }
320}
321
322impl From<ComponentOutput> for Arc<Dictionary> {
323 fn from(e: ComponentOutput) -> Self {
324 e.0
325 }
326}
327
328fn shallow_copy(src: &Arc<Dictionary>, dest: &Arc<Dictionary>, key: &Name) {
329 if let Some(d) = src.get(key) {
330 let Capability::Dictionary(d) = d else {
331 unreachable!("{key} entry must be a dictionary: {d:?}");
332 };
333 dest.insert(key.clone(), Capability::Dictionary(d.shallow_copy()));
334 }
335}
336
337fn get_or_insert(this: &Dictionary, key: &Name) -> Arc<Dictionary> {
338 let cap = this.get_or_insert(&key, || Capability::Dictionary(Dictionary::new()));
339 let Capability::Dictionary(dict) = cap else {
340 unreachable!("{key} entry must be a dict: {cap:?}");
341 };
342 dict
343}
344
345#[cfg(test)]
346mod tests {
347 use super::*;
348 use assert_matches::assert_matches;
349 use runtime_capabilities::DictKey;
350
351 impl StructuredDict for Arc<Dictionary> {
352 fn from_dict(dict: Arc<Dictionary>) -> Self {
353 dict
354 }
355 }
356
357 #[fuchsia::test]
358 async fn structured_dict_map() {
359 let dict1 = {
360 let dict = Dictionary::new();
361 dict.insert("a".parse().unwrap(), Dictionary::new().into());
362 dict
363 };
364 let dict2 = {
365 let dict = Dictionary::new();
366 dict.insert("b".parse().unwrap(), Dictionary::new().into());
367 dict
368 };
369 let dict2_alt = {
370 let dict = Dictionary::new();
371 dict.insert("c".parse().unwrap(), Dictionary::new().into());
372 dict
373 };
374 let name1 = Name::new("1").unwrap();
375 let name2 = Name::new("2").unwrap();
376
377 let map: StructuredDictMap<Arc<Dictionary>> = Default::default();
378 assert_matches!(map.get(&name1), None);
379 assert!(map.insert(name1.clone(), dict1).is_none());
380 let d = map.get(&name1).unwrap();
381 let key = DictKey::new("a").unwrap();
382 assert_matches!(d.get(&key), Some(_));
383
384 assert!(map.insert(name2.clone(), dict2).is_none());
385 let d = map.remove(&name2).unwrap();
386 assert_matches!(map.remove(&name2), None);
387 let key = DictKey::new("b").unwrap();
388 assert_matches!(d.get(&key), Some(_));
389
390 assert!(map.insert(name2.clone(), dict2_alt).is_none());
391 let d = map.get(&name2).unwrap();
392 let key = DictKey::new("c").unwrap();
393 assert_matches!(d.get(&key), Some(_));
394 }
395}