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