1use crate::error::RoutingError;
6use async_trait::async_trait;
7use capability_source::{CapabilitySource, RemotedAtSource};
8use cm_rust::CapabilityTypeName;
9use cm_types::{IterablePath, RelativePath};
10use fidl_fuchsia_component_runtime::RouteRequest;
11use itertools::Itertools;
12use moniker::ExtendedMoniker;
13use router_error::RouterError;
14use runtime_capabilities::{
15 Capability, CapabilityBound, Dictionary, Routable, Router, 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(
44 &self,
45 path: &impl IterablePath,
46 capability: Capability,
47 ) -> Option<Capability>;
48
49 fn remove_capability(&self, path: &impl IterablePath) -> Option<Capability>;
51
52 async fn get_with_request<'a>(
60 &self,
61 moniker: &ExtendedMoniker,
62 path: &'a impl IterablePath,
63 request: RouteRequest,
64 target: WeakInstanceToken,
65 ) -> Result<Option<Capability>, RouterError>;
66
67 async fn get_with_request_debug<'a>(
70 &self,
71 moniker: &ExtendedMoniker,
72 path: &'a impl IterablePath,
73 request: RouteRequest,
74 target: WeakInstanceToken,
75 ) -> Result<CapabilitySource, RouterError>;
76}
77
78#[async_trait]
79impl DictExt for Dictionary {
80 fn get_capability(&self, path: &impl IterablePath) -> Option<Capability> {
81 let mut segments = path.iter_segments();
82 let Some(mut current_name) = segments.next() else { return Some(self.clone().into()) };
83 let mut current_dict = self.clone();
84 loop {
85 match segments.next() {
86 Some(next_name) => {
87 let sub_dict =
88 current_dict.get(current_name).and_then(|value| value.to_dictionary())?;
89 current_dict = sub_dict;
90
91 current_name = next_name;
92 }
93 None => return current_dict.get(current_name),
94 }
95 }
96 }
97
98 fn get_router_or_not_found<T>(
99 &self,
100 path: &impl IterablePath,
101 not_found_error: RoutingError,
102 ) -> Router<T>
103 where
104 T: CapabilityBound,
105 Router<T>: TryFrom<Capability>,
106 {
107 let mut segments = path.iter_segments();
108 let root = segments.next().expect("path must be nonempty");
109
110 #[derive(Debug)]
111 struct ErrorRouter {
112 not_found_error: RouterError,
113 }
114
115 #[async_trait]
116 impl<T: CapabilityBound> Routable<T> for ErrorRouter {
117 async fn route(
118 &self,
119 _request: RouteRequest,
120 _target: WeakInstanceToken,
121 ) -> Result<Option<T>, RouterError> {
122 Err(self.not_found_error.clone())
123 }
124
125 async fn route_debug(
126 &self,
127 _request: RouteRequest,
128 _target: WeakInstanceToken,
129 ) -> Result<CapabilitySource, RouterError> {
130 Err(self.not_found_error.clone())
131 }
132 }
133
134 #[derive(Debug)]
138 struct ScopedDictRouter<P: IterablePath + Debug + 'static> {
139 router: Router<Dictionary>,
140 path: P,
141 not_found_error: RoutingError,
142 }
143
144 #[async_trait]
145 impl<P: IterablePath + Debug + 'static, T: CapabilityBound> Routable<T> for ScopedDictRouter<P> {
146 async fn route(
147 &self,
148 request: RouteRequest,
149 target: WeakInstanceToken,
150 ) -> Result<Option<T>, RouterError> {
151 let get_init_request = || request_with_dictionary_replacement(&request);
152
153 let init_request = (get_init_request)()?;
154 match self.router.route(init_request, target.clone()).await? {
155 Some(dict) => {
156 let moniker: ExtendedMoniker = self.not_found_error.clone().into();
157 match dict.get_with_request(&moniker, &self.path, request, target).await {
158 Err(router_error)
159 if let Ok(RoutingError::BedrockNotPresentInDictionary {
160 ..
161 }) = router_error.clone().try_into() =>
162 {
163 Err(self.not_found_error.clone().into())
164 }
165 Err(e) => Err(e),
166 Ok(None) => Ok(None),
167 Ok(Some(cap)) => {
168 let actual_type_name = cap.debug_typename();
169 let cap = T::try_from(cap).map_err(|_| {
170 RoutingError::BedrockWrongCapabilityType {
171 expected: T::debug_typename().into(),
172 actual: actual_type_name.into(),
173 moniker,
174 }
175 })?;
176 Ok(Some(cap))
177 }
178 }
179 }
180 None => Ok(None),
181 }
182 }
183
184 async fn route_debug(
185 &self,
186 request: RouteRequest,
187 target: WeakInstanceToken,
188 ) -> Result<CapabilitySource, RouterError> {
189 let get_init_request = || request_with_dictionary_replacement(&request);
190
191 let init_request = (get_init_request)()?;
195 match self.router.route(init_request, target.clone()).await? {
196 Some(dict) => {
197 let moniker: ExtendedMoniker = self.not_found_error.clone().into();
198 match dict
199 .get_with_request_debug(&moniker, &self.path, request, target)
200 .await
201 {
202 Err(router_error)
203 if let Ok(RoutingError::BedrockNotPresentInDictionary {
204 ..
205 }) = router_error.clone().try_into() =>
206 {
207 Err(self.not_found_error.clone().into())
208 }
209 other_result => other_result,
210 }
211 }
212 None => {
213 let init_request = (get_init_request)()?;
218 self.router.route_debug(init_request, target).await
219 }
220 }
221 }
222 }
223
224 if segments.next().is_none() {
225 let Some(router) = self.get(root).and_then(|cap| Router::<T>::try_from(cap).ok())
227 else {
228 return Router::<T>::new(ErrorRouter { not_found_error: not_found_error.into() });
229 };
230 return router;
231 }
232
233 let Some(cap) = self.get(root) else {
234 return Router::<T>::new(ErrorRouter { not_found_error: not_found_error.into() });
235 };
236 let router = match cap {
237 Capability::Dictionary(d) => Router::<Dictionary>::new_ok(d),
238 Capability::DictionaryRouter(r) => r,
239 _ => {
240 return Router::<T>::new(ErrorRouter { not_found_error: not_found_error.into() });
241 }
242 };
243
244 let mut segments = path.iter_segments();
245 let _ = segments.next().unwrap();
246 let path = RelativePath::from(segments.collect::<Vec<_>>());
247
248 Router::<T>::new(ScopedDictRouter { router, path, not_found_error: not_found_error.into() })
249 }
250
251 fn insert_capability(
252 &self,
253 path: &impl IterablePath,
254 capability: Capability,
255 ) -> Option<Capability> {
256 let mut segments = path.iter_segments();
257 let mut current_name = segments.next().expect("path must be non-empty");
258 let mut current_dict = self.clone();
259 loop {
260 match segments.next() {
261 Some(next_name) => {
262 let sub_dict = {
263 match current_dict.get(current_name) {
264 Some(Capability::Dictionary(dict)) => dict,
265 Some(Capability::DictionaryRouter(preexisting_router)) => {
266 let mut path = vec![next_name];
267 while let Some(name) = segments.next() {
268 path.push(name);
269 }
270 let path = RelativePath::from(path);
271 let new_router = Router::new(AdditiveDictionaryRouter {
272 preexisting_router,
273 path,
274 capability,
275 });
276
277 return current_dict.insert(current_name.into(), new_router.into());
279 }
280 None => {
281 let dict = Dictionary::new();
282 current_dict.insert(
283 current_name.into(),
284 Capability::Dictionary(dict.clone()),
285 );
286 dict
287 }
288 _ => return None,
289 }
290 };
291 current_dict = sub_dict;
292
293 current_name = next_name;
294 }
295 None => {
296 return current_dict.insert(current_name.into(), capability);
297 }
298 }
299 }
300 }
301
302 fn remove_capability(&self, path: &impl IterablePath) -> Option<Capability> {
303 let mut segments = path.iter_segments();
304 let mut current_name = segments.next().expect("path must be non-empty");
305 let mut current_dict = self.clone();
306 loop {
307 match segments.next() {
308 Some(next_name) => {
309 let sub_dict =
310 current_dict.get(current_name).and_then(|value| value.to_dictionary());
311 if sub_dict.is_none() {
312 return None;
314 }
315 current_dict = sub_dict.unwrap();
316 current_name = next_name;
317 }
318 None => {
319 return current_dict.remove(current_name);
320 }
321 }
322 }
323 }
324
325 async fn get_with_request<'a>(
326 &self,
327 moniker: &ExtendedMoniker,
328 path: &'a impl IterablePath,
329 request: RouteRequest,
330 target: WeakInstanceToken,
331 ) -> Result<Option<Capability>, RouterError> {
332 let mut current_dict = self.clone();
333 let num_segments = path.iter_segments().count();
334 for (next_idx, next_name) in path.iter_segments().enumerate() {
335 let capability = current_dict.get(next_name);
337
338 let Some(capability) = capability else {
340 return Err(RoutingError::BedrockNotPresentInDictionary {
341 name: path.iter_segments().join("/"),
342 moniker: moniker.clone(),
343 }
344 .into());
345 };
346
347 if next_idx < num_segments - 1 {
348 match capability {
351 Capability::Dictionary(d) => {
352 current_dict = d;
353 }
354 Capability::DictionaryRouter(r) => {
355 let request = request_with_dictionary_replacement(&request)?;
356 let Some(new_dictionary) = r.route(request, target.clone()).await? else {
357 return Ok(None);
358 };
359 current_dict = new_dictionary;
360 }
361 _ => {
362 return Err(RoutingError::BedrockWrongCapabilityType {
363 expected: Dictionary::debug_typename().into(),
364 actual: capability.debug_typename().into(),
365 moniker: moniker.clone(),
366 }
367 .into());
368 }
369 }
370 continue;
371 }
372
373 match capability {
379 Capability::DictionaryRouter(r) => {
380 return r.route(request, target).await.map(|option| option.map(Into::into));
381 }
382 Capability::ConnectorRouter(r) => {
383 return r.route(request, target).await.map(|option| option.map(Into::into));
384 }
385 Capability::DataRouter(r) => {
386 return r.route(request, target).await.map(|option| option.map(Into::into));
387 }
388 Capability::DirConnectorRouter(r) => {
389 return r.route(request, target).await.map(|option| option.map(Into::into));
390 }
391 other_capability => return Ok(Some(other_capability.into())),
392 };
393 }
394 unreachable!("get_with_request: All cases are handled in the loop");
395 }
396
397 async fn get_with_request_debug<'a>(
398 &self,
399 moniker: &ExtendedMoniker,
400 path: &'a impl IterablePath,
401 request: RouteRequest,
402 target: WeakInstanceToken,
403 ) -> Result<CapabilitySource, RouterError> {
404 let mut current_dict = self.clone();
405 let mut closest_moniker = moniker.clone();
406 let num_segments = path.iter_segments().count();
407 for (next_idx, next_name) in path.iter_segments().enumerate() {
408 let capability = current_dict.get(next_name);
410
411 let Some(capability) = capability else {
413 return Err(RoutingError::BedrockNotPresentInDictionary {
414 name: path.iter_segments().join("/"),
415 moniker: moniker.clone(),
416 }
417 .into());
418 };
419
420 if next_idx < num_segments - 1 {
421 match capability {
424 Capability::Dictionary(d) => {
425 current_dict = d;
426 }
427 Capability::DictionaryRouter(r) => {
428 let req = request_with_dictionary_replacement(&request)?;
433 let maybe_new_dictionary = r.route(req.clone(), target.clone()).await?;
434 let source = r.route_debug(req.clone(), target.clone()).await?;
435
436 let Some(new_dictionary) = maybe_new_dictionary else {
437 return Ok(source);
440 };
441 current_dict = new_dictionary;
442 closest_moniker = source.source_moniker();
443 }
444 _ => {
445 return Err(RoutingError::BedrockWrongCapabilityType {
446 expected: Dictionary::debug_typename().into(),
447 actual: capability.debug_typename().into(),
448 moniker: moniker.clone(),
449 }
450 .into());
451 }
452 }
453 continue;
454 }
455
456 match capability {
462 Capability::DictionaryRouter(r) => {
463 return r.route_debug(request, target).await;
464 }
465 Capability::ConnectorRouter(r) => {
466 return r.route_debug(request, target).await;
467 }
468 Capability::DataRouter(r) => {
469 return r.route_debug(request, target).await;
470 }
471 Capability::DirConnectorRouter(r) => {
472 return r.route_debug(request, target).await;
473 }
474 _other_capability => {
475 let remoted_at_moniker = match closest_moniker {
485 ExtendedMoniker::ComponentInstance(m) => m,
486 ExtendedMoniker::ComponentManager => {
490 panic!("component manager generated a non-router capability")
491 }
492 };
493 let type_name: Option<CapabilityTypeName> = request
494 .build_type_name
495 .as_ref()
496 .map(|s| std::str::FromStr::from_str(s.as_str()))
497 .transpose()
498 .expect("invalid type name");
499 return Ok(CapabilitySource::RemotedAt(RemotedAtSource {
500 moniker: remoted_at_moniker,
501 type_name,
502 }));
503 }
504 };
505 }
506 unreachable!("get_with_request_debug: All cases are handled in the loop");
507 }
508}
509
510pub(super) fn request_with_dictionary_replacement(
516 request: &RouteRequest,
517) -> Result<RouteRequest, RoutingError> {
518 if request == &RouteRequest::default() {
519 return Ok(RouteRequest::default());
520 }
521 let mut request_clone = request.clone();
522 request_clone.build_type_name = Some(CapabilityTypeName::Dictionary.to_string());
523 Ok(request_clone)
524}
525
526struct AdditiveDictionaryRouter {
527 preexisting_router: Router<Dictionary>,
528 path: RelativePath,
529 capability: Capability,
530}
531
532#[async_trait]
533impl Routable<Dictionary> for AdditiveDictionaryRouter {
534 async fn route(
535 &self,
536 request: RouteRequest,
537 target: WeakInstanceToken,
538 ) -> Result<Option<Dictionary>, RouterError> {
539 let dictionary = match self.preexisting_router.route(request, target).await {
540 Ok(Some(dictionary)) => dictionary.shallow_copy(),
541 other_response => return other_response,
542 };
543 let _ = dictionary.insert_capability(&self.path, self.capability.clone());
544 Ok(Some(dictionary))
545 }
546
547 async fn route_debug(
548 &self,
549 request: RouteRequest,
550 target: WeakInstanceToken,
551 ) -> Result<CapabilitySource, RouterError> {
552 self.preexisting_router.route_debug(request, target).await
553 }
554}