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 _ = self.0.insert(STOP_TIMEOUT.clone(), Capability::Data(Data::Int64(timeout)));
222    }
223
224    /// Returns the stop timeout (in milliseconds) for this environment.
225    pub fn stop_timeout(&self) -> Option<i64> {
226        let Some(Capability::Data(data_cap)) = self.0.get(&*STOP_TIMEOUT) else {
227            return None;
228        };
229        let Data::Int64(timeout) = &data_cap else {
230            return None;
231        };
232        Some(*timeout)
233    }
234
235    /// Sets the name for this environment.
236    pub fn set_name(&self, name: &Name) {
237        let _ = self.0.insert(NAME.clone(), Capability::Data(Data::String(name.as_str().into())));
238    }
239
240    /// Returns the name for this environment.
241    pub fn name(&self) -> Option<Name> {
242        let Some(Capability::Data(data_cap)) = self.0.get(&*NAME) else {
243            return None;
244        };
245        let Data::String(name) = &data_cap else {
246            return None;
247        };
248        Some(Name::new(name).unwrap())
249    }
250
251    pub fn shallow_copy(&self) -> Self {
252        // Note: We call [Dictionary::shallow_copy] on the nested [Dictionary]s, not the root
253        // [Dictionary], because [Dictionary::shallow_copy] only goes one level deep and we want to
254        // copy the contents of the inner sandboxes.
255        let dest = Dictionary::new();
256        shallow_copy(&self.0, &dest, &*DEBUG);
257        shallow_copy(&self.0, &dest, &*RUNNERS);
258        shallow_copy(&self.0, &dest, &*RESOLVERS);
259        Self(dest)
260    }
261}
262
263impl From<ComponentEnvironment> for Arc<Dictionary> {
264    fn from(e: ComponentEnvironment) -> Self {
265        e.0
266    }
267}
268
269/// Contains the capabilities a component makes available to its parent or the framework. Stored as
270/// a [Dictionary] containing two nested [Dictionary]s for the capabilities made available to the
271/// parent and to the framework.
272#[derive(Clone, Debug)]
273pub struct ComponentOutput(Arc<Dictionary>);
274
275impl Default for ComponentOutput {
276    fn default() -> Self {
277        Self::new()
278    }
279}
280
281impl StructuredDict for ComponentOutput {
282    fn from_dict(dict: Arc<Dictionary>) -> Self {
283        Self(dict)
284    }
285}
286
287impl ComponentOutput {
288    pub fn new() -> Self {
289        Self(Dictionary::new())
290    }
291
292    /// Creates a new ComponentOutput with entries cloned from this ComponentOutput.
293    ///
294    /// This is a shallow copy. Values are cloned, not copied, so are new references to the same
295    /// underlying data.
296    pub fn shallow_copy(&self) -> Self {
297        // Note: We call [Dictionary::copy] on the nested [Dictionary]s, not the root [Dictionary],
298        // because [Dictionary::copy] only goes one level deep and we want to copy the contents of
299        // the inner sandboxes.
300        let dest = Dictionary::new();
301        shallow_copy(&self.0, &dest, &*PARENT);
302        shallow_copy(&self.0, &dest, &*FRAMEWORK);
303        Self(dest)
304    }
305
306    /// Returns the sub-dictionary containing capabilities routed to the component's parent.
307    /// framework. Lazily adds the dictionary if it does not exist yet.
308    pub fn capabilities(&self) -> Arc<Dictionary> {
309        get_or_insert(&self.0, &*PARENT)
310    }
311
312    /// Returns the sub-dictionary containing capabilities exposed by the component to the
313    /// framework. Lazily adds the dictionary if it does not exist yet.
314    pub fn framework(&self) -> Arc<Dictionary> {
315        get_or_insert(&self.0, &*FRAMEWORK)
316    }
317}
318
319impl From<ComponentOutput> for Arc<Dictionary> {
320    fn from(e: ComponentOutput) -> Self {
321        e.0
322    }
323}
324
325fn shallow_copy(src: &Arc<Dictionary>, dest: &Arc<Dictionary>, key: &Name) {
326    if let Some(d) = src.get(key) {
327        let Capability::Dictionary(d) = d else {
328            unreachable!("{key} entry must be a dictionary: {d:?}");
329        };
330        dest.insert(key.clone(), Capability::Dictionary(d.shallow_copy()));
331    }
332}
333
334fn get_or_insert(this: &Dictionary, key: &Name) -> Arc<Dictionary> {
335    let cap = this.get_or_insert(&key, || Capability::Dictionary(Dictionary::new()));
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 runtime_capabilities::DictKey;
347
348    impl StructuredDict for Arc<Dictionary> {
349        fn from_dict(dict: Arc<Dictionary>) -> Self {
350            dict
351        }
352    }
353
354    #[fuchsia::test]
355    async fn structured_dict_map() {
356        let dict1 = {
357            let dict = Dictionary::new();
358            dict.insert("a".parse().unwrap(), Dictionary::new().into());
359            dict
360        };
361        let dict2 = {
362            let dict = Dictionary::new();
363            dict.insert("b".parse().unwrap(), Dictionary::new().into());
364            dict
365        };
366        let dict2_alt = {
367            let dict = Dictionary::new();
368            dict.insert("c".parse().unwrap(), Dictionary::new().into());
369            dict
370        };
371        let name1 = Name::new("1").unwrap();
372        let name2 = Name::new("2").unwrap();
373
374        let map: StructuredDictMap<Arc<Dictionary>> = Default::default();
375        assert_matches!(map.get(&name1), None);
376        assert!(map.insert(name1.clone(), dict1).is_none());
377        let d = map.get(&name1).unwrap();
378        let key = DictKey::new("a").unwrap();
379        assert_matches!(d.get(&key), Some(_));
380
381        assert!(map.insert(name2.clone(), dict2).is_none());
382        let d = map.remove(&name2).unwrap();
383        assert_matches!(map.remove(&name2), None);
384        let key = DictKey::new("b").unwrap();
385        assert_matches!(d.get(&key), Some(_));
386
387        assert!(map.insert(name2.clone(), dict2_alt).is_none());
388        let d = map.get(&name2).unwrap();
389        let key = DictKey::new("c").unwrap();
390        assert_matches!(d.get(&key), Some(_));
391    }
392}