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::{BorrowedName, 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: &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
94// Dictionary keys for different kinds of sandboxes.
95lazy_static! {
96    /// Dictionary of capabilities from or to the parent.
97    static ref PARENT: Name = "parent".parse().unwrap();
98
99    /// Dictionary of capabilities from a component's environment.
100    static ref ENVIRONMENT: Name = "environment".parse().unwrap();
101
102    /// Dictionary of debug capabilities in a component's environment.
103    static ref DEBUG: Name = "debug".parse().unwrap();
104
105    /// Dictionary of runner capabilities in a component's environment.
106    static ref RUNNERS: Name = "runners".parse().unwrap();
107
108    /// Dictionary of resolver capabilities in a component's environment.
109    static ref RESOLVERS: Name = "resolvers".parse().unwrap();
110
111    /// Dictionary of capabilities available to the framework.
112    static ref FRAMEWORK: Name = "framework".parse().unwrap();
113}
114
115/// Contains the capabilities component receives from its parent and environment. Stored as a
116/// [Dict] containing two nested [Dict]s for the parent and environment.
117#[derive(Clone, Debug)]
118pub struct ComponentInput(Dict);
119
120impl Default for ComponentInput {
121    fn default() -> Self {
122        Self::new(ComponentEnvironment::new())
123    }
124}
125
126impl StructuredDict for ComponentInput {
127    fn from_dict(dict: Dict) -> Self {
128        Self(dict)
129    }
130}
131
132impl ComponentInput {
133    pub fn new(environment: ComponentEnvironment) -> Self {
134        let dict = Dict::new();
135        dict.insert(PARENT.clone(), Dict::new().into()).ok();
136        dict.insert(ENVIRONMENT.clone(), Dict::from(environment).into()).ok();
137        Self(dict)
138    }
139
140    /// Creates a new ComponentInput with entries cloned from this ComponentInput.
141    ///
142    /// This is a shallow copy. Values are cloned, not copied, so are new references to the same
143    /// underlying data.
144    pub fn shallow_copy(&self) -> Result<Self, ()> {
145        // Note: We call [Dict::copy] on the nested [Dict]s, not the root [Dict], because
146        // [Dict::copy] only goes one level deep and we want to copy the contents of the
147        // inner sandboxes.
148        let dict = Dict::new();
149        dict.insert(PARENT.clone(), self.capabilities().shallow_copy()?.into()).ok();
150        dict.insert(ENVIRONMENT.clone(), Dict::from(self.environment()).shallow_copy()?.into())
151            .ok();
152        Ok(Self(dict))
153    }
154
155    /// Returns the sub-dictionary containing capabilities routed by the component's parent.
156    pub fn capabilities(&self) -> Dict {
157        let cap = self.0.get(&*PARENT).expect("capabilities must be cloneable").unwrap();
158        let Capability::Dictionary(dict) = cap else {
159            unreachable!("parent entry must be a dict: {cap:?}");
160        };
161        dict
162    }
163
164    /// Returns the sub-dictionary containing capabilities routed by the component's environment.
165    pub fn environment(&self) -> ComponentEnvironment {
166        let cap = self.0.get(&*ENVIRONMENT).expect("environment must be cloneable").unwrap();
167        let Capability::Dictionary(dict) = cap else {
168            unreachable!("environment entry must be a dict: {cap:?}");
169        };
170        ComponentEnvironment(dict)
171    }
172
173    pub fn insert_capability(
174        &self,
175        path: &impl IterablePath,
176        capability: Capability,
177    ) -> Result<(), fsandbox::CapabilityStoreError> {
178        self.capabilities().insert_capability(path, capability.into())
179    }
180}
181
182impl From<ComponentInput> for Dict {
183    fn from(e: ComponentInput) -> Self {
184        e.0
185    }
186}
187
188/// The capabilities a component has in its environment. Stored as a [Dict] containing a nested
189/// [Dict] holding the environment's debug capabilities.
190#[derive(Clone, Debug)]
191pub struct ComponentEnvironment(Dict);
192
193impl Default for ComponentEnvironment {
194    fn default() -> Self {
195        let dict = Dict::new();
196        dict.insert(DEBUG.clone(), Dict::new().into()).ok();
197        dict.insert(RUNNERS.clone(), Dict::new().into()).ok();
198        dict.insert(RESOLVERS.clone(), Dict::new().into()).ok();
199        Self(dict)
200    }
201}
202
203impl StructuredDict for ComponentEnvironment {
204    fn from_dict(dict: Dict) -> Self {
205        Self(dict)
206    }
207}
208
209impl ComponentEnvironment {
210    pub fn new() -> Self {
211        Self::default()
212    }
213
214    /// Capabilities listed in the `debug_capabilities` portion of its environment.
215    pub fn debug(&self) -> Dict {
216        let cap = self.0.get(&*DEBUG).expect("debug must be cloneable").unwrap();
217        let Capability::Dictionary(dict) = cap else {
218            unreachable!("debug entry must be a dict: {cap:?}");
219        };
220        dict
221    }
222
223    /// Capabilities listed in the `runners` portion of its environment.
224    pub fn runners(&self) -> Dict {
225        let cap = self.0.get(&*RUNNERS).expect("runners must be cloneable").unwrap();
226        let Capability::Dictionary(dict) = cap else {
227            unreachable!("runners entry must be a dict: {cap:?}");
228        };
229        dict
230    }
231
232    /// Capabilities listed in the `resolvers` portion of its environment.
233    pub fn resolvers(&self) -> Dict {
234        let cap = self.0.get(&*RESOLVERS).expect("resolvers must be cloneable").unwrap();
235        let Capability::Dictionary(dict) = cap else {
236            unreachable!("resolvers entry must be a dict: {cap:?}");
237        };
238        dict
239    }
240
241    pub fn shallow_copy(&self) -> Result<Self, ()> {
242        // Note: We call [Dict::shallow_copy] on the nested [Dict]s, not the root [Dict], because
243        // [Dict::shallow_copy] only goes one level deep and we want to copy the contents of the
244        // inner sandboxes.
245        let dict = Dict::new();
246        dict.insert(DEBUG.clone(), self.debug().shallow_copy()?.into()).ok();
247        dict.insert(RUNNERS.clone(), self.runners().shallow_copy()?.into()).ok();
248        dict.insert(RESOLVERS.clone(), self.resolvers().shallow_copy()?.into()).ok();
249        Ok(Self(dict))
250    }
251}
252
253impl From<ComponentEnvironment> for Dict {
254    fn from(e: ComponentEnvironment) -> Self {
255        e.0
256    }
257}
258
259/// Contains the capabilities a component makes available to its parent or the framework. Stored as
260/// a [Dict] containing two nested [Dict]s for the capabilities made available to the parent and to
261/// the framework.
262#[derive(Clone, Debug)]
263pub struct ComponentOutput(Dict);
264
265impl Default for ComponentOutput {
266    fn default() -> Self {
267        Self::new()
268    }
269}
270
271impl StructuredDict for ComponentOutput {
272    fn from_dict(dict: Dict) -> Self {
273        Self(dict)
274    }
275}
276
277impl ComponentOutput {
278    pub fn new() -> Self {
279        let dict = Dict::new();
280        dict.insert(PARENT.clone(), Dict::new().into()).ok();
281        dict.insert(FRAMEWORK.clone(), Dict::new().into()).ok();
282        Self(dict)
283    }
284
285    /// Creates a new ComponentOutput with entries cloned from this ComponentOutput.
286    ///
287    /// This is a shallow copy. Values are cloned, not copied, so are new references to the same
288    /// underlying data.
289    pub fn shallow_copy(&self) -> Result<Self, ()> {
290        // Note: We call [Dict::copy] on the nested [Dict]s, not the root [Dict], because
291        // [Dict::copy] only goes one level deep and we want to copy the contents of the
292        // inner sandboxes.
293        let dict = Dict::new();
294        dict.insert(PARENT.clone(), self.capabilities().shallow_copy()?.into()).ok();
295        dict.insert(FRAMEWORK.clone(), self.framework().shallow_copy()?.into()).ok();
296        Ok(Self(dict))
297    }
298
299    /// Returns the sub-dictionary containing capabilities routed by the component's parent.
300    pub fn capabilities(&self) -> Dict {
301        let cap = self.0.get(&*PARENT).expect("capabilities must be cloneable").unwrap();
302        let Capability::Dictionary(dict) = cap else {
303            unreachable!("parent entry must be a dict: {cap:?}");
304        };
305        dict
306    }
307
308    /// Returns the sub-dictionary containing capabilities routed to the component's framework.
309    pub fn framework(&self) -> Dict {
310        let cap = self.0.get(&*FRAMEWORK).expect("capabilities must be cloneable").unwrap();
311        let Capability::Dictionary(dict) = cap else {
312            unreachable!("framework entry must be a dict: {cap:?}");
313        };
314        dict
315    }
316}
317
318impl From<ComponentOutput> for Dict {
319    fn from(e: ComponentOutput) -> Self {
320        e.0
321    }
322}
323
324#[cfg(test)]
325mod tests {
326    use super::*;
327    use assert_matches::assert_matches;
328    use sandbox::DictKey;
329
330    impl StructuredDict for Dict {
331        fn from_dict(dict: Dict) -> Self {
332            dict
333        }
334    }
335
336    #[fuchsia::test]
337    async fn structured_dict_map() {
338        let dict1 = {
339            let dict = Dict::new();
340            dict.insert("a".parse().unwrap(), Dict::new().into())
341                .expect("dict entry already exists");
342            dict
343        };
344        let dict2 = {
345            let dict = Dict::new();
346            dict.insert("b".parse().unwrap(), Dict::new().into())
347                .expect("dict entry already exists");
348            dict
349        };
350        let dict2_alt = {
351            let dict = Dict::new();
352            dict.insert("c".parse().unwrap(), Dict::new().into())
353                .expect("dict entry already exists");
354            dict
355        };
356        let name1 = Name::new("1").unwrap();
357        let name2 = Name::new("2").unwrap();
358
359        let map: StructuredDictMap<Dict> = Default::default();
360        assert_matches!(map.get(&name1), None);
361        assert!(map.insert(name1.clone(), dict1).is_ok());
362        let d = map.get(&name1).unwrap();
363        let key = DictKey::new("a").unwrap();
364        assert_matches!(d.get(&key), Ok(Some(_)));
365
366        assert!(map.insert(name2.clone(), dict2).is_ok());
367        let d = map.remove(&name2).unwrap();
368        assert_matches!(map.remove(&name2), None);
369        let key = DictKey::new("b").unwrap();
370        assert_matches!(d.get(&key), Ok(Some(_)));
371
372        assert!(map.insert(name2.clone(), dict2_alt).is_ok());
373        let d = map.get(&name2).unwrap();
374        let key = DictKey::new("c").unwrap();
375        assert_matches!(d.get(&key), Ok(Some(_)));
376    }
377}