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