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