1use crate::bedrock::aggregate_router::{AggregateRouterFn, AggregateSource};
6use crate::bedrock::request_metadata::Metadata;
7use crate::bedrock::structured_dict::{
8 ComponentEnvironment, ComponentInput, ComponentOutput, StructuredDictMap,
9};
10use crate::bedrock::with_service_renames_and_filter::WithServiceRenamesAndFilter;
11use crate::capability_source::{
12 AggregateCapability, AggregateInstance, AggregateMember, AnonymizedAggregateSource,
13 CapabilitySource, FilteredAggregateProviderSource, InternalCapability, VoidSource,
14};
15use crate::component_instance::{ComponentInstanceInterface, WeakComponentInstanceInterface};
16use crate::error::{ErrorReporter, RouteRequestErrorInfo, RoutingError};
17use crate::{DictExt, LazyGet, Sources, WithPorcelain};
18use async_trait::async_trait;
19use cm_rust::{
20 CapabilityTypeName, ExposeDeclCommon, OfferDeclCommon, SourceName, SourcePath, UseDeclCommon,
21};
22use cm_types::{Availability, IterablePath, Name, SeparatedPath};
23use fidl::endpoints::DiscoverableProtocolMarker;
24use itertools::Itertools;
25use lazy_static::lazy_static;
26use log::warn;
27use moniker::{ChildName, Moniker};
28use router_error::RouterError;
29use sandbox::{
30 Capability, CapabilityBound, Connector, Data, Dict, DirEntry, Request, Routable, Router,
31 RouterResponse,
32};
33use std::collections::HashMap;
34use std::fmt::Debug;
35use std::sync::Arc;
36use {fidl_fuchsia_component_decl as fdecl, fidl_fuchsia_sys2 as fsys};
37
38lazy_static! {
39 static ref NAMESPACE: Name = "namespace".parse().unwrap();
40 static ref RUNNER: Name = "runner".parse().unwrap();
41 static ref CONFIG: Name = "config".parse().unwrap();
42}
43
44#[derive(Debug, Clone)]
46pub struct ProgramInput {
47 inner: Dict,
52}
53
54impl Default for ProgramInput {
55 fn default() -> Self {
56 Self::new(Dict::new(), None, Dict::new())
57 }
58}
59
60impl From<ProgramInput> for Dict {
61 fn from(program_input: ProgramInput) -> Self {
62 program_input.inner
63 }
64}
65
66impl ProgramInput {
67 pub fn new(namespace: Dict, runner: Option<Router<Connector>>, config: Dict) -> Self {
68 let inner = Dict::new();
69 inner.insert(NAMESPACE.clone(), namespace.into()).unwrap();
70 if let Some(runner) = runner {
71 inner.insert(RUNNER.clone(), runner.into()).unwrap();
72 }
73 inner.insert(CONFIG.clone(), config.into()).unwrap();
74 ProgramInput { inner }
75 }
76
77 pub fn namespace(&self) -> Dict {
79 let cap = self.inner.get(&*NAMESPACE).expect("capabilities must be cloneable").unwrap();
80 let Capability::Dictionary(dict) = cap else {
81 unreachable!("namespace entry must be a dict: {cap:?}");
82 };
83 dict
84 }
85
86 pub fn runner(&self) -> Option<Router<Connector>> {
88 let cap = self.inner.get(&*RUNNER).expect("capabilities must be cloneable");
89 match cap {
90 None => None,
91 Some(Capability::ConnectorRouter(r)) => Some(r),
92 cap => unreachable!("runner entry must be a router: {cap:?}"),
93 }
94 }
95
96 fn set_runner(&self, capability: Capability) {
97 self.inner.insert(RUNNER.clone(), capability).unwrap()
98 }
99
100 pub fn config(&self) -> Dict {
102 let cap = self.inner.get(&*CONFIG).expect("capabilities must be cloneable").unwrap();
103 let Capability::Dictionary(dict) = cap else {
104 unreachable!("config entry must be a dict: {cap:?}");
105 };
106 dict
107 }
108}
109
110#[derive(Default, Debug, Clone)]
113pub struct ComponentSandbox {
114 pub component_input: ComponentInput,
116
117 pub component_output: ComponentOutput,
119
120 pub program_input: ProgramInput,
122
123 pub program_output_dict: Dict,
125
126 pub framework_dict: Dict,
128
129 pub capability_sourced_capabilities_dict: Dict,
132
133 pub declared_dictionaries: Dict,
135
136 pub child_inputs: StructuredDictMap<ComponentInput>,
139
140 pub collection_inputs: StructuredDictMap<ComponentInput>,
144}
145
146impl ComponentSandbox {
147 pub fn append(&self, sandbox: &ComponentSandbox) {
150 let ComponentSandbox {
153 component_input,
154 component_output,
155 program_input,
156 program_output_dict,
157 framework_dict,
158 capability_sourced_capabilities_dict,
159 declared_dictionaries,
160 child_inputs,
161 collection_inputs,
162 } = sandbox;
163 for (copy_from, copy_to) in [
164 (&component_input.capabilities(), &self.component_input.capabilities()),
165 (&component_input.environment().debug(), &self.component_input.environment().debug()),
166 (
167 &component_input.environment().runners(),
168 &self.component_input.environment().runners(),
169 ),
170 (
171 &component_input.environment().resolvers(),
172 &self.component_input.environment().resolvers(),
173 ),
174 (&component_output.capabilities(), &self.component_output.capabilities()),
175 (&component_output.framework(), &self.component_output.framework()),
176 (&program_input.namespace(), &self.program_input.namespace()),
177 (&program_input.config(), &self.program_input.config()),
178 (&program_output_dict, &self.program_output_dict),
179 (&framework_dict, &self.framework_dict),
180 (&capability_sourced_capabilities_dict, &self.capability_sourced_capabilities_dict),
181 (&declared_dictionaries, &self.declared_dictionaries),
182 ] {
183 copy_to.append(copy_from).expect("sandbox capability is not cloneable");
184 }
185 if let Some(runner_router) = program_input.runner() {
186 self.program_input.set_runner(runner_router.into());
187 }
188 self.child_inputs.append(child_inputs).unwrap();
189 self.collection_inputs.append(collection_inputs).unwrap();
190 }
191}
192
193pub fn build_component_sandbox<C: ComponentInstanceInterface + 'static>(
196 component: &Arc<C>,
197 child_component_output_dictionary_routers: HashMap<ChildName, Router<Dict>>,
198 decl: &cm_rust::ComponentDecl,
199 component_input: ComponentInput,
200 program_output_dict: Dict,
201 framework_dict: Dict,
202 capability_sourced_capabilities_dict: Dict,
203 declared_dictionaries: Dict,
204 error_reporter: impl ErrorReporter,
205 aggregate_router_fn: &AggregateRouterFn<C>,
206) -> ComponentSandbox {
207 let component_output = ComponentOutput::new();
208 let program_input = ProgramInput::default();
209 let environments: StructuredDictMap<ComponentEnvironment> = Default::default();
210 let child_inputs: StructuredDictMap<ComponentInput> = Default::default();
211 let collection_inputs: StructuredDictMap<ComponentInput> = Default::default();
212
213 for environment_decl in &decl.environments {
214 environments
215 .insert(
216 environment_decl.name.clone(),
217 build_environment(
218 component,
219 &child_component_output_dictionary_routers,
220 &component_input,
221 environment_decl,
222 &program_output_dict,
223 &error_reporter,
224 ),
225 )
226 .ok();
227 }
228
229 for child in &decl.children {
230 let environment;
231 if let Some(environment_name) = child.environment.as_ref() {
232 environment = environments.get(environment_name).expect(
233 "child references nonexistent environment, \
234 this should be prevented in manifest validation",
235 );
236 } else {
237 environment = component_input.environment();
238 }
239 let input = ComponentInput::new(environment);
240 let name = Name::new(child.name.as_str()).expect("child is static so name is not long");
241 child_inputs.insert(name, input).ok();
242 }
243
244 for collection in &decl.collections {
245 let environment;
246 if let Some(environment_name) = collection.environment.as_ref() {
247 environment = environments.get(environment_name).expect(
248 "collection references nonexistent environment, \
249 this should be prevented in manifest validation",
250 )
251 } else {
252 environment = component_input.environment();
253 }
254 let input = ComponentInput::new(environment);
255 collection_inputs.insert(collection.name.clone(), input).ok();
256 }
257
258 for use_ in decl.uses.iter() {
259 match use_ {
260 cm_rust::UseDecl::Service(_)
261 if matches!(use_.source(), cm_rust::UseSource::Collection(_)) =>
262 {
263 let cm_rust::UseSource::Collection(collection_name) = use_.source() else {
264 unreachable!();
265 };
266 let availability = *use_.availability();
267 let aggregate = (aggregate_router_fn)(
268 component.clone(),
269 vec![AggregateSource::Collection { collection_name: collection_name.clone() }],
270 CapabilitySource::AnonymizedAggregate(AnonymizedAggregateSource {
271 capability: AggregateCapability::Service(use_.source_name().clone()),
272 moniker: component.moniker().clone(),
273 members: vec![AggregateMember::try_from(use_).unwrap()],
274 sources: Sources::new(cm_rust::CapabilityTypeName::Service),
275 instances: vec![],
276 }),
277 CapabilityTypeName::Service,
278 availability,
279 )
280 .with_porcelain_with_default(CapabilityTypeName::Service)
281 .availability(availability)
282 .target(component)
283 .error_info(use_)
284 .error_reporter(error_reporter.clone())
285 .build();
286 if let Err(e) = program_input
287 .namespace()
288 .insert_capability(use_.path().unwrap(), aggregate.into())
289 {
290 warn!("failed to insert {} in program input dict: {e:?}", use_.path().unwrap())
291 }
292 }
293 cm_rust::UseDecl::Service(_) => extend_dict_with_use::<DirEntry, _>(
294 component,
295 &child_component_output_dictionary_routers,
296 &component_input,
297 &program_input,
298 &program_output_dict,
299 &framework_dict,
300 &capability_sourced_capabilities_dict,
301 use_,
302 error_reporter.clone(),
303 ),
304 cm_rust::UseDecl::Protocol(_) | cm_rust::UseDecl::Runner(_) => {
305 extend_dict_with_use::<Connector, _>(
306 component,
307 &child_component_output_dictionary_routers,
308 &component_input,
309 &program_input,
310 &program_output_dict,
311 &framework_dict,
312 &capability_sourced_capabilities_dict,
313 use_,
314 error_reporter.clone(),
315 )
316 }
317 cm_rust::UseDecl::Config(config) => extend_dict_with_config_use(
318 component,
319 &child_component_output_dictionary_routers,
320 &component_input,
321 &program_input,
322 &program_output_dict,
323 config,
324 error_reporter.clone(),
325 ),
326 _ => (),
327 }
328 }
329
330 if !decl.uses.iter().any(|u| matches!(u, cm_rust::UseDecl::Runner(_))) {
334 if let Some(runner_name) = decl.program.as_ref().and_then(|p| p.runner.as_ref()) {
335 extend_dict_with_use::<Connector, _>(
336 component,
337 &child_component_output_dictionary_routers,
338 &component_input,
339 &program_input,
340 &program_output_dict,
341 &framework_dict,
342 &capability_sourced_capabilities_dict,
343 &cm_rust::UseDecl::Runner(cm_rust::UseRunnerDecl {
344 source: cm_rust::UseSource::Environment,
345 source_name: runner_name.clone(),
346 source_dictionary: Default::default(),
347 }),
348 error_reporter.clone(),
349 );
350 }
351 }
352
353 for offer_bundle in group_offer_aggregates(&decl.offers).into_iter() {
354 let first_offer = offer_bundle.first().unwrap();
355 let get_target_dict = || match first_offer.target() {
356 cm_rust::OfferTarget::Child(child_ref) => {
357 assert!(child_ref.collection.is_none(), "unexpected dynamic offer target");
358 let child_name = Name::new(child_ref.name.as_str())
359 .expect("child is static so name is not long");
360 if child_inputs.get(&child_name).is_none() {
361 child_inputs.insert(child_name.clone(), Default::default()).ok();
362 }
363 child_inputs
364 .get(&child_name)
365 .expect("component input was just added")
366 .capabilities()
367 }
368 cm_rust::OfferTarget::Collection(name) => {
369 if collection_inputs.get(&name).is_none() {
370 collection_inputs.insert(name.clone(), Default::default()).ok();
371 }
372 collection_inputs
373 .get(&name)
374 .expect("collection input was just added")
375 .capabilities()
376 }
377 cm_rust::OfferTarget::Capability(name) => {
378 let dict = match declared_dictionaries
379 .get(name)
380 .expect("dictionaries must be cloneable")
381 {
382 Some(dict) => dict,
383 None => {
384 let dict = Dict::new();
385 declared_dictionaries
386 .insert(name.clone(), Capability::Dictionary(dict.clone()))
387 .ok();
388 Capability::Dictionary(dict)
389 }
390 };
391 let Capability::Dictionary(dict) = dict else {
392 panic!("wrong type in dict");
393 };
394 dict
395 }
396 };
397 match first_offer {
398 cm_rust::OfferDecl::Service(_)
399 if offer_bundle.len() == 1
400 && !matches!(first_offer.source(), cm_rust::OfferSource::Collection(_)) =>
401 {
402 extend_dict_with_offer::<DirEntry, _>(
403 component,
404 &child_component_output_dictionary_routers,
405 &component_input,
406 &program_output_dict,
407 &framework_dict,
408 &capability_sourced_capabilities_dict,
409 first_offer,
410 &(get_target_dict)(),
411 error_reporter.clone(),
412 );
413 }
414 cm_rust::OfferDecl::Service(_) => {
415 let aggregate_router = new_aggregate_router_from_service_offers(
416 &offer_bundle,
417 component,
418 &child_component_output_dictionary_routers,
419 &component_input,
420 &program_output_dict,
421 &framework_dict,
422 &capability_sourced_capabilities_dict,
423 error_reporter.clone(),
424 aggregate_router_fn,
425 );
426 (get_target_dict)()
427 .insert(first_offer.target_name().clone(), aggregate_router.into())
428 .expect("failed to insert capability into target dict")
429 }
430 cm_rust::OfferDecl::Config(_) => extend_dict_with_offer::<Data, _>(
431 component,
432 &child_component_output_dictionary_routers,
433 &component_input,
434 &program_output_dict,
435 &framework_dict,
436 &capability_sourced_capabilities_dict,
437 first_offer,
438 &(get_target_dict)(),
439 error_reporter.clone(),
440 ),
441 cm_rust::OfferDecl::Dictionary(_) => extend_dict_with_offer::<Dict, _>(
442 component,
443 &child_component_output_dictionary_routers,
444 &component_input,
445 &program_output_dict,
446 &framework_dict,
447 &capability_sourced_capabilities_dict,
448 first_offer,
449 &(get_target_dict)(),
450 error_reporter.clone(),
451 ),
452 cm_rust::OfferDecl::Protocol(_)
453 | cm_rust::OfferDecl::Runner(_)
454 | cm_rust::OfferDecl::Resolver(_) => extend_dict_with_offer::<Connector, _>(
455 component,
456 &child_component_output_dictionary_routers,
457 &component_input,
458 &program_output_dict,
459 &framework_dict,
460 &capability_sourced_capabilities_dict,
461 first_offer,
462 &(get_target_dict)(),
463 error_reporter.clone(),
464 ),
465 _ => {}
466 }
467 }
468
469 for expose_bundle in group_expose_aggregates(&decl.exposes).into_iter() {
470 let first_expose = expose_bundle.first().unwrap();
471 match first_expose {
472 cm_rust::ExposeDecl::Service(_)
473 if expose_bundle.len() == 1
474 && !matches!(first_expose.source(), cm_rust::ExposeSource::Collection(_)) =>
475 {
476 extend_dict_with_expose::<DirEntry, _>(
477 component,
478 &child_component_output_dictionary_routers,
479 &program_output_dict,
480 &framework_dict,
481 &capability_sourced_capabilities_dict,
482 first_expose,
483 &component_output,
484 error_reporter.clone(),
485 );
486 }
487 cm_rust::ExposeDecl::Service(_) => {
488 let mut aggregate_sources = vec![];
489 let temp_component_output = ComponentOutput::new();
490 for expose in expose_bundle.iter() {
491 extend_dict_with_expose::<DirEntry, _>(
492 component,
493 &child_component_output_dictionary_routers,
494 &program_output_dict,
495 &framework_dict,
496 &capability_sourced_capabilities_dict,
497 expose,
498 &temp_component_output,
499 error_reporter.clone(),
500 );
501 match temp_component_output.capabilities().remove(first_expose.target_name()) {
502 Some(Capability::DirEntryRouter(router)) => {
503 let source_instance = match expose.source() {
504 cm_rust::ExposeSource::Self_ => AggregateInstance::Self_,
505 cm_rust::ExposeSource::Child(name) => AggregateInstance::Child(
506 moniker::ChildName::new(name.clone().to_long(), None),
507 ),
508 other_source => {
509 warn!(
510 "unsupported source found in expose aggregate: {:?}",
511 other_source
512 );
513 continue;
514 }
515 };
516 aggregate_sources
517 .push(AggregateSource::DirectoryRouter { source_instance, router })
518 }
519 None => match expose.source() {
520 cm_rust::ExposeSource::Collection(collection_name) => {
521 aggregate_sources.push(AggregateSource::Collection {
522 collection_name: collection_name.clone(),
523 });
524 }
525 _ => continue,
526 },
527 other_value => panic!("unexpected dictionary entry: {:?}", other_value),
528 }
529 }
530 let availability = *first_expose.availability();
531 let aggregate = (aggregate_router_fn)(
532 component.clone(),
533 aggregate_sources,
534 CapabilitySource::AnonymizedAggregate(AnonymizedAggregateSource {
535 capability: AggregateCapability::Service(
536 first_expose.target_name().clone(),
537 ),
538 moniker: component.moniker().clone(),
539 members: expose_bundle
540 .iter()
541 .filter_map(|e| AggregateMember::try_from(*e).ok())
542 .collect(),
543 sources: Sources::new(cm_rust::CapabilityTypeName::Service),
544 instances: vec![],
545 }),
546 CapabilityTypeName::Service,
547 availability,
548 );
549 component_output
550 .capabilities()
551 .insert(first_expose.target_name().clone(), aggregate.into())
552 .expect("failed to insert capability into target dict")
553 }
554 cm_rust::ExposeDecl::Config(_) => extend_dict_with_expose::<Data, _>(
555 component,
556 &child_component_output_dictionary_routers,
557 &program_output_dict,
558 &framework_dict,
559 &capability_sourced_capabilities_dict,
560 first_expose,
561 &component_output,
562 error_reporter.clone(),
563 ),
564 cm_rust::ExposeDecl::Dictionary(_) => extend_dict_with_expose::<Dict, _>(
565 component,
566 &child_component_output_dictionary_routers,
567 &program_output_dict,
568 &framework_dict,
569 &capability_sourced_capabilities_dict,
570 first_expose,
571 &component_output,
572 error_reporter.clone(),
573 ),
574 cm_rust::ExposeDecl::Protocol(_)
575 | cm_rust::ExposeDecl::Runner(_)
576 | cm_rust::ExposeDecl::Resolver(_) => extend_dict_with_expose::<Connector, _>(
577 component,
578 &child_component_output_dictionary_routers,
579 &program_output_dict,
580 &framework_dict,
581 &capability_sourced_capabilities_dict,
582 first_expose,
583 &component_output,
584 error_reporter.clone(),
585 ),
586 _ => {}
587 }
588 }
589
590 ComponentSandbox {
591 component_input,
592 component_output,
593 program_input,
594 program_output_dict,
595 framework_dict,
596 capability_sourced_capabilities_dict,
597 declared_dictionaries,
598 child_inputs,
599 collection_inputs,
600 }
601}
602
603fn new_aggregate_router_from_service_offers<C: ComponentInstanceInterface + 'static>(
604 offer_bundle: &Vec<&cm_rust::OfferDecl>,
605 component: &Arc<C>,
606 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dict>>,
607 component_input: &ComponentInput,
608 program_output_dict: &Dict,
609 framework_dict: &Dict,
610 capability_sourced_capabilities_dict: &Dict,
611 error_reporter: impl ErrorReporter,
612 aggregate_router_fn: &AggregateRouterFn<C>,
613) -> Router<DirEntry> {
614 let mut aggregate_sources = vec![];
615 let dict_for_source_router = Dict::new();
616 let source = new_aggregate_capability_source(component.moniker().clone(), offer_bundle.clone());
617 for offer in offer_bundle.iter() {
618 if matches!(&source, &CapabilitySource::FilteredAggregateProvider(_)) {
619 if let cm_rust::OfferDecl::Service(offer_service_decl) = offer {
620 if offer_service_decl
621 .source_instance_filter
622 .as_ref()
623 .and_then(|v| v.first())
624 .is_none()
625 && offer_service_decl
626 .renamed_instances
627 .as_ref()
628 .and_then(|v| v.first())
629 .is_none()
630 {
631 continue;
635 }
636 }
637 }
638 extend_dict_with_offer::<DirEntry, _>(
639 component,
640 &child_component_output_dictionary_routers,
641 &component_input,
642 &program_output_dict,
643 &framework_dict,
644 &capability_sourced_capabilities_dict,
645 offer,
646 &dict_for_source_router,
647 error_reporter.clone(),
648 );
649 match dict_for_source_router.remove(offer.target_name()) {
650 Some(Capability::DirEntryRouter(router)) => {
651 let source_instance = match offer.source() {
652 cm_rust::OfferSource::Self_ => AggregateInstance::Self_,
653 cm_rust::OfferSource::Parent => AggregateInstance::Parent,
654 cm_rust::OfferSource::Child(child_ref) => {
655 AggregateInstance::Child(moniker::ChildName::new(
656 child_ref.name.clone(),
657 child_ref.collection.clone(),
658 ))
659 }
660 other_source => {
661 warn!("unsupported source found in offer aggregate: {:?}", other_source);
662 continue;
663 }
664 };
665 aggregate_sources.push(AggregateSource::DirectoryRouter { source_instance, router })
666 }
667 None => match offer.source() {
668 cm_rust::OfferSource::Collection(collection_name) => {
672 aggregate_sources.push(AggregateSource::Collection {
673 collection_name: collection_name.clone(),
674 });
675 }
676 _ => continue,
677 },
678 other => warn!("found unexpected entry in dictionary: {:?}", other),
679 }
680 }
681 let availability = *offer_bundle.first().unwrap().availability();
682 (aggregate_router_fn)(
683 component.clone(),
684 aggregate_sources,
685 source,
686 CapabilityTypeName::Service,
687 availability,
688 )
689}
690
691fn new_aggregate_capability_source(
692 moniker: Moniker,
693 offers: Vec<&cm_rust::OfferDecl>,
694) -> CapabilitySource {
695 let offer_service_decls = offers
696 .iter()
697 .map(|o| match o {
698 cm_rust::OfferDecl::Service(o) => o,
699 _ => panic!("cannot aggregate non-service capabilities, manifest validation should prevent this"),
700 }).collect::<Vec<_>>();
701 let is_filtered_offer = offer_service_decls.iter().any(|o| {
703 o.source_instance_filter.as_ref().map(|v| !v.is_empty()).unwrap_or(false)
704 || o.renamed_instances.as_ref().map(|v| !v.is_empty()).unwrap_or(false)
705 });
706 let capability =
707 AggregateCapability::Service(offer_service_decls.first().unwrap().target_name.clone());
708 if is_filtered_offer {
709 CapabilitySource::FilteredAggregateProvider(FilteredAggregateProviderSource {
710 capability,
711 moniker,
712 offer_service_decls: offer_service_decls.into_iter().cloned().collect(),
713 sources: Sources::new(cm_rust::CapabilityTypeName::Service).component().collection(),
714 })
715 } else {
716 let members = offers.iter().filter_map(|o| AggregateMember::try_from(*o).ok()).collect();
717 CapabilitySource::AnonymizedAggregate(AnonymizedAggregateSource {
718 capability,
719 moniker,
720 members,
721 sources: Sources::new(cm_rust::CapabilityTypeName::Service).component().collection(),
722 instances: vec![],
723 })
724 }
725}
726
727fn group_offer_aggregates(offers: &Vec<cm_rust::OfferDecl>) -> Vec<Vec<&cm_rust::OfferDecl>> {
731 let mut groupings = HashMap::new();
732 for offer in offers.iter() {
733 groupings.entry((offer.target(), offer.target_name())).or_insert(vec![]).push(offer);
734 }
735 groupings.into_iter().map(|(_key, grouping)| grouping).collect()
736}
737
738fn group_expose_aggregates(exposes: &Vec<cm_rust::ExposeDecl>) -> Vec<Vec<&cm_rust::ExposeDecl>> {
740 let mut groupings = HashMap::new();
741 for expose in exposes.iter() {
742 groupings.entry((expose.target(), expose.target_name())).or_insert(vec![]).push(expose);
743 }
744 groupings.into_iter().map(|(_key, grouping)| grouping).collect()
745}
746
747fn build_environment<C: ComponentInstanceInterface + 'static>(
748 component: &Arc<C>,
749 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dict>>,
750 component_input: &ComponentInput,
751 environment_decl: &cm_rust::EnvironmentDecl,
752 program_output_dict: &Dict,
753 error_reporter: &impl ErrorReporter,
754) -> ComponentEnvironment {
755 let mut environment = ComponentEnvironment::new();
756 if environment_decl.extends == fdecl::EnvironmentExtends::Realm {
757 if let Ok(e) = component_input.environment().shallow_copy() {
758 environment = e;
759 } else {
760 warn!("failed to copy component_input.environment");
761 }
762 }
763 let debug = environment_decl.debug_capabilities.iter().map(|debug_registration| {
764 let cm_rust::DebugRegistration::Protocol(debug) = debug_registration;
765 (
766 &debug.source_name,
767 debug.target_name.clone(),
768 &debug.source,
769 CapabilityTypeName::Protocol,
770 RouteRequestErrorInfo::from(debug_registration),
771 )
772 });
773 let runners = environment_decl.runners.iter().map(|runner| {
774 (
775 &runner.source_name,
776 runner.target_name.clone(),
777 &runner.source,
778 CapabilityTypeName::Runner,
779 RouteRequestErrorInfo::from(runner),
780 )
781 });
782 let resolvers = environment_decl.resolvers.iter().map(|resolver| {
783 (
784 &resolver.resolver,
785 Name::new(&resolver.scheme).unwrap(),
786 &resolver.source,
787 CapabilityTypeName::Resolver,
788 RouteRequestErrorInfo::from(resolver),
789 )
790 });
791 let moniker = component.moniker();
792 for (source_name, target_name, source, porcelain_type, route_request) in
793 debug.chain(runners).chain(resolvers)
794 {
795 let source_path =
796 SeparatedPath { dirname: Default::default(), basename: source_name.clone() };
797 let router: Router<Connector> = match &source {
798 cm_rust::RegistrationSource::Parent => {
799 use_from_parent_router::<Connector>(component_input, source_path, moniker)
800 }
801 cm_rust::RegistrationSource::Self_ => program_output_dict
802 .get_router_or_not_found::<Connector>(
803 &source_path,
804 RoutingError::use_from_self_not_found(
805 moniker,
806 source_path.iter_segments().join("/"),
807 ),
808 ),
809 cm_rust::RegistrationSource::Child(child_name) => {
810 let child_name = ChildName::parse(child_name).expect("invalid child name");
811 let Some(child_component_output) =
812 child_component_output_dictionary_routers.get(&child_name)
813 else {
814 continue;
815 };
816 child_component_output.clone().lazy_get(
817 source_path,
818 RoutingError::use_from_child_expose_not_found(
819 &child_name,
820 moniker,
821 source_name.clone(),
822 ),
823 )
824 }
825 };
826 let router = router
827 .with_porcelain_no_default(porcelain_type)
828 .availability(Availability::Required)
829 .target(component)
830 .error_info(route_request)
831 .error_reporter(error_reporter.clone());
832 let dict_to_insert_to = match porcelain_type {
833 CapabilityTypeName::Protocol => environment.debug(),
834 CapabilityTypeName::Runner => environment.runners(),
835 CapabilityTypeName::Resolver => environment.resolvers(),
836 c => panic!("unexpected capability type {}", c),
837 };
838 match dict_to_insert_to.insert_capability(&target_name, router.into()) {
839 Ok(()) => (),
840 Err(_e) => {
841 }
845 }
846 }
847 environment
848}
849
850pub fn extend_dict_with_offers<C: ComponentInstanceInterface + 'static>(
852 component: &Arc<C>,
853 static_offers: &Vec<cm_rust::OfferDecl>,
854 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dict>>,
855 component_input: &ComponentInput,
856 dynamic_offers: &Vec<cm_rust::OfferDecl>,
857 program_output_dict: &Dict,
858 framework_dict: &Dict,
859 capability_sourced_capabilities_dict: &Dict,
860 target_input: &ComponentInput,
861 error_reporter: impl ErrorReporter,
862 aggregate_router_fn: &AggregateRouterFn<C>,
863) {
864 for offer_bundle in group_offer_aggregates(dynamic_offers).into_iter() {
865 let first_offer = offer_bundle.first().unwrap();
866 match first_offer {
867 cm_rust::OfferDecl::Service(_) => {
868 let static_offer_bundles = group_offer_aggregates(static_offers);
869 let maybe_static_offer_bundle = static_offer_bundles.into_iter().find(|bundle| {
870 bundle.first().unwrap().target_name() == first_offer.target_name()
871 });
872 let mut combined_offer_bundle = offer_bundle.clone();
873 if let Some(mut static_offer_bundle) = maybe_static_offer_bundle {
874 let _ = target_input.capabilities().remove(first_offer.target_name());
879 combined_offer_bundle.append(&mut static_offer_bundle);
880 }
881 if combined_offer_bundle.len() == 1
882 && !matches!(first_offer.source(), cm_rust::OfferSource::Collection(_))
883 {
884 extend_dict_with_offer::<DirEntry, _>(
885 component,
886 &child_component_output_dictionary_routers,
887 &component_input,
888 &program_output_dict,
889 &framework_dict,
890 &capability_sourced_capabilities_dict,
891 first_offer,
892 &target_input.capabilities(),
893 error_reporter.clone(),
894 );
895 } else {
896 let aggregate_router = new_aggregate_router_from_service_offers(
897 &combined_offer_bundle,
898 component,
899 &child_component_output_dictionary_routers,
900 &component_input,
901 &program_output_dict,
902 &framework_dict,
903 &capability_sourced_capabilities_dict,
904 error_reporter.clone(),
905 aggregate_router_fn,
906 );
907 target_input
908 .capabilities()
909 .insert(first_offer.target_name().clone(), aggregate_router.into())
910 .expect("failed to insert capability into target dict");
911 }
912 }
913 cm_rust::OfferDecl::Config(_) => extend_dict_with_offer::<Data, _>(
914 component,
915 &child_component_output_dictionary_routers,
916 component_input,
917 program_output_dict,
918 framework_dict,
919 capability_sourced_capabilities_dict,
920 first_offer,
921 &target_input.capabilities(),
922 error_reporter.clone(),
923 ),
924 cm_rust::OfferDecl::Dictionary(_) => extend_dict_with_offer::<Dict, _>(
925 component,
926 &child_component_output_dictionary_routers,
927 component_input,
928 program_output_dict,
929 framework_dict,
930 capability_sourced_capabilities_dict,
931 first_offer,
932 &target_input.capabilities(),
933 error_reporter.clone(),
934 ),
935 cm_rust::OfferDecl::Protocol(_)
936 | cm_rust::OfferDecl::Runner(_)
937 | cm_rust::OfferDecl::Resolver(_) => extend_dict_with_offer::<Connector, _>(
938 component,
939 &child_component_output_dictionary_routers,
940 component_input,
941 program_output_dict,
942 framework_dict,
943 capability_sourced_capabilities_dict,
944 first_offer,
945 &target_input.capabilities(),
946 error_reporter.clone(),
947 ),
948 _ => {}
949 }
950 }
951}
952
953pub fn is_supported_use(use_: &cm_rust::UseDecl) -> bool {
954 matches!(
955 use_,
956 cm_rust::UseDecl::Config(_)
957 | cm_rust::UseDecl::Protocol(_)
958 | cm_rust::UseDecl::Runner(_)
959 | cm_rust::UseDecl::Service(_)
960 )
961}
962
963fn extend_dict_with_config_use<C: ComponentInstanceInterface + 'static>(
966 component: &Arc<C>,
967 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dict>>,
968 component_input: &ComponentInput,
969 program_input: &ProgramInput,
970 program_output_dict: &Dict,
971 config_use: &cm_rust::UseConfigurationDecl,
972 error_reporter: impl ErrorReporter,
973) {
974 let moniker = component.moniker();
975 let source_path = config_use.source_path();
976 let porcelain_type = CapabilityTypeName::Config;
977 let router: Router<Data> = match config_use.source() {
978 cm_rust::UseSource::Parent => {
979 use_from_parent_router::<Data>(component_input, source_path.to_owned(), moniker)
980 }
981 cm_rust::UseSource::Self_ => program_output_dict.get_router_or_not_found::<Data>(
982 &source_path,
983 RoutingError::use_from_self_not_found(moniker, source_path.iter_segments().join("/")),
984 ),
985 cm_rust::UseSource::Child(child_name) => {
986 let child_name = ChildName::parse(child_name).expect("invalid child name");
987 let Some(child_component_output) =
988 child_component_output_dictionary_routers.get(&child_name)
989 else {
990 panic!("use declaration in manifest for component {} has a source of a nonexistent child {}, this should be prevented by manifest validation", moniker, child_name);
991 };
992 child_component_output.clone().lazy_get(
993 source_path.to_owned(),
994 RoutingError::use_from_child_expose_not_found(
995 &child_name,
996 &moniker,
997 config_use.source_name().clone(),
998 ),
999 )
1000 }
1001 cm_rust::UseSource::Environment => return,
1003 cm_rust::UseSource::Debug => return,
1004 cm_rust::UseSource::Capability(_) => return,
1005 cm_rust::UseSource::Framework => return,
1006 cm_rust::UseSource::Collection(_) => return,
1007 };
1008
1009 let availability = *config_use.availability();
1010 match program_input.config().insert_capability(
1011 &config_use.target_name,
1012 router
1013 .with_porcelain_with_default(porcelain_type)
1014 .availability(availability)
1015 .target(component)
1016 .error_info(config_use)
1017 .error_reporter(error_reporter)
1018 .into(),
1019 ) {
1020 Ok(()) => (),
1021 Err(e) => {
1022 warn!("failed to insert {} in program input dict: {e:?}", config_use.target_name)
1023 }
1024 }
1025}
1026
1027fn extend_dict_with_use<T, C: ComponentInstanceInterface + 'static>(
1028 component: &Arc<C>,
1029 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dict>>,
1030 component_input: &ComponentInput,
1031 program_input: &ProgramInput,
1032 program_output_dict: &Dict,
1033 framework_dict: &Dict,
1034 capability_sourced_capabilities_dict: &Dict,
1035 use_: &cm_rust::UseDecl,
1036 error_reporter: impl ErrorReporter,
1037) where
1038 T: CapabilityBound + Clone,
1039 Router<T>: TryFrom<Capability> + Into<Capability>,
1040{
1041 if !is_supported_use(use_) {
1042 return;
1043 }
1044 let moniker = component.moniker();
1045 if let cm_rust::UseDecl::Config(config) = use_ {
1046 return extend_dict_with_config_use(
1047 component,
1048 child_component_output_dictionary_routers,
1049 component_input,
1050 program_input,
1051 program_output_dict,
1052 config,
1053 error_reporter,
1054 );
1055 };
1056
1057 let source_path = use_.source_path();
1058 let porcelain_type = CapabilityTypeName::from(use_);
1059 let router: Router<T> = match use_.source() {
1060 cm_rust::UseSource::Parent => {
1061 use_from_parent_router::<T>(component_input, source_path.to_owned(), moniker)
1062 }
1063 cm_rust::UseSource::Self_ => program_output_dict.get_router_or_not_found::<T>(
1064 &source_path,
1065 RoutingError::use_from_self_not_found(moniker, source_path.iter_segments().join("/")),
1066 ),
1067 cm_rust::UseSource::Child(child_name) => {
1068 let child_name = ChildName::parse(child_name).expect("invalid child name");
1069 let Some(child_component_output) =
1070 child_component_output_dictionary_routers.get(&child_name)
1071 else {
1072 panic!("use declaration in manifest for component {} has a source of a nonexistent child {}, this should be prevented by manifest validation", moniker, child_name);
1073 };
1074 child_component_output.clone().lazy_get(
1075 source_path.to_owned(),
1076 RoutingError::use_from_child_expose_not_found(
1077 &child_name,
1078 &moniker,
1079 use_.source_name().clone(),
1080 ),
1081 )
1082 }
1083 cm_rust::UseSource::Framework if use_.is_from_dictionary() => {
1084 Router::<T>::new_error(RoutingError::capability_from_framework_not_found(
1085 moniker,
1086 source_path.iter_segments().join("/"),
1087 ))
1088 }
1089 cm_rust::UseSource::Framework => framework_dict.get_router_or_not_found::<T>(
1090 &source_path,
1091 RoutingError::capability_from_framework_not_found(
1092 moniker,
1093 source_path.iter_segments().join("/"),
1094 ),
1095 ),
1096 cm_rust::UseSource::Capability(capability_name) => {
1097 let err = RoutingError::capability_from_capability_not_found(
1098 moniker,
1099 capability_name.as_str().to_string(),
1100 );
1101 if source_path.iter_segments().join("/") == fsys::StorageAdminMarker::PROTOCOL_NAME {
1102 capability_sourced_capabilities_dict.get_router_or_not_found(&capability_name, err)
1103 } else {
1104 Router::<T>::new_error(err)
1105 }
1106 }
1107 cm_rust::UseSource::Debug => {
1108 let cm_rust::UseDecl::Protocol(use_protocol) = use_ else {
1109 panic!("non-protocol capability used with a debug source, this should be prevented by manifest validation");
1110 };
1111 component_input.environment().debug().get_router_or_not_found::<T>(
1112 &use_protocol.source_name,
1113 RoutingError::use_from_environment_not_found(
1114 moniker,
1115 "protocol",
1116 &use_protocol.source_name,
1117 ),
1118 )
1119 }
1120 cm_rust::UseSource::Environment => {
1121 let cm_rust::UseDecl::Runner(use_runner) = use_ else {
1122 panic!("non-runner capability used with an environment source, this should be prevented by manifest validation");
1123 };
1124 component_input.environment().runners().get_router_or_not_found::<T>(
1125 &use_runner.source_name,
1126 RoutingError::use_from_environment_not_found(
1127 moniker,
1128 "runner",
1129 &use_runner.source_name,
1130 ),
1131 )
1132 }
1133 cm_rust::UseSource::Collection(_) => {
1134 return;
1136 }
1137 };
1138
1139 let availability = *use_.availability();
1140 let router = router
1141 .with_porcelain_with_default(porcelain_type)
1142 .availability(availability)
1143 .target(&component)
1144 .error_info(use_)
1145 .error_reporter(error_reporter)
1146 .build();
1147
1148 if let Some(target_path) = use_.path() {
1149 if let Err(e) = program_input.namespace().insert_capability(target_path, router.into()) {
1150 warn!("failed to insert {} in program input dict: {e:?}", target_path)
1151 }
1152 } else {
1153 match use_ {
1154 cm_rust::UseDecl::Runner(_) => {
1155 assert!(program_input.runner().is_none(), "component can't use multiple runners");
1156 program_input.set_runner(router.into());
1157 }
1158 _ => panic!("unexpected capability type: {:?}", use_),
1159 }
1160 }
1161}
1162
1163fn use_from_parent_router<T>(
1165 component_input: &ComponentInput,
1166 source_path: impl IterablePath + 'static + Debug,
1167 moniker: &Moniker,
1168) -> Router<T>
1169where
1170 T: CapabilityBound + Clone,
1171 Router<T>: TryFrom<Capability>,
1172{
1173 let err = if moniker == &Moniker::root() {
1174 RoutingError::register_from_component_manager_not_found(
1175 source_path.iter_segments().join("/"),
1176 )
1177 } else {
1178 RoutingError::use_from_parent_not_found(moniker, source_path.iter_segments().join("/"))
1179 };
1180 component_input.capabilities().get_router_or_not_found::<T>(&source_path, err)
1181}
1182
1183fn is_supported_offer(offer: &cm_rust::OfferDecl) -> bool {
1184 matches!(
1185 offer,
1186 cm_rust::OfferDecl::Config(_)
1187 | cm_rust::OfferDecl::Protocol(_)
1188 | cm_rust::OfferDecl::Dictionary(_)
1189 | cm_rust::OfferDecl::Runner(_)
1190 | cm_rust::OfferDecl::Resolver(_)
1191 | cm_rust::OfferDecl::Service(_)
1192 )
1193}
1194
1195fn extend_dict_with_offer<T, C: ComponentInstanceInterface + 'static>(
1196 component: &Arc<C>,
1197 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dict>>,
1198 component_input: &ComponentInput,
1199 program_output_dict: &Dict,
1200 framework_dict: &Dict,
1201 capability_sourced_capabilities_dict: &Dict,
1202 offer: &cm_rust::OfferDecl,
1203 target_dict: &Dict,
1204 error_reporter: impl ErrorReporter,
1205) where
1206 T: CapabilityBound + Clone,
1207 Router<T>: TryFrom<Capability> + Into<Capability> + WithServiceRenamesAndFilter,
1208{
1209 assert!(is_supported_offer(offer), "{offer:?}");
1210
1211 let source_path = offer.source_path();
1212 let target_name = offer.target_name();
1213 if target_dict.get_capability(&target_name).is_some() {
1214 panic!(
1215 "duplicate sources for {} {} in a dict, unable to populate dict entry, manifest \
1216 validation should prevent this",
1217 cm_rust::CapabilityTypeName::from(offer),
1218 target_name
1219 );
1220 }
1221 let porcelain_type = CapabilityTypeName::from(offer);
1222 let router: Router<T> = match offer.source() {
1223 cm_rust::OfferSource::Parent => {
1224 let err = if component.moniker() == &Moniker::root() {
1225 RoutingError::register_from_component_manager_not_found(
1226 offer.source_name().to_string(),
1227 )
1228 } else {
1229 RoutingError::offer_from_parent_not_found(
1230 &component.moniker(),
1231 source_path.iter_segments().join("/"),
1232 )
1233 };
1234 component_input.capabilities().get_router_or_not_found::<T>(&source_path, err)
1235 }
1236 cm_rust::OfferSource::Self_ => program_output_dict.get_router_or_not_found::<T>(
1237 &source_path,
1238 RoutingError::offer_from_self_not_found(
1239 &component.moniker(),
1240 source_path.iter_segments().join("/"),
1241 ),
1242 ),
1243 cm_rust::OfferSource::Child(child_ref) => {
1244 let child_name: ChildName = child_ref.clone().try_into().expect("invalid child ref");
1245 match child_component_output_dictionary_routers.get(&child_name) {
1246 None => Router::<T>::new_error(RoutingError::offer_from_child_instance_not_found(
1247 &child_name,
1248 &component.moniker(),
1249 source_path.iter_segments().join("/"),
1250 )),
1251 Some(child_component_output) => child_component_output.clone().lazy_get(
1252 source_path.to_owned(),
1253 RoutingError::offer_from_child_expose_not_found(
1254 &child_name,
1255 &component.moniker(),
1256 offer.source_name().clone(),
1257 ),
1258 ),
1259 }
1260 }
1261 cm_rust::OfferSource::Framework => {
1262 if offer.is_from_dictionary() {
1263 warn!(
1264 "routing from framework with dictionary path is not supported: {source_path}"
1265 );
1266 return;
1267 }
1268 framework_dict.get_router_or_not_found::<T>(
1269 &source_path,
1270 RoutingError::capability_from_framework_not_found(
1271 &component.moniker(),
1272 source_path.iter_segments().join("/"),
1273 ),
1274 )
1275 }
1276 cm_rust::OfferSource::Capability(capability_name) => {
1277 let err = RoutingError::capability_from_capability_not_found(
1278 &component.moniker(),
1279 capability_name.as_str().to_string(),
1280 );
1281 if source_path.iter_segments().join("/") == fsys::StorageAdminMarker::PROTOCOL_NAME {
1282 capability_sourced_capabilities_dict.get_router_or_not_found(&capability_name, err)
1283 } else {
1284 Router::<T>::new_error(err)
1285 }
1286 }
1287 cm_rust::OfferSource::Void => UnavailableRouter::new::<T>(
1288 InternalCapability::Protocol(offer.source_name().clone()),
1289 component,
1290 ),
1291 cm_rust::OfferSource::Collection(_collection_name) => {
1292 return;
1297 }
1298 };
1299
1300 let availability = *offer.availability();
1309 let router = router
1310 .with_porcelain_with_default(porcelain_type)
1311 .availability(availability)
1312 .target(component)
1313 .error_info(offer)
1314 .error_reporter(error_reporter)
1315 .build()
1316 .with_service_renames_and_filter(offer.clone());
1317 match target_dict.insert_capability(target_name, router.into()) {
1318 Ok(()) => (),
1319 Err(e) => warn!("failed to insert {target_name} into target dict: {e:?}"),
1320 }
1321}
1322
1323pub fn is_supported_expose(expose: &cm_rust::ExposeDecl) -> bool {
1324 matches!(
1325 expose,
1326 cm_rust::ExposeDecl::Config(_)
1327 | cm_rust::ExposeDecl::Protocol(_)
1328 | cm_rust::ExposeDecl::Dictionary(_)
1329 | cm_rust::ExposeDecl::Runner(_)
1330 | cm_rust::ExposeDecl::Resolver(_)
1331 | cm_rust::ExposeDecl::Service(_)
1332 )
1333}
1334
1335fn extend_dict_with_expose<T, C: ComponentInstanceInterface + 'static>(
1336 component: &Arc<C>,
1337 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dict>>,
1338 program_output_dict: &Dict,
1339 framework_dict: &Dict,
1340 capability_sourced_capabilities_dict: &Dict,
1341 expose: &cm_rust::ExposeDecl,
1342 target_component_output: &ComponentOutput,
1343 error_reporter: impl ErrorReporter,
1344) where
1345 T: CapabilityBound + Clone,
1346 Router<T>: TryFrom<Capability> + Into<Capability>,
1347{
1348 assert!(is_supported_expose(expose), "{expose:?}");
1349
1350 let target_dict = match expose.target() {
1352 cm_rust::ExposeTarget::Parent => target_component_output.capabilities(),
1353 cm_rust::ExposeTarget::Framework => target_component_output.framework(),
1354 };
1355 let source_path = expose.source_path();
1356 let target_name = expose.target_name();
1357
1358 let porcelain_type = CapabilityTypeName::from(expose);
1359 let router: Router<T> = match expose.source() {
1360 cm_rust::ExposeSource::Self_ => program_output_dict.get_router_or_not_found::<T>(
1361 &source_path,
1362 RoutingError::expose_from_self_not_found(
1363 &component.moniker(),
1364 source_path.iter_segments().join("/"),
1365 ),
1366 ),
1367 cm_rust::ExposeSource::Child(child_name) => {
1368 let child_name = ChildName::parse(child_name).expect("invalid static child name");
1369 if let Some(child_component_output) =
1370 child_component_output_dictionary_routers.get(&child_name)
1371 {
1372 child_component_output.clone().lazy_get(
1373 source_path.to_owned(),
1374 RoutingError::expose_from_child_expose_not_found(
1375 &child_name,
1376 &component.moniker(),
1377 expose.source_name().clone(),
1378 ),
1379 )
1380 } else {
1381 Router::<T>::new_error(RoutingError::expose_from_child_instance_not_found(
1382 &child_name,
1383 &component.moniker(),
1384 expose.source_name().clone(),
1385 ))
1386 }
1387 }
1388 cm_rust::ExposeSource::Framework => {
1389 if expose.is_from_dictionary() {
1390 warn!(
1391 "routing from framework with dictionary path is not supported: {source_path}"
1392 );
1393 return;
1394 }
1395 framework_dict.get_router_or_not_found::<T>(
1396 &source_path,
1397 RoutingError::capability_from_framework_not_found(
1398 &component.moniker(),
1399 source_path.iter_segments().join("/"),
1400 ),
1401 )
1402 }
1403 cm_rust::ExposeSource::Capability(capability_name) => {
1404 let err = RoutingError::capability_from_capability_not_found(
1405 &component.moniker(),
1406 capability_name.as_str().to_string(),
1407 );
1408 if source_path.iter_segments().join("/") == fsys::StorageAdminMarker::PROTOCOL_NAME {
1409 capability_sourced_capabilities_dict
1410 .clone()
1411 .get_router_or_not_found::<T>(&capability_name, err)
1412 } else {
1413 Router::<T>::new_error(err)
1414 }
1415 }
1416 cm_rust::ExposeSource::Void => UnavailableRouter::new(
1417 InternalCapability::Protocol(expose.source_name().clone()),
1418 component,
1419 ),
1420 cm_rust::ExposeSource::Collection(_name) => return,
1425 };
1426 let availability = *expose.availability();
1427 match target_dict.insert_capability(
1428 target_name,
1429 router
1430 .with_porcelain_with_default(porcelain_type)
1431 .availability(availability)
1432 .target(component)
1433 .error_info(expose)
1434 .error_reporter(error_reporter)
1435 .into(),
1436 ) {
1437 Ok(()) => (),
1438 Err(e) => warn!("failed to insert {target_name} into target_dict: {e:?}"),
1439 }
1440}
1441
1442struct UnavailableRouter<C: ComponentInstanceInterface> {
1443 capability: InternalCapability,
1444 component: WeakComponentInstanceInterface<C>,
1445}
1446
1447impl<C: ComponentInstanceInterface + 'static> UnavailableRouter<C> {
1448 fn new<T: CapabilityBound>(capability: InternalCapability, component: &Arc<C>) -> Router<T> {
1449 Router::<T>::new(UnavailableRouter { capability, component: component.as_weak() })
1450 }
1451}
1452
1453#[async_trait]
1454impl<T: CapabilityBound, C: ComponentInstanceInterface + 'static> Routable<T>
1455 for UnavailableRouter<C>
1456{
1457 async fn route(
1458 &self,
1459 request: Option<Request>,
1460 debug: bool,
1461 ) -> Result<RouterResponse<T>, RouterError> {
1462 if debug {
1463 let data = CapabilitySource::Void(VoidSource {
1464 capability: self.capability.clone(),
1465 moniker: self.component.moniker.clone(),
1466 })
1467 .try_into()
1468 .expect("failed to convert capability source to Data");
1469 return Ok(RouterResponse::<T>::Debug(data));
1470 }
1471 let request = request.ok_or_else(|| RouterError::InvalidArgs)?;
1472 let availability = request.metadata.get_metadata().ok_or(RouterError::InvalidArgs)?;
1473 match availability {
1474 cm_rust::Availability::Required | cm_rust::Availability::SameAsTarget => {
1475 Err(RoutingError::SourceCapabilityIsVoid {
1476 moniker: self.component.moniker.clone(),
1477 }
1478 .into())
1479 }
1480 cm_rust::Availability::Optional | cm_rust::Availability::Transitional => {
1481 Ok(RouterResponse::Unavailable)
1482 }
1483 }
1484 }
1485}