Skip to main content

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