routing/bedrock/
sandbox_construction.rs

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