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