1use crate::bedrock::request_metadata::Metadata;
6use crate::error::RoutingError;
7use async_trait::async_trait;
8use cm_rust::CapabilityTypeName;
9use cm_types::{IterablePath, RelativePath};
10use fidl_fuchsia_component_sandbox as fsandbox;
11use moniker::ExtendedMoniker;
12use router_error::RouterError;
13use sandbox::{
14 Capability, CapabilityBound, Connector, Data, Dict, DirConnector, DirEntry, Request, Routable,
15 Router, RouterResponse, WeakInstanceToken,
16};
17use std::fmt::Debug;
18
19#[async_trait]
20pub trait DictExt {
21 fn get_capability(&self, path: &impl IterablePath) -> Option<Capability>;
23
24 fn get_router_or_not_found<T>(
33 &self,
34 path: &impl IterablePath,
35 not_found_error: RoutingError,
36 ) -> Router<T>
37 where
38 T: CapabilityBound,
39 Router<T>: TryFrom<Capability>;
40
41 fn insert_capability(
43 &self,
44 path: &impl IterablePath,
45 capability: Capability,
46 ) -> Result<(), fsandbox::CapabilityStoreError>;
47
48 fn remove_capability(&self, path: &impl IterablePath) -> Option<Capability>;
50
51 async fn get_with_request<'a>(
60 &self,
61 moniker: &ExtendedMoniker,
62 path: &'a impl IterablePath,
63 request: Option<Request>,
64 debug: bool,
65 target: WeakInstanceToken,
66 ) -> Result<Option<GenericRouterResponse>, RouterError>;
67}
68
69#[derive(Debug)]
72pub enum GenericRouterResponse {
73 Capability(Capability),
75
76 Unavailable,
78
79 Debug(Data),
81}
82
83impl<T: CapabilityBound> TryFrom<GenericRouterResponse> for RouterResponse<T> {
84 type Error = &'static str;
86
87 fn try_from(r: GenericRouterResponse) -> Result<Self, Self::Error> {
88 let r = match r {
89 GenericRouterResponse::Capability(c) => {
90 let debug_name = c.debug_typename();
91 RouterResponse::<T>::Capability(c.try_into().map_err(|_| debug_name)?)
92 }
93 GenericRouterResponse::Unavailable => RouterResponse::<T>::Unavailable,
94 GenericRouterResponse::Debug(d) => RouterResponse::<T>::Debug(d),
95 };
96 Ok(r)
97 }
98}
99
100#[async_trait]
101impl DictExt for Dict {
102 fn get_capability(&self, path: &impl IterablePath) -> Option<Capability> {
103 let mut segments = path.iter_segments();
104 let Some(mut current_name) = segments.next() else { return Some(self.clone().into()) };
105 let mut current_dict = self.clone();
106 loop {
107 match segments.next() {
108 Some(next_name) => {
109 let sub_dict = current_dict
110 .get(current_name)
111 .ok()
112 .flatten()
113 .and_then(|value| value.to_dictionary())?;
114 current_dict = sub_dict;
115
116 current_name = next_name;
117 }
118 None => return current_dict.get(current_name).ok().flatten(),
119 }
120 }
121 }
122
123 fn get_router_or_not_found<T>(
124 &self,
125 path: &impl IterablePath,
126 not_found_error: RoutingError,
127 ) -> Router<T>
128 where
129 T: CapabilityBound,
130 Router<T>: TryFrom<Capability>,
131 {
132 let mut segments = path.iter_segments();
133 let root = segments.next().expect("path must be nonempty");
134
135 #[derive(Debug)]
136 struct ErrorRouter {
137 not_found_error: RouterError,
138 }
139
140 #[async_trait]
141 impl<T: CapabilityBound> Routable<T> for ErrorRouter {
142 async fn route(
143 &self,
144 _request: Option<Request>,
145 _debug: bool,
146 _target: WeakInstanceToken,
147 ) -> Result<RouterResponse<T>, RouterError> {
148 Err(self.not_found_error.clone())
149 }
150 }
151
152 #[derive(Debug)]
156 struct ScopedDictRouter<P: IterablePath + Debug + 'static> {
157 router: Router<Dict>,
158 path: P,
159 not_found_error: RoutingError,
160 }
161
162 #[async_trait]
163 impl<P: IterablePath + Debug + 'static, T: CapabilityBound> Routable<T> for ScopedDictRouter<P> {
164 async fn route(
165 &self,
166 request: Option<Request>,
167 debug: bool,
168 target: WeakInstanceToken,
169 ) -> Result<RouterResponse<T>, RouterError> {
170 let get_init_request = || request_with_dictionary_replacement(request.as_ref());
171
172 let init_request = (get_init_request)()?;
177 match self.router.route(init_request, false, target.clone()).await? {
178 RouterResponse::<Dict>::Capability(dict) => {
179 let moniker: ExtendedMoniker = self.not_found_error.clone().into();
180 let resp = dict
181 .get_with_request(&moniker, &self.path, request, debug, target)
182 .await?;
183 let resp =
184 resp.ok_or_else(|| RouterError::from(self.not_found_error.clone()))?;
185 let resp = resp.try_into().map_err(|debug_name: &'static str| {
186 RoutingError::BedrockWrongCapabilityType {
187 expected: T::debug_typename().into(),
188 actual: debug_name.into(),
189 moniker,
190 }
191 })?;
192 Ok(resp)
193 }
194 RouterResponse::<Dict>::Debug(data) => Ok(RouterResponse::<T>::Debug(data)),
195 RouterResponse::<Dict>::Unavailable => {
196 if !debug {
197 Ok(RouterResponse::<T>::Unavailable)
198 } else {
199 let init_request = (get_init_request)()?;
204 match self.router.route(init_request, true, target).await? {
205 RouterResponse::<Dict>::Debug(d) => {
206 Ok(RouterResponse::<T>::Debug(d))
207 }
208 _ => {
209 let moniker = self.not_found_error.clone().into();
211 Err(RoutingError::BedrockWrongCapabilityType {
212 expected: "RouterResponse::Debug".into(),
213 actual: "not RouterResponse::Debug".into(),
214 moniker,
215 }
216 .into())
217 }
218 }
219 }
220 }
221 }
222 }
223 }
224
225 if segments.next().is_none() {
226 let Some(router) =
228 self.get(root).ok().flatten().and_then(|cap| Router::<T>::try_from(cap).ok())
229 else {
230 return Router::<T>::new(ErrorRouter { not_found_error: not_found_error.into() });
231 };
232 return router;
233 }
234
235 let Some(cap) = self.get(root).ok().flatten() else {
236 return Router::<T>::new(ErrorRouter { not_found_error: not_found_error.into() });
237 };
238 let router = match cap {
239 Capability::Dictionary(d) => Router::<Dict>::new_ok(d),
240 Capability::DictionaryRouter(r) => r,
241 _ => {
242 return Router::<T>::new(ErrorRouter { not_found_error: not_found_error.into() });
243 }
244 };
245
246 let mut segments = path.iter_segments();
247 let _ = segments.next().unwrap();
248 let path = RelativePath::from(segments.collect::<Vec<_>>());
249
250 Router::<T>::new(ScopedDictRouter { router, path, not_found_error: not_found_error.into() })
251 }
252
253 fn insert_capability(
254 &self,
255 path: &impl IterablePath,
256 capability: Capability,
257 ) -> Result<(), fsandbox::CapabilityStoreError> {
258 let mut segments = path.iter_segments();
259 let mut current_name = segments.next().expect("path must be non-empty");
260 let mut current_dict = self.clone();
261 loop {
262 match segments.next() {
263 Some(next_name) => {
264 let sub_dict = {
265 match current_dict.get(current_name) {
266 Ok(Some(Capability::Dictionary(dict))) => dict,
267 Ok(Some(Capability::DictionaryRouter(preexisting_router))) => {
268 let mut path = vec![next_name];
269 while let Some(name) = segments.next() {
270 path.push(name);
271 }
272 let path = RelativePath::from(path);
273 let new_router = Router::new(AdditiveDictionaryRouter {
274 preexisting_router,
275 path,
276 capability,
277 });
278
279 current_dict.remove(current_name).unwrap();
281 current_dict
282 .insert(current_name.into(), new_router.into())?;
283
284 return Ok(());
285 }
286 Ok(None) => {
287 let dict = Dict::new();
288 current_dict.insert(
289 current_name.into(),
290 Capability::Dictionary(dict.clone()),
291 )?;
292 dict
293 }
294 _ => return Err(fsandbox::CapabilityStoreError::ItemNotFound),
295 }
296 };
297 current_dict = sub_dict;
298
299 current_name = next_name;
300 }
301 None => {
302 return current_dict.insert(current_name.into(), capability);
303 }
304 }
305 }
306 }
307
308 fn remove_capability(&self, path: &impl IterablePath) -> Option<Capability> {
309 let mut segments = path.iter_segments();
310 let mut current_name = segments.next().expect("path must be non-empty");
311 let mut current_dict = self.clone();
312 loop {
313 match segments.next() {
314 Some(next_name) => {
315 let sub_dict = current_dict
316 .get(current_name)
317 .ok()
318 .flatten()
319 .and_then(|value| value.to_dictionary());
320 if sub_dict.is_none() {
321 return None;
323 }
324 current_dict = sub_dict.unwrap();
325 current_name = next_name;
326 }
327 None => {
328 return current_dict.remove(current_name);
329 }
330 }
331 }
332 }
333
334 async fn get_with_request<'a>(
335 &self,
336 moniker: &ExtendedMoniker,
337 path: &'a impl IterablePath,
338 request: Option<Request>,
339 debug: bool,
340 target: WeakInstanceToken,
341 ) -> Result<Option<GenericRouterResponse>, RouterError> {
342 let mut current_dict = self.clone();
343 let num_segments = path.iter_segments().count();
344 for (next_idx, next_name) in path.iter_segments().enumerate() {
345 let capability = current_dict
347 .get(next_name)
348 .map_err(|_| RoutingError::BedrockNotCloneable { moniker: moniker.clone() })?;
349
350 let Some(capability) = capability else {
352 return Ok(None);
353 };
354
355 if next_idx < num_segments - 1 {
356 let dict_request = request_with_dictionary_replacement(request.as_ref())?;
359 match capability {
360 Capability::Dictionary(d) => {
361 current_dict = d;
362 }
363 Capability::DictionaryRouter(r) => {
364 match r.route(dict_request, false, target.clone()).await? {
365 RouterResponse::<Dict>::Capability(d) => {
366 current_dict = d;
367 }
368 RouterResponse::<Dict>::Debug(d) => {
369 return Ok(Some(GenericRouterResponse::Debug(d)));
372 }
373 RouterResponse::<Dict>::Unavailable => {
374 if !debug {
375 return Ok(Some(GenericRouterResponse::Unavailable));
376 } else {
377 let dict_request =
383 request_with_dictionary_replacement(request.as_ref())?;
384 match r.route(dict_request, true, target).await? {
385 RouterResponse::<Dict>::Debug(d) => {
386 return Ok(Some(GenericRouterResponse::Debug(d)));
387 }
388 _ => {
389 return Err(RoutingError::BedrockWrongCapabilityType {
391 expected: "RouterResponse::Debug".into(),
392 actual: "not RouterResponse::Debug".into(),
393 moniker: moniker.clone(),
394 }
395 .into());
396 }
397 }
398 }
399 }
400 }
401 }
402 _ => {
403 return Err(RoutingError::BedrockWrongCapabilityType {
404 expected: Dict::debug_typename().into(),
405 actual: capability.debug_typename().into(),
406 moniker: moniker.clone(),
407 }
408 .into());
409 }
410 }
411 } else {
412 let request = request.as_ref().map(|r| r.try_clone()).transpose()?;
418 let capability: Capability = match capability {
419 Capability::DictionaryRouter(r) => {
420 match r.route(request, debug, target).await? {
421 RouterResponse::<Dict>::Capability(c) => c.into(),
422 RouterResponse::<Dict>::Unavailable => {
423 return Ok(Some(GenericRouterResponse::Unavailable));
424 }
425 RouterResponse::<Dict>::Debug(d) => {
426 return Ok(Some(GenericRouterResponse::Debug(d)));
427 }
428 }
429 }
430 Capability::ConnectorRouter(r) => {
431 match r.route(request, debug, target).await? {
432 RouterResponse::<Connector>::Capability(c) => c.into(),
433 RouterResponse::<Connector>::Unavailable => {
434 return Ok(Some(GenericRouterResponse::Unavailable));
435 }
436 RouterResponse::<Connector>::Debug(d) => {
437 return Ok(Some(GenericRouterResponse::Debug(d)));
438 }
439 }
440 }
441 Capability::DataRouter(r) => match r.route(request, debug, target).await? {
442 RouterResponse::<Data>::Capability(c) => c.into(),
443 RouterResponse::<Data>::Unavailable => {
444 return Ok(Some(GenericRouterResponse::Unavailable));
445 }
446 RouterResponse::<Data>::Debug(d) => {
447 return Ok(Some(GenericRouterResponse::Debug(d)));
448 }
449 },
450 Capability::DirEntryRouter(r) => match r.route(request, debug, target).await? {
451 RouterResponse::<DirEntry>::Capability(c) => c.into(),
452 RouterResponse::<DirEntry>::Unavailable => {
453 return Ok(Some(GenericRouterResponse::Unavailable));
454 }
455 RouterResponse::<DirEntry>::Debug(d) => {
456 return Ok(Some(GenericRouterResponse::Debug(d)));
457 }
458 },
459 Capability::DirConnectorRouter(r) => {
460 match r.route(request, debug, target).await? {
461 RouterResponse::<DirConnector>::Capability(c) => c.into(),
462 RouterResponse::<DirConnector>::Unavailable => {
463 return Ok(Some(GenericRouterResponse::Unavailable));
464 }
465 RouterResponse::<DirConnector>::Debug(d) => {
466 return Ok(Some(GenericRouterResponse::Debug(d)));
467 }
468 }
469 }
470 other => other,
471 };
472 return Ok(Some(GenericRouterResponse::Capability(capability)));
473 }
474 }
475 unreachable!("get_with_request: All cases are handled in the loop");
476 }
477}
478
479pub(super) fn request_with_dictionary_replacement(
485 request: Option<&Request>,
486) -> Result<Option<Request>, RoutingError> {
487 Ok(request.as_ref().map(|r| r.try_clone()).transpose()?.map(|r| {
488 let _ = r.metadata.set_metadata(CapabilityTypeName::Dictionary);
489 r
490 }))
491}
492
493struct AdditiveDictionaryRouter {
494 preexisting_router: Router<Dict>,
495 path: RelativePath,
496 capability: Capability,
497}
498
499#[async_trait]
500impl Routable<Dict> for AdditiveDictionaryRouter {
501 async fn route(
502 &self,
503 request: Option<Request>,
504 debug: bool,
505 target: WeakInstanceToken,
506 ) -> Result<RouterResponse<Dict>, RouterError> {
507 let dictionary = match self.preexisting_router.route(request, debug, target).await {
508 Ok(RouterResponse::<Dict>::Capability(dictionary)) => {
509 dictionary.shallow_copy().unwrap()
510 }
511 other_response => return other_response,
512 };
513 let _ = dictionary.insert_capability(&self.path, self.capability.try_clone().unwrap());
514 Ok(RouterResponse::Capability(dictionary))
515 }
516}