routing/
lib.rs

1// Copyright 2021 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
5pub mod availability;
6pub mod bedrock;
7pub mod capability_source;
8pub mod component_instance;
9pub mod config;
10pub mod environment;
11pub mod error;
12pub mod event;
13pub mod legacy_router;
14pub mod mapper;
15pub mod path;
16pub mod policy;
17pub mod resolving;
18pub mod rights;
19pub mod walk_state;
20
21use crate::bedrock::request_metadata::{
22    dictionary_metadata, protocol_metadata, resolver_metadata, runner_metadata, service_metadata,
23};
24use crate::capability_source::{
25    CapabilitySource, ComponentCapability, ComponentSource, InternalCapability, VoidSource,
26};
27use crate::component_instance::{
28    ComponentInstanceInterface, ResolvedInstanceInterface, WeakComponentInstanceInterface,
29};
30use crate::environment::DebugRegistration;
31use crate::error::RoutingError;
32use crate::legacy_router::{
33    CapabilityVisitor, ErrorNotFoundFromParent, ErrorNotFoundInChild, ExposeVisitor, NoopVisitor,
34    OfferVisitor, RouteBundle, Sources,
35};
36use crate::mapper::DebugRouteMapper;
37use crate::rights::RightsWalker;
38use crate::walk_state::WalkState;
39use cm_rust::{
40    Availability, CapabilityTypeName, ExposeConfigurationDecl, ExposeDecl, ExposeDeclCommon,
41    ExposeDirectoryDecl, ExposeProtocolDecl, ExposeResolverDecl, ExposeRunnerDecl,
42    ExposeServiceDecl, ExposeSource, ExposeTarget, OfferConfigurationDecl, OfferDeclCommon,
43    OfferDictionaryDecl, OfferDirectoryDecl, OfferEventStreamDecl, OfferProtocolDecl,
44    OfferResolverDecl, OfferRunnerDecl, OfferServiceDecl, OfferSource, OfferStorageDecl,
45    OfferTarget, RegistrationDeclCommon, RegistrationSource, ResolverRegistration,
46    RunnerRegistration, SourceName, StorageDecl, StorageDirectorySource, UseConfigurationDecl,
47    UseDecl, UseDeclCommon, UseDirectoryDecl, UseEventStreamDecl, UseProtocolDecl, UseRunnerDecl,
48    UseServiceDecl, UseSource, UseStorageDecl,
49};
50use cm_types::{IterablePath, Name, RelativePath};
51use from_enum::FromEnum;
52use itertools::Itertools;
53use moniker::{ChildName, ExtendedMoniker, Moniker, MonikerError};
54use router_error::Explain;
55use sandbox::{
56    Capability, CapabilityBound, Connector, Data, Dict, DirEntry, Request, Routable, Router,
57    RouterResponse,
58};
59use std::sync::Arc;
60use {fidl_fuchsia_component_decl as fdecl, fidl_fuchsia_io as fio, zx_status as zx};
61
62pub use bedrock::dict_ext::{DictExt, GenericRouterResponse};
63pub use bedrock::lazy_get::LazyGet;
64pub use bedrock::weak_instance_token_ext::{test_invalid_instance_token, WeakInstanceTokenExt};
65pub use bedrock::with_porcelain::WithPorcelain;
66
67#[cfg(feature = "serde")]
68use serde::{Deserialize, Serialize};
69
70/// A request to route a capability, together with the data needed to do so.
71#[derive(Clone, Debug)]
72pub enum RouteRequest {
73    // Route a capability from an ExposeDecl.
74    ExposeDirectory(ExposeDirectoryDecl),
75    ExposeProtocol(ExposeProtocolDecl),
76    ExposeService(RouteBundle<ExposeServiceDecl>),
77    ExposeRunner(ExposeRunnerDecl),
78    ExposeResolver(ExposeResolverDecl),
79    ExposeConfig(ExposeConfigurationDecl),
80
81    // Route a capability from a realm's environment.
82    Resolver(ResolverRegistration),
83
84    // Route the directory capability that backs a storage capability.
85    StorageBackingDirectory(StorageDecl),
86
87    // Route a capability from a UseDecl.
88    UseDirectory(UseDirectoryDecl),
89    UseEventStream(UseEventStreamDecl),
90    UseProtocol(UseProtocolDecl),
91    UseService(UseServiceDecl),
92    UseStorage(UseStorageDecl),
93    UseRunner(UseRunnerDecl),
94    UseConfig(UseConfigurationDecl),
95
96    // Route a capability from an OfferDecl.
97    OfferDirectory(OfferDirectoryDecl),
98    OfferEventStream(OfferEventStreamDecl),
99    OfferProtocol(OfferProtocolDecl),
100    OfferService(RouteBundle<OfferServiceDecl>),
101    OfferStorage(OfferStorageDecl),
102    OfferRunner(OfferRunnerDecl),
103    OfferResolver(OfferResolverDecl),
104    OfferConfig(OfferConfigurationDecl),
105    OfferDictionary(OfferDictionaryDecl),
106}
107
108impl From<UseDecl> for RouteRequest {
109    fn from(decl: UseDecl) -> Self {
110        match decl {
111            UseDecl::Directory(decl) => Self::UseDirectory(decl),
112            UseDecl::Protocol(decl) => Self::UseProtocol(decl),
113            UseDecl::Service(decl) => Self::UseService(decl),
114            UseDecl::Storage(decl) => Self::UseStorage(decl),
115            UseDecl::EventStream(decl) => Self::UseEventStream(decl),
116            UseDecl::Runner(decl) => Self::UseRunner(decl),
117            UseDecl::Config(decl) => Self::UseConfig(decl),
118        }
119    }
120}
121
122impl RouteRequest {
123    pub fn from_expose_decls(
124        moniker: &Moniker,
125        exposes: Vec<&ExposeDecl>,
126    ) -> Result<Self, RoutingError> {
127        let first_expose = exposes.first().expect("invalid empty expose list");
128        let first_type_name = CapabilityTypeName::from(*first_expose);
129        assert!(
130            exposes.iter().all(|e| {
131                let type_name: CapabilityTypeName = CapabilityTypeName::from(*e);
132                first_type_name == type_name && first_expose.target_name() == e.target_name()
133            }),
134            "invalid expose input: {:?}",
135            exposes
136        );
137        match first_expose {
138            ExposeDecl::Protocol(e) => {
139                assert!(exposes.len() == 1, "multiple exposes: {:?}", exposes);
140                Ok(Self::ExposeProtocol(e.clone()))
141            }
142            ExposeDecl::Service(_) => {
143                // Gather the exposes into a bundle. Services can aggregate, in which case
144                // multiple expose declarations map to one expose directory entry.
145                let exposes: Vec<_> = exposes
146                    .into_iter()
147                    .filter_map(|e| match e {
148                        cm_rust::ExposeDecl::Service(e) => Some(e.clone()),
149                        _ => None,
150                    })
151                    .collect();
152                Ok(Self::ExposeService(RouteBundle::from_exposes(exposes)))
153            }
154            ExposeDecl::Directory(e) => {
155                assert!(exposes.len() == 1, "multiple exposes");
156                Ok(Self::ExposeDirectory(e.clone()))
157            }
158            ExposeDecl::Runner(e) => {
159                assert!(exposes.len() == 1, "multiple exposes");
160                Ok(Self::ExposeRunner(e.clone()))
161            }
162            ExposeDecl::Resolver(e) => {
163                assert!(exposes.len() == 1, "multiple exposes");
164                Ok(Self::ExposeResolver(e.clone()))
165            }
166            ExposeDecl::Config(e) => {
167                assert!(exposes.len() == 1, "multiple exposes");
168                Ok(Self::ExposeConfig(e.clone()))
169            }
170            ExposeDecl::Dictionary(_) => {
171                // Only bedrock routing supports dictionaries, the legacy RouteRequest does not.
172                Err(RoutingError::unsupported_capability_type(
173                    moniker.clone(),
174                    CapabilityTypeName::Dictionary,
175                ))
176            }
177        }
178    }
179
180    /// Returns the availability of the RouteRequest if supported.
181    pub fn availability(&self) -> Option<Availability> {
182        use crate::RouteRequest::*;
183        match self {
184            UseDirectory(UseDirectoryDecl { availability, .. })
185            | UseEventStream(UseEventStreamDecl { availability, .. })
186            | UseProtocol(UseProtocolDecl { availability, .. })
187            | UseService(UseServiceDecl { availability, .. })
188            | UseConfig(UseConfigurationDecl { availability, .. })
189            | UseStorage(UseStorageDecl { availability, .. }) => Some(*availability),
190
191            ExposeDirectory(decl) => Some(*decl.availability()),
192            ExposeProtocol(decl) => Some(*decl.availability()),
193            ExposeService(decl) => Some(*decl.availability()),
194            ExposeRunner(decl) => Some(*decl.availability()),
195            ExposeResolver(decl) => Some(*decl.availability()),
196            ExposeConfig(decl) => Some(*decl.availability()),
197
198            OfferRunner(decl) => Some(*decl.availability()),
199            OfferResolver(decl) => Some(*decl.availability()),
200            OfferDirectory(decl) => Some(*decl.availability()),
201            OfferEventStream(decl) => Some(*decl.availability()),
202            OfferProtocol(decl) => Some(*decl.availability()),
203            OfferConfig(decl) => Some(*decl.availability()),
204            OfferStorage(decl) => Some(*decl.availability()),
205            OfferDictionary(decl) => Some(*decl.availability()),
206
207            OfferService(_) | Resolver(_) | StorageBackingDirectory(_) | UseRunner(_) => None,
208        }
209    }
210}
211
212impl std::fmt::Display for RouteRequest {
213    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
214        match self {
215            Self::ExposeDirectory(e) => {
216                write!(f, "directory `{}`", e.target_name)
217            }
218            Self::ExposeProtocol(e) => {
219                write!(f, "protocol `{}`", e.target_name)
220            }
221            Self::ExposeService(e) => {
222                write!(f, "service {:?}", e)
223            }
224            Self::ExposeRunner(e) => {
225                write!(f, "runner `{}`", e.target_name)
226            }
227            Self::ExposeResolver(e) => {
228                write!(f, "resolver `{}`", e.target_name)
229            }
230            Self::ExposeConfig(e) => {
231                write!(f, "config `{}`", e.target_name)
232            }
233            Self::Resolver(r) => {
234                write!(f, "resolver `{}`", r.resolver)
235            }
236            Self::UseDirectory(u) => {
237                write!(f, "directory `{}`", u.source_name)
238            }
239            Self::UseProtocol(u) => {
240                write!(f, "protocol `{}`", u.source_name)
241            }
242            Self::UseService(u) => {
243                write!(f, "service `{}`", u.source_name)
244            }
245            Self::UseStorage(u) => {
246                write!(f, "storage `{}`", u.source_name)
247            }
248            Self::UseEventStream(u) => {
249                write!(f, "event stream `{}`", u.source_name)
250            }
251            Self::UseRunner(u) => {
252                write!(f, "runner `{}`", u.source_name)
253            }
254            Self::UseConfig(u) => {
255                write!(f, "config `{}`", u.source_name)
256            }
257            Self::StorageBackingDirectory(u) => {
258                write!(f, "storage backing directory `{}`", u.backing_dir)
259            }
260            Self::OfferDirectory(o) => {
261                write!(f, "directory `{}`", o.target_name)
262            }
263            Self::OfferProtocol(o) => {
264                write!(f, "protocol `{}`", o.target_name)
265            }
266            Self::OfferService(o) => {
267                write!(f, "service {:?}", o)
268            }
269            Self::OfferEventStream(o) => {
270                write!(f, "event stream `{}`", o.target_name)
271            }
272            Self::OfferStorage(o) => {
273                write!(f, "storage `{}`", o.target_name)
274            }
275            Self::OfferResolver(o) => {
276                write!(f, "resolver `{}`", o.target_name)
277            }
278            Self::OfferRunner(o) => {
279                write!(f, "runner `{}`", o.target_name)
280            }
281            Self::OfferConfig(o) => {
282                write!(f, "config `{}`", o.target_name)
283            }
284            Self::OfferDictionary(o) => {
285                write!(f, "dictionary `{}`", o.target_name)
286            }
287        }
288    }
289}
290
291/// The data returned after successfully routing a capability to its source.
292#[derive(Debug)]
293pub struct RouteSource {
294    pub source: CapabilitySource,
295    pub relative_path: RelativePath,
296}
297
298impl RouteSource {
299    pub fn new(source: CapabilitySource) -> Self {
300        Self { source, relative_path: Default::default() }
301    }
302
303    pub fn new_with_relative_path(source: CapabilitySource, relative_path: RelativePath) -> Self {
304        Self { source, relative_path }
305    }
306}
307
308/// Performs a debug route from the `target` for the capability defined in `request`. The source of
309/// the route is returned if the route is valid, otherwise a routing error is returned.
310///
311/// If the capability is not allowed to be routed to the `target`, per the
312/// [`crate::model::policy::GlobalPolicyChecker`], then an error is returned.
313///
314/// This function will only be used for developer tools once the bedrock routing refactor has been
315/// completed, but for now it's the only way to route capabilities which are unsupported in
316/// bedrock.
317///
318/// For capabilities which are not supported in bedrock, the `mapper` is invoked on every step in
319/// the routing process and can be used to record the routing steps. Once all capabilities are
320/// supported in bedrock routing, the `mapper` argument will be removed.
321pub async fn route_capability<C>(
322    request: RouteRequest,
323    target: &Arc<C>,
324    mapper: &mut dyn DebugRouteMapper,
325) -> Result<RouteSource, RoutingError>
326where
327    C: ComponentInstanceInterface + 'static,
328{
329    match request {
330        // Route from an ExposeDecl
331        RouteRequest::ExposeDirectory(expose_directory_decl) => {
332            route_directory_from_expose(expose_directory_decl, target, mapper).await
333        }
334        RouteRequest::ExposeProtocol(expose_protocol_decl) => {
335            let sandbox = target.component_sandbox().await?;
336            let dictionary = match &expose_protocol_decl.target {
337                ExposeTarget::Parent => sandbox.component_output.capabilities(),
338                ExposeTarget::Framework => sandbox.component_output.framework(),
339            };
340            route_capability_inner::<Connector, _>(
341                &dictionary,
342                &expose_protocol_decl.target_name,
343                protocol_metadata(expose_protocol_decl.availability),
344                target,
345            )
346            .await
347        }
348        RouteRequest::ExposeService(expose_bundle) => {
349            let first_expose = expose_bundle.iter().next().expect("can't route empty bundle");
350            route_capability_inner::<DirEntry, _>(
351                &target.component_sandbox().await?.component_output.capabilities(),
352                first_expose.target_name(),
353                service_metadata(*first_expose.availability()),
354                target,
355            )
356            .await
357        }
358        RouteRequest::ExposeRunner(expose_runner_decl) => {
359            let sandbox = target.component_sandbox().await?;
360            let dictionary = match &expose_runner_decl.target {
361                ExposeTarget::Parent => sandbox.component_output.capabilities(),
362                ExposeTarget::Framework => sandbox.component_output.framework(),
363            };
364            route_capability_inner::<Connector, _>(
365                &dictionary,
366                &expose_runner_decl.target_name,
367                runner_metadata(Availability::Required),
368                target,
369            )
370            .await
371        }
372        RouteRequest::ExposeResolver(expose_resolver_decl) => {
373            let sandbox = target.component_sandbox().await?;
374            let dictionary = match &expose_resolver_decl.target {
375                ExposeTarget::Parent => sandbox.component_output.capabilities(),
376                ExposeTarget::Framework => sandbox.component_output.framework(),
377            };
378            route_capability_inner::<Connector, _>(
379                &dictionary,
380                &expose_resolver_decl.target_name,
381                resolver_metadata(Availability::Required),
382                target,
383            )
384            .await
385        }
386        RouteRequest::ExposeConfig(expose_config_decl) => {
387            route_config_from_expose(expose_config_decl, target, mapper).await
388        }
389
390        // Route a resolver or runner from an environment
391        RouteRequest::Resolver(resolver_registration) => {
392            let component_sandbox = target.component_sandbox().await?;
393            let source_dictionary = match &resolver_registration.source {
394                RegistrationSource::Parent => component_sandbox.component_input.capabilities(),
395                RegistrationSource::Self_ => component_sandbox.program_output_dict.clone(),
396                RegistrationSource::Child(static_name) => {
397                    let child_name = ChildName::parse(static_name).expect(
398                        "invalid child name, this should be prevented by manifest validation",
399                    );
400                    let child_component = target.lock_resolved_state().await?.get_child(&child_name).expect("resolver registration references nonexistent static child, this should be prevented by manifest validation");
401                    let child_sandbox = child_component.component_sandbox().await?;
402                    child_sandbox.component_output.capabilities().clone()
403                }
404            };
405            route_capability_inner::<Connector, _>(
406                &source_dictionary,
407                &resolver_registration.resolver,
408                resolver_metadata(Availability::Required),
409                target,
410            )
411            .await
412        }
413        // Route the backing directory for a storage capability
414        RouteRequest::StorageBackingDirectory(storage_decl) => {
415            route_storage_backing_directory(storage_decl, target, mapper).await
416        }
417
418        // Route from a UseDecl
419        RouteRequest::UseDirectory(use_directory_decl) => {
420            route_directory(use_directory_decl, target, mapper).await
421        }
422        RouteRequest::UseEventStream(use_event_stream_decl) => {
423            route_event_stream(use_event_stream_decl, target, mapper).await
424        }
425        RouteRequest::UseProtocol(use_protocol_decl) => {
426            route_capability_inner::<Connector, _>(
427                &target.component_sandbox().await?.program_input.namespace(),
428                &use_protocol_decl.target_path,
429                protocol_metadata(use_protocol_decl.availability),
430                target,
431            )
432            .await
433        }
434        RouteRequest::UseService(use_service_decl) => {
435            route_capability_inner::<DirEntry, _>(
436                &target.component_sandbox().await?.program_input.namespace(),
437                &use_service_decl.target_path,
438                service_metadata(use_service_decl.availability),
439                target,
440            )
441            .await
442        }
443        RouteRequest::UseStorage(use_storage_decl) => {
444            route_storage(use_storage_decl, target, mapper).await
445        }
446        RouteRequest::UseRunner(_use_runner_decl) => {
447            let router =
448                target.component_sandbox().await?.program_input.runner().expect("we have a use declaration for a runner but the program input dictionary has no runner, this should be impossible");
449            perform_route::<Connector, _>(router, runner_metadata(Availability::Required), target)
450                .await
451        }
452        RouteRequest::UseConfig(use_config_decl) => {
453            route_config(use_config_decl, target, mapper).await
454        }
455
456        // Route from a OfferDecl
457        RouteRequest::OfferProtocol(offer_protocol_decl) => {
458            let target_dictionary =
459                get_dictionary_for_offer_target(target, &offer_protocol_decl).await?;
460            let metadata = protocol_metadata(offer_protocol_decl.availability);
461            metadata
462                .insert(
463                    Name::new(crate::bedrock::with_policy_check::SKIP_POLICY_CHECKS).unwrap(),
464                    Capability::Data(Data::Uint64(1)),
465                )
466                .unwrap();
467            route_capability_inner::<Connector, _>(
468                &target_dictionary,
469                &offer_protocol_decl.target_name,
470                metadata,
471                target,
472            )
473            .await
474        }
475        RouteRequest::OfferDictionary(offer_dictionary_decl) => {
476            let target_dictionary =
477                get_dictionary_for_offer_target(target, &offer_dictionary_decl).await?;
478            let metadata = dictionary_metadata(offer_dictionary_decl.availability);
479            metadata
480                .insert(
481                    Name::new(crate::bedrock::with_policy_check::SKIP_POLICY_CHECKS).unwrap(),
482                    Capability::Data(Data::Uint64(1)),
483                )
484                .unwrap();
485            route_capability_inner::<Dict, _>(
486                &target_dictionary,
487                &offer_dictionary_decl.target_name,
488                metadata,
489                target,
490            )
491            .await
492        }
493        RouteRequest::OfferDirectory(offer_directory_decl) => {
494            route_directory_from_offer(offer_directory_decl, target, mapper).await
495        }
496        RouteRequest::OfferStorage(offer_storage_decl) => {
497            route_storage_from_offer(offer_storage_decl, target, mapper).await
498        }
499        RouteRequest::OfferService(offer_service_bundle) => {
500            let first_offer = offer_service_bundle.iter().next().expect("can't route empty bundle");
501            let target_dictionary = get_dictionary_for_offer_target(target, first_offer).await?;
502            let metadata = service_metadata(first_offer.availability);
503            metadata
504                .insert(
505                    Name::new(crate::bedrock::with_policy_check::SKIP_POLICY_CHECKS).unwrap(),
506                    Capability::Data(Data::Uint64(1)),
507                )
508                .unwrap();
509            route_capability_inner::<DirEntry, _>(
510                &target_dictionary,
511                &first_offer.target_name,
512                metadata,
513                target,
514            )
515            .await
516        }
517        RouteRequest::OfferEventStream(offer_event_stream_decl) => {
518            route_event_stream_from_offer(offer_event_stream_decl, target, mapper).await
519        }
520        RouteRequest::OfferRunner(offer_runner_decl) => {
521            let target_dictionary =
522                get_dictionary_for_offer_target(target, &offer_runner_decl).await?;
523            let metadata = runner_metadata(Availability::Required);
524            metadata
525                .insert(
526                    Name::new(crate::bedrock::with_policy_check::SKIP_POLICY_CHECKS).unwrap(),
527                    Capability::Data(Data::Uint64(1)),
528                )
529                .unwrap();
530            route_capability_inner::<Connector, _>(
531                &target_dictionary,
532                &offer_runner_decl.target_name,
533                metadata,
534                target,
535            )
536            .await
537        }
538        RouteRequest::OfferResolver(offer_resolver_decl) => {
539            let target_dictionary =
540                get_dictionary_for_offer_target(target, &offer_resolver_decl).await?;
541            let metadata = resolver_metadata(Availability::Required);
542            metadata
543                .insert(
544                    Name::new(crate::bedrock::with_policy_check::SKIP_POLICY_CHECKS).unwrap(),
545                    Capability::Data(Data::Uint64(1)),
546                )
547                .unwrap();
548            route_capability_inner::<Connector, _>(
549                &target_dictionary,
550                &offer_resolver_decl.target_name,
551                metadata,
552                target,
553            )
554            .await
555        }
556        RouteRequest::OfferConfig(offer) => route_config_from_offer(offer, target, mapper).await,
557    }
558}
559
560pub enum Never {}
561
562async fn route_capability_inner<T, C>(
563    dictionary: &Dict,
564    path: &impl IterablePath,
565    metadata: Dict,
566    target: &Arc<C>,
567) -> Result<RouteSource, RoutingError>
568where
569    C: ComponentInstanceInterface + 'static,
570    T: CapabilityBound,
571    Router<T>: TryFrom<Capability>,
572{
573    let router = dictionary
574        .get_capability(path)
575        .and_then(|c| Router::<T>::try_from(c).ok())
576        .ok_or_else(|| RoutingError::BedrockNotPresentInDictionary {
577            moniker: target.moniker().clone().into(),
578            name: path.iter_segments().join("/"),
579        })?;
580    perform_route::<T, C>(router, metadata, target).await
581}
582
583async fn perform_route<T, C>(
584    router: impl Routable<T>,
585    metadata: Dict,
586    target: &Arc<C>,
587) -> Result<RouteSource, RoutingError>
588where
589    C: ComponentInstanceInterface + 'static,
590    T: CapabilityBound,
591    Router<T>: TryFrom<Capability>,
592{
593    let request = Request { target: WeakComponentInstanceInterface::new(target).into(), metadata };
594    let data = match router.route(Some(request), true).await? {
595        RouterResponse::<T>::Debug(d) => d,
596        _ => panic!("Debug route did not return a debug response"),
597    };
598    Ok(RouteSource::new(data.try_into().unwrap()))
599}
600
601async fn get_dictionary_for_offer_target<C, O>(
602    target: &Arc<C>,
603    offer: &O,
604) -> Result<Dict, RoutingError>
605where
606    C: ComponentInstanceInterface + 'static,
607    O: OfferDeclCommon,
608{
609    match offer.target() {
610        OfferTarget::Child(child_ref) if child_ref.collection.is_none() => {
611            // For static children we can find their inputs in the component's sandbox.
612            let child_input_name = Name::new(&child_ref.name)
613                .map_err(MonikerError::InvalidMonikerPart)
614                .expect("static child names must be short");
615            let target_sandbox = target.component_sandbox().await?;
616            let child_input = target_sandbox.child_inputs.get(&child_input_name).ok_or(
617                RoutingError::OfferFromChildInstanceNotFound {
618                    child_moniker: child_ref.clone().into(),
619                    moniker: target.moniker().clone(),
620                    capability_id: offer.target_name().clone().to_string(),
621                },
622            )?;
623            Ok(child_input.capabilities())
624        }
625        OfferTarget::Child(child_ref) => {
626            // Offers targeting dynamic children are trickier. The input to the dynamic
627            // child wasn't created as part of the parent's sandbox, and dynamic offers
628            // (like the one we're currently looking at) won't have their routes reflected
629            // in the general component input for the collection. To work around this, we
630            // look up the dynamic child from the parent and access its component input
631            // from there. Unlike the code path for static children, this causes the child
632            // to be resolved.
633            let child = target
634                .lock_resolved_state()
635                .await?
636                .get_child(&ChildName::from(child_ref.clone()))
637                .ok_or(RoutingError::OfferFromChildInstanceNotFound {
638                    child_moniker: child_ref.clone().into(),
639                    moniker: target.moniker().clone(),
640                    capability_id: offer.target_name().clone().to_string(),
641                })?;
642            Ok(child.component_sandbox().await?.component_input.capabilities())
643        }
644        OfferTarget::Collection(collection_name) => {
645            // Offers targeting collections start at the component input generated for the
646            // collection, which is in the component's sandbox.
647            let target_sandbox = target.component_sandbox().await?;
648            let collection_input = target_sandbox.collection_inputs.get(collection_name).ok_or(
649                RoutingError::OfferFromCollectionNotFound {
650                    collection: collection_name.to_string(),
651                    moniker: target.moniker().clone(),
652                    capability: offer.target_name().clone(),
653                },
654            )?;
655            Ok(collection_input.capabilities())
656        }
657        OfferTarget::Capability(dictionary_name) => {
658            // Offers targeting another capability are for adding the capability to a dictionary
659            // declared by the same component. These dictionaries are stored in the target's
660            // sandbox.
661            let target_sandbox = target.component_sandbox().await?;
662            let capability =
663                target_sandbox.declared_dictionaries.get(dictionary_name).ok().flatten().ok_or(
664                    RoutingError::BedrockNotPresentInDictionary {
665                        name: dictionary_name.to_string(),
666                        moniker: target.moniker().clone().into(),
667                    },
668                )?;
669            match capability {
670                Capability::Dictionary(dictionary) => Ok(dictionary),
671                other_type => Err(RoutingError::BedrockWrongCapabilityType {
672                    actual: other_type.debug_typename().to_string(),
673                    expected: "Dictionary".to_string(),
674                    moniker: target.moniker().clone().into(),
675                }),
676            }
677        }
678    }
679}
680
681/// Routes a Directory capability from `target` to its source, starting from `offer_decl`.
682/// Returns the capability source, along with a `DirectoryState` accumulated from traversing
683/// the route.
684async fn route_directory_from_offer<C>(
685    offer_decl: OfferDirectoryDecl,
686    target: &Arc<C>,
687    mapper: &mut dyn DebugRouteMapper,
688) -> Result<RouteSource, RoutingError>
689where
690    C: ComponentInstanceInterface + 'static,
691{
692    let mut state = DirectoryState {
693        rights: WalkState::new(),
694        subdir: Default::default(),
695        availability_state: offer_decl.availability.into(),
696    };
697    let allowed_sources =
698        Sources::new(CapabilityTypeName::Directory).framework().namespace().component();
699    let source = legacy_router::route_from_offer(
700        RouteBundle::from_offer(offer_decl.into()),
701        target.clone(),
702        allowed_sources,
703        &mut state,
704        mapper,
705    )
706    .await?;
707    Ok(RouteSource::new_with_relative_path(source, state.subdir))
708}
709
710/// Routes an EventStream capability from `target` to its source, starting from `offer_decl`.
711async fn route_event_stream_from_offer<C>(
712    offer_decl: OfferEventStreamDecl,
713    target: &Arc<C>,
714    mapper: &mut dyn DebugRouteMapper,
715) -> Result<RouteSource, RoutingError>
716where
717    C: ComponentInstanceInterface + 'static,
718{
719    let allowed_sources = Sources::new(CapabilityTypeName::EventStream).builtin();
720
721    let mut availability_visitor = offer_decl.availability;
722    let source = legacy_router::route_from_offer(
723        RouteBundle::from_offer(offer_decl.into()),
724        target.clone(),
725        allowed_sources,
726        &mut availability_visitor,
727        mapper,
728    )
729    .await?;
730    Ok(RouteSource::new(source))
731}
732
733async fn route_storage_from_offer<C>(
734    offer_decl: OfferStorageDecl,
735    target: &Arc<C>,
736    mapper: &mut dyn DebugRouteMapper,
737) -> Result<RouteSource, RoutingError>
738where
739    C: ComponentInstanceInterface + 'static,
740{
741    let mut availability_visitor = offer_decl.availability;
742    let allowed_sources = Sources::new(CapabilityTypeName::Storage).component();
743    let source = legacy_router::route_from_offer(
744        RouteBundle::from_offer(offer_decl.into()),
745        target.clone(),
746        allowed_sources,
747        &mut availability_visitor,
748        mapper,
749    )
750    .await?;
751    Ok(RouteSource::new(source))
752}
753
754async fn route_config_from_offer<C>(
755    offer_decl: OfferConfigurationDecl,
756    target: &Arc<C>,
757    mapper: &mut dyn DebugRouteMapper,
758) -> Result<RouteSource, RoutingError>
759where
760    C: ComponentInstanceInterface + 'static,
761{
762    let allowed_sources = Sources::new(CapabilityTypeName::Config).builtin().component();
763    let source = legacy_router::route_from_offer(
764        RouteBundle::from_offer(offer_decl.into()),
765        target.clone(),
766        allowed_sources,
767        &mut NoopVisitor::new(),
768        mapper,
769    )
770    .await?;
771    Ok(RouteSource::new(source))
772}
773
774async fn route_config_from_expose<C>(
775    expose_decl: ExposeConfigurationDecl,
776    target: &Arc<C>,
777    mapper: &mut dyn DebugRouteMapper,
778) -> Result<RouteSource, RoutingError>
779where
780    C: ComponentInstanceInterface + 'static,
781{
782    let allowed_sources = Sources::new(CapabilityTypeName::Config).component().capability();
783    let source = legacy_router::route_from_expose(
784        RouteBundle::from_expose(expose_decl.into()),
785        target.clone(),
786        allowed_sources,
787        &mut NoopVisitor::new(),
788        mapper,
789    )
790    .await?;
791
792    target.policy_checker().can_route_capability(&source, target.moniker())?;
793    Ok(RouteSource::new(source))
794}
795
796/// The accumulated state of routing a Directory capability.
797#[derive(Clone, Debug)]
798pub struct DirectoryState {
799    rights: WalkState<RightsWalker>,
800    pub subdir: RelativePath,
801    availability_state: Availability,
802}
803
804impl DirectoryState {
805    fn new(rights: RightsWalker, subdir: RelativePath, availability: &Availability) -> Self {
806        DirectoryState {
807            rights: WalkState::at(rights),
808            subdir,
809            availability_state: availability.clone(),
810        }
811    }
812
813    fn advance_with_offer(
814        &mut self,
815        moniker: &ExtendedMoniker,
816        offer: &OfferDirectoryDecl,
817    ) -> Result<(), RoutingError> {
818        self.availability_state =
819            availability::advance_with_offer(moniker, self.availability_state, offer)?;
820        self.advance(moniker, offer.rights.clone(), offer.subdir.clone())
821    }
822
823    fn advance_with_expose(
824        &mut self,
825        moniker: &ExtendedMoniker,
826        expose: &ExposeDirectoryDecl,
827    ) -> Result<(), RoutingError> {
828        self.availability_state =
829            availability::advance_with_expose(moniker, self.availability_state, expose)?;
830        self.advance(moniker, expose.rights.clone(), expose.subdir.clone())
831    }
832
833    fn advance(
834        &mut self,
835        moniker: &ExtendedMoniker,
836        rights: Option<fio::Operations>,
837        mut subdir: RelativePath,
838    ) -> Result<(), RoutingError> {
839        self.rights = self.rights.advance(rights.map(|r| RightsWalker::new(r, moniker.clone())))?;
840        if !subdir.extend(self.subdir.clone()) {
841            return Err(RoutingError::PathTooLong {
842                moniker: moniker.clone(),
843                path: format!("{}/{}", subdir, self.subdir),
844                keyword: "subdir".into(),
845            });
846        }
847        self.subdir = subdir;
848        Ok(())
849    }
850
851    fn finalize(
852        &mut self,
853        moniker: &ExtendedMoniker,
854        rights: RightsWalker,
855        mut subdir: RelativePath,
856    ) -> Result<(), RoutingError> {
857        self.rights = self.rights.finalize(Some(rights))?;
858        if !subdir.extend(self.subdir.clone()) {
859            return Err(RoutingError::PathTooLong {
860                moniker: moniker.clone(),
861                path: format!("{}/{}", subdir, self.subdir),
862                keyword: "subdir".into(),
863            });
864        }
865        self.subdir = subdir;
866        Ok(())
867    }
868}
869
870impl OfferVisitor for DirectoryState {
871    fn visit(
872        &mut self,
873        moniker: &ExtendedMoniker,
874        offer: &cm_rust::OfferDecl,
875    ) -> Result<(), RoutingError> {
876        match offer {
877            cm_rust::OfferDecl::Directory(dir) => match dir.source {
878                OfferSource::Framework => self.finalize(
879                    moniker,
880                    RightsWalker::new(fio::RX_STAR_DIR, moniker.clone()),
881                    dir.subdir.clone(),
882                ),
883                _ => self.advance_with_offer(moniker, dir),
884            },
885            _ => Ok(()),
886        }
887    }
888}
889
890impl ExposeVisitor for DirectoryState {
891    fn visit(
892        &mut self,
893        moniker: &ExtendedMoniker,
894        expose: &cm_rust::ExposeDecl,
895    ) -> Result<(), RoutingError> {
896        match expose {
897            cm_rust::ExposeDecl::Directory(dir) => match dir.source {
898                ExposeSource::Framework => self.finalize(
899                    moniker,
900                    RightsWalker::new(fio::RX_STAR_DIR, moniker.clone()),
901                    dir.subdir.clone(),
902                ),
903                _ => self.advance_with_expose(moniker, dir),
904            },
905            _ => Ok(()),
906        }
907    }
908}
909
910impl CapabilityVisitor for DirectoryState {
911    fn visit(
912        &mut self,
913        moniker: &ExtendedMoniker,
914        capability: &cm_rust::CapabilityDecl,
915    ) -> Result<(), RoutingError> {
916        match capability {
917            cm_rust::CapabilityDecl::Directory(dir) => self.finalize(
918                moniker,
919                RightsWalker::new(dir.rights, moniker.clone()),
920                Default::default(),
921            ),
922            _ => Ok(()),
923        }
924    }
925}
926
927/// Routes a Directory capability from `target` to its source, starting from `use_decl`.
928/// Returns the capability source, along with a `DirectoryState` accumulated from traversing
929/// the route.
930async fn route_directory<C>(
931    use_decl: UseDirectoryDecl,
932    target: &Arc<C>,
933    mapper: &mut dyn DebugRouteMapper,
934) -> Result<RouteSource, RoutingError>
935where
936    C: ComponentInstanceInterface + 'static,
937{
938    match use_decl.source {
939        UseSource::Self_ => {
940            let mut availability_visitor = use_decl.availability;
941            let allowed_sources = Sources::new(CapabilityTypeName::Dictionary).component();
942            let source = legacy_router::route_from_self(
943                use_decl.into(),
944                target.clone(),
945                allowed_sources,
946                &mut availability_visitor,
947                mapper,
948            )
949            .await?;
950            Ok(RouteSource::new(source))
951        }
952        _ => {
953            let mut state = DirectoryState::new(
954                RightsWalker::new(use_decl.rights, target.moniker().clone()),
955                use_decl.subdir.clone(),
956                &use_decl.availability,
957            );
958            if let UseSource::Framework = &use_decl.source {
959                let moniker = target.moniker().clone();
960                state.finalize(
961                    &moniker.clone().into(),
962                    RightsWalker::new(fio::RX_STAR_DIR, moniker),
963                    Default::default(),
964                )?;
965            }
966            let allowed_sources =
967                Sources::new(CapabilityTypeName::Directory).framework().namespace().component();
968            let source = legacy_router::route_from_use(
969                use_decl.into(),
970                target.clone(),
971                allowed_sources,
972                &mut state,
973                mapper,
974            )
975            .await?;
976
977            target.policy_checker().can_route_capability(&source, target.moniker())?;
978            Ok(RouteSource::new_with_relative_path(source, state.subdir))
979        }
980    }
981}
982
983/// Routes a Directory capability from `target` to its source, starting from `expose_decl`.
984/// Returns the capability source, along with a `DirectoryState` accumulated from traversing
985/// the route.
986async fn route_directory_from_expose<C>(
987    expose_decl: ExposeDirectoryDecl,
988    target: &Arc<C>,
989    mapper: &mut dyn DebugRouteMapper,
990) -> Result<RouteSource, RoutingError>
991where
992    C: ComponentInstanceInterface + 'static,
993{
994    let mut state = DirectoryState {
995        rights: WalkState::new(),
996        subdir: Default::default(),
997        availability_state: expose_decl.availability.into(),
998    };
999    let allowed_sources =
1000        Sources::new(CapabilityTypeName::Directory).framework().namespace().component();
1001    let source = legacy_router::route_from_expose(
1002        RouteBundle::from_expose(expose_decl.into()),
1003        target.clone(),
1004        allowed_sources,
1005        &mut state,
1006        mapper,
1007    )
1008    .await?;
1009
1010    target.policy_checker().can_route_capability(&source, target.moniker())?;
1011    Ok(RouteSource::new_with_relative_path(source, state.subdir))
1012}
1013
1014/// Verifies that the given component is in the index if its `storage_id` is StaticInstanceId.
1015/// - On success, Ok(()) is returned
1016/// - RoutingError::ComponentNotInIndex is returned on failure.
1017pub async fn verify_instance_in_component_id_index<C>(
1018    source: &CapabilitySource,
1019    instance: &Arc<C>,
1020) -> Result<(), RoutingError>
1021where
1022    C: ComponentInstanceInterface + 'static,
1023{
1024    let (storage_decl, source_moniker) = match source {
1025        CapabilitySource::Component(ComponentSource {
1026            capability: ComponentCapability::Storage(storage_decl),
1027            moniker,
1028        }) => (storage_decl, moniker.clone()),
1029        CapabilitySource::Void(VoidSource { .. }) => return Ok(()),
1030        _ => unreachable!("unexpected storage source"),
1031    };
1032
1033    if storage_decl.storage_id == fdecl::StorageId::StaticInstanceId
1034        && instance.component_id_index().id_for_moniker(instance.moniker()).is_none()
1035    {
1036        return Err(RoutingError::ComponentNotInIdIndex {
1037            source_moniker,
1038            target_name: instance.moniker().leaf().map(Into::into),
1039        });
1040    }
1041    Ok(())
1042}
1043
1044/// Routes a Storage capability from `target` to its source, starting from `use_decl`.
1045/// Returns the StorageDecl and the storage component's instance.
1046pub async fn route_to_storage_decl<C>(
1047    use_decl: UseStorageDecl,
1048    target: &Arc<C>,
1049    mapper: &mut dyn DebugRouteMapper,
1050) -> Result<CapabilitySource, RoutingError>
1051where
1052    C: ComponentInstanceInterface + 'static,
1053{
1054    let mut availability_visitor = use_decl.availability;
1055    let allowed_sources = Sources::new(CapabilityTypeName::Storage).component();
1056    let source = legacy_router::route_from_use(
1057        use_decl.into(),
1058        target.clone(),
1059        allowed_sources,
1060        &mut availability_visitor,
1061        mapper,
1062    )
1063    .await?;
1064    Ok(source)
1065}
1066
1067/// Routes a Storage capability from `target` to its source, starting from `use_decl`.
1068/// The backing Directory capability is then routed to its source.
1069async fn route_storage<C>(
1070    use_decl: UseStorageDecl,
1071    target: &Arc<C>,
1072    mapper: &mut dyn DebugRouteMapper,
1073) -> Result<RouteSource, RoutingError>
1074where
1075    C: ComponentInstanceInterface + 'static,
1076{
1077    let source = route_to_storage_decl(use_decl, &target, mapper).await?;
1078    verify_instance_in_component_id_index(&source, target).await?;
1079    target.policy_checker().can_route_capability(&source, target.moniker())?;
1080    Ok(RouteSource::new(source))
1081}
1082
1083/// Routes the backing Directory capability of a Storage capability from `target` to its source,
1084/// starting from `storage_decl`.
1085async fn route_storage_backing_directory<C>(
1086    storage_decl: StorageDecl,
1087    target: &Arc<C>,
1088    mapper: &mut dyn DebugRouteMapper,
1089) -> Result<RouteSource, RoutingError>
1090where
1091    C: ComponentInstanceInterface + 'static,
1092{
1093    // Storage rights are always READ+WRITE.
1094    let mut state = DirectoryState::new(
1095        RightsWalker::new(fio::RW_STAR_DIR, target.moniker().clone()),
1096        Default::default(),
1097        &Availability::Required,
1098    );
1099    let allowed_sources = Sources::new(CapabilityTypeName::Directory).component().namespace();
1100    let source = legacy_router::route_from_registration(
1101        StorageDeclAsRegistration::from(storage_decl.clone()),
1102        target.clone(),
1103        allowed_sources,
1104        &mut state,
1105        mapper,
1106    )
1107    .await?;
1108
1109    target.policy_checker().can_route_capability(&source, target.moniker())?;
1110
1111    Ok(RouteSource::new_with_relative_path(source, state.subdir))
1112}
1113
1114/// Finds a Configuration capability that matches the given use.
1115async fn route_config<C>(
1116    use_decl: UseConfigurationDecl,
1117    target: &Arc<C>,
1118    mapper: &mut dyn DebugRouteMapper,
1119) -> Result<RouteSource, RoutingError>
1120where
1121    C: ComponentInstanceInterface + 'static,
1122{
1123    let allowed_sources = Sources::new(CapabilityTypeName::Config).component().capability();
1124    let mut availability_visitor = use_decl.availability().clone();
1125    let source = legacy_router::route_from_use(
1126        use_decl.clone().into(),
1127        target.clone(),
1128        allowed_sources,
1129        &mut availability_visitor,
1130        mapper,
1131    )
1132    .await;
1133    // If the route was not found, but it's a transitional availability then return
1134    // a successful Void capability.
1135    let source = match source {
1136        Ok(s) => s,
1137        Err(e) => {
1138            if *use_decl.availability() == Availability::Transitional
1139                && e.as_zx_status() == zx::Status::NOT_FOUND
1140            {
1141                CapabilitySource::Void(VoidSource {
1142                    capability: InternalCapability::Config(use_decl.source_name),
1143                    moniker: target.moniker().clone(),
1144                })
1145            } else {
1146                return Err(e);
1147            }
1148        }
1149    };
1150
1151    target.policy_checker().can_route_capability(&source, target.moniker())?;
1152    Ok(RouteSource::new(source))
1153}
1154
1155/// Routes an EventStream capability from `target` to its source, starting from `use_decl`.
1156///
1157/// If the capability is not allowed to be routed to the `target`, per the
1158/// [`crate::model::policy::GlobalPolicyChecker`], then an error is returned.
1159pub async fn route_event_stream<C>(
1160    use_decl: UseEventStreamDecl,
1161    target: &Arc<C>,
1162    mapper: &mut dyn DebugRouteMapper,
1163) -> Result<RouteSource, RoutingError>
1164where
1165    C: ComponentInstanceInterface + 'static,
1166{
1167    let allowed_sources = Sources::new(CapabilityTypeName::EventStream).builtin();
1168    let mut availability_visitor = use_decl.availability;
1169    let source = legacy_router::route_from_use(
1170        use_decl.into(),
1171        target.clone(),
1172        allowed_sources,
1173        &mut availability_visitor,
1174        mapper,
1175    )
1176    .await?;
1177    target.policy_checker().can_route_capability(&source, target.moniker())?;
1178    Ok(RouteSource::new(source))
1179}
1180
1181/// Intermediate type to masquerade as Registration-style routing start point for the storage
1182/// backing directory capability.
1183#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(rename_all = "snake_case"))]
1184#[derive(Debug, Clone, PartialEq, Eq)]
1185pub struct StorageDeclAsRegistration {
1186    source: RegistrationSource,
1187    name: Name,
1188}
1189
1190impl From<StorageDecl> for StorageDeclAsRegistration {
1191    fn from(decl: StorageDecl) -> Self {
1192        Self {
1193            name: decl.backing_dir,
1194            source: match decl.source {
1195                StorageDirectorySource::Parent => RegistrationSource::Parent,
1196                StorageDirectorySource::Self_ => RegistrationSource::Self_,
1197                StorageDirectorySource::Child(child) => RegistrationSource::Child(child),
1198            },
1199        }
1200    }
1201}
1202
1203impl SourceName for StorageDeclAsRegistration {
1204    fn source_name(&self) -> &Name {
1205        &self.name
1206    }
1207}
1208
1209impl RegistrationDeclCommon for StorageDeclAsRegistration {
1210    const TYPE: &'static str = "storage";
1211
1212    fn source(&self) -> &RegistrationSource {
1213        &self.source
1214    }
1215}
1216
1217/// An umbrella type for registration decls, making it more convenient to record route
1218/// maps for debug use.
1219#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(rename_all = "snake_case"))]
1220#[derive(FromEnum, Debug, Clone, PartialEq, Eq)]
1221pub enum RegistrationDecl {
1222    Resolver(ResolverRegistration),
1223    Runner(RunnerRegistration),
1224    Debug(DebugRegistration),
1225    Directory(StorageDeclAsRegistration),
1226}
1227
1228impl From<&RegistrationDecl> for cm_rust::CapabilityTypeName {
1229    fn from(registration: &RegistrationDecl) -> Self {
1230        match registration {
1231            RegistrationDecl::Directory(_) => Self::Directory,
1232            RegistrationDecl::Resolver(_) => Self::Resolver,
1233            RegistrationDecl::Runner(_) => Self::Runner,
1234            RegistrationDecl::Debug(_) => Self::Protocol,
1235        }
1236    }
1237}
1238
1239// Error trait impls
1240
1241impl ErrorNotFoundFromParent for cm_rust::UseDecl {
1242    fn error_not_found_from_parent(moniker: Moniker, capability_name: Name) -> RoutingError {
1243        RoutingError::UseFromParentNotFound { moniker, capability_id: capability_name.into() }
1244    }
1245}
1246
1247impl ErrorNotFoundFromParent for DebugRegistration {
1248    fn error_not_found_from_parent(moniker: Moniker, capability_name: Name) -> RoutingError {
1249        RoutingError::EnvironmentFromParentNotFound {
1250            moniker,
1251            capability_name,
1252            capability_type: DebugRegistration::TYPE.to_string(),
1253        }
1254    }
1255}
1256
1257impl ErrorNotFoundInChild for DebugRegistration {
1258    fn error_not_found_in_child(
1259        moniker: Moniker,
1260        child_moniker: ChildName,
1261        capability_name: Name,
1262    ) -> RoutingError {
1263        RoutingError::EnvironmentFromChildExposeNotFound {
1264            moniker,
1265            child_moniker,
1266            capability_name,
1267            capability_type: DebugRegistration::TYPE.to_string(),
1268        }
1269    }
1270}
1271
1272impl ErrorNotFoundInChild for cm_rust::UseDecl {
1273    fn error_not_found_in_child(
1274        moniker: Moniker,
1275        child_moniker: ChildName,
1276        capability_name: Name,
1277    ) -> RoutingError {
1278        RoutingError::UseFromChildExposeNotFound {
1279            child_moniker,
1280            moniker,
1281            capability_id: capability_name.into(),
1282        }
1283    }
1284}
1285
1286impl ErrorNotFoundInChild for cm_rust::ExposeDecl {
1287    fn error_not_found_in_child(
1288        moniker: Moniker,
1289        child_moniker: ChildName,
1290        capability_name: Name,
1291    ) -> RoutingError {
1292        RoutingError::ExposeFromChildExposeNotFound {
1293            moniker,
1294            child_moniker,
1295            capability_id: capability_name.into(),
1296        }
1297    }
1298}
1299
1300impl ErrorNotFoundInChild for cm_rust::OfferDecl {
1301    fn error_not_found_in_child(
1302        moniker: Moniker,
1303        child_moniker: ChildName,
1304        capability_name: Name,
1305    ) -> RoutingError {
1306        RoutingError::OfferFromChildExposeNotFound {
1307            moniker,
1308            child_moniker,
1309            capability_id: capability_name.into(),
1310        }
1311    }
1312}
1313
1314impl ErrorNotFoundFromParent for cm_rust::OfferDecl {
1315    fn error_not_found_from_parent(moniker: Moniker, capability_name: Name) -> RoutingError {
1316        RoutingError::OfferFromParentNotFound { moniker, capability_id: capability_name.into() }
1317    }
1318}
1319
1320impl ErrorNotFoundInChild for StorageDeclAsRegistration {
1321    fn error_not_found_in_child(
1322        moniker: Moniker,
1323        child_moniker: ChildName,
1324        capability_name: Name,
1325    ) -> RoutingError {
1326        RoutingError::StorageFromChildExposeNotFound {
1327            moniker,
1328            child_moniker,
1329            capability_id: capability_name.into(),
1330        }
1331    }
1332}
1333
1334impl ErrorNotFoundFromParent for StorageDeclAsRegistration {
1335    fn error_not_found_from_parent(moniker: Moniker, capability_name: Name) -> RoutingError {
1336        RoutingError::StorageFromParentNotFound { moniker, capability_id: capability_name.into() }
1337    }
1338}
1339
1340impl ErrorNotFoundFromParent for RunnerRegistration {
1341    fn error_not_found_from_parent(moniker: Moniker, capability_name: Name) -> RoutingError {
1342        RoutingError::UseFromEnvironmentNotFound {
1343            moniker,
1344            capability_name,
1345            capability_type: "runner".to_string(),
1346        }
1347    }
1348}
1349
1350impl ErrorNotFoundInChild for RunnerRegistration {
1351    fn error_not_found_in_child(
1352        moniker: Moniker,
1353        child_moniker: ChildName,
1354        capability_name: Name,
1355    ) -> RoutingError {
1356        RoutingError::EnvironmentFromChildExposeNotFound {
1357            moniker,
1358            child_moniker,
1359            capability_name,
1360            capability_type: "runner".to_string(),
1361        }
1362    }
1363}
1364
1365impl ErrorNotFoundFromParent for ResolverRegistration {
1366    fn error_not_found_from_parent(moniker: Moniker, capability_name: Name) -> RoutingError {
1367        RoutingError::EnvironmentFromParentNotFound {
1368            moniker,
1369            capability_name,
1370            capability_type: "resolver".to_string(),
1371        }
1372    }
1373}
1374
1375impl ErrorNotFoundInChild for ResolverRegistration {
1376    fn error_not_found_in_child(
1377        moniker: Moniker,
1378        child_moniker: ChildName,
1379        capability_name: Name,
1380    ) -> RoutingError {
1381        RoutingError::EnvironmentFromChildExposeNotFound {
1382            moniker,
1383            child_moniker,
1384            capability_name,
1385            capability_type: "resolver".to_string(),
1386        }
1387    }
1388}