router_error/
lib.rs

1// Copyright 2024 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 moniker::Moniker;
6use std::fmt::{self, Debug, Display};
7use std::sync::Arc;
8use thiserror::Error;
9use {
10    fidl_fuchsia_component_runtime as fruntime, fidl_fuchsia_component_sandbox as fsandbox,
11    zx_status as zx,
12};
13
14/// The error type returned by bedrock operations.
15#[derive(Debug, Error, Clone)]
16pub enum RouterError {
17    #[error("{0}")]
18    NotFound(Arc<dyn Explain>),
19
20    #[error("invalid arguments")]
21    InvalidArgs,
22
23    #[error("not supported")]
24    NotSupported,
25
26    #[error("internal")]
27    Internal,
28
29    #[error("unknown")]
30    Unknown,
31
32    #[error("route continues outside of component manager at component {moniker}")]
33    RemotedAt { moniker: Moniker },
34}
35
36impl From<fruntime::RouterError> for RouterError {
37    fn from(router_error: fruntime::RouterError) -> Self {
38        match router_error {
39            fruntime::RouterError::NotFound => Self::NotFound(Arc::new(ExternalNotFoundError {})),
40            fruntime::RouterError::InvalidArgs => RouterError::InvalidArgs,
41            fruntime::RouterError::NotSupported => RouterError::InvalidArgs,
42            fruntime::RouterError::Internal => RouterError::Internal,
43            _ => RouterError::Unknown,
44        }
45    }
46}
47
48impl From<RouterError> for fruntime::RouterError {
49    fn from(router_error: RouterError) -> Self {
50        match router_error {
51            RouterError::NotFound(_) => fruntime::RouterError::NotFound,
52            RouterError::InvalidArgs => fruntime::RouterError::InvalidArgs,
53            RouterError::NotSupported => fruntime::RouterError::InvalidArgs,
54            RouterError::RemotedAt { .. } => fruntime::RouterError::NotSupported,
55            RouterError::Internal => fruntime::RouterError::Internal,
56            RouterError::Unknown => fruntime::RouterError::Unknown,
57        }
58    }
59}
60
61impl From<fsandbox::RouterError> for RouterError {
62    fn from(err: fsandbox::RouterError) -> Self {
63        match err {
64            fsandbox::RouterError::NotFound => Self::NotFound(Arc::new(ExternalNotFoundError {})),
65            fsandbox::RouterError::InvalidArgs => Self::InvalidArgs,
66            fsandbox::RouterError::NotSupported => Self::NotSupported,
67            fsandbox::RouterError::Internal => Self::Internal,
68            fsandbox::RouterErrorUnknown!() => Self::Unknown,
69        }
70    }
71}
72
73impl From<RouterError> for fsandbox::RouterError {
74    fn from(err: RouterError) -> Self {
75        match err {
76            RouterError::NotFound(_) => Self::NotFound,
77            RouterError::InvalidArgs => Self::InvalidArgs,
78            RouterError::NotSupported => Self::NotSupported,
79            RouterError::RemotedAt { .. } => Self::NotSupported,
80            RouterError::Internal => Self::Internal,
81            RouterError::Unknown => Self::unknown(),
82        }
83    }
84}
85
86#[derive(Debug, Error, Clone)]
87struct ExternalNotFoundError {}
88
89impl Explain for ExternalNotFoundError {
90    fn as_zx_status(&self) -> zx::Status {
91        zx::Status::NOT_FOUND
92    }
93}
94
95impl fmt::Display for ExternalNotFoundError {
96    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97        write!(f, "external not found error")
98    }
99}
100
101/// All detailed error objects must implement the [`Explain`] trait, since:
102///
103/// - Some operations are not yet refactored into bedrock.
104/// - Some operations fundamentally are not fit for bedrock.
105///
106/// The detailed errors are hidden, but users may get strings or codes for debugging.
107pub trait Explain: std::error::Error + Debug + Display + Send + Sync + sealed::AnyCast {
108    fn as_zx_status(&self) -> zx::Status;
109}
110
111impl Explain for RouterError {
112    fn as_zx_status(&self) -> zx::Status {
113        match self {
114            Self::NotFound(err) => err.as_zx_status(),
115            Self::InvalidArgs => zx::Status::INVALID_ARGS,
116            Self::RemotedAt { .. } => zx::Status::NOT_SUPPORTED,
117            Self::NotSupported => zx::Status::NOT_SUPPORTED,
118            Self::Internal => zx::Status::INTERNAL,
119            Self::Unknown => zx::Status::INTERNAL,
120        }
121    }
122}
123
124/// To test the error case of e.g. a `Router` implementation, it will be helpful
125/// to cast the erased error back to an expected error type and match on it.
126///
127/// Do not use this in production as conditioning behavior on error cases is
128/// extremely fragile.
129pub trait DowncastErrorForTest {
130    /// For tests only. Downcast the erased error to `E` or panic if fails.
131    fn downcast_for_test<E: Explain>(&self) -> &E;
132}
133
134impl DowncastErrorForTest for dyn Explain {
135    fn downcast_for_test<E: Explain>(&self) -> &E {
136        match self.as_any().downcast_ref::<E>() {
137            Some(value) => value,
138            None => {
139                let expected = std::any::type_name::<E>();
140                panic!("Cannot downcast `{self:?}` to the {expected:?} error type!");
141            }
142        }
143    }
144}
145
146mod sealed {
147    use std::any::Any;
148
149    pub trait AnyCast: Any {
150        fn as_any(&self) -> &dyn Any;
151    }
152
153    impl<T: Any> AnyCast for T {
154        fn as_any(&self) -> &dyn Any {
155            self
156        }
157    }
158}