1use crate::bedrock::aggregate_router::{AggregateRouterFn, AggregateSource};
6use crate::bedrock::structured_dict::{
7 ComponentEnvironment, ComponentInput, ComponentOutput, StructuredDictMap,
8};
9use crate::bedrock::use_dictionary_router::UseDictionaryRouter;
10use crate::bedrock::with_service_renames_and_filter::WithServiceRenamesAndFilter;
11use crate::component_instance::{ComponentInstanceInterface, WeakComponentInstanceInterface};
12use crate::error::{ErrorReporter, RouteRequestErrorInfo, RoutingError};
13use crate::{DictExt, LazyGet, WithPorcelain};
14use async_trait::async_trait;
15use capability_source::{
16 AggregateCapability, AggregateInstance, AggregateMember, AnonymizedAggregateSource,
17 CapabilitySource, ComponentCapability, ComponentSource, FilteredAggregateProviderSource,
18 InternalCapability, InternalEventStreamCapability, VoidSource,
19};
20use cm_rust::offer::{OfferDecl, OfferDeclCommon};
21use cm_rust::{
22 CapabilityTypeName, DictionaryValue, ExposeDecl, ExposeDeclCommon, FidlIntoNative, SourceName,
23 SourcePath, UseDeclCommon,
24};
25use cm_types::{Availability, BorrowedSeparatedPath, IterablePath, Name, SeparatedPath};
26use fidl::endpoints::DiscoverableProtocolMarker;
27use fidl_fuchsia_component as fcomponent;
28use fidl_fuchsia_component_decl as fdecl;
29use fidl_fuchsia_component_runtime::RouteRequest;
30use fidl_fuchsia_io as fio;
31use fidl_fuchsia_sys2 as fsys;
32use fuchsia_sync::Mutex;
33use futures::FutureExt;
34use itertools::Itertools;
35use log::warn;
36use moniker::{ChildName, Moniker};
37use router_error::RouterError;
38use runtime_capabilities::{
39 Capability, CapabilityBound, Connector, Data, Dictionary, DirConnector, Routable, Router,
40 WeakInstanceToken,
41};
42use std::collections::{BTreeMap, HashMap};
43use std::fmt::Debug;
44use std::sync::{Arc, LazyLock};
45
46pub type EventStreamFilter = Option<BTreeMap<String, DictionaryValue>>;
48
49#[derive(Clone)]
51pub struct EventStreamSourceRouter {
52 pub router: Router<Dictionary>,
55 pub filter: EventStreamFilter,
58}
59pub type EventStreamUseRouterFn<C> =
60 dyn Fn(&Arc<C>, Vec<EventStreamSourceRouter>) -> Router<Connector>;
61
62static NAMESPACE: LazyLock<Name> = LazyLock::new(|| "namespace".parse().unwrap());
63static NUMBERED_HANDLES: LazyLock<Name> = LazyLock::new(|| "numbered_handles".parse().unwrap());
64static RUNNER: LazyLock<Name> = LazyLock::new(|| "runner".parse().unwrap());
65static CONFIG: LazyLock<Name> = LazyLock::new(|| "config".parse().unwrap());
66
67#[derive(Debug, Clone)]
69pub struct ProgramInput {
70 inner: Dictionary,
76}
77
78impl Default for ProgramInput {
79 fn default() -> Self {
80 Self::new(Dictionary::new(), None, Dictionary::new())
81 }
82}
83
84impl From<ProgramInput> for Dictionary {
85 fn from(program_input: ProgramInput) -> Self {
86 program_input.inner
87 }
88}
89
90impl ProgramInput {
91 pub fn new(
92 namespace: Dictionary,
93 runner: Option<Router<Connector>>,
94 config: Dictionary,
95 ) -> Self {
96 let inner = Dictionary::new();
97 inner.insert(NAMESPACE.clone(), namespace.into());
98 if let Some(runner) = runner {
99 inner.insert(RUNNER.clone(), runner.into());
100 }
101 inner.insert(NUMBERED_HANDLES.clone(), Dictionary::new().into());
102 inner.insert(CONFIG.clone(), config.into());
103 ProgramInput { inner }
104 }
105
106 pub fn namespace(&self) -> Dictionary {
108 let cap = self.inner.get(&*NAMESPACE).unwrap();
109 let Capability::Dictionary(dict) = cap else {
110 unreachable!("namespace entry must be a dictionary: {cap:?}");
111 };
112 dict
113 }
114
115 pub fn numbered_handles(&self) -> Dictionary {
117 let cap = self.inner.get(&*NUMBERED_HANDLES).unwrap();
118 let Capability::Dictionary(dict) = cap else {
119 unreachable!("numbered_handles entry must be a dictionary: {cap:?}");
120 };
121 dict
122 }
123
124 pub fn runner(&self) -> Option<Router<Connector>> {
126 let cap = self.inner.get(&*RUNNER);
127 match cap {
128 None => None,
129 Some(Capability::ConnectorRouter(r)) => Some(r),
130 cap => unreachable!("runner entry must be a router: {cap:?}"),
131 }
132 }
133
134 fn set_runner(&self, capability: Capability) {
135 let _ = self.inner.insert(RUNNER.clone(), capability);
136 }
137
138 pub fn config(&self) -> Dictionary {
140 let cap = self.inner.get(&*CONFIG).unwrap();
141 let Capability::Dictionary(dict) = cap else {
142 unreachable!("config entry must be a dictionary: {cap:?}");
143 };
144 dict
145 }
146}
147
148#[derive(Debug)]
151pub struct ComponentSandbox {
152 pub component_input: ComponentInput,
154
155 pub component_output: ComponentOutput,
157
158 pub program_input: ProgramInput,
160
161 pub program_output_dict: Dictionary,
163
164 framework_router: Mutex<Router<Dictionary>>,
174
175 pub capability_sourced_capabilities_dict: Dictionary,
178
179 pub declared_dictionaries: Dictionary,
181
182 pub child_inputs: StructuredDictMap<ComponentInput>,
185
186 pub collection_inputs: StructuredDictMap<ComponentInput>,
190}
191
192impl Default for ComponentSandbox {
193 fn default() -> Self {
194 static NULL_ROUTER: LazyLock<Router<Dictionary>> =
195 LazyLock::new(|| Router::new(NullRouter {}));
196 struct NullRouter;
197 #[async_trait]
198 impl Routable<Dictionary> for NullRouter {
199 async fn route(
200 &self,
201 _request: RouteRequest,
202 _target: WeakInstanceToken,
203 ) -> Result<Option<Dictionary>, RouterError> {
204 panic!("null router invoked");
205 }
206 async fn route_debug(
207 &self,
208 _request: RouteRequest,
209 _target: WeakInstanceToken,
210 ) -> Result<CapabilitySource, RouterError> {
211 panic!("null router invoked");
212 }
213 }
214 let framework_router = Mutex::new(NULL_ROUTER.clone());
215 Self {
216 framework_router,
217 component_input: Default::default(),
218 component_output: Default::default(),
219 program_input: Default::default(),
220 program_output_dict: Default::default(),
221 capability_sourced_capabilities_dict: Default::default(),
222 declared_dictionaries: Default::default(),
223 child_inputs: Default::default(),
224 collection_inputs: Default::default(),
225 }
226 }
227}
228
229impl From<ComponentSandbox> for Dictionary {
230 fn from(sandbox: ComponentSandbox) -> Dictionary {
231 let sandbox_dictionary = Dictionary::new();
232 sandbox_dictionary.insert(
233 Name::new("framework").unwrap(),
234 sandbox.framework_router.lock().clone().into(),
235 );
236 sandbox_dictionary.insert(
237 Name::new("component_input").unwrap(),
238 Capability::Dictionary(sandbox.component_input.into()),
239 );
240 sandbox_dictionary.insert(
241 Name::new("component_output").unwrap(),
242 Capability::Dictionary(sandbox.component_output.into()),
243 );
244 sandbox_dictionary.insert(
245 Name::new("program_input").unwrap(),
246 Capability::Dictionary(sandbox.program_input.into()),
247 );
248 sandbox_dictionary
249 .insert(Name::new("program_output").unwrap(), sandbox.program_output_dict.into());
250 sandbox_dictionary.insert(
251 Name::new("capability_sourced").unwrap(),
252 sandbox.capability_sourced_capabilities_dict.into(),
253 );
254 sandbox_dictionary.insert(
255 Name::new("declared_dictionaries").unwrap(),
256 sandbox.declared_dictionaries.into(),
257 );
258 sandbox_dictionary.insert(
259 Name::new("child_inputs").unwrap(),
260 Capability::Dictionary(sandbox.child_inputs.into()),
261 );
262 sandbox_dictionary.insert(
263 Name::new("collection_inputs").unwrap(),
264 Capability::Dictionary(sandbox.collection_inputs.into()),
265 );
266 sandbox_dictionary
267 }
268}
269
270impl Clone for ComponentSandbox {
271 fn clone(&self) -> Self {
272 let Self {
273 component_input,
274 component_output,
275 program_input,
276 program_output_dict,
277 framework_router,
278 capability_sourced_capabilities_dict,
279 declared_dictionaries,
280 child_inputs,
281 collection_inputs,
282 } = self;
283 Self {
284 component_input: component_input.clone(),
285 component_output: component_output.clone(),
286 program_input: program_input.clone(),
287 program_output_dict: program_output_dict.clone(),
288 framework_router: Mutex::new(framework_router.lock().clone()),
289 capability_sourced_capabilities_dict: capability_sourced_capabilities_dict.clone(),
290 declared_dictionaries: declared_dictionaries.clone(),
291 child_inputs: child_inputs.clone(),
292 collection_inputs: collection_inputs.clone(),
293 }
294 }
295}
296
297impl ComponentSandbox {
298 pub fn append(&self, sandbox: &ComponentSandbox) {
301 let ComponentSandbox {
304 component_input,
305 component_output,
306 program_input,
307 program_output_dict,
308 framework_router,
309 capability_sourced_capabilities_dict,
310 declared_dictionaries,
311 child_inputs,
312 collection_inputs,
313 } = sandbox;
314 for (copy_from, copy_to) in [
315 (&component_input.capabilities(), &self.component_input.capabilities()),
316 (&component_input.environment().debug(), &self.component_input.environment().debug()),
317 (
318 &component_input.environment().runners(),
319 &self.component_input.environment().runners(),
320 ),
321 (
322 &component_input.environment().resolvers(),
323 &self.component_input.environment().resolvers(),
324 ),
325 (&component_output.capabilities(), &self.component_output.capabilities()),
326 (&component_output.framework(), &self.component_output.framework()),
327 (&program_input.namespace(), &self.program_input.namespace()),
328 (&program_input.numbered_handles(), &self.program_input.numbered_handles()),
329 (&program_input.config(), &self.program_input.config()),
330 (&program_output_dict, &self.program_output_dict),
331 (&capability_sourced_capabilities_dict, &self.capability_sourced_capabilities_dict),
332 (&declared_dictionaries, &self.declared_dictionaries),
333 ] {
334 copy_to.append(copy_from).expect("conflicting entry found");
335 }
336 if let Some(timeout) = component_input.environment().stop_timeout() {
337 self.component_input.environment().set_stop_timeout(timeout as i64);
338 }
339 *self.framework_router.lock() = framework_router.lock().clone();
340 if let Some(runner_router) = program_input.runner() {
341 self.program_input.set_runner(runner_router.into());
342 }
343 self.child_inputs.append(child_inputs).unwrap();
344 self.collection_inputs.append(collection_inputs).unwrap();
345 }
346
347 pub fn framework_router(&self) -> Router<Dictionary> {
348 self.framework_router.lock().clone()
349 }
350}
351
352pub fn build_component_sandbox<C: ComponentInstanceInterface + 'static>(
355 component: &Arc<C>,
356 child_component_output_dictionary_routers: HashMap<ChildName, Router<Dictionary>>,
357 decl: &cm_rust::ComponentDecl,
358 component_input: ComponentInput,
359 program_output_dict: Dictionary,
360 framework_router: Router<Dictionary>,
361 capability_sourced_capabilities_dict: Dictionary,
362 declared_dictionaries: Dictionary,
363 error_reporter: impl ErrorReporter,
364 aggregate_router_fn: &AggregateRouterFn<C>,
365 event_stream_use_router_fn: &EventStreamUseRouterFn<C>,
366) -> ComponentSandbox {
367 let component_output = ComponentOutput::new();
368 let program_input = ProgramInput::default();
369 let environments: StructuredDictMap<ComponentEnvironment> = Default::default();
370 let child_inputs: StructuredDictMap<ComponentInput> = Default::default();
371 let collection_inputs: StructuredDictMap<ComponentInput> = Default::default();
372
373 for environment_decl in &decl.environments {
374 let _ = environments.insert(
375 environment_decl.name.clone(),
376 build_environment(
377 component,
378 &child_component_output_dictionary_routers,
379 &component_input,
380 environment_decl,
381 &program_output_dict,
382 &error_reporter,
383 ),
384 );
385 }
386
387 for child in &decl.children {
388 let environment;
389 if let Some(environment_name) = child.environment.as_ref() {
390 environment = environments.get(environment_name).expect(
391 "child references nonexistent environment, \
392 this should be prevented in manifest validation",
393 );
394 } else {
395 environment = component_input.environment();
396 }
397 let input = ComponentInput::new(environment);
398 let name = Name::new(child.name.as_str()).expect("child is static so name is not long");
399 let _ = child_inputs.insert(name, input);
400 }
401
402 for collection in &decl.collections {
403 let environment;
404 if let Some(environment_name) = collection.environment.as_ref() {
405 environment = environments.get(environment_name).expect(
406 "collection references nonexistent environment, \
407 this should be prevented in manifest validation",
408 )
409 } else {
410 environment = component_input.environment();
411 }
412 let input = ComponentInput::new(environment);
413 let _ = collection_inputs.insert(collection.name.clone(), input);
414 }
415
416 let mut dictionary_use_bundles = Vec::with_capacity(decl.uses.len());
417 for use_bundle in group_use_aggregates(&decl.uses).into_iter() {
418 let first_use = *use_bundle.first().unwrap();
419 match first_use {
420 cm_rust::UseDecl::Service(_)
421 if matches!(first_use.source(), cm_rust::UseSource::Collection(_)) =>
422 {
423 let cm_rust::UseSource::Collection(collection_name) = first_use.source() else {
424 unreachable!();
425 };
426 let availability = *first_use.availability();
427 let aggregate = (aggregate_router_fn)(
428 component.clone(),
429 vec![AggregateSource::Collection { collection_name: collection_name.clone() }],
430 CapabilitySource::AnonymizedAggregate(AnonymizedAggregateSource {
431 capability: AggregateCapability::Service(first_use.source_name().clone()),
432 moniker: component.moniker().clone(),
433 members: vec![AggregateMember::try_from(first_use).unwrap()],
434 instances: vec![],
435 }),
436 )
437 .with_porcelain_with_default(CapabilityTypeName::Service)
438 .availability(availability)
439 .target(component)
440 .error_info(first_use)
441 .error_reporter(error_reporter.clone())
442 .build();
443 let prev = program_input
444 .namespace()
445 .insert_capability(first_use.path().unwrap(), aggregate.into());
446 assert!(
447 prev.is_none(),
448 "failed to insert {}: preexisting value",
449 first_use.path().unwrap()
450 );
451 }
452 cm_rust::UseDecl::Service(_) => extend_dict_with_use::<DirConnector, _>(
453 component,
454 &child_component_output_dictionary_routers,
455 &component_input,
456 &program_input,
457 &program_output_dict,
458 &framework_router,
459 &capability_sourced_capabilities_dict,
460 first_use,
461 error_reporter.clone(),
462 ),
463 cm_rust::UseDecl::Directory(_) | cm_rust::UseDecl::Storage(_) => {
464 extend_dict_with_use::<DirConnector, C>(
465 component,
466 &child_component_output_dictionary_routers,
467 &component_input,
468 &program_input,
469 &program_output_dict,
470 &framework_router,
471 &capability_sourced_capabilities_dict,
472 first_use,
473 error_reporter.clone(),
474 )
475 }
476 cm_rust::UseDecl::Protocol(_) | cm_rust::UseDecl::Runner(_) => {
477 extend_dict_with_use::<Connector, _>(
478 component,
479 &child_component_output_dictionary_routers,
480 &component_input,
481 &program_input,
482 &program_output_dict,
483 &framework_router,
484 &capability_sourced_capabilities_dict,
485 first_use,
486 error_reporter.clone(),
487 )
488 }
489 cm_rust::UseDecl::Config(config) => extend_dict_with_config_use(
490 component,
491 &child_component_output_dictionary_routers,
492 &component_input,
493 &program_input,
494 &program_output_dict,
495 config,
496 error_reporter.clone(),
497 ),
498 cm_rust::UseDecl::EventStream(_) => extend_dict_with_event_stream_uses(
499 component,
500 &component_input,
501 &program_input,
502 use_bundle,
503 error_reporter.clone(),
504 event_stream_use_router_fn,
505 ),
506 cm_rust::UseDecl::Dictionary(_) => {
507 dictionary_use_bundles.push(use_bundle);
508 }
509 }
510 }
511
512 if !decl.uses.iter().any(|u| matches!(u, cm_rust::UseDecl::Runner(_))) {
516 if let Some(runner_name) = decl.program.as_ref().and_then(|p| p.runner.as_ref()) {
517 extend_dict_with_use::<Connector, _>(
518 component,
519 &child_component_output_dictionary_routers,
520 &component_input,
521 &program_input,
522 &program_output_dict,
523 &framework_router,
524 &capability_sourced_capabilities_dict,
525 &cm_rust::UseDecl::Runner(cm_rust::UseRunnerDecl {
526 source: cm_rust::UseSource::Environment,
527 source_name: runner_name.clone(),
528 source_dictionary: Default::default(),
529 }),
530 error_reporter.clone(),
531 )
532 }
533 }
534
535 for dictionary_use_bundle in dictionary_use_bundles {
542 extend_dict_with_dictionary_use(
543 component,
544 &child_component_output_dictionary_routers,
545 &component_input,
546 &program_input,
547 &program_output_dict,
548 &framework_router,
549 &capability_sourced_capabilities_dict,
550 dictionary_use_bundle,
551 error_reporter.clone(),
552 )
553 }
554
555 for offer_bundle in group_offer_aggregates(&decl.offers) {
556 let first_offer = offer_bundle.first().unwrap();
557 let get_target_dict = || match first_offer.target() {
558 cm_rust::offer::OfferTarget::Child(child_ref) => {
559 assert!(child_ref.collection.is_none(), "unexpected dynamic offer target");
560 let child_name = Name::new(child_ref.name.as_str())
561 .expect("child is static so name is not long");
562 if child_inputs.get(&child_name).is_none() {
563 let _ = child_inputs.insert(child_name.clone(), Default::default());
564 }
565 child_inputs
566 .get(&child_name)
567 .expect("component input was just added")
568 .capabilities()
569 }
570 cm_rust::offer::OfferTarget::Collection(name) => {
571 if collection_inputs.get(&name).is_none() {
572 let _ = collection_inputs.insert(name.clone(), Default::default());
573 }
574 collection_inputs
575 .get(&name)
576 .expect("collection input was just added")
577 .capabilities()
578 }
579 cm_rust::offer::OfferTarget::Capability(name) => {
580 let dict = match declared_dictionaries.get(name) {
581 Some(dict) => dict,
582 None => {
583 let dict = Dictionary::new();
584 let _ = declared_dictionaries
585 .insert(name.clone(), Capability::Dictionary(dict.clone()));
586 Capability::Dictionary(dict)
587 }
588 };
589 let Capability::Dictionary(dict) = dict else {
590 panic!("wrong type in dictionary");
591 };
592 dict
593 }
594 };
595 match first_offer {
596 cm_rust::offer::OfferDecl::Service(_)
597 if offer_bundle.len() == 1
598 && !matches!(
599 first_offer.source(),
600 cm_rust::offer::OfferSource::Collection(_)
601 ) =>
602 {
603 extend_dict_with_offer::<DirConnector, _>(
604 component,
605 &child_component_output_dictionary_routers,
606 &component_input,
607 &program_output_dict,
608 &framework_router,
609 &capability_sourced_capabilities_dict,
610 first_offer,
611 &(get_target_dict)(),
612 error_reporter.clone(),
613 )
614 }
615 cm_rust::offer::OfferDecl::Service(_) => {
616 let aggregate_router = new_aggregate_router_from_service_offers(
617 &offer_bundle,
618 component,
619 &child_component_output_dictionary_routers,
620 &component_input,
621 &program_output_dict,
622 &framework_router,
623 &capability_sourced_capabilities_dict,
624 error_reporter.clone(),
625 aggregate_router_fn,
626 );
627 (get_target_dict)()
628 .insert(first_offer.target_name().clone(), aggregate_router.into());
629 }
630 cm_rust::offer::OfferDecl::Config(_) => extend_dict_with_offer::<Data, _>(
631 component,
632 &child_component_output_dictionary_routers,
633 &component_input,
634 &program_output_dict,
635 &framework_router,
636 &capability_sourced_capabilities_dict,
637 first_offer,
638 &(get_target_dict)(),
639 error_reporter.clone(),
640 ),
641 cm_rust::offer::OfferDecl::Directory(_) | cm_rust::offer::OfferDecl::Storage(_) => {
642 extend_dict_with_offer::<DirConnector, _>(
643 component,
644 &child_component_output_dictionary_routers,
645 &component_input,
646 &program_output_dict,
647 &framework_router,
648 &capability_sourced_capabilities_dict,
649 first_offer,
650 &(get_target_dict)(),
651 error_reporter.clone(),
652 )
653 }
654 cm_rust::offer::OfferDecl::Dictionary(_)
655 | cm_rust::offer::OfferDecl::EventStream(_) => extend_dict_with_offer::<Dictionary, _>(
656 component,
657 &child_component_output_dictionary_routers,
658 &component_input,
659 &program_output_dict,
660 &framework_router,
661 &capability_sourced_capabilities_dict,
662 first_offer,
663 &(get_target_dict)(),
664 error_reporter.clone(),
665 ),
666 cm_rust::offer::OfferDecl::Protocol(_)
667 | cm_rust::offer::OfferDecl::Runner(_)
668 | cm_rust::offer::OfferDecl::Resolver(_) => extend_dict_with_offer::<Connector, _>(
669 component,
670 &child_component_output_dictionary_routers,
671 &component_input,
672 &program_output_dict,
673 &framework_router,
674 &capability_sourced_capabilities_dict,
675 first_offer,
676 &(get_target_dict)(),
677 error_reporter.clone(),
678 ),
679 }
680 }
681
682 for expose_bundle in group_expose_aggregates(&decl.exposes) {
683 let first_expose = expose_bundle.first().unwrap();
684 match first_expose {
685 cm_rust::ExposeDecl::Service(_)
686 if expose_bundle.len() == 1
687 && !matches!(first_expose.source(), cm_rust::ExposeSource::Collection(_)) =>
688 {
689 extend_dict_with_expose::<DirConnector, _>(
690 component,
691 &child_component_output_dictionary_routers,
692 &program_output_dict,
693 &framework_router,
694 &capability_sourced_capabilities_dict,
695 first_expose,
696 &component_output,
697 error_reporter.clone(),
698 )
699 }
700 cm_rust::ExposeDecl::Service(_) => {
701 let mut aggregate_sources = vec![];
702 let temp_component_output = ComponentOutput::new();
703 for expose in expose_bundle.iter() {
704 extend_dict_with_expose::<DirConnector, _>(
705 component,
706 &child_component_output_dictionary_routers,
707 &program_output_dict,
708 &framework_router,
709 &capability_sourced_capabilities_dict,
710 expose,
711 &temp_component_output,
712 error_reporter.clone(),
713 );
714 match temp_component_output.capabilities().remove(first_expose.target_name()) {
715 Some(Capability::DirConnectorRouter(router)) => {
716 let source_instance = match expose.source() {
717 cm_rust::ExposeSource::Self_ => AggregateInstance::Self_,
718 cm_rust::ExposeSource::Child(name) => AggregateInstance::Child(
719 moniker::ChildName::new(name.clone().to_long(), None),
720 ),
721 other_source => {
722 warn!(
723 "unsupported source found in expose aggregate: {:?}",
724 other_source
725 );
726 continue;
727 }
728 };
729 aggregate_sources
730 .push(AggregateSource::DirectoryRouter { source_instance, router })
731 }
732 None => match expose.source() {
733 cm_rust::ExposeSource::Collection(collection_name) => {
734 aggregate_sources.push(AggregateSource::Collection {
735 collection_name: collection_name.clone(),
736 });
737 }
738 _ => continue,
739 },
740 other_value => panic!("unexpected dictionary entry: {:?}", other_value),
741 }
742 }
743 let availability = *first_expose.availability();
744 let aggregate = (aggregate_router_fn)(
745 component.clone(),
746 aggregate_sources,
747 CapabilitySource::AnonymizedAggregate(AnonymizedAggregateSource {
748 capability: AggregateCapability::Service(
749 first_expose.target_name().clone(),
750 ),
751 moniker: component.moniker().clone(),
752 members: expose_bundle
753 .iter()
754 .filter_map(|e| AggregateMember::try_from(*e).ok())
755 .collect(),
756 instances: vec![],
757 }),
758 );
759 let router = aggregate
760 .with_porcelain_with_default(CapabilityTypeName::Service)
761 .availability(availability)
762 .target(component)
763 .error_info(*first_expose)
764 .error_reporter(error_reporter.clone())
765 .build();
766 let target_name = first_expose.target_name().clone();
767 let prev = component_output.capabilities().insert(target_name, router.into());
768 assert!(
769 prev.is_none(),
770 "failed to insert {}: preexisting value",
771 first_expose.target_name()
772 );
773 }
774 cm_rust::ExposeDecl::Config(_) => extend_dict_with_expose::<Data, _>(
775 component,
776 &child_component_output_dictionary_routers,
777 &program_output_dict,
778 &framework_router,
779 &capability_sourced_capabilities_dict,
780 first_expose,
781 &component_output,
782 error_reporter.clone(),
783 ),
784 cm_rust::ExposeDecl::Dictionary(_) => extend_dict_with_expose::<Dictionary, _>(
785 component,
786 &child_component_output_dictionary_routers,
787 &program_output_dict,
788 &framework_router,
789 &capability_sourced_capabilities_dict,
790 first_expose,
791 &component_output,
792 error_reporter.clone(),
793 ),
794 cm_rust::ExposeDecl::Directory(_) => extend_dict_with_expose::<DirConnector, _>(
795 component,
796 &child_component_output_dictionary_routers,
797 &program_output_dict,
798 &framework_router,
799 &capability_sourced_capabilities_dict,
800 first_expose,
801 &component_output,
802 error_reporter.clone(),
803 ),
804 cm_rust::ExposeDecl::Protocol(_)
805 | cm_rust::ExposeDecl::Runner(_)
806 | cm_rust::ExposeDecl::Resolver(_) => extend_dict_with_expose::<Connector, _>(
807 component,
808 &child_component_output_dictionary_routers,
809 &program_output_dict,
810 &framework_router,
811 &capability_sourced_capabilities_dict,
812 first_expose,
813 &component_output,
814 error_reporter.clone(),
815 ),
816 }
817 }
818
819 ComponentSandbox {
820 component_input,
821 component_output,
822 program_input,
823 program_output_dict,
824 framework_router: Mutex::new(framework_router),
825 capability_sourced_capabilities_dict,
826 declared_dictionaries,
827 child_inputs,
828 collection_inputs,
829 }
830}
831
832fn new_aggregate_router_from_service_offers<C: ComponentInstanceInterface + 'static>(
833 offer_bundle: &Vec<&cm_rust::offer::OfferDecl>,
834 component: &Arc<C>,
835 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dictionary>>,
836 component_input: &ComponentInput,
837 program_output_dict: &Dictionary,
838 framework_router: &Router<Dictionary>,
839 capability_sourced_capabilities_dict: &Dictionary,
840 error_reporter: impl ErrorReporter,
841 aggregate_router_fn: &AggregateRouterFn<C>,
842) -> Router<DirConnector> {
843 let mut aggregate_sources = vec![];
844 let dict_for_source_router = Dictionary::new();
845 let source = new_aggregate_capability_source(component.moniker().clone(), offer_bundle.clone());
846 for offer in offer_bundle.iter() {
847 if matches!(&source, &CapabilitySource::FilteredAggregateProvider(_)) {
848 if let cm_rust::offer::OfferDecl::Service(offer_service_decl) = offer {
849 if offer_service_decl
850 .source_instance_filter
851 .as_ref()
852 .and_then(|v| v.first())
853 .is_none()
854 && offer_service_decl
855 .renamed_instances
856 .as_ref()
857 .and_then(|v| v.first())
858 .is_none()
859 {
860 continue;
864 }
865 }
866 }
867 extend_dict_with_offer::<DirConnector, _>(
868 component,
869 &child_component_output_dictionary_routers,
870 &component_input,
871 &program_output_dict,
872 framework_router,
873 &capability_sourced_capabilities_dict,
874 offer,
875 &dict_for_source_router,
876 error_reporter.clone(),
877 );
878 match dict_for_source_router.remove(offer.target_name()) {
879 Some(Capability::DirConnectorRouter(router)) => {
880 let source_instance = match offer.source() {
881 cm_rust::offer::OfferSource::Self_ => AggregateInstance::Self_,
882 cm_rust::offer::OfferSource::Parent => AggregateInstance::Parent,
883 cm_rust::offer::OfferSource::Child(child_ref) => {
884 AggregateInstance::Child(moniker::ChildName::new(
885 child_ref.name.clone(),
886 child_ref.collection.clone(),
887 ))
888 }
889 other_source => {
890 warn!("unsupported source found in offer aggregate: {:?}", other_source);
891 continue;
892 }
893 };
894 aggregate_sources.push(AggregateSource::DirectoryRouter { source_instance, router })
895 }
896 None => match offer.source() {
897 cm_rust::offer::OfferSource::Collection(collection_name) => {
901 aggregate_sources.push(AggregateSource::Collection {
902 collection_name: collection_name.clone(),
903 });
904 }
905 _ => continue,
906 },
907 other => warn!("found unexpected entry in dictionary: {:?}", other),
908 }
909 }
910 (aggregate_router_fn)(component.clone(), aggregate_sources, source)
911}
912
913fn new_aggregate_capability_source(
914 moniker: Moniker,
915 offers: Vec<&cm_rust::offer::OfferDecl>,
916) -> CapabilitySource {
917 let offer_service_decls = offers
918 .iter()
919 .map(|o| match o {
920 cm_rust::offer::OfferDecl::Service(o) => o.as_ref(),
921 _ => panic!(
922 "cannot aggregate non-service capabilities, manifest validation should prevent this"
923 ),
924 })
925 .collect::<Vec<_>>();
926 let is_filtered_offer = offer_service_decls.iter().any(|o| {
928 o.source_instance_filter.as_ref().map(|v| !v.is_empty()).unwrap_or(false)
929 || o.renamed_instances.as_ref().map(|v| !v.is_empty()).unwrap_or(false)
930 });
931 let capability =
932 AggregateCapability::Service(offer_service_decls.first().unwrap().target_name.clone());
933 if is_filtered_offer {
934 CapabilitySource::FilteredAggregateProvider(FilteredAggregateProviderSource {
935 capability,
936 moniker,
937 offer_service_decls: offer_service_decls.into_iter().cloned().collect(),
938 })
939 } else {
940 let members = offers.iter().filter_map(|o| AggregateMember::try_from(*o).ok()).collect();
941 CapabilitySource::AnonymizedAggregate(AnonymizedAggregateSource {
942 capability,
943 moniker,
944 members,
945 instances: vec![],
946 })
947 }
948}
949
950fn group_use_aggregates<'a>(
954 uses: &'a [cm_rust::UseDecl],
955) -> impl Iterator<Item = Vec<&'a cm_rust::UseDecl>> + 'a {
956 let mut groupings = HashMap::with_capacity(uses.len());
957 let mut ungroupable_uses = Vec::new();
958 for use_ in uses.iter() {
959 if let Some(target_path) = use_.path() {
960 groupings.entry(target_path).or_insert_with(|| Vec::with_capacity(1)).push(use_);
961 } else {
962 ungroupable_uses.push(use_);
963 }
964 }
965 groupings.into_values().chain(ungroupable_uses.into_iter().map(|u| vec![u]))
966}
967
968fn group_offer_aggregates<'a>(
972 offers: &'a [cm_rust::offer::OfferDecl],
973) -> impl Iterator<Item = Vec<&'a cm_rust::offer::OfferDecl>> + 'a {
974 let mut groupings = HashMap::with_capacity(offers.len());
975
976 for offer in offers {
977 groupings
978 .entry((offer.target(), offer.target_name()))
979 .or_insert_with(|| Vec::with_capacity(1))
980 .push(offer);
981 }
982 groupings.into_values()
983}
984
985fn group_expose_aggregates<'a>(
987 exposes: &'a [cm_rust::ExposeDecl],
988) -> impl Iterator<Item = Vec<&'a cm_rust::ExposeDecl>> + 'a {
989 let mut groupings = HashMap::with_capacity(exposes.len());
990 for expose in exposes {
991 groupings
992 .entry((expose.target(), expose.target_name()))
993 .or_insert_with(|| Vec::with_capacity(1))
994 .push(expose);
995 }
996 groupings.into_values()
997}
998
999fn build_environment<C: ComponentInstanceInterface + 'static>(
1000 component: &Arc<C>,
1001 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dictionary>>,
1002 component_input: &ComponentInput,
1003 environment_decl: &cm_rust::EnvironmentDecl,
1004 program_output_dict: &Dictionary,
1005 error_reporter: &impl ErrorReporter,
1006) -> ComponentEnvironment {
1007 let mut environment = ComponentEnvironment::new();
1008 if environment_decl.extends == fdecl::EnvironmentExtends::Realm {
1009 environment = component_input.environment().shallow_copy();
1010 }
1011 environment.set_name(&environment_decl.name);
1012 if let Some(stop_timeout_ms) = environment_decl.stop_timeout_ms {
1013 environment.set_stop_timeout(stop_timeout_ms as i64);
1014 }
1015 let debug = environment_decl.debug_capabilities.iter().map(|debug_registration| {
1016 let cm_rust::DebugRegistration::Protocol(debug) = debug_registration;
1017 (
1018 &debug.source_name,
1019 debug.target_name.clone(),
1020 &debug.source,
1021 CapabilityTypeName::Protocol,
1022 RouteRequestErrorInfo::from(debug_registration),
1023 )
1024 });
1025 let runners = environment_decl.runners.iter().map(|runner| {
1026 (
1027 &runner.source_name,
1028 runner.target_name.clone(),
1029 &runner.source,
1030 CapabilityTypeName::Runner,
1031 RouteRequestErrorInfo::from(runner),
1032 )
1033 });
1034 let resolvers = environment_decl.resolvers.iter().map(|resolver| {
1035 (
1036 &resolver.resolver,
1037 Name::new(&resolver.scheme).unwrap(),
1038 &resolver.source,
1039 CapabilityTypeName::Resolver,
1040 RouteRequestErrorInfo::from(resolver),
1041 )
1042 });
1043 let moniker = component.moniker();
1044 for (source_name, target_name, source, porcelain_type, route_request) in
1045 debug.chain(runners).chain(resolvers)
1046 {
1047 let source_path =
1048 SeparatedPath { dirname: Default::default(), basename: source_name.clone() };
1049 let router: Router<Connector> = match &source {
1050 cm_rust::RegistrationSource::Parent => {
1051 use_from_parent_router::<Connector>(component_input, source_path, moniker)
1052 }
1053 cm_rust::RegistrationSource::Self_ => program_output_dict
1054 .get_router_or_not_found::<Connector>(
1055 &source_path,
1056 RoutingError::use_from_self_not_found(
1057 moniker,
1058 source_path.iter_segments().join("/"),
1059 ),
1060 ),
1061 cm_rust::RegistrationSource::Child(child_name) => {
1062 let child_name = ChildName::parse(child_name).expect("invalid child name");
1063 let Some(child_component_output) =
1064 child_component_output_dictionary_routers.get(&child_name)
1065 else {
1066 continue;
1067 };
1068 child_component_output.clone().lazy_get(
1069 source_path,
1070 RoutingError::use_from_child_expose_not_found(
1071 &child_name,
1072 moniker,
1073 source_name.clone(),
1074 ),
1075 )
1076 }
1077 };
1078 let router = router
1079 .with_porcelain_no_default(porcelain_type)
1080 .availability(Availability::Required)
1081 .target(component)
1082 .error_info(route_request)
1083 .error_reporter(error_reporter.clone());
1084 let dict_to_insert_to = match porcelain_type {
1085 CapabilityTypeName::Protocol => environment.debug(),
1086 CapabilityTypeName::Runner => environment.runners(),
1087 CapabilityTypeName::Resolver => environment.resolvers(),
1088 c => panic!("unexpected capability type {}", c),
1089 };
1090 let prev = dict_to_insert_to.insert_capability(&target_name, router.into());
1091 if prev.is_some() {
1092 log::warn!("failed to insert {porcelain_type} {target_name}: preexisting value");
1093 }
1094 }
1095 environment
1096}
1097
1098pub fn extend_dict_with_offers<C: ComponentInstanceInterface + 'static>(
1100 component: &Arc<C>,
1101 static_offers: &[cm_rust::offer::OfferDecl],
1102 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dictionary>>,
1103 component_input: &ComponentInput,
1104 dynamic_offers: &[cm_rust::offer::OfferDecl],
1105 program_output_dict: &Dictionary,
1106 framework_router: &Router<Dictionary>,
1107 capability_sourced_capabilities_dict: &Dictionary,
1108 target_input: &ComponentInput,
1109 error_reporter: impl ErrorReporter,
1110 aggregate_router_fn: &AggregateRouterFn<C>,
1111) {
1112 for offer_bundle in group_offer_aggregates(dynamic_offers).into_iter() {
1113 let first_offer = offer_bundle.first().unwrap();
1114 match first_offer {
1115 cm_rust::offer::OfferDecl::Service(_) => {
1116 let static_offer_bundles = group_offer_aggregates(static_offers);
1117 let maybe_static_offer_bundle = static_offer_bundles.into_iter().find(|bundle| {
1118 bundle.first().unwrap().target_name() == first_offer.target_name()
1119 });
1120 let mut combined_offer_bundle = offer_bundle.clone();
1121 if let Some(mut static_offer_bundle) = maybe_static_offer_bundle {
1122 let _ = target_input.capabilities().remove(first_offer.target_name());
1127 combined_offer_bundle.append(&mut static_offer_bundle);
1128 }
1129 if combined_offer_bundle.len() == 1
1130 && !matches!(first_offer.source(), cm_rust::offer::OfferSource::Collection(_))
1131 {
1132 extend_dict_with_offer::<DirConnector, _>(
1133 component,
1134 &child_component_output_dictionary_routers,
1135 &component_input,
1136 &program_output_dict,
1137 framework_router,
1138 &capability_sourced_capabilities_dict,
1139 first_offer,
1140 &target_input.capabilities(),
1141 error_reporter.clone(),
1142 )
1143 } else {
1144 let aggregate_router = new_aggregate_router_from_service_offers(
1145 &combined_offer_bundle,
1146 component,
1147 &child_component_output_dictionary_routers,
1148 &component_input,
1149 &program_output_dict,
1150 framework_router,
1151 &capability_sourced_capabilities_dict,
1152 error_reporter.clone(),
1153 aggregate_router_fn,
1154 );
1155 let prev = target_input
1156 .capabilities()
1157 .insert(first_offer.target_name().clone(), aggregate_router.into());
1158 assert!(prev.is_none(), "failed to insert capability into target dict");
1159 }
1160 }
1161 cm_rust::offer::OfferDecl::Config(_) => extend_dict_with_offer::<Data, _>(
1162 component,
1163 &child_component_output_dictionary_routers,
1164 component_input,
1165 program_output_dict,
1166 framework_router,
1167 capability_sourced_capabilities_dict,
1168 first_offer,
1169 &target_input.capabilities(),
1170 error_reporter.clone(),
1171 ),
1172 cm_rust::offer::OfferDecl::Dictionary(_) => extend_dict_with_offer::<Dictionary, _>(
1173 component,
1174 &child_component_output_dictionary_routers,
1175 component_input,
1176 program_output_dict,
1177 framework_router,
1178 capability_sourced_capabilities_dict,
1179 first_offer,
1180 &target_input.capabilities(),
1181 error_reporter.clone(),
1182 ),
1183 cm_rust::offer::OfferDecl::Directory(_) | cm_rust::offer::OfferDecl::Storage(_) => {
1184 extend_dict_with_offer::<DirConnector, _>(
1185 component,
1186 &child_component_output_dictionary_routers,
1187 component_input,
1188 program_output_dict,
1189 framework_router,
1190 capability_sourced_capabilities_dict,
1191 first_offer,
1192 &target_input.capabilities(),
1193 error_reporter.clone(),
1194 )
1195 }
1196 cm_rust::offer::OfferDecl::Protocol(_)
1197 | cm_rust::offer::OfferDecl::Runner(_)
1198 | cm_rust::offer::OfferDecl::Resolver(_) => extend_dict_with_offer::<Connector, _>(
1199 component,
1200 &child_component_output_dictionary_routers,
1201 component_input,
1202 program_output_dict,
1203 framework_router,
1204 capability_sourced_capabilities_dict,
1205 first_offer,
1206 &target_input.capabilities(),
1207 error_reporter.clone(),
1208 ),
1209 _ => {}
1210 }
1211 }
1212}
1213
1214pub fn is_supported_use(use_: &cm_rust::UseDecl) -> bool {
1215 matches!(
1216 use_,
1217 cm_rust::UseDecl::Config(_)
1218 | cm_rust::UseDecl::Protocol(_)
1219 | cm_rust::UseDecl::Runner(_)
1220 | cm_rust::UseDecl::Service(_)
1221 | cm_rust::UseDecl::Directory(_)
1222 | cm_rust::UseDecl::EventStream(_)
1223 | cm_rust::UseDecl::Dictionary(_)
1224 | cm_rust::UseDecl::Storage(_)
1225 )
1226}
1227
1228fn extend_dict_with_config_use<C: ComponentInstanceInterface + 'static>(
1231 component: &Arc<C>,
1232 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dictionary>>,
1233 component_input: &ComponentInput,
1234 program_input: &ProgramInput,
1235 program_output_dict: &Dictionary,
1236 config_use: &cm_rust::UseConfigurationDecl,
1237 error_reporter: impl ErrorReporter,
1238) {
1239 let moniker = component.moniker();
1240 let source_path = config_use.source_path();
1241 let porcelain_type = CapabilityTypeName::Config;
1242 let router: Router<Data> = match config_use.source() {
1243 cm_rust::UseSource::Parent => {
1244 use_from_parent_router::<Data>(component_input, source_path.to_owned(), moniker)
1245 }
1246 cm_rust::UseSource::Self_ => program_output_dict.get_router_or_not_found::<Data>(
1247 &source_path,
1248 RoutingError::use_from_self_not_found(moniker, source_path.iter_segments().join("/")),
1249 ),
1250 cm_rust::UseSource::Child(child_name) => {
1251 let child_name = ChildName::parse(child_name).expect("invalid child name");
1252 let Some(child_component_output) =
1253 child_component_output_dictionary_routers.get(&child_name)
1254 else {
1255 panic!(
1256 "use declaration in manifest for component {} has a source of a nonexistent child {}, this should be prevented by manifest validation",
1257 moniker, child_name
1258 );
1259 };
1260 child_component_output.clone().lazy_get(
1261 source_path.to_owned(),
1262 RoutingError::use_from_child_expose_not_found(
1263 &child_name,
1264 &moniker,
1265 config_use.source_name().clone(),
1266 ),
1267 )
1268 }
1269 cm_rust::UseSource::Environment => return,
1271 cm_rust::UseSource::Debug => return,
1272 cm_rust::UseSource::Capability(_) => return,
1273 cm_rust::UseSource::Framework => return,
1274 cm_rust::UseSource::Collection(_) => return,
1275 };
1276
1277 let availability = *config_use.availability();
1278 let prev = program_input.config().insert_capability(
1279 &config_use.target_name,
1280 router
1281 .with_porcelain_with_default(porcelain_type)
1282 .availability(availability)
1283 .target(component)
1284 .error_info(config_use)
1285 .error_reporter(error_reporter)
1286 .into(),
1287 );
1288 assert!(prev.is_none(), "failed to insert {}: preexisting value", config_use.target_name);
1289}
1290
1291fn extend_dict_with_event_stream_uses<C: ComponentInstanceInterface + 'static>(
1292 component: &Arc<C>,
1293 component_input: &ComponentInput,
1294 program_input: &ProgramInput,
1295 uses: Vec<&cm_rust::UseDecl>,
1296 error_reporter: impl ErrorReporter,
1297 event_stream_use_router_fn: &EventStreamUseRouterFn<C>,
1298) {
1299 let use_event_stream_decls = uses.into_iter().map(|u| match u {
1300 cm_rust::UseDecl::EventStream(decl) => decl,
1301 _other_use => panic!("conflicting use types share target path, this should be prevented by manifest validation"),
1302 }).collect::<Vec<_>>();
1303 let moniker = component.moniker();
1304 let porcelain_type = CapabilityTypeName::EventStream;
1305 let target_path = use_event_stream_decls.first().unwrap().target_path.clone();
1306 for use_event_stream_decl in &use_event_stream_decls {
1307 assert_eq!(
1308 &use_event_stream_decl.source,
1309 &cm_rust::UseSource::Parent,
1310 "event streams can only be used from parent, anything else should be caught by \
1311 manifest validation",
1312 );
1313 }
1314 let routers = use_event_stream_decls
1315 .into_iter()
1316 .map(|use_event_stream_decl| {
1317 let source_path = use_event_stream_decl.source_path().to_owned();
1318 let mut router_builder =
1319 use_from_parent_router::<Dictionary>(component_input, source_path, &moniker)
1320 .with_porcelain_with_default(porcelain_type)
1321 .availability(use_event_stream_decl.availability)
1322 .target(component)
1323 .error_info(RouteRequestErrorInfo::from(use_event_stream_decl.as_ref()))
1324 .error_reporter(error_reporter.clone());
1325 if let Some(scope) = &use_event_stream_decl.scope {
1326 router_builder =
1327 router_builder.event_stream_scope((component.moniker().clone(), scope.clone()));
1328 }
1329 let router = router_builder.build();
1330 let filter = use_event_stream_decl.filter.clone();
1331 EventStreamSourceRouter { router, filter }
1332 })
1333 .collect::<Vec<_>>();
1334
1335 let router = event_stream_use_router_fn(component, routers);
1336 let prev = program_input.namespace().insert_capability(&target_path, router.into());
1337 assert!(prev.is_none(), "failed to insert {target_path}: preexisting value");
1338}
1339
1340fn extend_dict_with_use<T, C: ComponentInstanceInterface + 'static>(
1341 component: &Arc<C>,
1342 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dictionary>>,
1343 component_input: &ComponentInput,
1344 program_input: &ProgramInput,
1345 program_output_dict: &Dictionary,
1346 framework_router: &Router<Dictionary>,
1347 capability_sourced_capabilities_dict: &Dictionary,
1348 use_: &cm_rust::UseDecl,
1349 error_reporter: impl ErrorReporter,
1350) where
1351 T: CapabilityBound + Clone,
1352 Router<T>: TryFrom<Capability> + Into<Capability>,
1353{
1354 if !is_supported_use(use_) {
1355 return;
1356 }
1357 let moniker = component.moniker();
1358 if let cm_rust::UseDecl::Config(config) = use_ {
1359 return extend_dict_with_config_use(
1360 component,
1361 child_component_output_dictionary_routers,
1362 component_input,
1363 program_input,
1364 program_output_dict,
1365 config,
1366 error_reporter,
1367 );
1368 };
1369
1370 let source_path = use_.source_path();
1371 let porcelain_type = CapabilityTypeName::from(use_);
1372 let router: Router<T> = match use_.source() {
1373 cm_rust::UseSource::Parent => {
1374 use_from_parent_router::<T>(component_input, source_path.to_owned(), moniker)
1375 }
1376 cm_rust::UseSource::Self_ => program_output_dict.get_router_or_not_found::<T>(
1377 &source_path,
1378 RoutingError::use_from_self_not_found(moniker, source_path.iter_segments().join("/")),
1379 ),
1380 cm_rust::UseSource::Child(child_name) => {
1381 let child_name = ChildName::parse(child_name).expect("invalid child name");
1382 let Some(child_component_output) =
1383 child_component_output_dictionary_routers.get(&child_name)
1384 else {
1385 panic!(
1386 "use declaration in manifest for component {} has a source of a nonexistent child {}, this should be prevented by manifest validation",
1387 moniker, child_name
1388 );
1389 };
1390 child_component_output.clone().lazy_get(
1391 source_path.to_owned(),
1392 RoutingError::use_from_child_expose_not_found(
1393 &child_name,
1394 &moniker,
1395 use_.source_name().clone(),
1396 ),
1397 )
1398 }
1399 cm_rust::UseSource::Framework if use_.is_from_dictionary() => {
1400 Router::<T>::new_error(RoutingError::capability_from_framework_not_found(
1401 moniker,
1402 source_path.iter_segments().join("/"),
1403 ))
1404 }
1405 cm_rust::UseSource::Framework => {
1406 query_framework_router_or_not_found(framework_router, &source_path, component)
1407 }
1408 cm_rust::UseSource::Capability(capability_name) => {
1409 let err = RoutingError::capability_from_capability_not_found(
1410 moniker,
1411 capability_name.as_str().to_string(),
1412 );
1413 if source_path.iter_segments().join("/") == fsys::StorageAdminMarker::PROTOCOL_NAME
1414 || source_path.iter_segments().join("/")
1415 == fcomponent::StorageAdminMarker::PROTOCOL_NAME
1416 {
1417 capability_sourced_capabilities_dict.get_router_or_not_found(&capability_name, err)
1418 } else {
1419 Router::<T>::new_error(err)
1420 }
1421 }
1422 cm_rust::UseSource::Debug => {
1423 let cm_rust::UseDecl::Protocol(use_protocol) = use_ else {
1424 panic!(
1425 "non-protocol capability used with a debug source, this should be prevented by manifest validation"
1426 );
1427 };
1428 component_input.environment().debug().get_router_or_not_found::<T>(
1429 &use_protocol.source_name,
1430 RoutingError::use_from_environment_not_found(
1431 moniker,
1432 "protocol",
1433 &use_protocol.source_name,
1434 ),
1435 )
1436 }
1437 cm_rust::UseSource::Environment => {
1438 let cm_rust::UseDecl::Runner(use_runner) = use_ else {
1439 panic!(
1440 "non-runner capability used with an environment source, this should be prevented by manifest validation"
1441 );
1442 };
1443 component_input.environment().runners().get_router_or_not_found::<T>(
1444 &use_runner.source_name,
1445 RoutingError::use_from_environment_not_found(
1446 moniker,
1447 "runner",
1448 &use_runner.source_name,
1449 ),
1450 )
1451 }
1452 cm_rust::UseSource::Collection(_) => {
1453 return;
1455 }
1456 };
1457
1458 let availability = *use_.availability();
1459 let mut router_builder = router
1460 .with_porcelain_with_default(porcelain_type)
1461 .availability(availability)
1462 .target(&component)
1463 .error_info(use_)
1464 .error_reporter(error_reporter);
1465 if let cm_rust::UseDecl::Directory(decl) = use_ {
1466 router_builder = router_builder
1467 .rights(Some(decl.rights.into()))
1468 .subdir(decl.subdir.clone().into())
1469 .inherit_rights(false);
1470 }
1471 if let cm_rust::UseDecl::Service(_) = use_ {
1472 router_builder = router_builder.rights(Some(fio::R_STAR_DIR.into())).inherit_rights(false);
1473 }
1474 if let cm_rust::UseDecl::Storage(_) = use_ {
1475 router_builder = router_builder
1476 .rights(Some(fidl_fuchsia_io::RW_STAR_DIR.into()))
1477 .subdir(cm_types::RelativePath::dot().into())
1478 .inherit_rights(false);
1479 }
1480 let router = router_builder.build();
1481
1482 match use_ {
1483 cm_rust::UseDecl::Protocol(cm_rust::UseProtocolDecl {
1484 numbered_handle: Some(numbered_handle),
1485 ..
1486 }) => {
1487 let numbered_handle = Name::from(*numbered_handle);
1488 let prev =
1489 program_input.numbered_handles().insert_capability(&numbered_handle, router.into());
1490 assert!(prev.is_none(), "failed to insert {numbered_handle}: preexisting value");
1491 }
1492 cm_rust::UseDecl::Runner(_) => {
1493 assert!(program_input.runner().is_none(), "component can't use multiple runners");
1494 program_input.set_runner(router.into());
1495 }
1496 _ => {
1497 let prev =
1498 program_input.namespace().insert_capability(use_.path().unwrap(), router.into());
1499 assert!(prev.is_none(), "failed to insert {}: preexisting value", use_.path().unwrap());
1500 }
1501 }
1502}
1503
1504fn extend_dict_with_dictionary_use<C: ComponentInstanceInterface + 'static>(
1505 component: &Arc<C>,
1506 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dictionary>>,
1507 component_input: &ComponentInput,
1508 program_input: &ProgramInput,
1509 program_output_dict: &Dictionary,
1510 framework_router: &Router<Dictionary>,
1511 capability_sourced_capabilities_dict: &Dictionary,
1512 use_bundle: Vec<&cm_rust::UseDecl>,
1513 error_reporter: impl ErrorReporter,
1514) {
1515 let path = use_bundle[0].path().unwrap();
1516 let mut dictionary_routers = vec![];
1517 for use_ in use_bundle.iter() {
1518 let dict_for_used_router = ProgramInput::new(Dictionary::new(), None, Dictionary::new());
1519 extend_dict_with_use::<Dictionary, _>(
1520 component,
1521 child_component_output_dictionary_routers,
1522 component_input,
1523 &dict_for_used_router,
1524 program_output_dict,
1525 framework_router,
1526 capability_sourced_capabilities_dict,
1527 use_,
1528 error_reporter.clone(),
1529 );
1530 let dictionary_router = match dict_for_used_router.namespace().get_capability(path) {
1531 Some(Capability::DictionaryRouter(router)) => router,
1532 other_value => panic!("unexpected dictionary get result: {other_value:?}"),
1533 };
1534 dictionary_routers.push(dictionary_router);
1535 }
1536 let original_dictionary = match program_input.namespace().get_capability(path) {
1537 Some(Capability::Dictionary(dictionary)) => dictionary,
1538 _ => Dictionary::new(),
1539 };
1540 let router = UseDictionaryRouter::new(
1541 path.clone(),
1542 component.moniker().clone(),
1543 original_dictionary,
1544 dictionary_routers,
1545 CapabilitySource::Component(ComponentSource {
1546 capability: ComponentCapability::Use_((*use_bundle.first().unwrap()).clone()),
1547 moniker: component.moniker().clone(),
1548 }),
1549 );
1550 let _ = program_input.namespace().insert_capability(path, router.into());
1553}
1554
1555fn use_from_parent_router<T>(
1557 component_input: &ComponentInput,
1558 source_path: impl IterablePath + 'static + Debug,
1559 moniker: &Moniker,
1560) -> Router<T>
1561where
1562 T: CapabilityBound + Clone,
1563 Router<T>: TryFrom<Capability>,
1564{
1565 let err = if moniker == &Moniker::root() {
1566 RoutingError::register_from_component_manager_not_found(
1567 source_path.iter_segments().join("/"),
1568 )
1569 } else {
1570 RoutingError::use_from_parent_not_found(moniker, source_path.iter_segments().join("/"))
1571 };
1572 component_input.capabilities().get_router_or_not_found::<T>(&source_path, err)
1573}
1574
1575fn is_supported_offer(offer: &cm_rust::offer::OfferDecl) -> bool {
1576 matches!(
1577 offer,
1578 cm_rust::offer::OfferDecl::Config(_)
1579 | cm_rust::offer::OfferDecl::Protocol(_)
1580 | cm_rust::offer::OfferDecl::Dictionary(_)
1581 | cm_rust::offer::OfferDecl::Directory(_)
1582 | cm_rust::offer::OfferDecl::Runner(_)
1583 | cm_rust::offer::OfferDecl::Resolver(_)
1584 | cm_rust::offer::OfferDecl::Service(_)
1585 | cm_rust::offer::OfferDecl::EventStream(_)
1586 | cm_rust::offer::OfferDecl::Storage(_)
1587 )
1588}
1589
1590fn extend_dict_with_offer<T, C: ComponentInstanceInterface + 'static>(
1591 component: &Arc<C>,
1592 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dictionary>>,
1593 component_input: &ComponentInput,
1594 program_output_dict: &Dictionary,
1595 framework_router: &Router<Dictionary>,
1596 capability_sourced_capabilities_dict: &Dictionary,
1597 offer: &cm_rust::offer::OfferDecl,
1598 target_dict: &Dictionary,
1599 error_reporter: impl ErrorReporter,
1600) where
1601 T: CapabilityBound + Clone,
1602 Router<T>: TryFrom<Capability> + Into<Capability> + WithServiceRenamesAndFilter,
1603{
1604 assert!(is_supported_offer(offer), "{offer:?}");
1605
1606 let source_path = offer.source_path();
1607 let target_name = offer.target_name();
1608 let porcelain_type = CapabilityTypeName::from(offer);
1609 let router: Router<T> = match offer.source() {
1610 cm_rust::offer::OfferSource::Parent => {
1611 let err = if component.moniker() == &Moniker::root() {
1612 RoutingError::register_from_component_manager_not_found(
1613 offer.source_name().to_string(),
1614 )
1615 } else {
1616 RoutingError::offer_from_parent_not_found(
1617 &component.moniker(),
1618 source_path.iter_segments().join("/"),
1619 )
1620 };
1621 component_input.capabilities().get_router_or_not_found::<T>(&source_path, err)
1622 }
1623 cm_rust::offer::OfferSource::Self_ => program_output_dict.get_router_or_not_found::<T>(
1624 &source_path,
1625 RoutingError::offer_from_self_not_found(
1626 &component.moniker(),
1627 source_path.iter_segments().join("/"),
1628 ),
1629 ),
1630 cm_rust::offer::OfferSource::Child(child_ref) => {
1631 let child_name: ChildName = child_ref.clone().try_into().expect("invalid child ref");
1632 match child_component_output_dictionary_routers.get(&child_name) {
1633 None => Router::<T>::new_error(RoutingError::offer_from_child_instance_not_found(
1634 &child_name,
1635 &component.moniker(),
1636 source_path.iter_segments().join("/"),
1637 )),
1638 Some(child_component_output) => child_component_output.clone().lazy_get(
1639 source_path.to_owned(),
1640 RoutingError::offer_from_child_expose_not_found(
1641 &child_name,
1642 &component.moniker(),
1643 offer.source_name().clone(),
1644 ),
1645 ),
1646 }
1647 }
1648 cm_rust::offer::OfferSource::Framework => {
1649 if offer.is_from_dictionary() {
1650 warn!(
1651 "routing from framework with dictionary path is not supported: {source_path}"
1652 );
1653 return;
1654 }
1655 query_framework_router_or_not_found(framework_router, &source_path, component)
1656 }
1657 cm_rust::offer::OfferSource::Capability(capability_name) => {
1658 let err = RoutingError::capability_from_capability_not_found(
1659 &component.moniker(),
1660 capability_name.as_str().to_string(),
1661 );
1662 if source_path.iter_segments().join("/") == fsys::StorageAdminMarker::PROTOCOL_NAME
1663 || source_path.iter_segments().join("/")
1664 == fcomponent::StorageAdminMarker::PROTOCOL_NAME
1665 {
1666 capability_sourced_capabilities_dict.get_router_or_not_found(&capability_name, err)
1667 } else {
1668 Router::<T>::new_error(err)
1669 }
1670 }
1671 cm_rust::offer::OfferSource::Void => UnavailableRouter::new_from_offer(offer, component),
1672 cm_rust::offer::OfferSource::Collection(_collection_name) => {
1673 return;
1678 }
1679 };
1680
1681 let availability = *offer.availability();
1682 let mut router_builder = router
1683 .with_porcelain_with_default(porcelain_type)
1684 .availability(availability)
1685 .target(component)
1686 .error_info(offer)
1687 .error_reporter(error_reporter);
1688 if let cm_rust::offer::OfferDecl::Directory(decl) = offer {
1689 router_builder = router_builder
1699 .rights(decl.rights.clone().map(Into::into))
1700 .subdir(decl.subdir.clone().into())
1701 .inherit_rights(true);
1702 }
1703 if let cm_rust::offer::OfferDecl::Storage(_) = offer {
1704 router_builder = router_builder
1705 .rights(Some(fio::RW_STAR_DIR.into()))
1706 .inherit_rights(false)
1707 .subdir(cm_types::RelativePath::dot().into());
1708 }
1709 if let cm_rust::offer::OfferDecl::Service(_) = offer {
1710 router_builder = router_builder.rights(Some(fio::R_STAR_DIR.into())).inherit_rights(true);
1711 }
1712 if let cm_rust::offer::OfferDecl::EventStream(offer_event_stream) = offer {
1713 if let Some(scope) = &offer_event_stream.scope {
1714 router_builder =
1715 router_builder.event_stream_scope((component.moniker().clone(), scope.clone()));
1716 }
1717 }
1718
1719 let router = if let cm_rust::OfferDecl::Service(_) = offer {
1720 router_builder.build().with_service_renames_and_filter(offer.clone())
1721 } else {
1722 router_builder.build().into()
1723 };
1724
1725 let prev = target_dict.insert_capability(target_name, router.into());
1726 assert!(prev.is_none(), "failed to insert {target_name}: preexisting value");
1727}
1728
1729fn query_framework_router_or_not_found<T, C>(
1730 router: &Router<Dictionary>,
1731 path: &BorrowedSeparatedPath<'_>,
1732 component: &Arc<C>,
1733) -> Router<T>
1734where
1735 T: CapabilityBound,
1736 Router<T>: TryFrom<Capability>,
1737 C: ComponentInstanceInterface + 'static,
1738{
1739 let dict: Result<Option<Dictionary>, RouterError> = router
1740 .route(RouteRequest::default(), component.as_weak().into())
1741 .now_or_never()
1742 .expect("failed to now_or_never");
1743 let dict = match dict {
1744 Ok(Some(dict)) => dict,
1745 _ => Dictionary::new(),
1747 };
1748 dict.get_router_or_not_found::<T>(
1751 path,
1752 RoutingError::capability_from_framework_not_found(
1753 &component.moniker(),
1754 path.iter_segments().join("/"),
1755 ),
1756 )
1757}
1758
1759pub fn is_supported_expose(expose: &cm_rust::ExposeDecl) -> bool {
1760 matches!(
1761 expose,
1762 cm_rust::ExposeDecl::Config(_)
1763 | cm_rust::ExposeDecl::Protocol(_)
1764 | cm_rust::ExposeDecl::Dictionary(_)
1765 | cm_rust::ExposeDecl::Directory(_)
1766 | cm_rust::ExposeDecl::Runner(_)
1767 | cm_rust::ExposeDecl::Resolver(_)
1768 | cm_rust::ExposeDecl::Service(_)
1769 )
1770}
1771
1772fn extend_dict_with_expose<T, C: ComponentInstanceInterface + 'static>(
1773 component: &Arc<C>,
1774 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dictionary>>,
1775 program_output_dict: &Dictionary,
1776 framework_router: &Router<Dictionary>,
1777 capability_sourced_capabilities_dict: &Dictionary,
1778 expose: &cm_rust::ExposeDecl,
1779 target_component_output: &ComponentOutput,
1780 error_reporter: impl ErrorReporter,
1781) where
1782 T: CapabilityBound + Clone,
1783 Router<T>: TryFrom<Capability> + Into<Capability>,
1784{
1785 assert!(is_supported_expose(expose), "{expose:?}");
1786
1787 let target_dict = match expose.target() {
1788 cm_rust::ExposeTarget::Parent => target_component_output.capabilities(),
1789 cm_rust::ExposeTarget::Framework => target_component_output.framework(),
1790 };
1791 let source_path = expose.source_path();
1792 let target_name = expose.target_name();
1793
1794 let porcelain_type = CapabilityTypeName::from(expose);
1795 let router: Router<T> = match expose.source() {
1796 cm_rust::ExposeSource::Self_ => program_output_dict.get_router_or_not_found::<T>(
1797 &source_path,
1798 RoutingError::expose_from_self_not_found(
1799 &component.moniker(),
1800 source_path.iter_segments().join("/"),
1801 ),
1802 ),
1803 cm_rust::ExposeSource::Child(child_name) => {
1804 let child_name = ChildName::parse(child_name).expect("invalid static child name");
1805 if let Some(child_component_output) =
1806 child_component_output_dictionary_routers.get(&child_name)
1807 {
1808 child_component_output.clone().lazy_get(
1809 source_path.to_owned(),
1810 RoutingError::expose_from_child_expose_not_found(
1811 &child_name,
1812 &component.moniker(),
1813 expose.source_name().clone(),
1814 ),
1815 )
1816 } else {
1817 Router::<T>::new_error(RoutingError::expose_from_child_instance_not_found(
1818 &child_name,
1819 &component.moniker(),
1820 expose.source_name().clone(),
1821 ))
1822 }
1823 }
1824 cm_rust::ExposeSource::Framework => {
1825 if expose.is_from_dictionary() {
1826 warn!(
1827 "routing from framework with dictionary path is not supported: {source_path}"
1828 );
1829 return;
1830 }
1831 query_framework_router_or_not_found(framework_router, &source_path, component)
1832 }
1833 cm_rust::ExposeSource::Capability(capability_name) => {
1834 let err = RoutingError::capability_from_capability_not_found(
1835 &component.moniker(),
1836 capability_name.as_str().to_string(),
1837 );
1838 if source_path.iter_segments().join("/") == fsys::StorageAdminMarker::PROTOCOL_NAME
1839 || source_path.iter_segments().join("/")
1840 == fcomponent::StorageAdminMarker::PROTOCOL_NAME
1841 {
1842 capability_sourced_capabilities_dict
1843 .clone()
1844 .get_router_or_not_found::<T>(&capability_name, err)
1845 } else {
1846 Router::<T>::new_error(err)
1847 }
1848 }
1849 cm_rust::ExposeSource::Void => UnavailableRouter::new_from_expose(expose, component),
1850 cm_rust::ExposeSource::Collection(_name) => return,
1855 };
1856 let availability = *expose.availability();
1857 let mut router_builder = router
1858 .with_porcelain_with_default(porcelain_type)
1859 .availability(availability)
1860 .target(component)
1861 .error_info(expose)
1862 .error_reporter(error_reporter);
1863 if let cm_rust::ExposeDecl::Directory(decl) = expose {
1864 router_builder = router_builder
1865 .rights(decl.rights.clone().map(Into::into))
1866 .subdir(decl.subdir.clone().into())
1867 .inherit_rights(true);
1868 };
1869 if let cm_rust::ExposeDecl::Service(_) = expose {
1870 router_builder = router_builder.rights(Some(fio::R_STAR_DIR.into())).inherit_rights(true);
1871 };
1872 let prev = target_dict.insert_capability(target_name, router_builder.build().into());
1873 assert!(prev.is_none(), "failed to insert {target_name}: preexisting value");
1874}
1875
1876struct UnavailableRouter<C: ComponentInstanceInterface> {
1877 capability: InternalCapability,
1878 component: WeakComponentInstanceInterface<C>,
1879}
1880
1881impl<C: ComponentInstanceInterface + 'static> UnavailableRouter<C> {
1882 fn new<T: CapabilityBound>(capability: InternalCapability, component: &Arc<C>) -> Router<T> {
1883 Router::<T>::new(Self { capability, component: component.as_weak() })
1884 }
1885
1886 fn new_from_offer<T: CapabilityBound>(offer: &OfferDecl, component: &Arc<C>) -> Router<T> {
1887 let name = offer.source_name().clone();
1888 let capability = match offer {
1889 OfferDecl::Service(_) => InternalCapability::Service(name),
1890 OfferDecl::Protocol(_) => InternalCapability::Protocol(name),
1891 OfferDecl::Directory(_) => InternalCapability::Directory(name),
1892 OfferDecl::Storage(_) => InternalCapability::Storage(name),
1893 OfferDecl::Runner(_) => InternalCapability::Runner(name),
1894 OfferDecl::Resolver(_) => InternalCapability::Resolver(name),
1895 OfferDecl::EventStream(_) => {
1896 InternalCapability::EventStream(InternalEventStreamCapability {
1897 name,
1898 scope_moniker: None,
1899 scope: None,
1900 })
1901 }
1902 OfferDecl::Dictionary(_) => InternalCapability::Dictionary(name),
1903 OfferDecl::Config(_) => InternalCapability::Config(name),
1904 };
1905 Self::new(capability, component)
1906 }
1907
1908 fn new_from_expose<T: CapabilityBound>(expose: &ExposeDecl, component: &Arc<C>) -> Router<T> {
1909 let name = expose.source_name().clone();
1910 let capability = match expose {
1911 ExposeDecl::Service(_) => InternalCapability::Service(name),
1912 ExposeDecl::Protocol(_) => InternalCapability::Protocol(name),
1913 ExposeDecl::Directory(_) => InternalCapability::Directory(name),
1914 ExposeDecl::Runner(_) => InternalCapability::Runner(name),
1915 ExposeDecl::Resolver(_) => InternalCapability::Resolver(name),
1916 ExposeDecl::Dictionary(_) => InternalCapability::Dictionary(name),
1917 ExposeDecl::Config(_) => InternalCapability::Config(name),
1918 };
1919 Self::new(capability, component)
1920 }
1921}
1922
1923#[async_trait]
1924impl<T: CapabilityBound, C: ComponentInstanceInterface + 'static> Routable<T>
1925 for UnavailableRouter<C>
1926{
1927 async fn route(
1928 &self,
1929 request: RouteRequest,
1930 _target: WeakInstanceToken,
1931 ) -> Result<Option<T>, RouterError> {
1932 let availability = request.availability.ok_or(RouterError::InvalidArgs)?.fidl_into_native();
1933 match availability {
1934 cm_rust::Availability::Required | cm_rust::Availability::SameAsTarget => {
1935 Err(RoutingError::SourceCapabilityIsVoid {
1936 moniker: self.component.moniker.clone(),
1937 }
1938 .into())
1939 }
1940 cm_rust::Availability::Optional | cm_rust::Availability::Transitional => Ok(None),
1941 }
1942 }
1943
1944 async fn route_debug(
1945 &self,
1946 _request: RouteRequest,
1947 _target: WeakInstanceToken,
1948 ) -> Result<CapabilitySource, RouterError> {
1949 Ok(CapabilitySource::Void(VoidSource {
1950 capability: self.capability.clone(),
1951 moniker: self.component.moniker.clone(),
1952 }))
1953 }
1954}