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
140 if !environment.0.is_empty() {
141 dict.insert(ENVIRONMENT.clone(), Dict::from(environment).into()).unwrap();
142 }
143 Self(dict)
144 }
145
146 pub fn shallow_copy(&self) -> Result<Self, ()> {
151 let dest = Dict::new();
155 shallow_copy(&self.0, &dest, &*PARENT)?;
156 shallow_copy(&self.0, &dest, &*ENVIRONMENT)?;
157 Ok(Self(dest))
158 }
159
160 pub fn capabilities(&self) -> Dict {
162 get_or_insert(&self.0, &*PARENT)
163 }
164
165 pub fn environment(&self) -> ComponentEnvironment {
167 ComponentEnvironment(get_or_insert(&self.0, &*ENVIRONMENT))
168 }
169
170 pub fn insert_capability(
171 &self,
172 path: &impl IterablePath,
173 capability: Capability,
174 ) -> Result<(), fsandbox::CapabilityStoreError> {
175 self.capabilities().insert_capability(path, capability.into())
176 }
177}
178
179impl From<ComponentInput> for Dict {
180 fn from(e: ComponentInput) -> Self {
181 e.0
182 }
183}
184
185#[derive(Clone, Debug)]
188pub struct ComponentEnvironment(Dict);
189
190impl Default for ComponentEnvironment {
191 fn default() -> Self {
192 Self(Dict::new())
193 }
194}
195
196impl StructuredDict for ComponentEnvironment {
197 fn from_dict(dict: Dict) -> Self {
198 Self(dict)
199 }
200}
201
202impl ComponentEnvironment {
203 pub fn new() -> Self {
204 Self::default()
205 }
206
207 pub fn debug(&self) -> Dict {
209 get_or_insert(&self.0, &*DEBUG)
210 }
211
212 pub fn runners(&self) -> Dict {
214 get_or_insert(&self.0, &*RUNNERS)
215 }
216
217 pub fn resolvers(&self) -> Dict {
219 get_or_insert(&self.0, &*RESOLVERS)
220 }
221
222 pub fn set_stop_timeout(&self, timeout: i64) {
224 let _ = self.0.insert(STOP_TIMEOUT.clone(), Data::Int64(timeout).into());
225 }
226
227 pub fn stop_timeout(&self) -> Option<i64> {
229 let Ok(Some(Capability::Data(Data::Int64(timeout)))) = self.0.get(&*STOP_TIMEOUT) 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(), Data::String(name.as_str().into()).into());
238 }
239
240 pub fn name(&self) -> Option<Name> {
242 let Ok(Some(Capability::Data(Data::String(name)))) = self.0.get(&*NAME) else {
243 return None;
244 };
245 Some(Name::new(name).unwrap())
246 }
247
248 pub fn shallow_copy(&self) -> Result<Self, ()> {
249 let dest = Dict::new();
253 shallow_copy(&self.0, &dest, &*DEBUG)?;
254 shallow_copy(&self.0, &dest, &*RUNNERS)?;
255 shallow_copy(&self.0, &dest, &*RESOLVERS)?;
256 Ok(Self(dest))
257 }
258}
259
260impl From<ComponentEnvironment> for Dict {
261 fn from(e: ComponentEnvironment) -> Self {
262 e.0
263 }
264}
265
266#[derive(Clone, Debug)]
270pub struct ComponentOutput(Dict);
271
272impl Default for ComponentOutput {
273 fn default() -> Self {
274 Self::new()
275 }
276}
277
278impl StructuredDict for ComponentOutput {
279 fn from_dict(dict: Dict) -> Self {
280 Self(dict)
281 }
282}
283
284impl ComponentOutput {
285 pub fn new() -> Self {
286 Self(Dict::new())
287 }
288
289 pub fn shallow_copy(&self) -> Result<Self, ()> {
294 let dest = Dict::new();
298 shallow_copy(&self.0, &dest, &*PARENT)?;
299 shallow_copy(&self.0, &dest, &*FRAMEWORK)?;
300 Ok(Self(dest))
301 }
302
303 pub fn capabilities(&self) -> Dict {
306 get_or_insert(&self.0, &*PARENT)
307 }
308
309 pub fn framework(&self) -> Dict {
312 get_or_insert(&self.0, &*FRAMEWORK)
313 }
314}
315
316impl From<ComponentOutput> for Dict {
317 fn from(e: ComponentOutput) -> Self {
318 e.0
319 }
320}
321
322fn shallow_copy(src: &Dict, dest: &Dict, key: &Name) -> Result<(), ()> {
323 if let Some(d) = src.get(key).expect("must be cloneable") {
324 let Capability::Dictionary(d) = d else {
325 unreachable!("{key} entry must be a dict: {d:?}");
326 };
327 dest.insert(key.clone(), d.shallow_copy()?.into()).ok();
328 }
329 Ok(())
330}
331
332fn get_or_insert(this: &Dict, key: &Name) -> Dict {
333 let cap = this
334 .get_or_insert(&key, || Capability::Dictionary(Dict::new()))
335 .expect("capabilities must be cloneable");
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 sandbox::DictKey;
347
348 impl StructuredDict for Dict {
349 fn from_dict(dict: Dict) -> Self {
350 dict
351 }
352 }
353
354 #[fuchsia::test]
355 async fn structured_dict_map() {
356 let dict1 = {
357 let dict = Dict::new();
358 dict.insert("a".parse().unwrap(), Dict::new().into())
359 .expect("dict entry already exists");
360 dict
361 };
362 let dict2 = {
363 let dict = Dict::new();
364 dict.insert("b".parse().unwrap(), Dict::new().into())
365 .expect("dict entry already exists");
366 dict
367 };
368 let dict2_alt = {
369 let dict = Dict::new();
370 dict.insert("c".parse().unwrap(), Dict::new().into())
371 .expect("dict entry already exists");
372 dict
373 };
374 let name1 = Name::new("1").unwrap();
375 let name2 = Name::new("2").unwrap();
376
377 let map: StructuredDictMap<Dict> = Default::default();
378 assert_matches!(map.get(&name1), None);
379 assert!(map.insert(name1.clone(), dict1).is_ok());
380 let d = map.get(&name1).unwrap();
381 let key = DictKey::new("a").unwrap();
382 assert_matches!(d.get(&key), Ok(Some(_)));
383
384 assert!(map.insert(name2.clone(), dict2).is_ok());
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), Ok(Some(_)));
389
390 assert!(map.insert(name2.clone(), dict2_alt).is_ok());
391 let d = map.get(&name2).unwrap();
392 let key = DictKey::new("c").unwrap();
393 assert_matches!(d.get(&key), Ok(Some(_)));
394 }
395}