loga/
conversion.rs

1use std::{
2    collections::HashMap,
3};
4use crate::{
5    entry::{
6        err,
7        err_with,
8    },
9    types::{
10        Error,
11        Error_,
12    },
13    Level,
14    Log,
15};
16
17/// A trait adding helper methods to standard errors to convert to `loga::Error`.
18pub trait ErrContext {
19    /// Add a simple context string onto an error, converting it to `loga::Error` in
20    /// the process.
21    fn context(self, message: impl ToString) -> Error;
22
23    /// Add a simple context string and attributes pairs onto an error, converting it
24    /// to `loga::Error` in the process.
25    fn context_with(self, message: impl ToString, attrs: impl Fn(&mut HashMap<&'static str, String>) -> ()) -> Error;
26
27    /// Add attributes from the log as well as a simple context string to an error,
28    /// converting it to `loga::Error` in the process.
29    fn stack_context(self, log: &Log, message: impl ToString) -> Error;
30
31    /// Add attributes from the log as well as the specified attributes and a simple
32    /// context string to an error, converting it to `loga::Error` in the process.
33    fn stack_context_with(
34        self,
35        log: &Log,
36        message: impl ToString,
37        attrs: impl Fn(&mut HashMap<&'static str, String>) -> (),
38    ) -> Error;
39}
40
41impl<T: Into<Error>> ErrContext for T {
42    fn context(self, message: impl ToString) -> Error {
43        return Error(Box::new(Error_ {
44            message: message.to_string(),
45            attrs: HashMap::new(),
46            context: vec![],
47            causes: vec![self.into()],
48            incidental: vec![],
49        }));
50    }
51
52    fn context_with(
53        self,
54        message: impl ToString,
55        attrs: impl Fn(&mut HashMap<&'static str, String>) -> (),
56    ) -> Error {
57        let mut new_attrs = HashMap::new();
58        attrs(&mut new_attrs);
59        return Error(Box::new(Error_ {
60            message: message.to_string(),
61            attrs: new_attrs,
62            context: vec![],
63            causes: vec![self.into()],
64            incidental: vec![],
65        }));
66    }
67
68    fn stack_context(self, log: &Log, message: impl ToString) -> Error {
69        return Error(Box::new(Error_ {
70            message: message.to_string(),
71            attrs: HashMap::new(),
72            context: vec![log.clone()],
73            causes: vec![self.into()],
74            incidental: vec![],
75        }));
76    }
77
78    fn stack_context_with(
79        self,
80        log: &Log,
81        message: impl ToString,
82        attrs: impl Fn(&mut HashMap<&'static str, String>) -> (),
83    ) -> Error {
84        let mut new_attrs = HashMap::new();
85        attrs(&mut new_attrs);
86        return Error(Box::new(Error_ {
87            message: message.to_string(),
88            attrs: new_attrs,
89            context: vec![log.clone()],
90            causes: vec![self.into()],
91            incidental: vec![],
92        }));
93    }
94}
95
96/// A trait adding helper methods to `Result` to convert to
97/// `Result<_, loga::Error>`.
98pub trait ResultContext<O> {
99    /// If the value is Err/None, add a simple context string onto an error, converting
100    /// it to `loga::Error` in the process.
101    fn context(self, message: impl ToString) -> Result<O, Error>;
102
103    /// If the value is Err/None, add a simple context string and attributes pairs onto
104    /// an error, converting it to `loga::Error` in the process.
105    fn context_with(
106        self,
107        message: impl ToString,
108        attrs: impl Fn(&mut HashMap<&'static str, String>) -> (),
109    ) -> Result<O, Error>;
110
111    /// If the value is Err/None, add attributes from the log as well as a simple
112    /// context string to an error, converting it to `loga::Error` in the process.
113    fn stack_context(self, log: &Log, message: impl ToString) -> Result<O, Error>;
114
115    /// If the value is Err/None, add attributes from the log as well as the specified
116    /// attributes and a simple context string to an error, converting it to
117    /// `loga::Error` in the process.
118    fn stack_context_with(
119        self,
120        log: &Log,
121        message: impl ToString,
122        attrs: impl Fn(&mut HashMap<&'static str, String>) -> (),
123    ) -> Result<O, Error>;
124
125    /// If this is an error and the argument result is an error, attach the argument
126    /// result to this error as an incidental error. If this isn't an error and the
127    /// argument result is an error, return the argument result alone.
128    fn also<O2, E: Into<Error>>(self, r: Result<O2, E>) -> Result<O, Error>;
129
130    /// If the value is Err/None, consume it, logging it with the additional context
131    /// message.
132    fn log(self, log: &Log, level: Level, message: impl ToString);
133
134    /// If the value is Err/None, consume it, logging it with the additional context
135    /// message and attributes.
136    fn log_with(
137        self,
138        log: &Log,
139        level: Level,
140        message: impl ToString,
141        attrs: impl Fn(&mut HashMap<&'static str, String>) -> (),
142    );
143}
144
145impl<O, E: Into<Error>> ResultContext<O> for Result<O, E> {
146    fn context(self, message: impl ToString) -> Result<O, Error> {
147        match self {
148            Ok(x) => Ok(x),
149            Err(e) => Err(e.context(message)),
150        }
151    }
152
153    fn context_with(
154        self,
155        message: impl ToString,
156        attrs: impl Fn(&mut HashMap<&'static str, String>) -> (),
157    ) -> Result<O, Error> {
158        match self {
159            Ok(x) => Ok(x),
160            Err(e) => Err(e.context_with(message, attrs)),
161        }
162    }
163
164    fn stack_context(self, log: &Log, message: impl ToString) -> Result<O, Error> {
165        match self {
166            Ok(x) => Ok(x),
167            Err(e) => Err(e.stack_context(log, message)),
168        }
169    }
170
171    fn stack_context_with(
172        self,
173        log: &Log,
174        message: impl ToString,
175        attrs: impl Fn(&mut HashMap<&'static str, String>) -> (),
176    ) -> Result<O, Error> {
177        match self {
178            Ok(x) => Ok(x),
179            Err(e) => Err(e.stack_context_with(log, message, attrs)),
180        }
181    }
182
183    fn also<O2, E2: Into<Error>>(self, r: Result<O2, E2>) -> Result<O, Error> {
184        match self {
185            Ok(o) => match r {
186                Ok(_) => {
187                    return Ok(o);
188                },
189                Err(e2) => {
190                    return Err(e2.into());
191                },
192            },
193            Err(e) => match r {
194                Ok(_) => return Err(e.into()),
195                Err(e2) => return Err(e.into().also(e2.into())),
196            },
197        }
198    }
199
200    fn log(self, log: &Log, level: Level, message: impl ToString) {
201        if let Err(e) = self.context(message) {
202            log.log_err(level, e);
203        }
204    }
205
206    fn log_with(
207        self,
208        log: &Log,
209        level: Level,
210        message: impl ToString,
211        attrs: impl Fn(&mut HashMap<&'static str, String>) -> (),
212    ) {
213        if let Err(e) = self.context_with(message, attrs) {
214            log.log_err(level, e);
215        }
216    }
217}
218
219impl<O> ResultContext<O> for Option<O> {
220    fn context(self, message: impl ToString) -> Result<O, Error> {
221        match self {
222            Some(x) => Ok(x),
223            None => Err(err(message)),
224        }
225    }
226
227    fn context_with(
228        self,
229        message: impl ToString,
230        attrs: impl Fn(&mut HashMap<&'static str, String>) -> (),
231    ) -> Result<O, Error> {
232        match self {
233            Some(x) => Ok(x),
234            None => Err(err_with(message, attrs)),
235        }
236    }
237
238    fn stack_context(self, log: &Log, message: impl ToString) -> Result<O, Error> {
239        match self {
240            Some(x) => Ok(x),
241            None => Err(log.err(message)),
242        }
243    }
244
245    fn stack_context_with(
246        self,
247        log: &Log,
248        message: impl ToString,
249        attrs: impl Fn(&mut HashMap<&'static str, String>) -> (),
250    ) -> Result<O, Error> {
251        match self {
252            Some(x) => Ok(x),
253            None => Err(log.err_with(message, attrs)),
254        }
255    }
256
257    fn also<O2, E2: Into<Error>>(self, r: Result<O2, E2>) -> Result<O, Error> {
258        match self {
259            Some(o) => match r {
260                Ok(_) => {
261                    return Ok(o);
262                },
263                Err(e2) => {
264                    return Err(e2.into());
265                },
266            },
267            None => match r {
268                Ok(_) => return Err(err("No value")),
269                Err(e2) => return Err(err("No value").also(e2.into())),
270            },
271        }
272    }
273
274    fn log(self, log: &Log, level: Level, message: impl ToString) {
275        if self.is_none() {
276            log.log_err(level, err("No value").context(message));
277        }
278    }
279
280    fn log_with(
281        self,
282        log: &Log,
283        level: Level,
284        message: impl ToString,
285        attrs: impl Fn(&mut HashMap<&'static str, String>) -> (),
286    ) {
287        if self.is_none() {
288            log.log_err(level, err("No value").context_with(message, attrs));
289        }
290    }
291}
292
293pub trait ResultIgnore {
294    /// Ignore a result entirely. This should generally not be done, but it's safer
295    /// than doing `_ =` because this only ignores errors and will prevent you from
296    /// accidentally ignoring futures or other critical values if you're missing a
297    /// conversion somewhere.
298    fn ignore(self);
299}
300
301impl<T, E> ResultIgnore for Result<T, E> {
302    fn ignore(self) {
303        _ = self;
304    }
305}