Skip to main content

routing/bedrock/
sandbox_construction.rs

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