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