routing/bedrock/
sandbox_construction.rs

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