error_support/
lib.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5// kinda abusing features here, but features "override" builtin support.
6#[cfg(not(feature = "tracing-logging"))]
7pub use log::{debug, error, info, trace, warn, Level};
8
9#[cfg(feature = "tracing-logging")]
10pub use tracing_support::{debug, error, info, trace, warn, Level};
11
12#[cfg(all(feature = "testing", not(feature = "tracing-logging")))]
13pub fn init_for_tests() {
14    let _ = env_logger::try_init();
15}
16
17#[cfg(all(feature = "testing", not(feature = "tracing-logging")))]
18pub fn init_for_tests_with_level(level: Level) {
19    // There's gotta be a better way :(
20    let level_name = match level {
21        Level::Debug => "debug",
22        Level::Trace => "trace",
23        Level::Info => "info",
24        Level::Warn => "warn",
25        Level::Error => "error",
26    };
27    env_logger::Builder::from_env(env_logger::Env::default().default_filter_or(level_name)).init();
28}
29
30#[cfg(all(feature = "testing", feature = "tracing-logging"))]
31pub use tracing_support::init_for_tests;
32
33mod macros;
34
35#[cfg(feature = "backtrace")]
36/// Re-export of the `backtrace` crate for use in macros and
37/// to ensure the needed version is kept in sync in dependents.
38pub use backtrace;
39
40#[cfg(not(feature = "backtrace"))]
41/// A compatibility shim for `backtrace`.
42pub mod backtrace {
43    use std::fmt;
44
45    pub struct Backtrace;
46
47    impl Backtrace {
48        pub fn new() -> Self {
49            Backtrace
50        }
51    }
52
53    impl Default for Backtrace {
54        fn default() -> Self {
55            Self::new()
56        }
57    }
58
59    impl fmt::Debug for Backtrace {
60        #[cold]
61        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
62            write!(f, "Not available")
63        }
64    }
65}
66
67mod redact;
68pub use redact::*;
69
70#[cfg(not(feature = "tracing-reporting"))]
71mod reporting;
72#[cfg(not(feature = "tracing-reporting"))]
73pub use reporting::{
74    set_application_error_reporter, unset_application_error_reporter, ApplicationErrorReporter,
75};
76
77#[cfg(feature = "tracing-reporting")]
78mod reporting {
79    pub fn report_error_to_app(type_name: String, message: String) {
80        tracing::event!(target: "app-services-error-reporter::error", tracing::Level::ERROR, message, type_name);
81    }
82
83    pub fn report_breadcrumb(message: String, module: String, line: u32, column: u32) {
84        tracing::event!(target: "app-services-error-reporter::breadcrumb", tracing::Level::INFO, message, module, line, column);
85    }
86}
87
88pub use reporting::{report_breadcrumb, report_error_to_app};
89
90pub use error_support_macros::handle_error;
91
92mod handling;
93pub use handling::{convert_log_report_error, ErrorHandling, ErrorReporting, GetErrorHandling};
94
95/// XXX - Most of this is now considered deprecated - only FxA uses it, and
96/// should be replaced with the facilities in the `handling` module.
97///
98/// Define a wrapper around the the provided ErrorKind type.
99/// See also `define_error` which is more likely to be what you want.
100#[macro_export]
101macro_rules! define_error_wrapper {
102    ($Kind:ty) => {
103        pub type Result<T, E = Error> = std::result::Result<T, E>;
104        struct ErrorData {
105            kind: $Kind,
106            backtrace: Option<std::sync::Mutex<$crate::backtrace::Backtrace>>,
107        }
108
109        impl ErrorData {
110            #[cold]
111            fn new(kind: $Kind) -> Self {
112                ErrorData {
113                    kind,
114                    #[cfg(feature = "backtrace")]
115                    backtrace: Some(std::sync::Mutex::new(
116                        $crate::backtrace::Backtrace::new_unresolved(),
117                    )),
118                    #[cfg(not(feature = "backtrace"))]
119                    backtrace: None,
120                }
121            }
122
123            #[cfg(feature = "backtrace")]
124            #[cold]
125            fn get_backtrace(&self) -> Option<&std::sync::Mutex<$crate::backtrace::Backtrace>> {
126                self.backtrace.as_ref().map(|mutex| {
127                    mutex.lock().unwrap().resolve();
128                    mutex
129                })
130            }
131
132            #[cfg(not(feature = "backtrace"))]
133            #[cold]
134            fn get_backtrace(&self) -> Option<&std::sync::Mutex<$crate::backtrace::Backtrace>> {
135                None
136            }
137        }
138
139        impl std::fmt::Debug for ErrorData {
140            #[cfg(feature = "backtrace")]
141            #[cold]
142            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
143                let mut bt = self.backtrace.as_ref().unwrap().lock().unwrap();
144                bt.resolve();
145                write!(f, "{:?}\n\n{}", bt, self.kind)
146            }
147
148            #[cfg(not(feature = "backtrace"))]
149            #[cold]
150            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
151                write!(f, "{}", self.kind)
152            }
153        }
154
155        #[derive(Debug, thiserror::Error)]
156        pub struct Error(Box<ErrorData>);
157        impl Error {
158            #[cold]
159            pub fn kind(&self) -> &$Kind {
160                &self.0.kind
161            }
162
163            #[cold]
164            pub fn backtrace(&self) -> Option<&std::sync::Mutex<$crate::backtrace::Backtrace>> {
165                self.0.get_backtrace()
166            }
167        }
168
169        impl std::fmt::Display for Error {
170            #[cold]
171            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
172                std::fmt::Display::fmt(self.kind(), f)
173            }
174        }
175
176        impl From<$Kind> for Error {
177            // Cold to optimize in favor of non-error cases.
178            #[cold]
179            fn from(ctx: $Kind) -> Error {
180                Error(Box::new(ErrorData::new(ctx)))
181            }
182        }
183    };
184}
185
186/// Define a set of conversions from external error types into the provided
187/// error kind. Use `define_error` to do this at the same time as
188/// `define_error_wrapper`.
189#[macro_export]
190macro_rules! define_error_conversions {
191    ($Kind:ident { $(($variant:ident, $type:ty)),* $(,)? }) => ($(
192        impl From<$type> for Error {
193            // Cold to optimize in favor of non-error cases.
194            #[cold]
195            fn from(e: $type) -> Self {
196                Error::from($Kind::$variant(e))
197            }
198        }
199    )*);
200}
201
202/// All the error boilerplate (okay, with a couple exceptions in some cases) in
203/// one place.
204#[macro_export]
205macro_rules! define_error {
206    ($Kind:ident { $(($variant:ident, $type:ty)),* $(,)? }) => {
207        $crate::define_error_wrapper!($Kind);
208        $crate::define_error_conversions! {
209            $Kind {
210                $(($variant, $type)),*
211            }
212        }
213    };
214}
215
216uniffi::setup_scaffolding!("errorsupport");