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, Name};
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/// Errors produced during routing.
140#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(rename_all = "snake_case"))]
141#[derive(Debug, Error, Clone, PartialEq)]
142pub enum RoutingError {
143    #[error(
144        "backing directory `{capability_id}` was not exposed to `{moniker}` from `#{child_moniker}`"
145    )]
146    StorageFromChildExposeNotFound {
147        child_moniker: ChildName,
148        moniker: Moniker,
149        capability_id: String,
150    },
151
152    #[error(
153        "`{target_name:?}` tried to use a storage capability from `{source_moniker}` but it is \
154        not in the component id index. https://fuchsia.dev/go/components/instance-id"
155    )]
156    ComponentNotInIdIndex { source_moniker: Moniker, target_name: Option<ChildName> },
157
158    #[error("`{capability_id}` is not a built-in capability")]
159    UseFromComponentManagerNotFound { capability_id: String },
160
161    #[error("`{capability_id}` is not a built-in capability")]
162    RegisterFromComponentManagerNotFound { capability_id: String },
163
164    #[error("`{capability_id}` is not a built-in capability")]
165    OfferFromComponentManagerNotFound { capability_id: String },
166
167    #[error("`{capability_id}` was not offered to `{moniker}` by parent")]
168    UseFromParentNotFound { moniker: Moniker, capability_id: String },
169
170    #[error("`{capability_id}` was not declared as a capability by `{moniker}`")]
171    UseFromSelfNotFound { moniker: Moniker, capability_id: String },
172
173    #[error("`{moniker}` does not have child `#{child_moniker}`")]
174    UseFromChildInstanceNotFound {
175        child_moniker: ChildName,
176        moniker: Moniker,
177        capability_id: String,
178    },
179
180    #[error(
181        "{capability_type} `{capability_name}` was not registered in environment of `{moniker}`"
182    )]
183    UseFromEnvironmentNotFound { moniker: Moniker, capability_type: String, capability_name: Name },
184
185    #[error(
186        "`{moniker}` tried to use {capability_type} `{capability_name}` from the root environment"
187    )]
188    UseFromRootEnvironmentNotAllowed {
189        moniker: Moniker,
190        capability_type: String,
191        capability_name: Name,
192    },
193
194    #[error("{capability_type} `{capability_name}` was not offered to `{moniker}` by parent")]
195    EnvironmentFromParentNotFound {
196        moniker: Moniker,
197        capability_type: String,
198        capability_name: Name,
199    },
200
201    #[error("`{capability_name}` was not exposed to `{moniker}` from `#{child_moniker}`")]
202    EnvironmentFromChildExposeNotFound {
203        child_moniker: ChildName,
204        moniker: Moniker,
205        capability_type: String,
206        capability_name: Name,
207    },
208
209    #[error("`{moniker}` does not have child `#{child_moniker}`")]
210    EnvironmentFromChildInstanceNotFound {
211        child_moniker: ChildName,
212        moniker: Moniker,
213        capability_name: Name,
214        capability_type: String,
215    },
216
217    #[error("`{capability_id}` was not offered to `{moniker}` by parent")]
218    OfferFromParentNotFound { moniker: Moniker, capability_id: String },
219
220    #[error(
221        "cannot offer `{capability_id}` because was not declared as a capability by `{moniker}`"
222    )]
223    OfferFromSelfNotFound { moniker: Moniker, capability_id: String },
224
225    #[error("`{capability_id}` was not offered to `{moniker}` by parent")]
226    StorageFromParentNotFound { moniker: Moniker, capability_id: String },
227
228    #[error("`{moniker}` does not have child `#{child_moniker}`")]
229    OfferFromChildInstanceNotFound {
230        child_moniker: ChildName,
231        moniker: Moniker,
232        capability_id: String,
233    },
234
235    #[error("`{moniker}` does not have collection `#{collection}`")]
236    OfferFromCollectionNotFound { collection: String, moniker: Moniker, capability: Name },
237
238    #[error("`{capability_id}` was not exposed to `{moniker}` from `#{child_moniker}`")]
239    OfferFromChildExposeNotFound {
240        child_moniker: ChildName,
241        moniker: Moniker,
242        capability_id: String,
243    },
244
245    // TODO: Could this be distinguished by use/offer/expose?
246    #[error("`{capability_id}` is not a framework capability (at component `{moniker}`)")]
247    CapabilityFromFrameworkNotFound { moniker: Moniker, capability_id: String },
248
249    #[error(
250        "A capability was sourced to a base capability `{capability_id}` from `{moniker}`, but this is unsupported"
251    )]
252    CapabilityFromCapabilityNotFound { moniker: Moniker, capability_id: String },
253
254    // TODO: Could this be distinguished by use/offer/expose?
255    #[error("`{capability_id}` is not a framework capability")]
256    CapabilityFromComponentManagerNotFound { capability_id: String },
257
258    #[error(
259        "unable to expose `{capability_id}` because it was not declared as a capability by `{moniker}`"
260    )]
261    ExposeFromSelfNotFound { moniker: Moniker, capability_id: String },
262
263    #[error("`{moniker}` does not have child `#{child_moniker}`")]
264    ExposeFromChildInstanceNotFound {
265        child_moniker: ChildName,
266        moniker: Moniker,
267        capability_id: String,
268    },
269
270    #[error("`{moniker}` does not have collection `#{collection}`")]
271    ExposeFromCollectionNotFound { collection: String, moniker: Moniker, capability: Name },
272
273    #[error("`{capability_id}` was not exposed to `{moniker}` from `#{child_moniker}`")]
274    ExposeFromChildExposeNotFound {
275        child_moniker: ChildName,
276        moniker: Moniker,
277        capability_id: String,
278    },
279
280    #[error(
281        "`{moniker}` tried to expose `{capability_id}` from the framework, but no such framework capability was found"
282    )]
283    ExposeFromFrameworkNotFound { moniker: Moniker, capability_id: String },
284
285    #[error("`{capability_id}` was not exposed to `{moniker}` from `#{child_moniker}`")]
286    UseFromChildExposeNotFound { child_moniker: ChildName, moniker: Moniker, capability_id: String },
287
288    #[error("`{capability_id}` was not exposed from `/`")]
289    UseFromRootExposeNotFound { capability_id: String },
290
291    #[error("routing a capability from an unsupported source type `{source_type}` at `{moniker}`")]
292    UnsupportedRouteSource { source_type: String, moniker: ExtendedMoniker },
293
294    #[error("routing a capability of an unsupported type `{type_name}` at `{moniker}`")]
295    UnsupportedCapabilityType { type_name: CapabilityTypeName, moniker: ExtendedMoniker },
296
297    #[error(
298        "dictionaries are not yet supported for {cap_type} capabilities at component `{moniker}`"
299    )]
300    DictionariesNotSupported { moniker: Moniker, cap_type: CapabilityTypeName },
301
302    #[error("dynamic dictionaries are not allowed at component `{moniker}`")]
303    DynamicDictionariesNotAllowed { moniker: Moniker },
304
305    #[error("the capability does not support member access at `{moniker}`")]
306    BedrockMemberAccessUnsupported { moniker: ExtendedMoniker },
307
308    #[error("item `{name}` is not present in dictionary at component `{moniker}`")]
309    BedrockNotPresentInDictionary { name: String, moniker: ExtendedMoniker },
310
311    #[error(
312        "routed capability was the wrong type at component `{moniker}`. Was: {actual}, expected: {expected}"
313    )]
314    BedrockWrongCapabilityType { actual: String, expected: String, moniker: ExtendedMoniker },
315
316    #[error(
317        "expected type {type_name} for routed capability at component `{moniker}`, but type was missing"
318    )]
319    BedrockMissingCapabilityType { type_name: String, moniker: ExtendedMoniker },
320
321    #[error("there was an error remoting a capability at component `{moniker}`")]
322    BedrockRemoteCapability { moniker: Moniker },
323
324    #[error("source dictionary was not found in child's exposes at component `{moniker}`")]
325    BedrockSourceDictionaryExposeNotFound { moniker: Moniker },
326
327    #[error("Some capability in the routing chain could not be cloned at `{moniker}`.")]
328    BedrockNotCloneable { moniker: ExtendedMoniker },
329
330    #[error(
331        "a capability in a dictionary extended from a source dictionary collides with \
332        a capability in the source dictionary that has the same key at `{moniker}`"
333    )]
334    BedrockSourceDictionaryCollision { moniker: ExtendedMoniker },
335
336    #[error("failed to send message for capability `{capability_id}` from component `{moniker}`")]
337    BedrockFailedToSend { moniker: ExtendedMoniker, capability_id: String },
338
339    #[error(
340        "failed to route capability because the route source has been shutdown and possibly destroyed"
341    )]
342    RouteSourceShutdown { moniker: Moniker },
343
344    #[error(transparent)]
345    ComponentInstanceError(#[from] ComponentInstanceError),
346
347    #[error(transparent)]
348    EventsRoutingError(#[from] EventsRoutingError),
349
350    #[error(transparent)]
351    RightsRoutingError(#[from] RightsRoutingError),
352
353    #[error(transparent)]
354    AvailabilityRoutingError(#[from] AvailabilityRoutingError),
355
356    #[error(transparent)]
357    PolicyError(#[from] PolicyError),
358
359    #[error(
360        "source capability at component {moniker} is void. \
361        If the offer/expose declaration has `source_availability` set to `unknown`, \
362        the source component instance likely isn't defined in the component declaration"
363    )]
364    SourceCapabilityIsVoid { moniker: Moniker },
365
366    #[error(
367        "routes that do not set the `debug` flag are unsupported in the current configuration (at `{moniker}`)."
368    )]
369    NonDebugRoutesUnsupported { moniker: ExtendedMoniker },
370
371    #[error("debug routes are unsupported for external routers (at `{moniker}`).")]
372    DebugRoutesUnsupported { moniker: ExtendedMoniker },
373
374    #[error("{type_name} router unexpectedly returned debug info for target {moniker}")]
375    RouteUnexpectedDebug { type_name: CapabilityTypeName, moniker: ExtendedMoniker },
376
377    #[error("{type_name} router unexpectedly returned unavailable for target {moniker}")]
378    RouteUnexpectedUnavailable { type_name: CapabilityTypeName, moniker: ExtendedMoniker },
379
380    #[error("{name} at {moniker} is missing porcelain type metadata.")]
381    MissingPorcelainType { name: Name, moniker: Moniker },
382
383    #[error("path at `{moniker}` was too long for `{keyword}`: {path}")]
384    PathTooLong { moniker: ExtendedMoniker, path: String, keyword: String },
385
386    #[error(
387        "conflicting dictionary entries detected component `{moniker}`: {}",
388        conflicting_names.iter().map(|n| format!("{}", n)).join(", ")
389    )]
390    ConflictingDictionaryEntries { moniker: ExtendedMoniker, conflicting_names: Vec<Name> },
391
392    #[error("FIDL error encountered while talking to a router implemented by component {moniker}")]
393    RemoteFIDLError { moniker: Moniker },
394
395    // We store the raw value of a zx::Status here because zx::Status does not implement Serialize
396    #[error("error returned by a router implemented by component {moniker}")]
397    RemoteRouterError { moniker: Moniker, error_code: i32 },
398
399    #[error("unexpected error received")]
400    UnexpectedError,
401}
402
403impl Explain for RoutingError {
404    /// Convert this error into its approximate `zx::Status` equivalent.
405    fn as_zx_status(&self) -> zx::Status {
406        match self {
407            RoutingError::UseFromRootEnvironmentNotAllowed { .. }
408            | RoutingError::DynamicDictionariesNotAllowed { .. } => zx::Status::ACCESS_DENIED,
409            RoutingError::StorageFromChildExposeNotFound { .. }
410            | RoutingError::ComponentNotInIdIndex { .. }
411            | RoutingError::UseFromComponentManagerNotFound { .. }
412            | RoutingError::RegisterFromComponentManagerNotFound { .. }
413            | RoutingError::OfferFromComponentManagerNotFound { .. }
414            | RoutingError::UseFromParentNotFound { .. }
415            | RoutingError::UseFromSelfNotFound { .. }
416            | RoutingError::UseFromChildInstanceNotFound { .. }
417            | RoutingError::UseFromEnvironmentNotFound { .. }
418            | RoutingError::EnvironmentFromParentNotFound { .. }
419            | RoutingError::EnvironmentFromChildExposeNotFound { .. }
420            | RoutingError::EnvironmentFromChildInstanceNotFound { .. }
421            | RoutingError::OfferFromParentNotFound { .. }
422            | RoutingError::OfferFromSelfNotFound { .. }
423            | RoutingError::StorageFromParentNotFound { .. }
424            | RoutingError::OfferFromChildInstanceNotFound { .. }
425            | RoutingError::OfferFromCollectionNotFound { .. }
426            | RoutingError::OfferFromChildExposeNotFound { .. }
427            | RoutingError::CapabilityFromFrameworkNotFound { .. }
428            | RoutingError::CapabilityFromCapabilityNotFound { .. }
429            | RoutingError::CapabilityFromComponentManagerNotFound { .. }
430            | RoutingError::ConflictingDictionaryEntries { .. }
431            | RoutingError::ExposeFromSelfNotFound { .. }
432            | RoutingError::ExposeFromChildInstanceNotFound { .. }
433            | RoutingError::ExposeFromCollectionNotFound { .. }
434            | RoutingError::ExposeFromChildExposeNotFound { .. }
435            | RoutingError::ExposeFromFrameworkNotFound { .. }
436            | RoutingError::UseFromChildExposeNotFound { .. }
437            | RoutingError::UseFromRootExposeNotFound { .. }
438            | RoutingError::UnsupportedRouteSource { .. }
439            | RoutingError::UnsupportedCapabilityType { .. }
440            | RoutingError::EventsRoutingError(_)
441            | RoutingError::BedrockNotPresentInDictionary { .. }
442            | RoutingError::BedrockSourceDictionaryExposeNotFound { .. }
443            | RoutingError::BedrockSourceDictionaryCollision { .. }
444            | RoutingError::BedrockFailedToSend { .. }
445            | RoutingError::RouteSourceShutdown { .. }
446            | RoutingError::BedrockMissingCapabilityType { .. }
447            | RoutingError::BedrockWrongCapabilityType { .. }
448            | RoutingError::BedrockRemoteCapability { .. }
449            | RoutingError::BedrockNotCloneable { .. }
450            | RoutingError::AvailabilityRoutingError(_)
451            | RoutingError::PathTooLong { .. } => zx::Status::NOT_FOUND,
452            RoutingError::BedrockMemberAccessUnsupported { .. }
453            | RoutingError::NonDebugRoutesUnsupported { .. }
454            | RoutingError::DebugRoutesUnsupported { .. }
455            | RoutingError::DictionariesNotSupported { .. } => zx::Status::NOT_SUPPORTED,
456            RoutingError::ComponentInstanceError(err) => err.as_zx_status(),
457            RoutingError::RightsRoutingError(err) => err.as_zx_status(),
458            RoutingError::PolicyError(err) => err.as_zx_status(),
459            RoutingError::SourceCapabilityIsVoid { .. } => zx::Status::NOT_FOUND,
460            RoutingError::RouteUnexpectedDebug { .. }
461            | RoutingError::RouteUnexpectedUnavailable { .. }
462            | RoutingError::MissingPorcelainType { .. }
463            | RoutingError::UnexpectedError => zx::Status::INTERNAL,
464            RoutingError::RemoteFIDLError { .. } => zx::Status::PEER_CLOSED,
465            RoutingError::RemoteRouterError { error_code, .. } => zx::Status::from_raw(*error_code),
466        }
467    }
468}
469
470impl From<RoutingError> for ExtendedMoniker {
471    fn from(err: RoutingError) -> ExtendedMoniker {
472        match err {
473            RoutingError::BedrockRemoteCapability { moniker, .. }
474            | RoutingError::BedrockSourceDictionaryExposeNotFound { moniker, .. }
475            | RoutingError::CapabilityFromCapabilityNotFound { moniker, .. }
476            | RoutingError::CapabilityFromFrameworkNotFound { moniker, .. }
477            | RoutingError::ComponentNotInIdIndex { source_moniker: moniker, .. }
478            | RoutingError::DictionariesNotSupported { moniker, .. }
479            | RoutingError::EnvironmentFromChildExposeNotFound { moniker, .. }
480            | RoutingError::EnvironmentFromChildInstanceNotFound { moniker, .. }
481            | RoutingError::EnvironmentFromParentNotFound { moniker, .. }
482            | RoutingError::ExposeFromChildExposeNotFound { moniker, .. }
483            | RoutingError::ExposeFromChildInstanceNotFound { moniker, .. }
484            | RoutingError::ExposeFromCollectionNotFound { moniker, .. }
485            | RoutingError::ExposeFromFrameworkNotFound { moniker, .. }
486            | RoutingError::ExposeFromSelfNotFound { moniker, .. }
487            | RoutingError::OfferFromChildExposeNotFound { moniker, .. }
488            | RoutingError::OfferFromChildInstanceNotFound { moniker, .. }
489            | RoutingError::OfferFromCollectionNotFound { moniker, .. }
490            | RoutingError::OfferFromParentNotFound { moniker, .. }
491            | RoutingError::OfferFromSelfNotFound { moniker, .. }
492            | RoutingError::SourceCapabilityIsVoid { moniker, .. }
493            | RoutingError::StorageFromChildExposeNotFound { moniker, .. }
494            | RoutingError::StorageFromParentNotFound { moniker, .. }
495            | RoutingError::UseFromChildExposeNotFound { moniker, .. }
496            | RoutingError::UseFromChildInstanceNotFound { moniker, .. }
497            | RoutingError::UseFromEnvironmentNotFound { moniker, .. }
498            | RoutingError::UseFromParentNotFound { moniker, .. }
499            | RoutingError::UseFromRootEnvironmentNotAllowed { moniker, .. }
500            | RoutingError::DynamicDictionariesNotAllowed { moniker, .. }
501            | RoutingError::RouteSourceShutdown { moniker }
502            | RoutingError::UseFromSelfNotFound { moniker, .. }
503            | RoutingError::MissingPorcelainType { moniker, .. }
504            | RoutingError::RemoteFIDLError { moniker }
505            | RoutingError::RemoteRouterError { moniker, .. } => moniker.into(),
506            RoutingError::PathTooLong { moniker, .. } => moniker,
507
508            RoutingError::BedrockMemberAccessUnsupported { moniker }
509            | RoutingError::BedrockNotPresentInDictionary { moniker, .. }
510            | RoutingError::BedrockNotCloneable { moniker }
511            | RoutingError::BedrockSourceDictionaryCollision { moniker }
512            | RoutingError::BedrockFailedToSend { moniker, .. }
513            | RoutingError::BedrockMissingCapabilityType { moniker, .. }
514            | RoutingError::BedrockWrongCapabilityType { moniker, .. }
515            | RoutingError::ConflictingDictionaryEntries { moniker, .. }
516            | RoutingError::NonDebugRoutesUnsupported { moniker }
517            | RoutingError::DebugRoutesUnsupported { moniker }
518            | RoutingError::RouteUnexpectedDebug { moniker, .. }
519            | RoutingError::RouteUnexpectedUnavailable { moniker, .. }
520            | RoutingError::UnsupportedCapabilityType { moniker, .. }
521            | RoutingError::UnsupportedRouteSource { moniker, .. } => moniker,
522            RoutingError::AvailabilityRoutingError(err) => err.into(),
523            RoutingError::ComponentInstanceError(err) => err.into(),
524            RoutingError::EventsRoutingError(err) => err.into(),
525            RoutingError::PolicyError(err) => err.into(),
526            RoutingError::RightsRoutingError(err) => err.into(),
527
528            RoutingError::CapabilityFromComponentManagerNotFound { .. }
529            | RoutingError::OfferFromComponentManagerNotFound { .. }
530            | RoutingError::RegisterFromComponentManagerNotFound { .. }
531            | RoutingError::UseFromComponentManagerNotFound { .. }
532            | RoutingError::UseFromRootExposeNotFound { .. }
533            | RoutingError::UnexpectedError => ExtendedMoniker::ComponentManager,
534        }
535    }
536}
537
538impl From<RoutingError> for RouterError {
539    fn from(value: RoutingError) -> Self {
540        Self::NotFound(Arc::new(value))
541    }
542}
543
544impl TryFrom<RouterError> for RoutingError {
545    type Error = RouterError;
546
547    fn try_from(value: RouterError) -> Result<Self, Self::Error> {
548        match value {
549            RouterError::NotFound(arc_dyn_explain) => {
550                match arc_dyn_explain.as_any().downcast_ref::<Self>() {
551                    Some(routing_error) => Ok(routing_error.clone()),
552                    None => Err(RouterError::NotFound(arc_dyn_explain)),
553                }
554            }
555            err => Err(err),
556        }
557    }
558}
559
560impl RoutingError {
561    /// Convert this error into its approximate `fuchsia.component.Error` equivalent.
562    pub fn as_fidl_error(&self) -> fcomponent::Error {
563        fcomponent::Error::ResourceUnavailable
564    }
565
566    pub fn storage_from_child_expose_not_found(
567        child_moniker: &ChildName,
568        moniker: &Moniker,
569        capability_id: impl Into<String>,
570    ) -> Self {
571        Self::StorageFromChildExposeNotFound {
572            child_moniker: child_moniker.clone(),
573            moniker: moniker.clone(),
574            capability_id: capability_id.into(),
575        }
576    }
577
578    pub fn use_from_component_manager_not_found(capability_id: impl Into<String>) -> Self {
579        Self::UseFromComponentManagerNotFound { capability_id: capability_id.into() }
580    }
581
582    pub fn register_from_component_manager_not_found(capability_id: impl Into<String>) -> Self {
583        Self::RegisterFromComponentManagerNotFound { capability_id: capability_id.into() }
584    }
585
586    pub fn offer_from_component_manager_not_found(capability_id: impl Into<String>) -> Self {
587        Self::OfferFromComponentManagerNotFound { capability_id: capability_id.into() }
588    }
589
590    pub fn use_from_parent_not_found(moniker: &Moniker, capability_id: impl Into<String>) -> Self {
591        Self::UseFromParentNotFound {
592            moniker: moniker.clone(),
593            capability_id: capability_id.into(),
594        }
595    }
596
597    pub fn use_from_self_not_found(moniker: &Moniker, capability_id: impl Into<String>) -> Self {
598        Self::UseFromSelfNotFound { moniker: moniker.clone(), capability_id: capability_id.into() }
599    }
600
601    pub fn use_from_child_instance_not_found(
602        child_moniker: &ChildName,
603        moniker: &Moniker,
604        capability_id: impl Into<String>,
605    ) -> Self {
606        Self::UseFromChildInstanceNotFound {
607            child_moniker: child_moniker.clone(),
608            moniker: moniker.clone(),
609            capability_id: capability_id.into(),
610        }
611    }
612
613    pub fn use_from_environment_not_found(
614        moniker: &Moniker,
615        capability_type: impl Into<String>,
616        capability_name: &Name,
617    ) -> Self {
618        Self::UseFromEnvironmentNotFound {
619            moniker: moniker.clone(),
620            capability_type: capability_type.into(),
621            capability_name: capability_name.clone(),
622        }
623    }
624
625    pub fn offer_from_parent_not_found(
626        moniker: &Moniker,
627        capability_id: impl Into<String>,
628    ) -> Self {
629        Self::OfferFromParentNotFound {
630            moniker: moniker.clone(),
631            capability_id: capability_id.into(),
632        }
633    }
634
635    pub fn offer_from_self_not_found(moniker: &Moniker, capability_id: impl Into<String>) -> Self {
636        Self::OfferFromSelfNotFound {
637            moniker: moniker.clone(),
638            capability_id: capability_id.into(),
639        }
640    }
641
642    pub fn storage_from_parent_not_found(
643        moniker: &Moniker,
644        capability_id: impl Into<String>,
645    ) -> Self {
646        Self::StorageFromParentNotFound {
647            moniker: moniker.clone(),
648            capability_id: capability_id.into(),
649        }
650    }
651
652    pub fn offer_from_child_instance_not_found(
653        child_moniker: &ChildName,
654        moniker: &Moniker,
655        capability_id: impl Into<String>,
656    ) -> Self {
657        Self::OfferFromChildInstanceNotFound {
658            child_moniker: child_moniker.clone(),
659            moniker: moniker.clone(),
660            capability_id: capability_id.into(),
661        }
662    }
663
664    pub fn offer_from_child_expose_not_found(
665        child_moniker: &ChildName,
666        moniker: &Moniker,
667        capability_id: impl Into<String>,
668    ) -> Self {
669        Self::OfferFromChildExposeNotFound {
670            child_moniker: child_moniker.clone(),
671            moniker: moniker.clone(),
672            capability_id: capability_id.into(),
673        }
674    }
675
676    pub fn use_from_child_expose_not_found(
677        child_moniker: &ChildName,
678        moniker: &Moniker,
679        capability_id: impl Into<String>,
680    ) -> Self {
681        Self::UseFromChildExposeNotFound {
682            child_moniker: child_moniker.clone(),
683            moniker: moniker.clone(),
684            capability_id: capability_id.into(),
685        }
686    }
687
688    pub fn expose_from_self_not_found(moniker: &Moniker, capability_id: impl Into<String>) -> Self {
689        Self::ExposeFromSelfNotFound {
690            moniker: moniker.clone(),
691            capability_id: capability_id.into(),
692        }
693    }
694
695    pub fn expose_from_child_instance_not_found(
696        child_moniker: &ChildName,
697        moniker: &Moniker,
698        capability_id: impl Into<String>,
699    ) -> Self {
700        Self::ExposeFromChildInstanceNotFound {
701            child_moniker: child_moniker.clone(),
702            moniker: moniker.clone(),
703            capability_id: capability_id.into(),
704        }
705    }
706
707    pub fn expose_from_child_expose_not_found(
708        child_moniker: &ChildName,
709        moniker: &Moniker,
710        capability_id: impl Into<String>,
711    ) -> Self {
712        Self::ExposeFromChildExposeNotFound {
713            child_moniker: child_moniker.clone(),
714            moniker: moniker.clone(),
715            capability_id: capability_id.into(),
716        }
717    }
718
719    pub fn capability_from_framework_not_found(
720        moniker: &Moniker,
721        capability_id: impl Into<String>,
722    ) -> Self {
723        Self::CapabilityFromFrameworkNotFound {
724            moniker: moniker.clone(),
725            capability_id: capability_id.into(),
726        }
727    }
728
729    pub fn capability_from_capability_not_found(
730        moniker: &Moniker,
731        capability_id: impl Into<String>,
732    ) -> Self {
733        Self::CapabilityFromCapabilityNotFound {
734            moniker: moniker.clone(),
735            capability_id: capability_id.into(),
736        }
737    }
738
739    pub fn capability_from_component_manager_not_found(capability_id: impl Into<String>) -> Self {
740        Self::CapabilityFromComponentManagerNotFound { capability_id: capability_id.into() }
741    }
742
743    pub fn expose_from_framework_not_found(
744        moniker: &Moniker,
745        capability_id: impl Into<String>,
746    ) -> Self {
747        Self::ExposeFromFrameworkNotFound {
748            moniker: moniker.clone(),
749            capability_id: capability_id.into(),
750        }
751    }
752
753    pub fn unsupported_route_source(
754        moniker: impl Into<ExtendedMoniker>,
755        source: impl Into<String>,
756    ) -> Self {
757        Self::UnsupportedRouteSource { source_type: source.into(), moniker: moniker.into() }
758    }
759
760    pub fn unsupported_capability_type(
761        moniker: impl Into<ExtendedMoniker>,
762        type_name: impl Into<CapabilityTypeName>,
763    ) -> Self {
764        Self::UnsupportedCapabilityType { type_name: type_name.into(), moniker: moniker.into() }
765    }
766}
767
768/// Errors produced during routing specific to events.
769#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(rename_all = "snake_case"))]
770#[derive(Error, Debug, Clone, PartialEq)]
771pub enum EventsRoutingError {
772    #[error("filter is not a subset at `{moniker}`")]
773    InvalidFilter { moniker: ExtendedMoniker },
774
775    #[error("event routes must end at source with a filter declaration at `{moniker}`")]
776    MissingFilter { moniker: ExtendedMoniker },
777}
778
779impl From<EventsRoutingError> for ExtendedMoniker {
780    fn from(err: EventsRoutingError) -> ExtendedMoniker {
781        match err {
782            EventsRoutingError::InvalidFilter { moniker }
783            | EventsRoutingError::MissingFilter { moniker } => moniker,
784        }
785    }
786}
787
788#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(rename_all = "snake_case"))]
789#[derive(Debug, Error, Clone, PartialEq)]
790pub enum RightsRoutingError {
791    #[error(
792        "requested rights ({requested}) greater than provided rights ({provided}) at \"{moniker}\""
793    )]
794    Invalid { moniker: ExtendedMoniker, requested: Rights, provided: Rights },
795
796    #[error(
797        "directory routes must end at source with a rights declaration, it's missing at \"{moniker}\""
798    )]
799    MissingRightsSource { moniker: ExtendedMoniker },
800}
801
802impl RightsRoutingError {
803    /// Convert this error into its approximate `zx::Status` equivalent.
804    pub fn as_zx_status(&self) -> zx::Status {
805        match self {
806            RightsRoutingError::Invalid { .. } => zx::Status::ACCESS_DENIED,
807            RightsRoutingError::MissingRightsSource { .. } => zx::Status::NOT_FOUND,
808        }
809    }
810}
811
812impl From<RightsRoutingError> for ExtendedMoniker {
813    fn from(err: RightsRoutingError) -> ExtendedMoniker {
814        match err {
815            RightsRoutingError::Invalid { moniker, .. }
816            | RightsRoutingError::MissingRightsSource { moniker } => moniker,
817        }
818    }
819}
820
821#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(rename_all = "snake_case"))]
822#[derive(Debug, Error, Clone, PartialEq)]
823pub enum AvailabilityRoutingError {
824    #[error(
825        "availability requested by the target has stronger guarantees than what \
826    is being provided at the source at `{moniker}`"
827    )]
828    TargetHasStrongerAvailability { moniker: ExtendedMoniker },
829
830    #[error("offer uses void source, but target requires the capability at `{moniker}`")]
831    OfferFromVoidToRequiredTarget { moniker: ExtendedMoniker },
832
833    #[error("expose uses void source, but target requires the capability at `{moniker}`")]
834    ExposeFromVoidToRequiredTarget { moniker: ExtendedMoniker },
835}
836
837impl From<availability::TargetHasStrongerAvailability> for AvailabilityRoutingError {
838    fn from(value: availability::TargetHasStrongerAvailability) -> Self {
839        let availability::TargetHasStrongerAvailability { moniker } = value;
840        AvailabilityRoutingError::TargetHasStrongerAvailability { moniker }
841    }
842}
843
844impl From<AvailabilityRoutingError> for ExtendedMoniker {
845    fn from(err: AvailabilityRoutingError) -> ExtendedMoniker {
846        match err {
847            AvailabilityRoutingError::ExposeFromVoidToRequiredTarget { moniker }
848            | AvailabilityRoutingError::OfferFromVoidToRequiredTarget { moniker }
849            | AvailabilityRoutingError::TargetHasStrongerAvailability { moniker } => moniker,
850        }
851    }
852}
853
854// Implements error reporting upon routing failure. For example, component
855// manager logs the error.
856#[async_trait]
857pub trait ErrorReporter: Clone + Send + Sync + 'static {
858    async fn report(
859        &self,
860        request: &RouteRequestErrorInfo,
861        err: &RouterError,
862        route_target: sandbox::WeakInstanceToken,
863    );
864}
865
866/// What to print in an error if a route request fails.
867pub struct RouteRequestErrorInfo {
868    capability_type: cm_rust::CapabilityTypeName,
869    name: cm_types::Name,
870    availability: cm_rust::Availability,
871}
872
873impl RouteRequestErrorInfo {
874    pub fn availability(&self) -> cm_rust::Availability {
875        self.availability
876    }
877
878    pub fn for_builtin(capability_type: CapabilityTypeName, name: &Name) -> Self {
879        Self { capability_type, name: name.clone(), availability: Availability::Required }
880    }
881}
882
883impl From<&cm_rust::UseDecl> for RouteRequestErrorInfo {
884    fn from(value: &cm_rust::UseDecl) -> Self {
885        RouteRequestErrorInfo {
886            capability_type: value.into(),
887            name: value.source_name().clone(),
888            availability: value.availability().clone(),
889        }
890    }
891}
892
893impl From<&cm_rust::UseConfigurationDecl> for RouteRequestErrorInfo {
894    fn from(value: &cm_rust::UseConfigurationDecl) -> Self {
895        RouteRequestErrorInfo {
896            capability_type: CapabilityTypeName::Config,
897            name: value.source_name().clone(),
898            availability: value.availability().clone(),
899        }
900    }
901}
902
903impl From<&cm_rust::UseEventStreamDecl> for RouteRequestErrorInfo {
904    fn from(value: &cm_rust::UseEventStreamDecl) -> Self {
905        RouteRequestErrorInfo {
906            capability_type: CapabilityTypeName::EventStream,
907            name: value.source_name.clone(),
908            availability: value.availability,
909        }
910    }
911}
912
913impl From<&cm_rust::ExposeDecl> for RouteRequestErrorInfo {
914    fn from(value: &cm_rust::ExposeDecl) -> Self {
915        RouteRequestErrorInfo {
916            capability_type: value.into(),
917            name: value.target_name().clone(),
918            availability: value.availability().clone(),
919        }
920    }
921}
922
923impl From<&cm_rust::offer::OfferDecl> for RouteRequestErrorInfo {
924    fn from(value: &cm_rust::offer::OfferDecl) -> Self {
925        RouteRequestErrorInfo {
926            capability_type: value.into(),
927            name: value.target_name().clone(),
928            availability: value.availability().clone(),
929        }
930    }
931}
932
933impl From<&cm_rust::ResolverRegistration> for RouteRequestErrorInfo {
934    fn from(value: &cm_rust::ResolverRegistration) -> Self {
935        RouteRequestErrorInfo {
936            capability_type: CapabilityTypeName::Resolver,
937            name: value.source_name().clone(),
938            availability: Availability::Required,
939        }
940    }
941}
942
943impl From<&cm_rust::RunnerRegistration> for RouteRequestErrorInfo {
944    fn from(value: &cm_rust::RunnerRegistration) -> Self {
945        RouteRequestErrorInfo {
946            capability_type: CapabilityTypeName::Runner,
947            name: value.source_name().clone(),
948            availability: Availability::Required,
949        }
950    }
951}
952
953impl From<&cm_rust::DebugRegistration> for RouteRequestErrorInfo {
954    fn from(value: &cm_rust::DebugRegistration) -> Self {
955        RouteRequestErrorInfo {
956            capability_type: CapabilityTypeName::Protocol,
957            name: value.source_name().clone(),
958            availability: Availability::Required,
959        }
960    }
961}
962
963impl From<&cm_rust::CapabilityDecl> for RouteRequestErrorInfo {
964    fn from(value: &cm_rust::CapabilityDecl) -> Self {
965        RouteRequestErrorInfo {
966            capability_type: value.into(),
967            name: value.name().clone(),
968            availability: Availability::Required,
969        }
970    }
971}
972
973impl std::fmt::Display for RouteRequestErrorInfo {
974    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
975        write!(f, "{} `{}`", self.capability_type, self.name)
976    }
977}