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