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