Skip to main content

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 runtime_capabilities::{Capability, Data, Dictionary};
8use std::fmt;
9use std::marker::PhantomData;
10use std::sync::{Arc, LazyLock};
11
12/// This trait is implemented by types that wrap a [Dictionary] and wish to present an abstracted
13/// interface over the [Dictionary].
14///
15/// All such types are defined in this module, so this trait is private.
16///
17/// See also: [StructuredDictMap]
18trait StructuredDict: Into<Arc<Dictionary>> + Default + Clone + fmt::Debug {
19    /// Converts from [Dictionary] to `Self`.
20    ///
21    /// REQUIRES: [Dictionary] is a valid representation of `Self`.
22    ///
23    /// IMPORTANT: The caller should know that [Dictionary] is a valid representation of [Self].
24    /// This function is not guaranteed to perform any validation.
25    fn from_dict(dict: Arc<Dictionary>) -> Self;
26}
27
28/// A collection type for mapping [Name] to [StructuredDict], using [Dictionary] as the underlying
29/// representation.
30///
31/// For example, this can be used to store a map of child or collection names to [ComponentInput]s
32/// (where [ComponentInput] is the type that implements [StructuredDict]).
33///
34/// Because the representation of this type is [Dictionary], this type itself implements
35/// [StructuredDict].
36#[derive(Clone, Debug, Default)]
37#[allow(private_bounds)]
38pub struct StructuredDictMap<T: StructuredDict> {
39    inner: Arc<Dictionary>,
40    phantom: PhantomData<T>,
41}
42
43impl<T: StructuredDict> StructuredDict for StructuredDictMap<T> {
44    fn from_dict(dict: Arc<Dictionary>) -> Self {
45        Self { inner: dict, phantom: Default::default() }
46    }
47}
48
49#[allow(private_bounds)]
50impl<T: StructuredDict> StructuredDictMap<T> {
51    pub fn insert(&self, key: Name, value: T) -> Option<Capability> {
52        self.inner.insert(key, Capability::Dictionary(value.into()))
53    }
54
55    pub fn get(&self, key: &BorrowedName) -> Option<T> {
56        self.inner.get(key).map(|cap| {
57            let Capability::Dictionary(dict) = cap else {
58                unreachable!("structured map entry must be a dict: {cap:?}");
59            };
60            T::from_dict(dict)
61        })
62    }
63
64    pub fn remove(&self, key: &Name) -> Option<T> {
65        self.inner.remove(&*key).map(|cap| {
66            let Capability::Dictionary(dict) = cap else {
67                unreachable!("structured map entry must be a dict: {cap:?}");
68            };
69            T::from_dict(dict)
70        })
71    }
72
73    pub fn append(&self, other: &Self) -> Result<(), ()> {
74        self.inner.append(&other.inner)
75    }
76
77    pub fn enumerate(&self) -> impl Iterator<Item = (Name, T)> {
78        self.inner.enumerate().map(|(key, capability_res)| match capability_res {
79            Capability::Dictionary(dict) => (key, T::from_dict(dict)),
80            cap => unreachable!("structured map entry must be a dict: {cap:?}"),
81        })
82    }
83}
84
85impl<T: StructuredDict> From<StructuredDictMap<T>> for Arc<Dictionary> {
86    fn from(m: StructuredDictMap<T>) -> Self {
87        m.inner
88    }
89}
90
91// Dictionary keys for different kinds of sandboxes.
92/// Dictionary of capabilities from or to the parent.
93static PARENT: LazyLock<Name> = LazyLock::new(|| "parent".parse().unwrap());
94
95/// Dictionary of capabilities from a component's environment.
96static ENVIRONMENT: LazyLock<Name> = LazyLock::new(|| "environment".parse().unwrap());
97
98/// Dictionary of debug capabilities in a component's environment.
99static DEBUG: LazyLock<Name> = LazyLock::new(|| "debug".parse().unwrap());
100
101/// Dictionary of runner capabilities in a component's environment.
102static RUNNERS: LazyLock<Name> = LazyLock::new(|| "runners".parse().unwrap());
103
104/// Dictionary of resolver capabilities in a component's environment.
105static RESOLVERS: LazyLock<Name> = LazyLock::new(|| "resolvers".parse().unwrap());
106
107/// Dictionary of capabilities the component exposes to the framework.
108static FRAMEWORK: LazyLock<Name> = LazyLock::new(|| "framework".parse().unwrap());
109
110/// The stop timeout for a component environment.
111static STOP_TIMEOUT: LazyLock<Name> = LazyLock::new(|| "stop_timeout".parse().unwrap());
112
113/// The name of a component environment.
114static NAME: LazyLock<Name> = LazyLock::new(|| "name".parse().unwrap());
115
116/// Contains the capabilities component receives from its parent and environment. Stored as a
117/// [Dictionary] containing two nested [Dictionary]s for the parent and environment.
118#[derive(Clone, Debug)]
119pub struct ComponentInput(Arc<Dictionary>);
120
121impl Default for ComponentInput {
122    fn default() -> Self {
123        Self::new(ComponentEnvironment::new())
124    }
125}
126
127impl StructuredDict for ComponentInput {
128    fn from_dict(dict: Arc<Dictionary>) -> Self {
129        Self(dict)
130    }
131}
132
133impl ComponentInput {
134    pub fn new(environment: ComponentEnvironment) -> Self {
135        let dict = Dictionary::new();
136
137        if !environment.0.is_empty() {
138            dict.insert(ENVIRONMENT.clone(), Capability::Dictionary(environment.into()));
139        }
140        Self(dict)
141    }
142
143    /// Creates a new ComponentInput with entries cloned from this ComponentInput.
144    ///
145    /// This is a shallow copy. Values are cloned, not copied, so are new references to the same
146    /// underlying data.
147    pub fn shallow_copy(&self) -> Self {
148        // Note: We call [Dictionary::copy] on the nested [Dictionary]s, not the root [Dictionary],
149        // because [Dictionary::copy] only goes one level deep and we want to copy the contents of
150        // the inner sandboxes.
151        let dest = Dictionary::new();
152        shallow_copy(&self.0, &dest, &*PARENT);
153        shallow_copy(&self.0, &dest, &*ENVIRONMENT);
154        Self(dest)
155    }
156
157    /// Returns the sub-dictionary containing capabilities routed by the component's parent.
158    pub fn capabilities(&self) -> Arc<Dictionary> {
159        get_or_insert(&self.0, &*PARENT)
160    }
161
162    /// Returns the sub-dictionary containing capabilities routed by the component's environment.
163    pub fn environment(&self) -> ComponentEnvironment {
164        ComponentEnvironment(get_or_insert(&self.0, &*ENVIRONMENT))
165    }
166
167    pub fn insert_capability(
168        &self,
169        path: &impl IterablePath,
170        capability: Capability,
171    ) -> Option<Capability> {
172        self.capabilities().insert_capability(path, capability.into())
173    }
174}
175
176impl From<ComponentInput> for Arc<Dictionary> {
177    fn from(e: ComponentInput) -> Self {
178        e.0
179    }
180}
181
182/// The capabilities a component has in its environment. Stored as a [Dictionary] containing a
183/// nested [Dictionary] holding the environment's debug capabilities.
184#[derive(Clone, Debug)]
185pub struct ComponentEnvironment(Arc<Dictionary>);
186
187impl Default for ComponentEnvironment {
188    fn default() -> Self {
189        Self(Dictionary::new())
190    }
191}
192
193impl StructuredDict for ComponentEnvironment {
194    fn from_dict(dict: Arc<Dictionary>) -> Self {
195        Self(dict)
196    }
197}
198
199impl ComponentEnvironment {
200    pub fn new() -> Self {
201        Self::default()
202    }
203
204    /// Capabilities listed in the `debug_capabilities` portion of its environment.
205    pub fn debug(&self) -> Arc<Dictionary> {
206        get_or_insert(&self.0, &*DEBUG)
207    }
208
209    /// Capabilities listed in the `runners` portion of its environment.
210    pub fn runners(&self) -> Arc<Dictionary> {
211        get_or_insert(&self.0, &*RUNNERS)
212    }
213
214    /// Capabilities listed in the `resolvers` portion of its environment.
215    pub fn resolvers(&self) -> Arc<Dictionary> {
216        get_or_insert(&self.0, &*RESOLVERS)
217    }
218
219    /// Sets the stop timeout (in milliseconds) for this environment.
220    pub fn set_stop_timeout(&self, timeout: i64) {
221        let _ =
222            self.0.insert(STOP_TIMEOUT.clone(), Capability::Data(Arc::new(Data::Int64(timeout))));
223    }
224
225    /// Returns the stop timeout (in milliseconds) for this environment.
226    pub fn stop_timeout(&self) -> Option<i64> {
227        let Some(Capability::Data(data_cap)) = self.0.get(&*STOP_TIMEOUT) else {
228            return None;
229        };
230        let Data::Int64(timeout) = &*data_cap else {
231            return None;
232        };
233        Some(*timeout)
234    }
235
236    /// Sets the name for this environment.
237    pub fn set_name(&self, name: &Name) {
238        let _ = self
239            .0
240            .insert(NAME.clone(), Capability::Data(Arc::new(Data::String(name.as_str().into()))));
241    }
242
243    /// Returns the name for this environment.
244    pub fn name(&self) -> Option<Name> {
245        let Some(Capability::Data(data_cap)) = self.0.get(&*NAME) else {
246            return None;
247        };
248        let Data::String(name) = &*data_cap else {
249            return None;
250        };
251        Some(Name::new(name).unwrap())
252    }
253
254    pub fn shallow_copy(&self) -> Self {
255        // Note: We call [Dictionary::shallow_copy] on the nested [Dictionary]s, not the root
256        // [Dictionary], because [Dictionary::shallow_copy] only goes one level deep and we want to
257        // copy the contents of the inner sandboxes.
258        let dest = Dictionary::new();
259        shallow_copy(&self.0, &dest, &*DEBUG);
260        shallow_copy(&self.0, &dest, &*RUNNERS);
261        shallow_copy(&self.0, &dest, &*RESOLVERS);
262        Self(dest)
263    }
264}
265
266impl From<ComponentEnvironment> for Arc<Dictionary> {
267    fn from(e: ComponentEnvironment) -> Self {
268        e.0
269    }
270}
271
272/// Contains the capabilities a component makes available to its parent or the framework. Stored as
273/// a [Dictionary] containing two nested [Dictionary]s for the capabilities made available to the
274/// parent and to the framework.
275#[derive(Clone, Debug)]
276pub struct ComponentOutput(Arc<Dictionary>);
277
278impl Default for ComponentOutput {
279    fn default() -> Self {
280        Self::new()
281    }
282}
283
284impl StructuredDict for ComponentOutput {
285    fn from_dict(dict: Arc<Dictionary>) -> Self {
286        Self(dict)
287    }
288}
289
290impl ComponentOutput {
291    pub fn new() -> Self {
292        Self(Dictionary::new())
293    }
294
295    /// Creates a new ComponentOutput with entries cloned from this ComponentOutput.
296    ///
297    /// This is a shallow copy. Values are cloned, not copied, so are new references to the same
298    /// underlying data.
299    pub fn shallow_copy(&self) -> Self {
300        // Note: We call [Dictionary::copy] on the nested [Dictionary]s, not the root [Dictionary],
301        // because [Dictionary::copy] only goes one level deep and we want to copy the contents of
302        // the inner sandboxes.
303        let dest = Dictionary::new();
304        shallow_copy(&self.0, &dest, &*PARENT);
305        shallow_copy(&self.0, &dest, &*FRAMEWORK);
306        Self(dest)
307    }
308
309    /// Returns the sub-dictionary containing capabilities routed to the component's parent.
310    /// framework. Lazily adds the dictionary if it does not exist yet.
311    pub fn capabilities(&self) -> Arc<Dictionary> {
312        get_or_insert(&self.0, &*PARENT)
313    }
314
315    /// Returns the sub-dictionary containing capabilities exposed by the component to the
316    /// framework. Lazily adds the dictionary if it does not exist yet.
317    pub fn framework(&self) -> Arc<Dictionary> {
318        get_or_insert(&self.0, &*FRAMEWORK)
319    }
320}
321
322impl From<ComponentOutput> for Arc<Dictionary> {
323    fn from(e: ComponentOutput) -> Self {
324        e.0
325    }
326}
327
328fn shallow_copy(src: &Arc<Dictionary>, dest: &Arc<Dictionary>, key: &Name) {
329    if let Some(d) = src.get(key) {
330        let Capability::Dictionary(d) = d else {
331            unreachable!("{key} entry must be a dictionary: {d:?}");
332        };
333        dest.insert(key.clone(), Capability::Dictionary(d.shallow_copy()));
334    }
335}
336
337fn get_or_insert(this: &Dictionary, key: &Name) -> Arc<Dictionary> {
338    let cap = this.get_or_insert(&key, || Capability::Dictionary(Dictionary::new()));
339    let Capability::Dictionary(dict) = cap else {
340        unreachable!("{key} entry must be a dict: {cap:?}");
341    };
342    dict
343}
344
345#[cfg(test)]
346mod tests {
347    use super::*;
348    use assert_matches::assert_matches;
349    use runtime_capabilities::DictKey;
350
351    impl StructuredDict for Arc<Dictionary> {
352        fn from_dict(dict: Arc<Dictionary>) -> Self {
353            dict
354        }
355    }
356
357    #[fuchsia::test]
358    async fn structured_dict_map() {
359        let dict1 = {
360            let dict = Dictionary::new();
361            dict.insert("a".parse().unwrap(), Dictionary::new().into());
362            dict
363        };
364        let dict2 = {
365            let dict = Dictionary::new();
366            dict.insert("b".parse().unwrap(), Dictionary::new().into());
367            dict
368        };
369        let dict2_alt = {
370            let dict = Dictionary::new();
371            dict.insert("c".parse().unwrap(), Dictionary::new().into());
372            dict
373        };
374        let name1 = Name::new("1").unwrap();
375        let name2 = Name::new("2").unwrap();
376
377        let map: StructuredDictMap<Arc<Dictionary>> = Default::default();
378        assert_matches!(map.get(&name1), None);
379        assert!(map.insert(name1.clone(), dict1).is_none());
380        let d = map.get(&name1).unwrap();
381        let key = DictKey::new("a").unwrap();
382        assert_matches!(d.get(&key), Some(_));
383
384        assert!(map.insert(name2.clone(), dict2).is_none());
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), Some(_));
389
390        assert!(map.insert(name2.clone(), dict2_alt).is_none());
391        let d = map.get(&name2).unwrap();
392        let key = DictKey::new("c").unwrap();
393        assert_matches!(d.get(&key), Some(_));
394    }
395}