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;
18use std::sync::Arc;
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 ) -> Arc<Router<T>>
38 where
39 T: CapabilityBound,
40 Arc<T>: TryFrom<Capability>,
41 Arc<Router<T>>: TryFrom<Capability>,
42 Capability: From<Arc<T>>,
43 Capability: From<Arc<Router<T>>>,
44 Router<T>: CapabilityBound;
45
46 fn insert_capability(
49 &self,
50 path: &impl IterablePath,
51 capability: Capability,
52 ) -> Option<Capability>;
53
54 fn remove_capability(&self, path: &impl IterablePath) -> Option<Capability>;
56
57 async fn get_with_request<'a>(
65 &self,
66 moniker: &ExtendedMoniker,
67 path: &'a impl IterablePath,
68 request: RouteRequest,
69 target: Arc<WeakInstanceToken>,
70 ) -> Result<Option<Capability>, RouterError>;
71
72 async fn get_with_request_debug<'a>(
75 &self,
76 moniker: &ExtendedMoniker,
77 path: &'a impl IterablePath,
78 request: RouteRequest,
79 target: Arc<WeakInstanceToken>,
80 ) -> Result<CapabilitySource, RouterError>;
81}
82
83#[async_trait]
84impl DictExt for Arc<Dictionary> {
85 fn get_capability(&self, path: &impl IterablePath) -> Option<Capability> {
86 let mut segments = path.iter_segments();
87 let Some(mut current_name) = segments.next() else {
88 return Some(Capability::Dictionary(self.clone()));
89 };
90 let mut current_dict = self.clone();
91 loop {
92 match segments.next() {
93 Some(next_name) => {
94 let sub_dict =
95 current_dict.get(current_name).and_then(|value| value.to_dictionary())?;
96 current_dict = sub_dict;
97
98 current_name = next_name;
99 }
100 None => return current_dict.get(current_name),
101 }
102 }
103 }
104
105 fn get_router_or_not_found<T>(
106 &self,
107 path: &impl IterablePath,
108 not_found_error: RoutingError,
109 ) -> Arc<Router<T>>
110 where
111 T: CapabilityBound,
112 Arc<T>: TryFrom<Capability>,
113 Arc<Router<T>>: TryFrom<Capability>,
114 Router<T>: CapabilityBound,
115 Capability: From<Arc<T>>,
116 Capability: From<Arc<Router<T>>>,
117 {
118 let mut segments = path.iter_segments();
119 let root = segments.next().expect("path must be nonempty");
120
121 #[derive(Debug)]
122 struct ErrorRouter {
123 not_found_error: RouterError,
124 }
125
126 #[async_trait]
127 impl<T: CapabilityBound> Routable<T> for ErrorRouter {
128 async fn route(
129 &self,
130 _request: RouteRequest,
131 _target: Arc<WeakInstanceToken>,
132 ) -> Result<Option<Arc<T>>, RouterError> {
133 Err(self.not_found_error.clone())
134 }
135
136 async fn route_debug(
137 &self,
138 _request: RouteRequest,
139 _target: Arc<WeakInstanceToken>,
140 ) -> Result<CapabilitySource, RouterError> {
141 Err(self.not_found_error.clone())
142 }
143 }
144
145 #[derive(Debug)]
149 struct ScopedDictRouter<P: IterablePath + Debug + 'static> {
150 router: Arc<Router<Dictionary>>,
151 path: P,
152 not_found_error: RoutingError,
153 }
154
155 #[async_trait]
156 impl<P: IterablePath + Debug + 'static, T: CapabilityBound> Routable<T> for ScopedDictRouter<P>
157 where
158 Arc<T>: TryFrom<Capability>,
159 Arc<Router<T>>: TryFrom<Capability>,
160 Capability: From<Arc<Router<T>>>,
161 {
162 async fn route(
163 &self,
164 request: RouteRequest,
165 target: Arc<WeakInstanceToken>,
166 ) -> Result<Option<Arc<T>>, RouterError> {
167 let get_init_request = || request_with_dictionary_replacement(&request);
168
169 let init_request = (get_init_request)()?;
170 match self.router.route(init_request, target.clone()).await? {
171 Some(dict) => {
172 let moniker: ExtendedMoniker = self.not_found_error.clone().into();
173 match dict.get_with_request(&moniker, &self.path, request, target).await {
174 Err(router_error)
175 if let Ok(RoutingError::BedrockNotPresentInDictionary {
176 ..
177 }) = router_error.clone().try_into() =>
178 {
179 Err(self.not_found_error.clone().into())
180 }
181 Err(e) => Err(e),
182 Ok(None) => Ok(None),
183 Ok(Some(cap)) => {
184 let actual_type_name = cap.debug_typename();
185 let cap: Arc<T> = cap.try_into().map_err(|_| {
186 RoutingError::BedrockWrongCapabilityType {
187 expected: T::debug_typename().into(),
188 actual: actual_type_name.into(),
189 moniker,
190 }
191 })?;
192 Ok(Some(cap))
193 }
194 }
195 }
196 None => Ok(None),
197 }
198 }
199
200 async fn route_debug(
201 &self,
202 request: RouteRequest,
203 target: Arc<WeakInstanceToken>,
204 ) -> Result<CapabilitySource, RouterError> {
205 let get_init_request = || request_with_dictionary_replacement(&request);
206
207 let init_request = (get_init_request)()?;
211 match self.router.route(init_request, target.clone()).await? {
212 Some(dict) => {
213 let moniker: ExtendedMoniker = self.not_found_error.clone().into();
214 match dict
215 .get_with_request_debug(&moniker, &self.path, request, target)
216 .await
217 {
218 Err(router_error)
219 if let Ok(RoutingError::BedrockNotPresentInDictionary {
220 ..
221 }) = router_error.clone().try_into() =>
222 {
223 Err(self.not_found_error.clone().into())
224 }
225 other_result => other_result,
226 }
227 }
228 None => {
229 let init_request = (get_init_request)()?;
234 self.router.route_debug(init_request, target).await
235 }
236 }
237 }
238 }
239
240 if segments.next().is_none() {
241 let Some(router) = self.get(root).and_then(|cap| cap.try_into().ok()) else {
243 return Router::<T>::new(ErrorRouter { not_found_error: not_found_error.into() });
244 };
245 return router;
246 }
247
248 let Some(cap) = self.get(root) else {
249 return Router::<T>::new(ErrorRouter { not_found_error: not_found_error.into() });
250 };
251 let router = match cap {
252 Capability::Dictionary(d) => Router::<Dictionary>::new_ok(d),
253 Capability::DictionaryRouter(r) => r,
254 _ => {
255 return Router::<T>::new(ErrorRouter { not_found_error: not_found_error.into() });
256 }
257 };
258
259 let mut segments = path.iter_segments();
260 let _ = segments.next().unwrap();
261 let path = RelativePath::from(segments.collect::<Vec<_>>());
262
263 Router::<T>::new(ScopedDictRouter { router, path, not_found_error: not_found_error.into() })
264 }
265
266 fn insert_capability(
267 &self,
268 path: &impl IterablePath,
269 capability: Capability,
270 ) -> Option<Capability> {
271 let mut segments = path.iter_segments();
272 let mut current_name = segments.next().expect("path must be non-empty");
273 let mut current_dict = self.clone();
274 loop {
275 match segments.next() {
276 Some(next_name) => {
277 let sub_dict = {
278 match current_dict.get(current_name) {
279 Some(Capability::Dictionary(dict)) => dict,
280 Some(Capability::DictionaryRouter(preexisting_router)) => {
281 let mut path = vec![next_name];
282 while let Some(name) = segments.next() {
283 path.push(name);
284 }
285 let path = RelativePath::from(path);
286 let new_router = Router::new(AdditiveDictionaryRouter {
287 preexisting_router,
288 path,
289 capability,
290 });
291
292 return current_dict.insert(current_name.into(), new_router.into());
294 }
295 None => {
296 let dict = Dictionary::new();
297 current_dict.insert(
298 current_name.into(),
299 Capability::Dictionary(dict.clone()),
300 );
301 dict
302 }
303 _ => return None,
304 }
305 };
306 current_dict = sub_dict;
307
308 current_name = next_name;
309 }
310 None => {
311 return current_dict.insert(current_name.into(), capability);
312 }
313 }
314 }
315 }
316
317 fn remove_capability(&self, path: &impl IterablePath) -> Option<Capability> {
318 let mut segments = path.iter_segments();
319 let mut current_name = segments.next().expect("path must be non-empty");
320 let mut current_dict = self.clone();
321 loop {
322 match segments.next() {
323 Some(next_name) => {
324 let sub_dict =
325 current_dict.get(current_name).and_then(|value| value.to_dictionary());
326 if sub_dict.is_none() {
327 return None;
329 }
330 current_dict = sub_dict.unwrap();
331 current_name = next_name;
332 }
333 None => {
334 return current_dict.remove(current_name);
335 }
336 }
337 }
338 }
339
340 async fn get_with_request<'a>(
341 &self,
342 moniker: &ExtendedMoniker,
343 path: &'a impl IterablePath,
344 request: RouteRequest,
345 target: Arc<WeakInstanceToken>,
346 ) -> Result<Option<Capability>, RouterError> {
347 let mut current_dict = self.clone();
348 let num_segments = path.iter_segments().count();
349 for (next_idx, next_name) in path.iter_segments().enumerate() {
350 let capability = current_dict.get(next_name);
352
353 let Some(capability) = capability else {
355 return Err(RoutingError::BedrockNotPresentInDictionary {
356 name: path.iter_segments().join("/"),
357 moniker: moniker.clone(),
358 }
359 .into());
360 };
361
362 if next_idx < num_segments - 1 {
363 match capability {
366 Capability::Dictionary(d) => {
367 current_dict = d;
368 }
369 Capability::DictionaryRouter(r) => {
370 let request = request_with_dictionary_replacement(&request)?;
371 let Some(new_dictionary) = r.route(request, target.clone()).await? else {
372 return Ok(None);
373 };
374 current_dict = new_dictionary;
375 }
376 _ => {
377 return Err(RoutingError::BedrockWrongCapabilityType {
378 expected: Dictionary::debug_typename().into(),
379 actual: capability.debug_typename().into(),
380 moniker: moniker.clone(),
381 }
382 .into());
383 }
384 }
385 continue;
386 }
387
388 match capability {
394 Capability::DictionaryRouter(r) => {
395 return r.route(request, target).await.map(|option| option.map(Into::into));
396 }
397 Capability::ConnectorRouter(r) => {
398 return r.route(request, target).await.map(|option| option.map(Into::into));
399 }
400 Capability::DataRouter(r) => {
401 return r.route(request, target).await.map(|option| option.map(Into::into));
402 }
403 Capability::DirConnectorRouter(r) => {
404 return r.route(request, target).await.map(|option| option.map(Into::into));
405 }
406 other_capability => return Ok(Some(other_capability.into())),
407 };
408 }
409 unreachable!("get_with_request: All cases are handled in the loop");
410 }
411
412 async fn get_with_request_debug<'a>(
413 &self,
414 moniker: &ExtendedMoniker,
415 path: &'a impl IterablePath,
416 request: RouteRequest,
417 target: Arc<WeakInstanceToken>,
418 ) -> Result<CapabilitySource, RouterError> {
419 let mut current_dict = self.clone();
420 let mut closest_moniker = moniker.clone();
421 let num_segments = path.iter_segments().count();
422 for (next_idx, next_name) in path.iter_segments().enumerate() {
423 let capability = current_dict.get(next_name);
425
426 let Some(capability) = capability else {
428 return Err(RoutingError::BedrockNotPresentInDictionary {
429 name: path.iter_segments().join("/"),
430 moniker: moniker.clone(),
431 }
432 .into());
433 };
434
435 if next_idx < num_segments - 1 {
436 match capability {
439 Capability::Dictionary(d) => {
440 current_dict = d;
441 }
442 Capability::DictionaryRouter(r) => {
443 let req = request_with_dictionary_replacement(&request)?;
448 let maybe_new_dictionary = r.route(req.clone(), target.clone()).await?;
449 let source = r.route_debug(req.clone(), target.clone()).await?;
450
451 let Some(new_dictionary) = maybe_new_dictionary else {
452 return Ok(source);
455 };
456 current_dict = new_dictionary;
457 closest_moniker = source.source_moniker();
458 }
459 _ => {
460 return Err(RoutingError::BedrockWrongCapabilityType {
461 expected: Dictionary::debug_typename().into(),
462 actual: capability.debug_typename().into(),
463 moniker: moniker.clone(),
464 }
465 .into());
466 }
467 }
468 continue;
469 }
470
471 match capability {
477 Capability::DictionaryRouter(r) => {
478 return r.route_debug(request, target).await;
479 }
480 Capability::ConnectorRouter(r) => {
481 return r.route_debug(request, target).await;
482 }
483 Capability::DataRouter(r) => {
484 return r.route_debug(request, target).await;
485 }
486 Capability::DirConnectorRouter(r) => {
487 return r.route_debug(request, target).await;
488 }
489 _other_capability => {
490 let remoted_at_moniker = match closest_moniker {
500 ExtendedMoniker::ComponentInstance(m) => m,
501 ExtendedMoniker::ComponentManager => {
505 panic!("component manager generated a non-router capability")
506 }
507 };
508 let type_name: Option<CapabilityTypeName> = request
509 .build_type_name
510 .as_ref()
511 .map(|s| std::str::FromStr::from_str(s.as_str()))
512 .transpose()
513 .expect("invalid type name");
514 return Ok(CapabilitySource::RemotedAt(RemotedAtSource {
515 moniker: remoted_at_moniker,
516 type_name,
517 }));
518 }
519 };
520 }
521 unreachable!("get_with_request_debug: All cases are handled in the loop");
522 }
523}
524
525pub(super) fn request_with_dictionary_replacement(
531 request: &RouteRequest,
532) -> Result<RouteRequest, RoutingError> {
533 if request == &RouteRequest::default() {
534 return Ok(RouteRequest::default());
535 }
536 let mut request_clone = request.clone();
537 request_clone.build_type_name = Some(CapabilityTypeName::Dictionary.to_string());
538 Ok(request_clone)
539}
540
541struct AdditiveDictionaryRouter {
542 preexisting_router: Arc<Router<Dictionary>>,
543 path: RelativePath,
544 capability: Capability,
545}
546
547#[async_trait]
548impl Routable<Dictionary> for AdditiveDictionaryRouter {
549 async fn route(
550 &self,
551 request: RouteRequest,
552 target: Arc<WeakInstanceToken>,
553 ) -> Result<Option<Arc<Dictionary>>, RouterError> {
554 let dictionary = match self.preexisting_router.route(request, target).await {
555 Ok(Some(dictionary)) => dictionary.shallow_copy(),
556 other_response => return other_response,
557 };
558 let _ = dictionary.insert_capability(&self.path, self.capability.clone());
559 Ok(Some(dictionary))
560 }
561
562 async fn route_debug(
563 &self,
564 request: RouteRequest,
565 target: Arc<WeakInstanceToken>,
566 ) -> Result<CapabilitySource, RouterError> {
567 self.preexisting_router.route_debug(request, target).await
568 }
569}