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