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