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 _ = self.0.insert(STOP_TIMEOUT.clone(), Capability::Data(Data::Int64(timeout)));
222 }
223
224 pub fn stop_timeout(&self) -> Option<i64> {
226 let Some(Capability::Data(data_cap)) = self.0.get(&*STOP_TIMEOUT) else {
227 return None;
228 };
229 let Data::Int64(timeout) = &data_cap else {
230 return None;
231 };
232 Some(*timeout)
233 }
234
235 pub fn set_name(&self, name: &Name) {
237 let _ = self.0.insert(NAME.clone(), Capability::Data(Data::String(name.as_str().into())));
238 }
239
240 pub fn name(&self) -> Option<Name> {
242 let Some(Capability::Data(data_cap)) = self.0.get(&*NAME) else {
243 return None;
244 };
245 let Data::String(name) = &data_cap else {
246 return None;
247 };
248 Some(Name::new(name).unwrap())
249 }
250
251 pub fn shallow_copy(&self) -> Self {
252 let dest = Dictionary::new();
256 shallow_copy(&self.0, &dest, &*DEBUG);
257 shallow_copy(&self.0, &dest, &*RUNNERS);
258 shallow_copy(&self.0, &dest, &*RESOLVERS);
259 Self(dest)
260 }
261}
262
263impl From<ComponentEnvironment> for Arc<Dictionary> {
264 fn from(e: ComponentEnvironment) -> Self {
265 e.0
266 }
267}
268
269#[derive(Clone, Debug)]
273pub struct ComponentOutput(Arc<Dictionary>);
274
275impl Default for ComponentOutput {
276 fn default() -> Self {
277 Self::new()
278 }
279}
280
281impl StructuredDict for ComponentOutput {
282 fn from_dict(dict: Arc<Dictionary>) -> Self {
283 Self(dict)
284 }
285}
286
287impl ComponentOutput {
288 pub fn new() -> Self {
289 Self(Dictionary::new())
290 }
291
292 pub fn shallow_copy(&self) -> Self {
297 let dest = Dictionary::new();
301 shallow_copy(&self.0, &dest, &*PARENT);
302 shallow_copy(&self.0, &dest, &*FRAMEWORK);
303 Self(dest)
304 }
305
306 pub fn capabilities(&self) -> Arc<Dictionary> {
309 get_or_insert(&self.0, &*PARENT)
310 }
311
312 pub fn framework(&self) -> Arc<Dictionary> {
315 get_or_insert(&self.0, &*FRAMEWORK)
316 }
317}
318
319impl From<ComponentOutput> for Arc<Dictionary> {
320 fn from(e: ComponentOutput) -> Self {
321 e.0
322 }
323}
324
325fn shallow_copy(src: &Arc<Dictionary>, dest: &Arc<Dictionary>, key: &Name) {
326 if let Some(d) = src.get(key) {
327 let Capability::Dictionary(d) = d else {
328 unreachable!("{key} entry must be a dictionary: {d:?}");
329 };
330 dest.insert(key.clone(), Capability::Dictionary(d.shallow_copy()));
331 }
332}
333
334fn get_or_insert(this: &Dictionary, key: &Name) -> Arc<Dictionary> {
335 let cap = this.get_or_insert(&key, || Capability::Dictionary(Dictionary::new()));
336 let Capability::Dictionary(dict) = cap else {
337 unreachable!("{key} entry must be a dict: {cap:?}");
338 };
339 dict
340}
341
342#[cfg(test)]
343mod tests {
344 use super::*;
345 use assert_matches::assert_matches;
346 use runtime_capabilities::DictKey;
347
348 impl StructuredDict for Arc<Dictionary> {
349 fn from_dict(dict: Arc<Dictionary>) -> Self {
350 dict
351 }
352 }
353
354 #[fuchsia::test]
355 async fn structured_dict_map() {
356 let dict1 = {
357 let dict = Dictionary::new();
358 dict.insert("a".parse().unwrap(), Dictionary::new().into());
359 dict
360 };
361 let dict2 = {
362 let dict = Dictionary::new();
363 dict.insert("b".parse().unwrap(), Dictionary::new().into());
364 dict
365 };
366 let dict2_alt = {
367 let dict = Dictionary::new();
368 dict.insert("c".parse().unwrap(), Dictionary::new().into());
369 dict
370 };
371 let name1 = Name::new("1").unwrap();
372 let name2 = Name::new("2").unwrap();
373
374 let map: StructuredDictMap<Arc<Dictionary>> = Default::default();
375 assert_matches!(map.get(&name1), None);
376 assert!(map.insert(name1.clone(), dict1).is_none());
377 let d = map.get(&name1).unwrap();
378 let key = DictKey::new("a").unwrap();
379 assert_matches!(d.get(&key), Some(_));
380
381 assert!(map.insert(name2.clone(), dict2).is_none());
382 let d = map.remove(&name2).unwrap();
383 assert_matches!(map.remove(&name2), None);
384 let key = DictKey::new("b").unwrap();
385 assert_matches!(d.get(&key), Some(_));
386
387 assert!(map.insert(name2.clone(), dict2_alt).is_none());
388 let d = map.get(&name2).unwrap();
389 let key = DictKey::new("c").unwrap();
390 assert_matches!(d.get(&key), Some(_));
391 }
392}