Skip to main content

routing/bedrock/
dict_ext.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 async_trait::async_trait;
6use capability_source::CapabilitySource;
7use cm_types::{IterablePath, RelativePath};
8use fidl_fuchsia_component_runtime::RouteRequest;
9use router_error::RouterError;
10use runtime_capabilities::{Capability, Dictionary, Routable, Router, WeakInstanceToken};
11use std::sync::Arc;
12
13#[async_trait]
14pub trait DictExt {
15    /// Returns the capability at the path, if it exists. Returns `None` if path is empty.
16    fn get_capability(&self, path: &impl IterablePath) -> Option<Capability>;
17
18    /// Inserts the capability at the path. Intermediary dictionaries are created as needed. If
19    /// there's already a capability at the path, then the preexisting value is returned.
20    fn insert_capability(
21        &self,
22        path: &impl IterablePath,
23        capability: Capability,
24    ) -> Option<Capability>;
25
26    /// Removes the capability at the path, if it exists, and returns it.
27    fn remove_capability(&self, path: &impl IterablePath) -> Option<Capability>;
28}
29
30#[async_trait]
31impl DictExt for Arc<Dictionary> {
32    fn get_capability(&self, path: &impl IterablePath) -> Option<Capability> {
33        let mut segments = path.iter_segments();
34        let Some(mut current_name) = segments.next() else {
35            return Some(Capability::Dictionary(self.clone()));
36        };
37        let mut current_dict = self.clone();
38        loop {
39            match segments.next() {
40                Some(next_name) => {
41                    let sub_dict =
42                        current_dict.get(current_name).and_then(|value| value.to_dictionary())?;
43                    current_dict = sub_dict;
44
45                    current_name = next_name;
46                }
47                None => return current_dict.get(current_name),
48            }
49        }
50    }
51
52    fn insert_capability(
53        &self,
54        path: &impl IterablePath,
55        capability: Capability,
56    ) -> Option<Capability> {
57        let mut segments = path.iter_segments();
58        let mut current_name = segments.next().expect("path must be non-empty");
59        let mut current_dict = self.clone();
60        loop {
61            match segments.next() {
62                Some(next_name) => {
63                    let sub_dict = {
64                        match current_dict.get(current_name) {
65                            Some(Capability::Dictionary(dict)) => dict,
66                            Some(Capability::DictionaryRouter(preexisting_router)) => {
67                                let mut path = vec![next_name];
68                                while let Some(name) = segments.next() {
69                                    path.push(name);
70                                }
71                                let path = RelativePath::from(path);
72                                let new_router = Router::new(AdditiveDictionaryRouter {
73                                    preexisting_router,
74                                    path,
75                                    capability,
76                                });
77
78                                // Replace the entry in current_dict.
79                                return current_dict.insert(current_name.into(), new_router.into());
80                            }
81                            None => {
82                                let dict = Dictionary::new();
83                                current_dict.insert(
84                                    current_name.into(),
85                                    Capability::Dictionary(dict.clone()),
86                                );
87                                dict
88                            }
89                            _ => return None,
90                        }
91                    };
92                    current_dict = sub_dict;
93
94                    current_name = next_name;
95                }
96                None => {
97                    return current_dict.insert(current_name.into(), capability);
98                }
99            }
100        }
101    }
102
103    fn remove_capability(&self, path: &impl IterablePath) -> Option<Capability> {
104        let mut segments = path.iter_segments();
105        let mut current_name = segments.next().expect("path must be non-empty");
106        let mut current_dict = self.clone();
107        loop {
108            match segments.next() {
109                Some(next_name) => {
110                    let sub_dict =
111                        current_dict.get(current_name).and_then(|value| value.to_dictionary());
112                    if sub_dict.is_none() {
113                        // The capability doesn't exist, there's nothing to remove.
114                        return None;
115                    }
116                    current_dict = sub_dict.unwrap();
117                    current_name = next_name;
118                }
119                None => {
120                    return current_dict.remove(current_name);
121                }
122            }
123        }
124    }
125}
126
127struct AdditiveDictionaryRouter {
128    preexisting_router: Arc<Router<Dictionary>>,
129    path: RelativePath,
130    capability: Capability,
131}
132
133#[async_trait]
134impl Routable<Dictionary> for AdditiveDictionaryRouter {
135    async fn route(
136        &self,
137        request: RouteRequest,
138        target: Arc<WeakInstanceToken>,
139    ) -> Result<Option<Arc<Dictionary>>, RouterError> {
140        let dictionary = match self.preexisting_router.route(request, target).await {
141            Ok(Some(dictionary)) => dictionary.shallow_copy(),
142            other_response => return other_response,
143        };
144        let _ = dictionary.insert_capability(&self.path, self.capability.clone());
145        Ok(Some(dictionary))
146    }
147
148    async fn route_debug(
149        &self,
150        request: RouteRequest,
151        target: Arc<WeakInstanceToken>,
152    ) -> Result<CapabilitySource, RouterError> {
153        self.preexisting_router.route_debug(request, target).await
154    }
155}