routing/bedrock/
structured_dict.rs1use crate::DictExt;
6use cm_types::{BorrowedName, IterablePath, Name};
7use fidl_fuchsia_component_sandbox as fsandbox;
8use sandbox::{Capability, 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
113#[derive(Clone, Debug)]
116pub struct ComponentInput(Dict);
117
118impl Default for ComponentInput {
119 fn default() -> Self {
120 Self::new(ComponentEnvironment::new())
121 }
122}
123
124impl StructuredDict for ComponentInput {
125 fn from_dict(dict: Dict) -> Self {
126 Self(dict)
127 }
128}
129
130impl ComponentInput {
131 pub fn new(environment: ComponentEnvironment) -> Self {
132 let dict = Dict::new();
133 if let Some(_) = environment.0.keys().next() {
134 dict.insert(ENVIRONMENT.clone(), Dict::from(environment).into()).unwrap();
135 }
136 Self(dict)
137 }
138
139 pub fn shallow_copy(&self) -> Result<Self, ()> {
144 let dest = Dict::new();
148 shallow_copy(&self.0, &dest, &*PARENT)?;
149 shallow_copy(&self.0, &dest, &*ENVIRONMENT)?;
150 Ok(Self(dest))
151 }
152
153 pub fn capabilities(&self) -> Dict {
155 get_or_insert(&self.0, &*PARENT)
156 }
157
158 pub fn environment(&self) -> ComponentEnvironment {
160 ComponentEnvironment(get_or_insert(&self.0, &*ENVIRONMENT))
161 }
162
163 pub fn insert_capability(
164 &self,
165 path: &impl IterablePath,
166 capability: Capability,
167 ) -> Result<(), fsandbox::CapabilityStoreError> {
168 self.capabilities().insert_capability(path, capability.into())
169 }
170}
171
172impl From<ComponentInput> for Dict {
173 fn from(e: ComponentInput) -> Self {
174 e.0
175 }
176}
177
178#[derive(Clone, Debug)]
181pub struct ComponentEnvironment(Dict);
182
183impl Default for ComponentEnvironment {
184 fn default() -> Self {
185 Self(Dict::new())
186 }
187}
188
189impl StructuredDict for ComponentEnvironment {
190 fn from_dict(dict: Dict) -> Self {
191 Self(dict)
192 }
193}
194
195impl ComponentEnvironment {
196 pub fn new() -> Self {
197 Self::default()
198 }
199
200 pub fn debug(&self) -> Dict {
202 get_or_insert(&self.0, &*DEBUG)
203 }
204
205 pub fn runners(&self) -> Dict {
207 get_or_insert(&self.0, &*RUNNERS)
208 }
209
210 pub fn resolvers(&self) -> Dict {
212 get_or_insert(&self.0, &*RESOLVERS)
213 }
214
215 pub fn shallow_copy(&self) -> Result<Self, ()> {
216 let dest = Dict::new();
220 shallow_copy(&self.0, &dest, &*DEBUG)?;
221 shallow_copy(&self.0, &dest, &*RUNNERS)?;
222 shallow_copy(&self.0, &dest, &*RESOLVERS)?;
223 Ok(Self(dest))
224 }
225}
226
227impl From<ComponentEnvironment> for Dict {
228 fn from(e: ComponentEnvironment) -> Self {
229 e.0
230 }
231}
232
233#[derive(Clone, Debug)]
237pub struct ComponentOutput(Dict);
238
239impl Default for ComponentOutput {
240 fn default() -> Self {
241 Self::new()
242 }
243}
244
245impl StructuredDict for ComponentOutput {
246 fn from_dict(dict: Dict) -> Self {
247 Self(dict)
248 }
249}
250
251impl ComponentOutput {
252 pub fn new() -> Self {
253 Self(Dict::new())
254 }
255
256 pub fn shallow_copy(&self) -> Result<Self, ()> {
261 let dest = Dict::new();
265 shallow_copy(&self.0, &dest, &*PARENT)?;
266 shallow_copy(&self.0, &dest, &*FRAMEWORK)?;
267 Ok(Self(dest))
268 }
269
270 pub fn capabilities(&self) -> Dict {
273 get_or_insert(&self.0, &*PARENT)
274 }
275
276 pub fn framework(&self) -> Dict {
279 get_or_insert(&self.0, &*FRAMEWORK)
280 }
281}
282
283impl From<ComponentOutput> for Dict {
284 fn from(e: ComponentOutput) -> Self {
285 e.0
286 }
287}
288
289fn shallow_copy(src: &Dict, dest: &Dict, key: &Name) -> Result<(), ()> {
290 if let Some(d) = src.get(key).expect("must be cloneable") {
291 let Capability::Dictionary(d) = d else {
292 unreachable!("{key} entry must be a dict: {d:?}");
293 };
294 dest.insert(key.clone(), d.shallow_copy()?.into()).ok();
295 }
296 Ok(())
297}
298
299fn get_or_insert(this: &Dict, key: &Name) -> Dict {
300 let cap = this
301 .get_or_insert(&key, || Capability::Dictionary(Dict::new()))
302 .expect("capabilities must be cloneable");
303 let Capability::Dictionary(dict) = cap else {
304 unreachable!("{key} entry must be a dict: {cap:?}");
305 };
306 dict
307}
308
309#[cfg(test)]
310mod tests {
311 use super::*;
312 use assert_matches::assert_matches;
313 use sandbox::DictKey;
314
315 impl StructuredDict for Dict {
316 fn from_dict(dict: Dict) -> Self {
317 dict
318 }
319 }
320
321 #[fuchsia::test]
322 async fn structured_dict_map() {
323 let dict1 = {
324 let dict = Dict::new();
325 dict.insert("a".parse().unwrap(), Dict::new().into())
326 .expect("dict entry already exists");
327 dict
328 };
329 let dict2 = {
330 let dict = Dict::new();
331 dict.insert("b".parse().unwrap(), Dict::new().into())
332 .expect("dict entry already exists");
333 dict
334 };
335 let dict2_alt = {
336 let dict = Dict::new();
337 dict.insert("c".parse().unwrap(), Dict::new().into())
338 .expect("dict entry already exists");
339 dict
340 };
341 let name1 = Name::new("1").unwrap();
342 let name2 = Name::new("2").unwrap();
343
344 let map: StructuredDictMap<Dict> = Default::default();
345 assert_matches!(map.get(&name1), None);
346 assert!(map.insert(name1.clone(), dict1).is_ok());
347 let d = map.get(&name1).unwrap();
348 let key = DictKey::new("a").unwrap();
349 assert_matches!(d.get(&key), Ok(Some(_)));
350
351 assert!(map.insert(name2.clone(), dict2).is_ok());
352 let d = map.remove(&name2).unwrap();
353 assert_matches!(map.remove(&name2), None);
354 let key = DictKey::new("b").unwrap();
355 assert_matches!(d.get(&key), Ok(Some(_)));
356
357 assert!(map.insert(name2.clone(), dict2_alt).is_ok());
358 let d = map.get(&name2).unwrap();
359 let key = DictKey::new("c").unwrap();
360 assert_matches!(d.get(&key), Ok(Some(_)));
361 }
362}