1use crate::error::RoutingError;
6use async_trait::async_trait;
7use cm_types::{IterablePath, RelativePath};
8use fidl_fuchsia_component_sandbox as fsandbox;
9use moniker::ExtendedMoniker;
10use router_error::RouterError;
11use sandbox::{
12 Capability, CapabilityBound, Connector, Data, Dict, DirEntry, Request, Routable, Router,
13 RouterResponse,
14};
15use std::fmt::Debug;
16
17#[async_trait]
18pub trait DictExt {
19 fn get_capability(&self, path: &impl IterablePath) -> Option<Capability>;
21
22 fn get_router_or_not_found<T>(
31 &self,
32 path: &impl IterablePath,
33 not_found_error: RoutingError,
34 ) -> Router<T>
35 where
36 T: CapabilityBound,
37 Router<T>: TryFrom<Capability>;
38
39 fn insert_capability(
41 &self,
42 path: &impl IterablePath,
43 capability: Capability,
44 ) -> Result<(), fsandbox::CapabilityStoreError>;
45
46 fn remove_capability(&self, path: &impl IterablePath);
48
49 async fn get_with_request<'a>(
58 &self,
59 moniker: &ExtendedMoniker,
60 path: &'a impl IterablePath,
61 request: Option<Request>,
62 debug: bool,
63 ) -> Result<Option<GenericRouterResponse>, RouterError>;
64}
65
66#[derive(Debug)]
69pub enum GenericRouterResponse {
70 Capability(Capability),
72
73 Unavailable,
75
76 Debug(Data),
78}
79
80impl<T: CapabilityBound> TryFrom<GenericRouterResponse> for RouterResponse<T> {
81 type Error = &'static str;
83
84 fn try_from(r: GenericRouterResponse) -> Result<Self, Self::Error> {
85 let r = match r {
86 GenericRouterResponse::Capability(c) => {
87 let debug_name = c.debug_typename();
88 RouterResponse::<T>::Capability(c.try_into().map_err(|_| debug_name)?)
89 }
90 GenericRouterResponse::Unavailable => RouterResponse::<T>::Unavailable,
91 GenericRouterResponse::Debug(d) => RouterResponse::<T>::Debug(d),
92 };
93 Ok(r)
94 }
95}
96
97#[async_trait]
98impl DictExt for Dict {
99 fn get_capability(&self, path: &impl IterablePath) -> Option<Capability> {
100 let mut segments = path.iter_segments();
101 let Some(mut current_name) = segments.next() else { return Some(self.clone().into()) };
102 let mut current_dict = self.clone();
103 loop {
104 match segments.next() {
105 Some(next_name) => {
106 let sub_dict = current_dict
107 .get(current_name)
108 .ok()
109 .flatten()
110 .and_then(|value| value.to_dictionary())?;
111 current_dict = sub_dict;
112
113 current_name = next_name;
114 }
115 None => return current_dict.get(current_name).ok().flatten(),
116 }
117 }
118 }
119
120 fn get_router_or_not_found<T>(
121 &self,
122 path: &impl IterablePath,
123 not_found_error: RoutingError,
124 ) -> Router<T>
125 where
126 T: CapabilityBound,
127 Router<T>: TryFrom<Capability>,
128 {
129 let mut segments = path.iter_segments();
130 let root = segments.next().expect("path must be nonempty");
131
132 #[derive(Debug)]
133 struct ErrorRouter {
134 not_found_error: RouterError,
135 }
136
137 #[async_trait]
138 impl<T: CapabilityBound> Routable<T> for ErrorRouter {
139 async fn route(
140 &self,
141 _request: Option<Request>,
142 _debug: bool,
143 ) -> Result<RouterResponse<T>, RouterError> {
144 Err(self.not_found_error.clone())
145 }
146 }
147
148 #[derive(Debug)]
152 struct ScopedDictRouter<P: IterablePath + Debug + 'static> {
153 router: Router<Dict>,
154 path: P,
155 not_found_error: RoutingError,
156 }
157
158 #[async_trait]
159 impl<P: IterablePath + Debug + 'static, T: CapabilityBound> Routable<T> for ScopedDictRouter<P> {
160 async fn route(
161 &self,
162 request: Option<Request>,
163 debug: bool,
164 ) -> Result<RouterResponse<T>, RouterError> {
165 let init_request = request.as_ref().map(|r| r.try_clone()).transpose()?;
169 match self.router.route(init_request, false).await? {
170 RouterResponse::<Dict>::Capability(dict) => {
171 let moniker: ExtendedMoniker = self.not_found_error.clone().into();
172 let resp =
173 dict.get_with_request(&moniker, &self.path, request, debug).await?;
174 let resp =
175 resp.ok_or_else(|| RouterError::from(self.not_found_error.clone()))?;
176 let resp = resp.try_into().map_err(|debug_name: &'static str| {
177 RoutingError::BedrockWrongCapabilityType {
178 expected: T::debug_typename().into(),
179 actual: debug_name.into(),
180 moniker,
181 }
182 })?;
183 Ok(resp)
184 }
185 _ => Err(RoutingError::BedrockMemberAccessUnsupported {
186 moniker: self.not_found_error.clone().into(),
187 }
188 .into()),
189 }
190 }
191 }
192
193 if segments.next().is_none() {
194 let Some(router) =
196 self.get(root).ok().flatten().and_then(|cap| Router::<T>::try_from(cap).ok())
197 else {
198 return Router::<T>::new(ErrorRouter { not_found_error: not_found_error.into() });
199 };
200 return router;
201 }
202
203 let Some(cap) = self.get(root).ok().flatten() else {
204 return Router::<T>::new(ErrorRouter { not_found_error: not_found_error.into() });
205 };
206 let router = match cap {
207 Capability::Dictionary(d) => Router::<Dict>::new_ok(d),
208 Capability::DictionaryRouter(r) => r,
209 _ => {
210 return Router::<T>::new(ErrorRouter { not_found_error: not_found_error.into() });
211 }
212 };
213
214 let mut segments = path.iter_segments();
215 let _ = segments.next().unwrap();
216 let path = RelativePath::from(segments.map(|s| s.clone()).collect::<Vec<_>>());
217
218 Router::<T>::new(ScopedDictRouter { router, path, not_found_error: not_found_error.into() })
219 }
220
221 fn insert_capability(
222 &self,
223 path: &impl IterablePath,
224 capability: Capability,
225 ) -> Result<(), fsandbox::CapabilityStoreError> {
226 let mut segments = path.iter_segments();
227 let mut current_name = segments.next().expect("path must be non-empty");
228 let mut current_dict = self.clone();
229 loop {
230 match segments.next() {
231 Some(next_name) => {
232 let sub_dict = {
233 match current_dict.get(current_name) {
234 Ok(Some(cap)) => cap
235 .to_dictionary()
236 .ok_or(fsandbox::CapabilityStoreError::ItemNotFound)?,
237 Ok(None) => {
238 let dict = Dict::new();
239 current_dict.insert(
240 current_name.clone(),
241 Capability::Dictionary(dict.clone()),
242 )?;
243 dict
244 }
245 Err(_) => return Err(fsandbox::CapabilityStoreError::ItemNotFound),
246 }
247 };
248 current_dict = sub_dict;
249
250 current_name = next_name;
251 }
252 None => {
253 return current_dict.insert(current_name.clone(), capability);
254 }
255 }
256 }
257 }
258
259 fn remove_capability(&self, path: &impl IterablePath) {
260 let mut segments = path.iter_segments();
261 let mut current_name = segments.next().expect("path must be non-empty");
262 let mut current_dict = self.clone();
263 loop {
264 match segments.next() {
265 Some(next_name) => {
266 let sub_dict = current_dict
267 .get(current_name)
268 .ok()
269 .flatten()
270 .and_then(|value| value.to_dictionary());
271 if sub_dict.is_none() {
272 return;
274 }
275 current_dict = sub_dict.unwrap();
276 current_name = next_name;
277 }
278 None => {
279 current_dict.remove(current_name);
280 return;
281 }
282 }
283 }
284 }
285
286 async fn get_with_request<'a>(
287 &self,
288 moniker: &ExtendedMoniker,
289 path: &'a impl IterablePath,
290 request: Option<Request>,
291 debug: bool,
292 ) -> Result<Option<GenericRouterResponse>, RouterError> {
293 let mut current_dict = self.clone();
294 let num_segments = path.iter_segments().count();
295 for (next_idx, next_name) in path.iter_segments().enumerate() {
296 let capability = current_dict
298 .get(next_name)
299 .map_err(|_| RoutingError::BedrockNotCloneable { moniker: moniker.clone() })?;
300
301 let Some(capability) = capability else {
303 return Ok(None);
304 };
305
306 let debug = if next_idx < num_segments - 1 {
308 false
312 } else {
313 debug
314 };
315 let request = request.as_ref().map(|r| r.try_clone()).transpose()?;
316
317 if next_idx < num_segments - 1 {
318 match capability {
321 Capability::Dictionary(d) => {
322 current_dict = d;
323 }
324 Capability::DictionaryRouter(r) => match r.route(request, false).await? {
325 RouterResponse::<Dict>::Capability(d) => {
326 current_dict = d;
327 }
328 RouterResponse::<Dict>::Unavailable => {
329 return Ok(Some(GenericRouterResponse::Unavailable));
330 }
331 RouterResponse::<Dict>::Debug(d) => {
332 return Ok(Some(GenericRouterResponse::Debug(d)));
335 }
336 },
337 _ => {
338 return Err(RoutingError::BedrockWrongCapabilityType {
339 expected: Dict::debug_typename().into(),
340 actual: capability.debug_typename().into(),
341 moniker: moniker.clone(),
342 }
343 .into());
344 }
345 }
346 } else {
347 let capability: Capability = match capability {
353 Capability::DictionaryRouter(r) => match r.route(request, debug).await? {
354 RouterResponse::<Dict>::Capability(c) => c.into(),
355 RouterResponse::<Dict>::Unavailable => {
356 return Ok(Some(GenericRouterResponse::Unavailable));
357 }
358 RouterResponse::<Dict>::Debug(d) => {
359 return Ok(Some(GenericRouterResponse::Debug(d)));
360 }
361 },
362 Capability::ConnectorRouter(r) => match r.route(request, debug).await? {
363 RouterResponse::<Connector>::Capability(c) => c.into(),
364 RouterResponse::<Connector>::Unavailable => {
365 return Ok(Some(GenericRouterResponse::Unavailable));
366 }
367 RouterResponse::<Connector>::Debug(d) => {
368 return Ok(Some(GenericRouterResponse::Debug(d)));
369 }
370 },
371 Capability::DataRouter(r) => match r.route(request, debug).await? {
372 RouterResponse::<Data>::Capability(c) => c.into(),
373 RouterResponse::<Data>::Unavailable => {
374 return Ok(Some(GenericRouterResponse::Unavailable));
375 }
376 RouterResponse::<Data>::Debug(d) => {
377 return Ok(Some(GenericRouterResponse::Debug(d)));
378 }
379 },
380 Capability::DirEntryRouter(r) => match r.route(request, debug).await? {
381 RouterResponse::<DirEntry>::Capability(c) => c.into(),
382 RouterResponse::<DirEntry>::Unavailable => {
383 return Ok(Some(GenericRouterResponse::Unavailable));
384 }
385 RouterResponse::<DirEntry>::Debug(d) => {
386 return Ok(Some(GenericRouterResponse::Debug(d)));
387 }
388 },
389 other => other,
390 };
391 return Ok(Some(GenericRouterResponse::Capability(capability)));
392 }
393 }
394 unreachable!("get_with_request: All cases are handled in the loop");
395 }
396}