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