simple_error/
lib.rs

1#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
2       html_favicon_url = "https://www.rust-lang.org/favicon.ico",
3       html_root_url = "https://docs.rs/simple-error")]
4#![deny(missing_docs)]
5//! A simple error type backed by a string.
6//!
7//! This crate provides a `SimpleError` type, which implements `std::error::Error`.
8//! The underlying type is a `String` used as the error message.
9//!
10//! It should be used when all you care about is an error string.
11//!
12//! It should not be used when you want to programmatically handle different kinds of an error.
13
14use std::fmt;
15
16/// A type that represents a simple error.
17///
18/// This type uses a `String` to store the error string, and it implements `std::error::Error`.
19#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
20pub struct SimpleError {
21    err: String,
22}
23
24impl SimpleError {
25    /// Creates a new simple error.
26    ///
27    /// This function can take any type that implements `Into<String>`.
28    ///
29    /// # Examples
30    ///
31    /// ```
32    /// use simple_error::SimpleError;
33    ///
34    /// // errors can be created from `str`
35    /// let err = SimpleError::new("an error from str");
36    ///
37    /// // errors can also be created from `String`
38    /// let err = SimpleError::new(String::from("an error from String"));
39    /// ```
40    #[inline]
41    pub fn new<T: Into<String>>(t: T) -> SimpleError {
42        SimpleError{ err: t.into() }
43    }
44
45    /// Creates a new simple error from another error.
46    ///
47    /// This function can take any type that implements `std::error::Error`.
48    /// The error string will be the `Display` of the `std::error::Error`.
49    ///
50    /// # Examples
51    ///
52    /// ```
53    /// use simple_error::SimpleError;
54    /// use std::io;
55    ///
56    /// // errors can be created from `io::Error`
57    /// let err = SimpleError::from(io::Error::new(io::ErrorKind::Other, "oh no"));
58    /// assert_eq!("oh no", format!("{}", err));
59    /// ```
60    #[inline]
61    pub fn from<T: std::error::Error>(t: T) -> SimpleError {
62        SimpleError{ err: format!("{t}") }
63    }
64
65    /// Creates a new simple error from a string with another error.
66    ///
67    /// This function takes a string as error and a type that implements `std::error::Error` as
68    /// reason.
69    /// The error string will be the `Display` of the `std::error::Error` prefixed with the string
70    /// and ", ".
71    ///
72    /// # Examples
73    ///
74    /// ```
75    /// use simple_error::SimpleError;
76    ///
77    /// let err = SimpleError::with("cannot turn on tv", SimpleError::new("remote not found"));
78    /// assert_eq!("cannot turn on tv, remote not found", format!("{}", err));
79    /// ```
80    #[inline]
81    pub fn with<T: std::error::Error>(s: &str, t: T) -> SimpleError {
82        SimpleError{ err: format!("{s}, {t}") }
83    }
84
85    /// Extracts a string slice describing the error.
86    ///
87    /// # Examples
88    ///
89    /// ```
90    /// use simple_error::SimpleError;
91    ///
92    /// let s = SimpleError::new("critical error");
93    /// assert_eq!("critical error", s.as_str());
94    /// ```
95    #[inline]
96    pub fn as_str(&self) -> &str {
97        &self.err
98    }
99}
100
101// TODO: implement From<T> where T: std::error::Error when specialization lands, and remove
102// inherent from function.
103
104impl From<&str> for SimpleError {
105    #[inline]
106    fn from(s: &str) -> SimpleError {
107        SimpleError{ err: s.into() }
108    }
109}
110
111impl fmt::Display for SimpleError {
112    #[inline]
113    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
114        self.err.fmt(f)
115    }
116}
117
118impl std::error::Error for SimpleError {
119}
120
121/// Result type in which the error is a simple error
122pub type SimpleResult<T> = Result<T, SimpleError>;
123
124/// Helper macro for unwrapping `Result` values while returning early with a
125/// newly constructed `SimpleError` if the value of the expression is `Err`.
126/// Can be used in functions that return `Result<_, E>` where `E: From<SimpleError>`
127/// (e.g. `SimpleError` or `Box<dyn Error>`).
128///
129///
130/// # Examples
131///
132    /// ```
133    /// # fn main() {
134    /// use simple_error::{SimpleError, try_with};
135    /// use std::error::Error;
136///
137/// fn try_block(result: Result<(), SimpleError>, s: &str) -> Result<(), SimpleError> {
138///     Ok(try_with!(result, s))
139/// }
140///
141/// // Above is equivalent to below.
142///
143/// fn try_block_equivalent(result: Result<(), SimpleError>, s: &str) -> Result<(), SimpleError> {
144///     match result {
145///         Ok(v) => Ok(v),
146///         Err(e) => {
147///             return Err(SimpleError::with(s, e));
148///         },
149///     }
150/// }
151///
152/// // Use a format string
153///
154/// fn try_block_format(result: Result<(), SimpleError>, s: &str) -> Result<(), SimpleError> {
155///     Ok(try_with!(result, "with {}", s))
156/// }
157///
158/// // Use a format string to a boxed error
159///
160/// fn try_block_format_to_box_error(result: Result<(), SimpleError>, s: &str) -> Result<(), Box<dyn Error>> {
161///     Ok(try_with!(result, "with {}", s))
162/// }
163/// # }
164/// ```
165#[macro_export]
166macro_rules! try_with {
167    ($expr: expr, $fmt:literal) => (match $expr {
168        Ok(val) => val,
169        Err(err) => {
170            return Err(::std::convert::From::from($crate::SimpleError::with(&format!($fmt), err)));
171        },
172    });
173    ($expr: expr, $str: expr) => (match $expr {
174        Ok(val) => val,
175        Err(err) => {
176            return Err(::std::convert::From::from($crate::SimpleError::with($str.as_ref(), err)));
177        },
178    });
179    ($expr: expr, $fmt:literal, $($arg:tt)+) => (match $expr {
180        Ok(val) => val,
181        Err(err) => {
182            return Err(::std::convert::From::from($crate::SimpleError::with(&format!($fmt, $($arg)+), err)));
183        },
184    });
185}
186
187/// Helper macro for unwrapping `Option` values while returning early with a
188/// newly constructed `SimpleError` if the value of the expression is `None`.
189/// Can be used in functions that return `Result<_, E>` where `E: From<SimpleError>`
190/// (e.g. `SimpleError` or `Box<dyn Error>`).
191///
192///
193/// # Examples
194///
195    /// ```
196    /// # fn main() {
197    /// use simple_error::{SimpleError, require_with};
198    /// use std::error::Error;
199///
200/// fn require_block(maybe: Option<()>, s: &str) -> Result<(), SimpleError> {
201///     Ok(require_with!(maybe, s))
202/// }
203///
204/// // Above is equivalent to below.
205///
206/// fn require_block_equivalent(maybe: Option<()>, s: &str) -> Result<(), SimpleError> {
207///     match maybe {
208///         Some(v) => Ok(v),
209///         None => {
210///             return Err(SimpleError::new(s));
211///         },
212///     }
213/// }
214///
215/// // Use a format string
216///
217/// fn require_block_format(maybe: Option<()>, s: &str) -> Result<(), SimpleError> {
218///     Ok(require_with!(maybe, "with {}", s))
219/// }
220///
221/// // Use a format string to a boxed error
222///
223/// fn require_block_format_to_box_error(maybe: Option<()>, s: &str) -> Result<(), Box<dyn Error>> {
224///     Ok(require_with!(maybe, "with {}", s))
225/// }
226/// # }
227/// ```
228#[macro_export]
229macro_rules! require_with {
230    ($expr: expr, $fmt:literal) => (match $expr {
231        Some(val) => val,
232        None => {
233            return Err(::std::convert::From::from($crate::SimpleError::new(format!($fmt))));
234        },
235    });
236    ($expr: expr, $str: expr) => (match $expr {
237        Some(val) => val,
238        None => {
239            return Err(::std::convert::From::from($crate::SimpleError::new(::std::convert::AsRef::<str>::as_ref($str))));
240        },
241    });
242    ($expr: expr, $fmt:literal, $($arg:tt)+) => (match $expr {
243        Some(val) => val,
244        None => {
245            return Err(::std::convert::From::from($crate::SimpleError::new(format!($fmt, $($arg)+))));
246        },
247    });
248}
249
250/// Helper macro for ensuring a boolean condition while returning early with a
251/// newly constructed `SimpleError` if the condition is `false`.
252/// Can be used in functions that return `Result<_, E>` where `E: From<SimpleError>`
253/// (e.g. `SimpleError` or `Box<dyn Error>`).
254///
255/// # Examples
256///
257    /// ```
258    /// # fn main() {
259    /// use simple_error::{SimpleError, ensure_with};
260    /// use std::error::Error;
261///
262/// fn ensure_block(cond: bool, s: &str) -> Result<(), SimpleError> {
263///     ensure_with!(cond, s);
264///     Ok(())
265/// }
266///
267/// // Above is equivalent to below.
268///
269/// fn ensure_block_equivalent(cond: bool, s: &str) -> Result<(), SimpleError> {
270///     if !cond {
271///         return Err(SimpleError::new(s));
272///     }
273///     Ok(())
274/// }
275///
276/// // Use a format string
277/// fn ensure_block_format(cond: bool, s: &str) -> Result<(), SimpleError> {
278///     ensure_with!(cond, "with {}", s);
279///     Ok(())
280/// }
281///
282/// // Use a format string to a boxed error
283/// fn ensure_block_format_to_box_error(cond: bool, s: &str) -> Result<(), Box<dyn Error>> {
284///     ensure_with!(cond, "with {}", s);
285///     Ok(())
286/// }
287/// # }
288/// ```
289#[macro_export]
290macro_rules! ensure_with {
291    ($cond: expr, $fmt:literal) => ({
292        if !$cond {
293            return Err(::std::convert::From::from($crate::SimpleError::new(format!($fmt))));
294        }
295    });
296    ($cond: expr, $str: expr) => ({
297        if !$cond {
298            return Err(::std::convert::From::from($crate::SimpleError::new(::std::convert::AsRef::<str>::as_ref($str))));
299        }
300    });
301    ($cond: expr, $fmt:literal, $($arg:tt)+) => ({
302        if !$cond {
303            return Err(::std::convert::From::from($crate::SimpleError::new(format!($fmt, $($arg)+))));
304        }
305    });
306}
307
308/// Helper macro for returning a `SimpleError` constructed from either a `Into<SimpleError>`, a
309/// string slice, or a formatted string.
310///
311/// # Examples
312///
313    /// ```
314    /// # fn main() {
315    /// use simple_error::{SimpleError, bail};
316    /// use std::error::Error;
317/// // Use with a `Into<SimpleError>`
318///
319/// struct ErrorSeed;
320///
321/// impl From<ErrorSeed> for SimpleError {
322///     fn from(_: ErrorSeed) -> SimpleError {
323///         SimpleError::new(".")
324///     }
325/// }
326///
327/// fn bail_block_into(es: ErrorSeed) -> Result<(), SimpleError> {
328///     bail!(es);
329/// }
330///
331/// // Use with a string slice
332/// fn bail_block_str(s: &str) -> Result<(), SimpleError> {
333///     bail!(s);
334/// }
335///
336/// // Use with a formatted string
337/// fn bail_block_format(s: &str) -> Result<(), SimpleError> {
338///     bail!("reason: {}", s);
339/// }
340///
341/// // Use with a formatted string to a boxed error
342/// fn bail_block_format_to_box_error(s: &str) -> Result<(), Box<dyn Error>> {
343///     bail!("reason: {}", s);
344/// }
345/// # }
346/// ```
347#[macro_export]
348macro_rules! bail {
349    ($fmt:literal) => {
350        return Err(::std::convert::From::from($crate::SimpleError::new(format!($fmt))));
351    };
352    ($e:expr) => {
353        return Err(::std::convert::From::from($e));
354    };
355    ($fmt:literal, $($arg:tt)+) => {
356        return Err(::std::convert::From::from($crate::SimpleError::new(format!($fmt, $($arg)+))));
357    };
358}
359
360/// Construct an ad-hoc `SimpleError` from a string.
361///
362/// It can take either just a string, or a format string with arguments.
363///
364/// # Example
365///
366    /// ```
367    /// # fn main() {
368    /// use simple_error::{SimpleResult, simple_error};
369///
370/// fn add_reason(r: Result<(), ()>) -> SimpleResult<()> {
371///     // Use with a string slice
372///     r.map_err(|_| simple_error!("no reason"))
373/// }
374///
375/// fn add_reason_with_str(r: Result<(), ()>, s: &str) -> SimpleResult<()> {
376///     // Use with a formatted string
377///     r.map_err(|_| simple_error!("reason: {}", s))
378/// }
379/// # }
380/// ```
381#[macro_export]
382macro_rules! simple_error {
383    ($fmt:literal) => {
384        $crate::SimpleError::new(format!($fmt))
385    };
386    ($e:expr) => {
387        $crate::SimpleError::new($e)
388    };
389    ($fmt:literal, $($arg:tt)+) => {
390        $crate::SimpleError::new(format!($fmt, $($arg)+))
391    };
392}
393
394/// Map a result type error to simple error with format
395///
396/// It can take either just a string, or a format string with arguments.
397///
398/// # Example
399///
400    /// ```
401    /// # fn main() {
402    /// use simple_error::{SimpleResult, map_err_with};
403///
404/// fn map_err_with_reason(r: Result<(), std::io::Error>) -> SimpleResult<()> {
405///     // Use with a string slice
406///     map_err_with!(r, "no reason")
407/// }
408///
409/// fn map_err_with_reason_with_str(r: Result<(), std::io::Error>, s: &str) -> SimpleResult<()> {
410///     // Use with a formatted string
411///     map_err_with!(r, "no reason: {}", s)
412/// }
413/// # }
414/// ```
415#[macro_export]
416macro_rules! map_err_with {
417    ($r: expr, $fmt:literal) => {
418        $r.map_err(|e| $crate::SimpleError::with(&format!($fmt), e))
419    };
420    ($r: expr, $str: expr) => {
421        $r.map_err(|e| $crate::SimpleError::with($str.as_ref(), e))
422    };
423    ($r: expr, $fmt:literal, $($arg:tt)+) => {
424        $r.map_err(|e| $crate::SimpleError::with(&format!($fmt, $($arg)+), e))
425    };
426}
427
428
429#[cfg(test)]
430mod tests {
431    use super::SimpleError;
432    use std::error::Error;
433    use std::io;
434
435    pub struct ErrorSeed;
436
437    impl From<ErrorSeed> for SimpleError {
438        fn from(_: ErrorSeed) -> SimpleError {
439            SimpleError::new(".")
440        }
441    }
442
443    #[test]
444    fn new_from_string() {
445        let err = SimpleError::new(String::from("an error from String"));
446        assert_eq!("an error from String", format!("{err}"));
447    }
448
449    #[test]
450    fn new_from_str() {
451        let err = SimpleError::new("an error from str");
452        assert_eq!("an error from str", format!("{err}"));
453    }
454
455    #[test]
456    fn new_from_simple_error_macro() {
457        let err = SimpleError::new("an error from str");
458        assert_eq!(SimpleError::new("an error from str"), simple_error!("{err}"));
459    }
460
461    #[test]
462    fn map_error_with_macro() {
463        let err = crate::SimpleResult::<()>::Err(SimpleError::new("an error from str"));
464        let additional_reason = "error in current function";
465        assert_eq!(Err(SimpleError::new("error in current function, an error from str")), map_err_with!(err, "{additional_reason}"));
466    }
467
468    #[test]
469    fn from_io_error() {
470        let err = SimpleError::from(io::Error::new(io::ErrorKind::Other, "oh no"));
471        assert_eq!("oh no", format!("{err}"));
472    }
473
474    fn try_block(result: Result<(), SimpleError>, s: &str) -> Result<(), SimpleError> {
475        let _: () = try_with!(result, s);
476        Ok(())
477    }
478
479    fn try_block_format(result: Result<(), SimpleError>, s: &str) -> Result<(), SimpleError> {
480        let _: () = try_with!(result, "with {}", s);
481        Ok(())
482    }
483
484    fn try_block_format_with_capture(result: Result<(), SimpleError>, s: &str) -> Result<(), SimpleError> {
485        let _: () = try_with!(result, "with {s}");
486        Ok(())
487    }
488
489    #[test]
490    fn macro_try_with() {
491        assert_eq!(Ok(()), try_block(Ok(()), ""));
492        assert_eq!(Err(SimpleError::new("try block error, error foo")), try_block(Err(SimpleError::new("error foo")), "try block error"));
493        assert_eq!(Err(SimpleError::new("with try block error, error foo")), try_block_format(Err(SimpleError::new("error foo")), "try block error"));
494        assert_eq!(Err(SimpleError::new("with try block error, error foo")), try_block_format_with_capture(Err(SimpleError::new("error foo")), "try block error"));
495    }
496
497    fn require_block(option: Option<()>, s: &str) -> Result<(), SimpleError> {
498        let _: () = require_with!(option, s);
499        Ok(())
500    }
501
502    fn require_block_str_as_ref(option: Option<()>, s: &String) -> Result<(), SimpleError> {
503        let _: () = require_with!(option, s);
504        Ok(())
505    }
506
507    fn require_block_format(maybe: Option<()>, s: &str) -> Result<(), SimpleError> {
508        let _: () = require_with!(maybe, "with {}", s);
509        Ok(())
510    }
511
512    fn require_block_format_with_capture(maybe: Option<()>, s: &str) -> Result<(), SimpleError> {
513        let _: () = require_with!(maybe, "with {s}");
514        Ok(())
515    }
516
517    #[test]
518    fn macro_require_with() {
519        assert_eq!(Ok(()), require_block(Some(()), ""));
520        assert_eq!(Err(SimpleError::new("require block error")), require_block(None, "require block error"));
521        assert_eq!(Err(SimpleError::new("require block error")), require_block_str_as_ref(None, &"require block error".to_owned()));
522        assert_eq!(Err(SimpleError::new("with require block error")), require_block_format(None, "require block error"));
523        assert_eq!(Err(SimpleError::new("with require block error")), require_block_format_with_capture(None, "require block error"));
524    }
525
526    fn ensure_block(cond: bool, s: &str) -> Result<(), SimpleError> {
527        ensure_with!(cond, s);
528        Ok(())
529    }
530
531    fn ensure_block_str_as_ref(cond: bool, s: &String) -> Result<(), SimpleError> {
532        ensure_with!(cond, s);
533        Ok(())
534    }
535
536    fn ensure_block_format(cond: bool, s: &str) -> Result<(), SimpleError> {
537        ensure_with!(cond, "with {}", s);
538        Ok(())
539    }
540
541    fn ensure_block_format_with_capture(cond: bool, s: &str) -> Result<(), SimpleError> {
542        ensure_with!(cond, "with {s}");
543        Ok(())
544    }
545
546    fn ensure_block_format_to_box_error(cond: bool, s: &str) -> Result<(), Box<dyn Error>> {
547        ensure_with!(cond, "with {}", s);
548        Ok(())
549    }
550
551    #[test]
552    fn macro_ensure_with() {
553        assert_eq!(Ok(()), ensure_block(true, ""));
554        assert_eq!(Err(SimpleError::new("ensure block error")), ensure_block(false, "ensure block error"));
555        assert_eq!(Err(SimpleError::new("ensure block error")), ensure_block_str_as_ref(false, &"ensure block error".to_owned()));
556        assert_eq!(Err(SimpleError::new("with ensure block error")), ensure_block_format(false, "ensure block error"));
557        assert_eq!(Err(SimpleError::new("with ensure block error")), ensure_block_format_with_capture(false, "ensure block error"));
558        assert!(ensure_block_format_to_box_error(false, "ensure block error").is_err());
559    }
560
561    fn bail_block_into(es: ErrorSeed) -> Result<(), SimpleError> {
562        bail!(es);
563    }
564
565    fn bail_block_str(s: &str) -> Result<(), SimpleError> {
566        bail!(s);
567    }
568
569    fn bail_block_format(s: &str) -> Result<(), SimpleError> {
570        bail!("reason: {}", s);
571    }
572
573    fn bail_block_format_with_capture(s: &str) -> Result<(), SimpleError> {
574        bail!("reason: {s}");
575    }
576
577    fn bail_block_format_to_box_error(s: &str) -> Result<(), Box<dyn Error>> {
578        bail!("reason: {}", s);
579    }
580
581    #[test]
582    fn macro_bail() {
583        assert_eq!(Err(SimpleError::new(".")), bail_block_into(ErrorSeed));
584        assert_eq!(Err(SimpleError::new("no reason")), bail_block_str("no reason"));
585        assert_eq!(Err(SimpleError::new("reason: plane crashed")), bail_block_format("plane crashed"));
586        assert!(bail_block_format_to_box_error("plane crashed").is_err());
587    }
588
589    #[test]
590    fn inline_format_arg_capture() {
591        assert_eq!(Err(SimpleError::new("reason: plane crashed")), bail_block_format_with_capture("plane crashed"));
592    }
593}