mesh_channel/
error.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Remotable errors.
5
6use mesh_protobuf::EncodeAs;
7use mesh_protobuf::Protobuf;
8use std::fmt;
9use std::fmt::Display;
10
11/// An error that can be remoted across a mesh channel.
12///
13/// This erases the error's type, but preserves the source error chain when sent
14/// between processes.
15#[derive(Protobuf)]
16pub struct RemoteError(EncodeAs<BoxedError, EncodedError>);
17
18type BoxedError = Box<dyn std::error::Error + Send + Sync>;
19
20impl RemoteError {
21    /// Returns a new remote error wrapping `error` (including the error's
22    /// source).
23    pub fn new<T: Into<BoxedError>>(error: T) -> Self {
24        Self(error.into().into())
25    }
26}
27
28impl Display for RemoteError {
29    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30        Display::fmt(&self.0, f)
31    }
32}
33
34impl fmt::Debug for RemoteError {
35    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36        fmt::Debug::fmt(&self.0, f)
37    }
38}
39
40impl std::error::Error for RemoteError {
41    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
42        self.0.source()
43    }
44}
45
46/// An error encoded for serialization as a mesh message.
47#[derive(Protobuf)]
48struct EncodedError {
49    errors: Vec<String>,
50}
51
52impl From<EncodedError> for BoxedError {
53    fn from(error: EncodedError) -> Self {
54        Box::new(DecodedError::from(error))
55    }
56}
57
58impl From<BoxedError> for EncodedError {
59    fn from(error: BoxedError) -> Self {
60        let mut errors = Vec::new();
61        let mut error = error.as_ref() as &dyn std::error::Error;
62        loop {
63            errors.push(error.to_string());
64            if let Some(source) = error.source() {
65                error = source;
66            } else {
67                break;
68            }
69        }
70        Self { errors }
71    }
72}
73
74/// An error decoded from an [`EncodedError`].
75///
76/// This is a distinct type so that we can implement
77/// [`std::error::Error::source`] for it.
78#[derive(Debug)]
79struct DecodedError {
80    source: Option<Box<DecodedError>>,
81    error: String,
82}
83
84impl From<EncodedError> for DecodedError {
85    fn from(value: EncodedError) -> Self {
86        let mut errors = value.errors;
87        let last_error = errors.pop().unwrap_or("no error information".to_string());
88        let mut decoded = DecodedError {
89            source: None,
90            error: last_error,
91        };
92        for error in errors.into_iter().rev() {
93            decoded = DecodedError {
94                source: Some(Box::new(decoded)),
95                error,
96            };
97        }
98        decoded
99    }
100}
101
102impl Display for DecodedError {
103    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104        f.write_str(&self.error)
105    }
106}
107
108impl std::error::Error for DecodedError {
109    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
110        self.source.as_ref().map(|s| s as _)
111    }
112}
113
114/// Alias for a [`Result`] with a [`RemoteError`] error.
115pub type RemoteResult<T> = Result<T, RemoteError>;