Skip to main content

routing/
error.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
5use crate::policy::PolicyError;
6use crate::rights::Rights;
7use async_trait::async_trait;
8use clonable_error::ClonableError;
9use cm_rust::offer::OfferDeclCommon;
10use cm_rust::{CapabilityTypeName, ExposeDeclCommon, GenericRef, SourceName, UseDeclCommon};
11use cm_types::{Availability, Name, RelativePath};
12use fidl_fuchsia_component as fcomponent;
13use itertools::Itertools;
14use moniker::{ChildName, ExtendedMoniker, Moniker};
15use router_error::{Explain, RouterError};
16use std::sync::Arc;
17use thiserror::Error;
18use zx_status as zx;
19
20#[cfg(feature = "serde")]
21use serde::{Deserialize, Serialize};
22
23/// Errors produced by `ComponentInstanceInterface`.
24#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(rename_all = "snake_case"))]
25#[derive(Debug, Error, Clone)]
26pub enum ComponentInstanceError {
27    #[error("could not find `{moniker}`")]
28    InstanceNotFound { moniker: Moniker },
29    #[error("component is not executable `{moniker}`")]
30    InstanceNotExecutable { moniker: Moniker },
31    #[error("component manager instance unavailable")]
32    ComponentManagerInstanceUnavailable {},
33    #[error("expected a component instance, but got component manager's instance")]
34    ComponentManagerInstanceUnexpected {},
35    #[error("malformed url `{url}` for `{moniker}`")]
36    MalformedUrl { url: String, moniker: Moniker },
37    #[error("url `{url}` for `{moniker}` does not resolve to an absolute url")]
38    NoAbsoluteUrl { url: String, moniker: Moniker },
39    // The capability routing static analyzer never produces this error subtype, so we don't need
40    // to serialize it.
41    #[cfg_attr(feature = "serde", serde(skip))]
42    #[error("failed to resolve `{moniker}`:\n\t{err}")]
43    ResolveFailed {
44        moniker: Moniker,
45        #[source]
46        err: ClonableError,
47    },
48    // The capability routing static analyzer never produces this error subtype, so we don't need
49    // to serialize it.
50    #[cfg_attr(feature = "serde", serde(skip))]
51    #[error("failed to start `{moniker}`:\n\t{err_msg}")]
52    StartFailed {
53        moniker: Moniker,
54        // This error always comes from a StartActionError in
55        // //src/sys/component_manager/lib/errors, but we can't directly use the error value here
56        // because that library already depends on us.
57        err_msg: String,
58        err_as_zx: zx::Status,
59    },
60    #[error("failed to create storage for `{moniker}`:\n\t{err_msg}")]
61    FailedToCreateStorage { moniker: Moniker, err_msg: String },
62}
63
64impl ComponentInstanceError {
65    pub fn as_zx_status(&self) -> zx::Status {
66        match self {
67            ComponentInstanceError::ResolveFailed { .. }
68            | ComponentInstanceError::InstanceNotFound { .. }
69            | ComponentInstanceError::ComponentManagerInstanceUnavailable {}
70            | ComponentInstanceError::InstanceNotExecutable { .. }
71            | ComponentInstanceError::NoAbsoluteUrl { .. }
72            | ComponentInstanceError::FailedToCreateStorage { .. } => zx::Status::NOT_FOUND,
73            ComponentInstanceError::StartFailed { err_as_zx, .. } => *err_as_zx,
74            ComponentInstanceError::MalformedUrl { .. }
75            | ComponentInstanceError::ComponentManagerInstanceUnexpected { .. } => {
76                zx::Status::INTERNAL
77            }
78        }
79    }
80
81    pub fn instance_not_found(moniker: Moniker) -> ComponentInstanceError {
82        ComponentInstanceError::InstanceNotFound { moniker }
83    }
84
85    pub fn cm_instance_unavailable() -> ComponentInstanceError {
86        ComponentInstanceError::ComponentManagerInstanceUnavailable {}
87    }
88
89    pub fn resolve_failed(moniker: Moniker, err: impl Into<anyhow::Error>) -> Self {
90        Self::ResolveFailed { moniker, err: err.into().into() }
91    }
92}
93
94impl Explain for ComponentInstanceError {
95    fn as_zx_status(&self) -> zx::Status {
96        self.as_zx_status()
97    }
98}
99
100impl From<ComponentInstanceError> for ExtendedMoniker {
101    fn from(err: ComponentInstanceError) -> ExtendedMoniker {
102        match err {
103            ComponentInstanceError::InstanceNotFound { moniker }
104            | ComponentInstanceError::MalformedUrl { moniker, .. }
105            | ComponentInstanceError::NoAbsoluteUrl { moniker, .. }
106            | ComponentInstanceError::InstanceNotExecutable { moniker }
107            | ComponentInstanceError::ResolveFailed { moniker, .. }
108            | ComponentInstanceError::StartFailed { moniker, .. }
109            | ComponentInstanceError::FailedToCreateStorage { moniker, .. } => {
110                ExtendedMoniker::ComponentInstance(moniker)
111            }
112            ComponentInstanceError::ComponentManagerInstanceUnavailable {}
113            | ComponentInstanceError::ComponentManagerInstanceUnexpected {} => {
114                ExtendedMoniker::ComponentManager
115            }
116        }
117    }
118}
119
120// Custom implementation of PartialEq in which two ComponentInstanceError::ResolveFailed errors are
121// never equal.
122impl PartialEq for ComponentInstanceError {
123    fn eq(&self, other: &Self) -> bool {
124        match (self, other) {
125            (
126                Self::InstanceNotFound { moniker: self_moniker },
127                Self::InstanceNotFound { moniker: other_moniker },
128            ) => self_moniker.eq(other_moniker),
129            (
130                Self::ComponentManagerInstanceUnavailable {},
131                Self::ComponentManagerInstanceUnavailable {},
132            ) => true,
133            (Self::ResolveFailed { .. }, Self::ResolveFailed { .. }) => false,
134            _ => false,
135        }
136    }
137}
138
139#[derive(Clone, Copy, Debug, PartialEq)]
140#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(rename_all = "snake_case"))]
141pub enum RouteVerb {
142    Use,
143    Offer,
144    Expose,
145    Declare,
146    Contain,
147    Register,
148    IncludeInAggregate,
149}
150
151impl std::fmt::Display for RouteVerb {
152    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153        match self {
154            RouteVerb::Use => write!(f, "use"),
155            RouteVerb::Offer => write!(f, "offer"),
156            RouteVerb::Expose => write!(f, "expose"),
157            RouteVerb::Declare => write!(f, "declare"),
158            RouteVerb::Contain => write!(f, "contain"),
159            RouteVerb::Register => write!(f, "register in environment"),
160            RouteVerb::IncludeInAggregate => write!(f, "include in aggregate"),
161        }
162    }
163}
164
165impl From<&cm_rust::ExposeDecl> for RouteVerb {
166    fn from(_decl: &cm_rust::ExposeDecl) -> Self {
167        Self::Expose
168    }
169}
170
171impl From<&cm_rust::OfferDecl> for RouteVerb {
172    fn from(_decl: &cm_rust::OfferDecl) -> Self {
173        Self::Offer
174    }
175}
176
177impl From<&cm_rust::UseDecl> for RouteVerb {
178    fn from(_decl: &cm_rust::UseDecl) -> Self {
179        Self::Use
180    }
181}
182
183impl From<&cm_rust::DebugRegistration> for RouteVerb {
184    fn from(_decl: &cm_rust::DebugRegistration) -> Self {
185        Self::Register
186    }
187}
188
189impl From<&cm_rust::RunnerRegistration> for RouteVerb {
190    fn from(_decl: &cm_rust::RunnerRegistration) -> Self {
191        Self::Register
192    }
193}
194
195impl From<&cm_rust::ResolverRegistration> for RouteVerb {
196    fn from(_decl: &cm_rust::ResolverRegistration) -> Self {
197        Self::Register
198    }
199}
200
201/// Errors produced during routing.
202#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(rename_all = "snake_case"))]
203#[derive(Debug, Error, Clone, PartialEq)]
204pub enum RoutingError {
205    #[error(
206        "cannot {verb} {capability_type} {capability_name} from {source} at {moniker} because {source} does not {counter_verb} the capability"
207    )]
208    RouteSourceNotFound {
209        moniker: Moniker,
210        verb: RouteVerb,
211        counter_verb: RouteVerb,
212        source: GenericRef,
213        capability_type: CapabilityTypeName,
214        capability_name: RelativePath,
215    },
216
217    #[error("route request received by `{moniker}` is unexpectedly unset")]
218    RouteRequestUnset { moniker: ExtendedMoniker },
219
220    #[error(
221        "route request received by `{moniker}` has been rejected because the following field is unset: `{missing_field}`"
222    )]
223    RouteRequestMissingField { moniker: ExtendedMoniker, missing_field: String },
224
225    #[error(
226        "failed to parse field `{field}` in route request received by `{moniker}`: {parse_error}"
227    )]
228    RouteRequestFailedToParseField { moniker: ExtendedMoniker, field: String, parse_error: String },
229
230    #[error(
231        "`{target_name:?}` tried to use a storage capability from `{source_moniker}` but it is \
232        not in the component id index. https://fuchsia.dev/go/components/instance-id"
233    )]
234    ComponentNotInIdIndex { source_moniker: Moniker, target_name: Option<ChildName> },
235
236    #[error(
237        "`{moniker}` tried to use {capability_type} `{capability_name}` from the root environment"
238    )]
239    UseFromRootEnvironmentNotAllowed {
240        moniker: Moniker,
241        capability_type: String,
242        capability_name: Name,
243    },
244
245    #[error(
246        "`{moniker}` tried to expose `{capability_id}` from the framework, but no such framework capability was found"
247    )]
248    ExposeFromFrameworkNotFound { moniker: Moniker, capability_id: String },
249
250    #[error("`{capability_id}` was not exposed from `/`")]
251    UseFromRootExposeNotFound { capability_id: String },
252
253    #[error("routing a capability from an unsupported source type `{source_type}` at `{moniker}`")]
254    UnsupportedRouteSource { source_type: String, moniker: ExtendedMoniker },
255
256    #[error("routing a capability of an unsupported type `{type_name}` at `{moniker}`")]
257    UnsupportedCapabilityType { type_name: CapabilityTypeName, moniker: ExtendedMoniker },
258
259    #[error("dynamic dictionaries are not allowed at component `{moniker}`")]
260    DynamicDictionariesNotAllowed { moniker: Moniker },
261
262    #[error("item `{name}` is not present in dictionary at component `{moniker}`")]
263    BedrockNotPresentInDictionary { name: String, moniker: ExtendedMoniker },
264
265    #[error(
266        "routed capability was the wrong type at component `{moniker}`. Was: {actual}, expected: {expected}"
267    )]
268    BedrockWrongCapabilityType { actual: String, expected: String, moniker: ExtendedMoniker },
269
270    #[error("failed to send message for capability `{capability_id}` from component `{moniker}`")]
271    BedrockFailedToSend { moniker: ExtendedMoniker, capability_id: String },
272
273    #[error(
274        "the source of capability `{capability_id}` at component `{moniker}` is unknown because we did not perform any routing tasks to find it"
275    )]
276    SourceUnknown { moniker: ExtendedMoniker, capability_id: String },
277
278    #[error(
279        "failed to route capability because the route source has been shutdown and possibly destroyed"
280    )]
281    RouteSourceShutdown { moniker: Moniker },
282
283    #[error(transparent)]
284    ComponentInstanceError(#[from] ComponentInstanceError),
285
286    #[error(transparent)]
287    EventsRoutingError(#[from] EventsRoutingError),
288
289    #[error(transparent)]
290    RightsRoutingError(#[from] RightsRoutingError),
291
292    #[error(transparent)]
293    AvailabilityRoutingError(#[from] AvailabilityRoutingError),
294
295    #[error(transparent)]
296    PolicyError(#[from] PolicyError),
297
298    #[error(
299        "source capability at component {moniker} is void. \
300        If the offer/expose declaration has `source_availability` set to `unknown`, \
301        the source component instance likely isn't defined in the component declaration"
302    )]
303    SourceCapabilityIsVoid { moniker: ExtendedMoniker },
304
305    #[error(
306        "routes that do not set the `debug` flag are unsupported in the current configuration (at `{moniker}`)."
307    )]
308    NonDebugRoutesUnsupported { moniker: ExtendedMoniker },
309
310    #[error("debug routes are unsupported for external routers (at `{moniker}`).")]
311    DebugRoutesUnsupported { moniker: ExtendedMoniker },
312
313    #[error("{type_name} router unexpectedly returned unavailable for target {moniker}")]
314    RouteUnexpectedUnavailable { type_name: CapabilityTypeName, moniker: ExtendedMoniker },
315
316    #[error("path at `{moniker}` was too long for `{keyword}`: {path}")]
317    PathTooLong { moniker: ExtendedMoniker, path: String, keyword: String },
318
319    #[error(
320        "conflicting dictionary entries detected component `{moniker}`: {}",
321        conflicting_names.iter().map(|n| format!("{}", n)).join(", ")
322    )]
323    ConflictingDictionaryEntries { moniker: ExtendedMoniker, conflicting_names: Vec<Name> },
324
325    #[error("FIDL error encountered while talking to a router implemented by component {moniker}")]
326    RemoteFIDLError { moniker: Moniker },
327
328    // We store the raw value of a zx::Status here because zx::Status does not implement Serialize
329    #[error("error returned by a router implemented by component {moniker}")]
330    RemoteRouterError { moniker: Moniker, error_code: i32 },
331}
332
333impl Explain for RoutingError {
334    /// Convert this error into its approximate `zx::Status` equivalent.
335    fn as_zx_status(&self) -> zx::Status {
336        match self {
337            RoutingError::UseFromRootEnvironmentNotAllowed { .. }
338            | RoutingError::DynamicDictionariesNotAllowed { .. } => zx::Status::ACCESS_DENIED,
339            RoutingError::RouteRequestMissingField { .. }
340            | RoutingError::RouteRequestFailedToParseField { .. }
341            | RoutingError::RouteRequestUnset { .. }
342            | RoutingError::ComponentNotInIdIndex { .. }
343            | RoutingError::ConflictingDictionaryEntries { .. }
344            | RoutingError::ExposeFromFrameworkNotFound { .. }
345            | RoutingError::UseFromRootExposeNotFound { .. }
346            | RoutingError::UnsupportedRouteSource { .. }
347            | RoutingError::UnsupportedCapabilityType { .. }
348            | RoutingError::EventsRoutingError(_)
349            | RoutingError::BedrockNotPresentInDictionary { .. }
350            | RoutingError::BedrockFailedToSend { .. }
351            | RoutingError::SourceUnknown { .. }
352            | RoutingError::RouteSourceShutdown { .. }
353            | RoutingError::BedrockWrongCapabilityType { .. }
354            | RoutingError::SourceCapabilityIsVoid { .. }
355            | RoutingError::AvailabilityRoutingError(_)
356            | RoutingError::RouteSourceNotFound { .. }
357            | RoutingError::PathTooLong { .. } => zx::Status::NOT_FOUND,
358            RoutingError::NonDebugRoutesUnsupported { .. }
359            | RoutingError::DebugRoutesUnsupported { .. } => zx::Status::NOT_SUPPORTED,
360            RoutingError::ComponentInstanceError(err) => err.as_zx_status(),
361            RoutingError::RightsRoutingError(err) => err.as_zx_status(),
362            RoutingError::PolicyError(err) => err.as_zx_status(),
363            RoutingError::RouteUnexpectedUnavailable { .. } => zx::Status::INTERNAL,
364            RoutingError::RemoteFIDLError { .. } => zx::Status::PEER_CLOSED,
365            RoutingError::RemoteRouterError { error_code, .. } => {
366                zx::Status::err_from_raw(*error_code)
367            }
368        }
369    }
370}
371
372impl From<RoutingError> for ExtendedMoniker {
373    fn from(err: RoutingError) -> ExtendedMoniker {
374        match err {
375            RoutingError::ComponentNotInIdIndex { source_moniker: moniker, .. }
376            | RoutingError::ExposeFromFrameworkNotFound { moniker, .. }
377            | RoutingError::UseFromRootEnvironmentNotAllowed { moniker, .. }
378            | RoutingError::DynamicDictionariesNotAllowed { moniker, .. }
379            | RoutingError::RouteSourceShutdown { moniker }
380            | RoutingError::RemoteFIDLError { moniker }
381            | RoutingError::RouteSourceNotFound { moniker, .. }
382            | RoutingError::RemoteRouterError { moniker, .. } => moniker.into(),
383            RoutingError::PathTooLong { moniker, .. } => moniker,
384
385            RoutingError::BedrockNotPresentInDictionary { moniker, .. }
386            | RoutingError::BedrockFailedToSend { moniker, .. }
387            | RoutingError::SourceUnknown { moniker, .. }
388            | RoutingError::BedrockWrongCapabilityType { moniker, .. }
389            | RoutingError::RouteRequestMissingField { moniker, .. }
390            | RoutingError::RouteRequestFailedToParseField { moniker, .. }
391            | RoutingError::RouteRequestUnset { moniker, .. }
392            | RoutingError::SourceCapabilityIsVoid { moniker, .. }
393            | RoutingError::ConflictingDictionaryEntries { moniker, .. }
394            | RoutingError::NonDebugRoutesUnsupported { moniker }
395            | RoutingError::DebugRoutesUnsupported { moniker }
396            | RoutingError::RouteUnexpectedUnavailable { moniker, .. }
397            | RoutingError::UnsupportedCapabilityType { moniker, .. }
398            | RoutingError::UnsupportedRouteSource { moniker, .. } => moniker,
399            RoutingError::AvailabilityRoutingError(err) => err.into(),
400            RoutingError::ComponentInstanceError(err) => err.into(),
401            RoutingError::EventsRoutingError(err) => err.into(),
402            RoutingError::PolicyError(err) => err.into(),
403            RoutingError::RightsRoutingError(err) => err.into(),
404
405            RoutingError::UseFromRootExposeNotFound { .. } => ExtendedMoniker::ComponentManager,
406        }
407    }
408}
409
410impl From<RoutingError> for RouterError {
411    fn from(value: RoutingError) -> Self {
412        Self::NotFound(Arc::new(value))
413    }
414}
415
416impl TryFrom<RouterError> for RoutingError {
417    type Error = RouterError;
418
419    fn try_from(value: RouterError) -> Result<Self, Self::Error> {
420        match value {
421            RouterError::NotFound(arc_dyn_explain) => {
422                match arc_dyn_explain.as_any().downcast_ref::<Self>() {
423                    Some(routing_error) => Ok(routing_error.clone()),
424                    None => Err(RouterError::NotFound(arc_dyn_explain)),
425                }
426            }
427            err => Err(err),
428        }
429    }
430}
431
432impl RoutingError {
433    /// Convert this error into its approximate `fuchsia.component.Error` equivalent.
434    pub fn as_fidl_error(&self) -> fcomponent::Error {
435        fcomponent::Error::ResourceUnavailable
436    }
437
438    pub fn expose_from_framework_not_found(
439        moniker: &Moniker,
440        capability_id: impl Into<String>,
441    ) -> Self {
442        Self::ExposeFromFrameworkNotFound {
443            moniker: moniker.clone(),
444            capability_id: capability_id.into(),
445        }
446    }
447
448    pub fn unsupported_route_source(
449        moniker: impl Into<ExtendedMoniker>,
450        source: impl Into<String>,
451    ) -> Self {
452        Self::UnsupportedRouteSource { source_type: source.into(), moniker: moniker.into() }
453    }
454
455    pub fn unsupported_capability_type(
456        moniker: impl Into<ExtendedMoniker>,
457        type_name: impl Into<CapabilityTypeName>,
458    ) -> Self {
459        Self::UnsupportedCapabilityType { type_name: type_name.into(), moniker: moniker.into() }
460    }
461}
462
463/// Errors produced during routing specific to events.
464#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(rename_all = "snake_case"))]
465#[derive(Error, Debug, Clone, PartialEq)]
466pub enum EventsRoutingError {
467    #[error("filter is not a subset at `{moniker}`")]
468    InvalidFilter { moniker: ExtendedMoniker },
469
470    #[error("event routes must end at source with a filter declaration at `{moniker}`")]
471    MissingFilter { moniker: ExtendedMoniker },
472}
473
474impl From<EventsRoutingError> for ExtendedMoniker {
475    fn from(err: EventsRoutingError) -> ExtendedMoniker {
476        match err {
477            EventsRoutingError::InvalidFilter { moniker }
478            | EventsRoutingError::MissingFilter { moniker } => moniker,
479        }
480    }
481}
482
483#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(rename_all = "snake_case"))]
484#[derive(Debug, Error, Clone, PartialEq)]
485pub enum RightsRoutingError {
486    #[error(
487        "requested rights ({requested}) greater than provided rights ({provided}) at \"{moniker}\""
488    )]
489    Invalid { moniker: ExtendedMoniker, requested: Rights, provided: Rights },
490
491    #[error(
492        "directory routes must end at source with a rights declaration, it's missing at \"{moniker}\""
493    )]
494    MissingRightsSource { moniker: ExtendedMoniker },
495}
496
497impl RightsRoutingError {
498    /// Convert this error into its approximate `zx::Status` equivalent.
499    pub fn as_zx_status(&self) -> zx::Status {
500        match self {
501            RightsRoutingError::Invalid { .. } => zx::Status::ACCESS_DENIED,
502            RightsRoutingError::MissingRightsSource { .. } => zx::Status::NOT_FOUND,
503        }
504    }
505}
506
507impl From<RightsRoutingError> for ExtendedMoniker {
508    fn from(err: RightsRoutingError) -> ExtendedMoniker {
509        match err {
510            RightsRoutingError::Invalid { moniker, .. }
511            | RightsRoutingError::MissingRightsSource { moniker } => moniker,
512        }
513    }
514}
515
516#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(rename_all = "snake_case"))]
517#[derive(Debug, Error, Clone, PartialEq)]
518pub enum AvailabilityRoutingError {
519    #[error(
520        "availability requested by the target has stronger guarantees than what \
521    is being provided at the source at `{moniker}`"
522    )]
523    TargetHasStrongerAvailability { moniker: ExtendedMoniker },
524
525    #[error("offer uses void source, but target requires the capability at `{moniker}`")]
526    OfferFromVoidToRequiredTarget { moniker: ExtendedMoniker },
527
528    #[error("expose uses void source, but target requires the capability at `{moniker}`")]
529    ExposeFromVoidToRequiredTarget { moniker: ExtendedMoniker },
530}
531
532impl From<availability::TargetHasStrongerAvailability> for AvailabilityRoutingError {
533    fn from(value: availability::TargetHasStrongerAvailability) -> Self {
534        let availability::TargetHasStrongerAvailability { moniker } = value;
535        AvailabilityRoutingError::TargetHasStrongerAvailability { moniker }
536    }
537}
538
539impl From<AvailabilityRoutingError> for ExtendedMoniker {
540    fn from(err: AvailabilityRoutingError) -> ExtendedMoniker {
541        match err {
542            AvailabilityRoutingError::ExposeFromVoidToRequiredTarget { moniker }
543            | AvailabilityRoutingError::OfferFromVoidToRequiredTarget { moniker }
544            | AvailabilityRoutingError::TargetHasStrongerAvailability { moniker } => moniker,
545        }
546    }
547}
548
549// Implements error reporting upon routing failure. For example, component
550// manager logs the error.
551#[async_trait]
552pub trait ErrorReporter: Clone + Send + Sync + 'static {
553    async fn report(
554        &self,
555        request: &RouteRequestErrorInfo,
556        err: &RouterError,
557        route_target: Arc<runtime_capabilities::WeakInstanceToken>,
558    );
559}
560
561/// What to print in an error if a route request fails.
562#[derive(Clone, Debug, PartialEq, Eq)]
563pub struct RouteRequestErrorInfo {
564    capability_type: cm_rust::CapabilityTypeName,
565    name: cm_types::Name,
566    availability: cm_rust::Availability,
567}
568
569impl RouteRequestErrorInfo {
570    pub fn availability(&self) -> cm_rust::Availability {
571        self.availability
572    }
573
574    pub fn name(&self) -> &Name {
575        &self.name
576    }
577
578    pub fn type_name(&self) -> &CapabilityTypeName {
579        &self.capability_type
580    }
581
582    pub fn for_builtin(capability_type: CapabilityTypeName, name: &Name) -> Self {
583        Self { capability_type, name: name.clone(), availability: Availability::Required }
584    }
585}
586
587impl From<&RouteRequestErrorInfo> for runtime_capabilities::RouterErrorInfo {
588    fn from(value: &RouteRequestErrorInfo) -> Self {
589        Self {
590            capability_type: value.capability_type,
591            name: value.name.clone(),
592            availability: value.availability,
593        }
594    }
595}
596
597impl From<runtime_capabilities::RouterErrorInfo> for RouteRequestErrorInfo {
598    fn from(value: runtime_capabilities::RouterErrorInfo) -> Self {
599        Self {
600            capability_type: value.capability_type,
601            name: value.name,
602            availability: value.availability,
603        }
604    }
605}
606
607impl From<&cm_rust::UseDecl> for RouteRequestErrorInfo {
608    fn from(value: &cm_rust::UseDecl) -> Self {
609        RouteRequestErrorInfo {
610            capability_type: value.into(),
611            name: value.source_name().clone(),
612            availability: value.availability().clone(),
613        }
614    }
615}
616
617impl From<&cm_rust::UseConfigurationDecl> for RouteRequestErrorInfo {
618    fn from(value: &cm_rust::UseConfigurationDecl) -> Self {
619        RouteRequestErrorInfo {
620            capability_type: CapabilityTypeName::Config,
621            name: value.source_name().clone(),
622            availability: value.availability().clone(),
623        }
624    }
625}
626
627impl From<&cm_rust::UseEventStreamDecl> for RouteRequestErrorInfo {
628    fn from(value: &cm_rust::UseEventStreamDecl) -> Self {
629        RouteRequestErrorInfo {
630            capability_type: CapabilityTypeName::EventStream,
631            name: value.source_name.clone(),
632            availability: value.availability,
633        }
634    }
635}
636
637impl From<&cm_rust::ExposeDecl> for RouteRequestErrorInfo {
638    fn from(value: &cm_rust::ExposeDecl) -> Self {
639        RouteRequestErrorInfo {
640            capability_type: value.into(),
641            name: value.target_name().clone(),
642            availability: value.availability().clone(),
643        }
644    }
645}
646
647impl From<&cm_rust::offer::OfferDecl> for RouteRequestErrorInfo {
648    fn from(value: &cm_rust::offer::OfferDecl) -> Self {
649        RouteRequestErrorInfo {
650            capability_type: value.into(),
651            name: value.target_name().clone(),
652            availability: value.availability().clone(),
653        }
654    }
655}
656
657impl From<&cm_rust::ResolverRegistration> for RouteRequestErrorInfo {
658    fn from(value: &cm_rust::ResolverRegistration) -> Self {
659        RouteRequestErrorInfo {
660            capability_type: CapabilityTypeName::Resolver,
661            name: value.source_name().clone(),
662            availability: Availability::Required,
663        }
664    }
665}
666
667impl From<&cm_rust::RunnerRegistration> for RouteRequestErrorInfo {
668    fn from(value: &cm_rust::RunnerRegistration) -> Self {
669        RouteRequestErrorInfo {
670            capability_type: CapabilityTypeName::Runner,
671            name: value.source_name().clone(),
672            availability: Availability::Required,
673        }
674    }
675}
676
677impl From<&cm_rust::DebugRegistration> for RouteRequestErrorInfo {
678    fn from(value: &cm_rust::DebugRegistration) -> Self {
679        RouteRequestErrorInfo {
680            capability_type: CapabilityTypeName::Protocol,
681            name: value.source_name().clone(),
682            availability: Availability::Required,
683        }
684    }
685}
686
687impl From<&cm_rust::CapabilityDecl> for RouteRequestErrorInfo {
688    fn from(value: &cm_rust::CapabilityDecl) -> Self {
689        RouteRequestErrorInfo {
690            capability_type: value.into(),
691            name: value.name().clone(),
692            availability: Availability::Required,
693        }
694    }
695}
696
697impl std::fmt::Display for RouteRequestErrorInfo {
698    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
699        write!(f, "{} `{}`", self.capability_type, self.name)
700    }
701}