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