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 sandbox::{Capability, Dict};
9use std::fmt;
10use std::marker::PhantomData;
11use std::sync::LazyLock;
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.
95/// Dictionary of capabilities from or to the parent.
96static PARENT: LazyLock<Name> = LazyLock::new(|| "parent".parse().unwrap());
97
98/// Dictionary of capabilities from a component's environment.
99static ENVIRONMENT: LazyLock<Name> = LazyLock::new(|| "environment".parse().unwrap());
100
101/// Dictionary of debug capabilities in a component's environment.
102static DEBUG: LazyLock<Name> = LazyLock::new(|| "debug".parse().unwrap());
103
104/// Dictionary of runner capabilities in a component's environment.
105static RUNNERS: LazyLock<Name> = LazyLock::new(|| "runners".parse().unwrap());
106
107/// Dictionary of resolver capabilities in a component's environment.
108static RESOLVERS: LazyLock<Name> = LazyLock::new(|| "resolvers".parse().unwrap());
109
110/// Dictionary of capabilities the component exposes to the framework.
111static FRAMEWORK: LazyLock<Name> = LazyLock::new(|| "framework".parse().unwrap());
112
113/// Contains the capabilities component receives from its parent and environment. Stored as a
114/// [Dict] containing two nested [Dict]s for the parent and environment.
115#[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    /// Creates a new ComponentInput with entries cloned from this ComponentInput.
140    ///
141    /// This is a shallow copy. Values are cloned, not copied, so are new references to the same
142    /// underlying data.
143    pub fn shallow_copy(&self) -> Result<Self, ()> {
144        // Note: We call [Dict::copy] on the nested [Dict]s, not the root [Dict], because
145        // [Dict::copy] only goes one level deep and we want to copy the contents of the
146        // inner sandboxes.
147        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    /// Returns the sub-dictionary containing capabilities routed by the component's parent.
154    pub fn capabilities(&self) -> Dict {
155        get_or_insert(&self.0, &*PARENT)
156    }
157
158    /// Returns the sub-dictionary containing capabilities routed by the component's environment.
159    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/// The capabilities a component has in its environment. Stored as a [Dict] containing a nested
179/// [Dict] holding the environment's debug capabilities.
180#[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    /// Capabilities listed in the `debug_capabilities` portion of its environment.
201    pub fn debug(&self) -> Dict {
202        get_or_insert(&self.0, &*DEBUG)
203    }
204
205    /// Capabilities listed in the `runners` portion of its environment.
206    pub fn runners(&self) -> Dict {
207        get_or_insert(&self.0, &*RUNNERS)
208    }
209
210    /// Capabilities listed in the `resolvers` portion of its environment.
211    pub fn resolvers(&self) -> Dict {
212        get_or_insert(&self.0, &*RESOLVERS)
213    }
214
215    pub fn shallow_copy(&self) -> Result<Self, ()> {
216        // Note: We call [Dict::shallow_copy] on the nested [Dict]s, not the root [Dict], because
217        // [Dict::shallow_copy] only goes one level deep and we want to copy the contents of the
218        // inner sandboxes.
219        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/// Contains the capabilities a component makes available to its parent or the framework. Stored as
234/// a [Dict] containing two nested [Dict]s for the capabilities made available to the parent and to
235/// the framework.
236#[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    /// Creates a new ComponentOutput with entries cloned from this ComponentOutput.
257    ///
258    /// This is a shallow copy. Values are cloned, not copied, so are new references to the same
259    /// underlying data.
260    pub fn shallow_copy(&self) -> Result<Self, ()> {
261        // Note: We call [Dict::copy] on the nested [Dict]s, not the root [Dict], because
262        // [Dict::copy] only goes one level deep and we want to copy the contents of the
263        // inner sandboxes.
264        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    /// Returns the sub-dictionary containing capabilities routed to the component's parent.
271    /// framework. Lazily adds the dictionary if it does not exist yet.
272    pub fn capabilities(&self) -> Dict {
273        get_or_insert(&self.0, &*PARENT)
274    }
275
276    /// Returns the sub-dictionary containing capabilities exposed by the component to the
277    /// framework. Lazily adds the dictionary if it does not exist yet.
278    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}