routing/bedrock/
structured_dict.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::DictExt;
6use cm_types::{IterablePath, Name};
7use fidl_fuchsia_component_sandbox as fsandbox;
8use lazy_static::lazy_static;
9use sandbox::{Capability, Dict};
10use std::fmt;
11use std::marker::PhantomData;
12
13/// This trait is implemented by types that wrap a [Dict] and wish to present an abstracted
14/// interface over the [Dict].
15///
16/// All such types are defined in this module, so this trait is private.
17///
18/// See also: [StructuredDictMap]
19trait StructuredDict: Into<Dict> + Default + Clone + fmt::Debug {
20    /// Converts from [Dict] to `Self`.
21    ///
22    /// REQUIRES: [Dict] is a valid representation of `Self`.
23    ///
24    /// IMPORTANT: The caller should know that [Dict] is a valid representation of [Self]. This
25    /// function is not guaranteed to perform any validation.
26    fn from_dict(dict: Dict) -> Self;
27}
28
29/// A collection type for mapping [Name] to [StructuredDict], using [Dict] as the underlying
30/// representation.
31///
32/// For example, this can be used to store a map of child or collection names to [ComponentInput]s
33/// (where [ComponentInput] is the type that implements [StructuredDict]).
34///
35/// Because the representation of this type is [Dict], this type itself implements
36/// [StructuredDict].
37#[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: &Name) -> 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 enumerate(&self) -> impl Iterator<Item = (Name, T)> {
76        self.inner.enumerate().map(|(key, capability_res)| match capability_res {
77            Ok(Capability::Dictionary(dict)) => (key, T::from_dict(dict)),
78            Ok(cap) => unreachable!("structured map entry must be a dict: {cap:?}"),
79            Err(_) => panic!("structured map entry must be cloneable"),
80        })
81    }
82}
83
84impl<T: StructuredDict> From<StructuredDictMap<T>> for Dict {
85    fn from(m: StructuredDictMap<T>) -> Self {
86        m.inner
87    }
88}
89
90// Dictionary keys for different kinds of sandboxes.
91lazy_static! {
92    /// Dictionary of capabilities from or to the parent.
93    static ref PARENT: Name = "parent".parse().unwrap();
94
95    /// Dictionary of capabilities from a component's environment.
96    static ref ENVIRONMENT: Name = "environment".parse().unwrap();
97
98    /// Dictionary of debug capabilities in a component's environment.
99    static ref DEBUG: Name = "debug".parse().unwrap();
100
101    /// Dictionary of runner capabilities in a component's environment.
102    static ref RUNNERS: Name = "runners".parse().unwrap();
103
104    /// Dictionary of resolver capabilities in a component's environment.
105    static ref RESOLVERS: Name = "resolvers".parse().unwrap();
106
107    /// Dictionary of capabilities available to the framework.
108    static ref FRAMEWORK: Name = "framework".parse().unwrap();
109}
110
111/// Contains the capabilities component receives from its parent and environment. Stored as a
112/// [Dict] containing two nested [Dict]s for the parent and environment.
113#[derive(Clone, Debug)]
114pub struct ComponentInput(Dict);
115
116impl Default for ComponentInput {
117    fn default() -> Self {
118        Self::new(ComponentEnvironment::new())
119    }
120}
121
122impl StructuredDict for ComponentInput {
123    fn from_dict(dict: Dict) -> Self {
124        Self(dict)
125    }
126}
127
128impl ComponentInput {
129    pub fn new(environment: ComponentEnvironment) -> Self {
130        let dict = Dict::new();
131        dict.insert(PARENT.clone(), Dict::new().into()).ok();
132        dict.insert(ENVIRONMENT.clone(), Dict::from(environment).into()).ok();
133        Self(dict)
134    }
135
136    /// Creates a new ComponentInput with entries cloned from this ComponentInput.
137    ///
138    /// This is a shallow copy. Values are cloned, not copied, so are new references to the same
139    /// underlying data.
140    pub fn shallow_copy(&self) -> Result<Self, ()> {
141        // Note: We call [Dict::copy] on the nested [Dict]s, not the root [Dict], because
142        // [Dict::copy] only goes one level deep and we want to copy the contents of the
143        // inner sandboxes.
144        let dict = Dict::new();
145        dict.insert(PARENT.clone(), self.capabilities().shallow_copy()?.into()).ok();
146        dict.insert(ENVIRONMENT.clone(), Dict::from(self.environment()).shallow_copy()?.into())
147            .ok();
148        Ok(Self(dict))
149    }
150
151    /// Returns the sub-dictionary containing capabilities routed by the component's parent.
152    pub fn capabilities(&self) -> Dict {
153        let cap = self.0.get(&*PARENT).expect("capabilities must be cloneable").unwrap();
154        let Capability::Dictionary(dict) = cap else {
155            unreachable!("parent entry must be a dict: {cap:?}");
156        };
157        dict
158    }
159
160    /// Returns the sub-dictionary containing capabilities routed by the component's environment.
161    pub fn environment(&self) -> ComponentEnvironment {
162        let cap = self.0.get(&*ENVIRONMENT).expect("environment must be cloneable").unwrap();
163        let Capability::Dictionary(dict) = cap else {
164            unreachable!("environment entry must be a dict: {cap:?}");
165        };
166        ComponentEnvironment(dict)
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/// The capabilities a component has in its environment. Stored as a [Dict] containing a nested
185/// [Dict] holding the environment's debug capabilities.
186#[derive(Clone, Debug)]
187pub struct ComponentEnvironment(Dict);
188
189impl Default for ComponentEnvironment {
190    fn default() -> Self {
191        let dict = Dict::new();
192        dict.insert(DEBUG.clone(), Dict::new().into()).ok();
193        dict.insert(RUNNERS.clone(), Dict::new().into()).ok();
194        dict.insert(RESOLVERS.clone(), Dict::new().into()).ok();
195        Self(dict)
196    }
197}
198
199impl StructuredDict for ComponentEnvironment {
200    fn from_dict(dict: Dict) -> Self {
201        Self(dict)
202    }
203}
204
205impl ComponentEnvironment {
206    pub fn new() -> Self {
207        Self::default()
208    }
209
210    /// Capabilities listed in the `debug_capabilities` portion of its environment.
211    pub fn debug(&self) -> Dict {
212        let cap = self.0.get(&*DEBUG).expect("debug must be cloneable").unwrap();
213        let Capability::Dictionary(dict) = cap else {
214            unreachable!("debug entry must be a dict: {cap:?}");
215        };
216        dict
217    }
218
219    /// Capabilities listed in the `runners` portion of its environment.
220    pub fn runners(&self) -> Dict {
221        let cap = self.0.get(&*RUNNERS).expect("runners must be cloneable").unwrap();
222        let Capability::Dictionary(dict) = cap else {
223            unreachable!("runners entry must be a dict: {cap:?}");
224        };
225        dict
226    }
227
228    /// Capabilities listed in the `resolvers` portion of its environment.
229    pub fn resolvers(&self) -> Dict {
230        let cap = self.0.get(&*RESOLVERS).expect("resolvers must be cloneable").unwrap();
231        let Capability::Dictionary(dict) = cap else {
232            unreachable!("resolvers entry must be a dict: {cap:?}");
233        };
234        dict
235    }
236
237    pub fn shallow_copy(&self) -> Result<Self, ()> {
238        // Note: We call [Dict::shallow_copy] on the nested [Dict]s, not the root [Dict], because
239        // [Dict::shallow_copy] only goes one level deep and we want to copy the contents of the
240        // inner sandboxes.
241        let dict = Dict::new();
242        dict.insert(DEBUG.clone(), self.debug().shallow_copy()?.into()).ok();
243        dict.insert(RUNNERS.clone(), self.runners().shallow_copy()?.into()).ok();
244        dict.insert(RESOLVERS.clone(), self.resolvers().shallow_copy()?.into()).ok();
245        Ok(Self(dict))
246    }
247}
248
249impl From<ComponentEnvironment> for Dict {
250    fn from(e: ComponentEnvironment) -> Self {
251        e.0
252    }
253}
254
255/// Contains the capabilities a component makes available to its parent or the framework. Stored as
256/// a [Dict] containing two nested [Dict]s for the capabilities made available to the parent and to
257/// the framework.
258#[derive(Clone, Debug)]
259pub struct ComponentOutput(Dict);
260
261impl Default for ComponentOutput {
262    fn default() -> Self {
263        Self::new()
264    }
265}
266
267impl StructuredDict for ComponentOutput {
268    fn from_dict(dict: Dict) -> Self {
269        Self(dict)
270    }
271}
272
273impl ComponentOutput {
274    pub fn new() -> Self {
275        let dict = Dict::new();
276        dict.insert(PARENT.clone(), Dict::new().into()).ok();
277        dict.insert(FRAMEWORK.clone(), Dict::new().into()).ok();
278        Self(dict)
279    }
280
281    /// Creates a new ComponentOutput with entries cloned from this ComponentOutput.
282    ///
283    /// This is a shallow copy. Values are cloned, not copied, so are new references to the same
284    /// underlying data.
285    pub fn shallow_copy(&self) -> Result<Self, ()> {
286        // Note: We call [Dict::copy] on the nested [Dict]s, not the root [Dict], because
287        // [Dict::copy] only goes one level deep and we want to copy the contents of the
288        // inner sandboxes.
289        let dict = Dict::new();
290        dict.insert(PARENT.clone(), self.capabilities().shallow_copy()?.into()).ok();
291        dict.insert(FRAMEWORK.clone(), self.framework().shallow_copy()?.into()).ok();
292        Ok(Self(dict))
293    }
294
295    /// Returns the sub-dictionary containing capabilities routed by the component's parent.
296    pub fn capabilities(&self) -> Dict {
297        let cap = self.0.get(&*PARENT).expect("capabilities must be cloneable").unwrap();
298        let Capability::Dictionary(dict) = cap else {
299            unreachable!("parent entry must be a dict: {cap:?}");
300        };
301        dict
302    }
303
304    /// Returns the sub-dictionary containing capabilities routed to the component's framework.
305    pub fn framework(&self) -> Dict {
306        let cap = self.0.get(&*FRAMEWORK).expect("capabilities must be cloneable").unwrap();
307        let Capability::Dictionary(dict) = cap else {
308            unreachable!("framework entry must be a dict: {cap:?}");
309        };
310        dict
311    }
312}
313
314impl From<ComponentOutput> for Dict {
315    fn from(e: ComponentOutput) -> Self {
316        e.0
317    }
318}
319
320#[cfg(test)]
321mod tests {
322    use super::*;
323    use assert_matches::assert_matches;
324    use sandbox::DictKey;
325
326    impl StructuredDict for Dict {
327        fn from_dict(dict: Dict) -> Self {
328            dict
329        }
330    }
331
332    #[fuchsia::test]
333    async fn structured_dict_map() {
334        let dict1 = {
335            let dict = Dict::new();
336            dict.insert("a".parse().unwrap(), Dict::new().into())
337                .expect("dict entry already exists");
338            dict
339        };
340        let dict2 = {
341            let dict = Dict::new();
342            dict.insert("b".parse().unwrap(), Dict::new().into())
343                .expect("dict entry already exists");
344            dict
345        };
346        let dict2_alt = {
347            let dict = Dict::new();
348            dict.insert("c".parse().unwrap(), Dict::new().into())
349                .expect("dict entry already exists");
350            dict
351        };
352        let name1 = Name::new("1").unwrap();
353        let name2 = Name::new("2").unwrap();
354
355        let map: StructuredDictMap<Dict> = Default::default();
356        assert_matches!(map.get(&name1), None);
357        assert!(map.insert(name1.clone(), dict1).is_ok());
358        let d = map.get(&name1).unwrap();
359        let key = DictKey::new("a").unwrap();
360        assert_matches!(d.get(&key), Ok(Some(_)));
361
362        assert!(map.insert(name2.clone(), dict2).is_ok());
363        let d = map.remove(&name2).unwrap();
364        assert_matches!(map.remove(&name2), None);
365        let key = DictKey::new("b").unwrap();
366        assert_matches!(d.get(&key), Ok(Some(_)));
367
368        assert!(map.insert(name2.clone(), dict2_alt).is_ok());
369        let d = map.get(&name2).unwrap();
370        let key = DictKey::new("c").unwrap();
371        assert_matches!(d.get(&key), Ok(Some(_)));
372    }
373}