error_support/handling.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//! Helpers for components to "handle" errors.
6
7/// Describes what error reporting action should be taken.
8#[derive(Debug, Default)]
9pub struct ErrorReporting {
10 /// If Some(level), will write a log message at that level.
11 log_level: Option<crate::Level>,
12 /// If Some(report_class) will call the error reporter with details.
13 report_class: Option<String>,
14}
15
16/// Specifies how an "internal" error is converted to an "external" public error and
17/// any logging or reporting that should happen.
18pub struct ErrorHandling<E> {
19 /// The external error that should be returned.
20 pub err: E,
21 /// How the error should be reported.
22 pub reporting: ErrorReporting,
23}
24
25impl<E> ErrorHandling<E> {
26 /// Create an ErrorHandling instance with an error conversion.
27 ///
28 /// ErrorHandling instance are created using a builder-style API. This is always the first
29 /// function in the chain, optionally followed by `log()`, `report()`, etc.
30 pub fn convert(err: E) -> Self {
31 Self {
32 err,
33 reporting: ErrorReporting::default(),
34 }
35 }
36
37 /// Add logging to an ErrorHandling instance
38 pub fn log(self, level: crate::Level) -> Self {
39 Self {
40 err: self.err,
41 reporting: ErrorReporting {
42 log_level: Some(level),
43 ..self.reporting
44 },
45 }
46 }
47
48 /// Add reporting to an ErrorHandling instance
49 pub fn report(self, report_class: impl Into<String>) -> Self {
50 Self {
51 err: self.err,
52 reporting: ErrorReporting {
53 report_class: Some(report_class.into()),
54 ..self.reporting
55 },
56 }
57 }
58
59 // Convenience functions for the most common error reports
60
61 /// log a warning
62 pub fn log_warning(self) -> Self {
63 self.log(crate::Level::Warn)
64 }
65
66 /// log an info
67 pub fn log_info(self) -> Self {
68 self.log(crate::Level::Info)
69 }
70
71 /// Add reporting to an ErrorHandling instance and also log an Error
72 pub fn report_error(self, report_class: impl Into<String>) -> Self {
73 Self {
74 err: self.err,
75 reporting: ErrorReporting {
76 log_level: Some(crate::Level::Error),
77 report_class: Some(report_class.into()),
78 },
79 }
80 }
81}
82
83/// A trait to define how errors are converted and reported.
84pub trait GetErrorHandling {
85 type ExternalError;
86
87 /// Return how to handle our internal errors
88 fn get_error_handling(&self) -> ErrorHandling<Self::ExternalError>;
89}
90
91/// Handle the specified "internal" error, taking any logging or error
92/// reporting actions and converting the error to the public error.
93/// Called by our `handle_error` macro so needs to be public.
94pub fn convert_log_report_error<IE, EE>(e: IE) -> EE
95where
96 IE: GetErrorHandling<ExternalError = EE> + std::error::Error,
97 EE: std::error::Error,
98{
99 let handling = e.get_error_handling();
100 let reporting = handling.reporting;
101 if let Some(level) = reporting.log_level {
102 // tracing dosn't seem to have anything close enough to `log::log`, so we have to match levels explicitly.
103 let message = match &reporting.report_class {
104 Some(report_class) => format!("{report_class}: {}", e),
105 None => format!("{}", e),
106 };
107 match level {
108 crate::Level::Trace => crate::trace!("{}", message),
109 crate::Level::Debug => crate::debug!("{}", message),
110 crate::Level::Info => crate::info!("{}", message),
111 crate::Level::Warn => crate::warn!("{}", message),
112 crate::Level::Error => crate::error!("{}", message),
113 }
114 }
115 if let Some(report_class) = reporting.report_class {
116 // notify the error reporter if the feature is enabled.
117 // XXX - should we arrange for the `report_class` to have the
118 // original crate calling this as a prefix, or will we still be
119 // able to identify that?
120 crate::report_error_to_app(report_class, e.to_string());
121 }
122 handling.err
123}