reproto_core/
errors.rs

1use std::borrow::Cow;
2use std::env;
3use std::ffi;
4use std::fmt;
5use std::result;
6use std::sync::atomic;
7
8const RUST_BACKTRACE: &str = "RUST_BACKTRACE";
9
10#[cfg(all(feature = "backtrace", feature = "std"))]
11mod internal {
12    pub const HAS_BACKTRACE: bool = true;
13
14    pub use backtrace::Backtrace;
15}
16
17#[cfg(not(all(feature = "backtrace", feature = "std")))]
18mod internal {
19    pub const HAS_BACKTRACE: bool = false;
20
21    use std::fmt;
22
23    /// Fake internal representation.
24    pub struct Backtrace(());
25
26    impl Backtrace {
27        pub fn new() -> Backtrace {
28            Backtrace(())
29        }
30    }
31
32    impl fmt::Debug for Backtrace {
33        fn fmt(&self, _fmt: &mut fmt::Formatter) -> fmt::Result {
34            Ok(())
35        }
36    }
37}
38
39pub use self::internal::{Backtrace, HAS_BACKTRACE};
40
41pub type Result<T> = result::Result<T, Error>;
42
43/// Extra convenience functions for results based on core errors.
44pub trait ResultExt<T>
45where
46    Self: Sized,
47{
48    fn chain_err<C, D>(self, chain: C) -> Result<T>
49    where
50        C: FnOnce() -> D,
51        D: Into<Error>;
52}
53
54impl<T> ResultExt<T> for result::Result<T, Error> {
55    fn chain_err<C, D>(self, chain: C) -> Result<T>
56    where
57        C: FnOnce() -> D,
58        D: Into<Error>,
59    {
60        match self {
61            Err(e) => {
62                let mut new = chain().into();
63                new.cause = Some(Box::new(e));
64                Err(new)
65            }
66            Ok(value) => Ok(value),
67        }
68    }
69}
70
71#[derive(Debug, Clone)]
72pub struct Causes<'a> {
73    current: Option<&'a Error>,
74}
75
76impl<'a> Iterator for Causes<'a> {
77    type Item = &'a Error;
78
79    fn next(&mut self) -> Option<Self::Item> {
80        if let Some(e) = self.current {
81            self.current = e.cause();
82            return Some(e);
83        }
84
85        None
86    }
87}
88
89/// Error display type.
90///
91/// Since Error can't implement fmt::Display, this acts as a proxy.
92pub struct Display<'a> {
93    message: Cow<'a, str>,
94}
95
96impl<'a> fmt::Display for Display<'a> {
97    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
98        fmt.write_str(self.message.as_ref())
99    }
100}
101
102fn is_backtrace_enabled<F: Fn(&str) -> Option<ffi::OsString>>(get_var: F) -> bool {
103    match get_var(RUST_BACKTRACE) {
104        Some(ref val) if val != "0" => true,
105        _ => false,
106    }
107}
108
109pub struct Error {
110    message: Cow<'static, str>,
111    cause: Option<Box<Error>>,
112    suppressed: Vec<Error>,
113    backtrace: Option<Backtrace>,
114}
115
116impl Error {
117    pub fn new<M: Into<Cow<'static, str>>>(message: M) -> Self {
118        Self {
119            message: message.into(),
120            cause: None,
121            suppressed: Vec::new(),
122            backtrace: Self::new_backtrace(),
123        }
124    }
125
126    fn new_backtrace() -> Option<Backtrace> {
127        if !HAS_BACKTRACE {
128            return None;
129        }
130
131        static ENABLED: atomic::AtomicUsize = atomic::ATOMIC_USIZE_INIT;
132
133        match ENABLED.load(atomic::Ordering::SeqCst) {
134            0 => {
135                let enabled = is_backtrace_enabled(|var| env::var_os(var));
136
137                ENABLED.store(enabled as usize + 1, atomic::Ordering::SeqCst);
138
139                if !enabled {
140                    return None;
141                }
142            }
143            1 => return None,
144            _ => {}
145        }
146
147        return Some(Backtrace::new());
148    }
149
150    /// Set the position for this error.
151    pub fn with_suppressed<S: IntoIterator<Item = Error>>(self, suppressed: S) -> Error {
152        Error {
153            suppressed: suppressed.into_iter().collect(),
154            ..self
155        }
156    }
157
158    /// Convert errro into a type that is `fmt::Display`.
159    ///
160    /// WARNING: drops error information. Only use if absolutely necessary!
161    pub fn display(&self) -> Display {
162        Display {
163            message: Cow::from(self.message.as_ref()),
164        }
165    }
166
167    /// Get backtrace.
168    pub fn backtrace(&self) -> Option<&Backtrace> {
169        self.backtrace.as_ref()
170    }
171
172    /// Get the message for the error.
173    pub fn message(&self) -> &str {
174        self.message.as_ref()
175    }
176
177    /// Get the cause of this error.
178    pub fn cause(&self) -> Option<&Error> {
179        self.cause.as_ref().map(AsRef::as_ref)
180    }
181
182    /// Get all suppressed errors.
183    pub fn suppressed(&self) -> Vec<&Error> {
184        self.suppressed.iter().collect()
185    }
186
187    /// Iterate over all causes.
188    pub fn causes(&self) -> Causes {
189        Causes {
190            current: Some(self),
191        }
192    }
193}
194
195impl<T> From<T> for Error
196where
197    T: fmt::Display,
198{
199    fn from(value: T) -> Error {
200        Error::new(value.to_string())
201    }
202}
203
204impl fmt::Debug for Error {
205    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
206        fmt.debug_struct("Error")
207            .field("message", &self.message)
208            .finish()
209    }
210}