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