Skip to main content

strerror/
lib.rs

1//! A string-based error type. **No longer developed. Recommended
2//! replacement: Anyhow and Thiserror crates.**
3//!
4//! # Introduction
5//!
6//! This crate provides a string-based error type,
7//! [`StrError`](StrError), that implements
8//! [`std::error::Error`](std::error::Error). It is for simple use
9//! cases where you wish to work with string errors and/or box
10//! existing errors of any type, adding context to them.
11//!
12//! [`StrError`](StrError)s behave in many ways like
13//! [`String`](String)s, except they may also contain another error
14//! boxed inside them, known as the "source" or "cause". Since the
15//! source can have a source itself, sources form chains of errors,
16//! each error adding context to the preceeding one.
17//!
18//! When a [`StrError`](StrError) is returned from `main`, its
19//! [`Debug`](Debug) implementation causes the output of a CLI
20//! application to look like this
21//!
22//! ```text
23//! Error: ...
24//! Caused by: ...
25//! Caused by: ...
26//! ...
27//! ```
28//!
29//! Each "Caused by:" line corresponds to a boxed error in the chain
30//! of sources.
31//!
32//! # The prelude
33//!
34//! This crate has a prelude to bring in all the things you need at
35//! once.
36//!
37//! ```
38//! use strerror::prelude::*;
39//! ```
40//!
41//! The examples below all assume use of the prelude.
42//!
43//! # Creating `StrError`s
44//!
45//! As with [`String`](String)s, there are quite a few ways to create
46//! a [`StrError`](StrError). Some have an analagous
47//! [`String`](String) equivalent, so are presented side-by-side with
48//! them.
49//!
50//! ```
51//! # use strerror::prelude::*;
52//! // String                             // StrError
53//! let str1 = "Error!".to_string();      let err1 = "Error!".into_error();
54//! let str2 = String::from("Error!");    let err2 = StrError::from("Error!");
55//! let str3: String = "Error!".into();   let err3: StrError = "Error!".into();
56//! let str4 = format!("Error! #{}", 1);  let err4 = eformat!("Error! #{}", 1);
57//! ```
58//!
59//! The above lines all create [`StrError`](StrError)s without a
60//! "source" or "cause". To create a [`StrError`](StrError) with a
61//! source you can use [`chain_error`](ChainError::chain_error).
62//!
63//! ```
64//! # use strerror::prelude::*;
65//! use std::io::Error as IoError;
66//!
67//! let source = IoError::from_raw_os_error(5);
68//! let err = source.chain_error("I/O error occurred");
69//! ```
70//!
71//! Chaining [`chain_error`](ChainError::chain_error) method calls
72//! together creates an error chain.
73//!
74//! ```
75//! # use strerror::prelude::*;
76//! fn main() -> Result<(), StrError> {
77//! # || -> Result<(), StrError> {
78//!     let err = "Base error".into_error()
79//!         .chain_error("Higher level error")
80//!         .chain_error("Application error");
81//!     Err(err)
82//! # }().unwrap_err(); Ok(())
83//! }
84//! ```
85//!
86//! gives output
87//!
88//! ```text
89//! Error: Application error
90//! Caused by: Higher level error
91//! Caused by: Base error
92//! ```
93//!
94//! # Returning `Result`s
95//!
96//! While the [`chain_error`](ChainError::chain_error) method adds
97//! context to error types directly, we can do a similar thing with
98//! the [`Err`](Err) variant values in [`Result`](Result)s, with the
99//! [`chain_err`](ChainErr::chain_err) method.
100//!
101//! ```
102//! # use strerror::prelude::*;
103//! use std::fs::File;
104//!
105//! fn main() -> Result<(), StrError> {
106//! # || -> Result<(), StrError> {
107//!     let file = "missing-file";
108//!     let _ = File::open(file)                                // a Result
109//!         .chain_err(|| format!("Failed to open {}", file))?; // main exits
110//!     Ok(())
111//! # }().unwrap_err(); Ok(())
112//! }
113//! ```
114//!
115//! gives output
116//!
117//! ```text
118//! Error: Failed to open missing-file
119//! Caused by: No such file or directory (os error 2)
120//! ```
121//!
122//! The [`Result`](Result) is converted to the correct type by
123//! [`chain_err`](ChainErr::chain_err) and context is applied to the
124//! boxed error. Note that in the example above
125//! [`chair_err`](ChainErr::chain_err) takes a closure, and not a
126//! [`String`](String) directly. This is so the construction of the
127//! [`String`](String) is not performed unless needed. You do not need
128//! to use closures however, since [`chair_err`](ChainErr::chain_err)
129//! accepts [`String`](String)s, and [`&str`](str)s as well.
130//!
131//! Returning a new [`StrError`](StrError) in a [`Result`](Result) can
132//! be done in the following way, taking advantage of [`From`](From)
133//! conversion from [`&str`](str).
134//!
135//! ```
136//! # use strerror::prelude::*;
137//!
138//! fn main() -> Result<(), StrError> {
139//! # || -> Result<(), StrError> {
140//!     return Err("an error".into());
141//! # }().unwrap_err(); Ok(())
142//! }
143//! ```
144//!
145//! An alternative is the [`into_err`](IntoErr::into_err) method,
146//! which will call [`into`](Into::into) and create the [`Err`](Err)
147//! variant in one step.
148//!
149//!
150//! ```
151//! # use strerror::prelude::*;
152//!
153//! fn main() -> Result<(), StrError> {
154//! # || -> Result<(), StrError> {
155//!     return "an error".into_err();
156//! # }().unwrap_err(); Ok(())
157//! }
158//! ```
159//!
160//! # Converting `Option`s
161//!
162//! Sometimes [`None`](None) represents an application error and it is
163//! desirable to convert an [`Option<T>`](Option) into a
164//! `Result<T, StrError>`. There is no special method needed in this
165//! case. You can use [`ok_or`](Option::ok_or) or
166//! [`ok_or_else`](Option::ok_or_else).
167//!
168//! ```
169//! # use strerror::prelude::*;
170//! use std::env;
171//!
172//! fn main() -> Result<(), StrError> {
173//! # || -> Result<(), StrError> {
174//!     let _ = env::var_os("MISSING_VAR")    // an Option
175//!         .ok_or("MISSING_VAR not found")?; // main exits
176//!     Ok(())
177//! # }().unwrap_err(); Ok(())
178//! }
179//! ```
180//!
181//! gives output
182//!
183//! ```text
184//! Error: MISSING_VAR not found
185//! ```
186//!
187//! We take advantage again of [`From`](From) conversion from
188//! [`&str`](str) to return a [`StrError`](StrError).
189//!
190//! # `From` conversions to `StrError`
191//!
192//! [`From`](From) conversions are implemented for most of the
193//! standard library error types, so you can return a
194//! [`Result`](Result) containing one directly from a function
195//! expecting a `Result<T, StrError>`, using the `?` operator.
196//!
197//! ```
198//! # use strerror::prelude::*;
199//! use std::fs::File;
200//!
201//! fn main() -> Result<(), StrError> {
202//! # || -> Result<(), StrError> {
203//!     let file = "missing-file";
204//!     let _ = File::open(file)?; // main exits
205//!     Ok(())
206//! # }().unwrap_err(); Ok(())
207//! }
208//! ```
209//!
210//! gives output
211//!
212//! ```text
213//! Error: std::io::Error
214//! Caused by: No such file or directory (os error 2)
215//! ```
216//!
217//! [`From`](From) conversions are also implemented for [`&str`](str)
218//! and [`String`](String), as mentioned previously.
219//!
220//! However, for other error types, if you wish to use the `?`
221//! operator you will first need to call the
222//! [`chain_err`](ChainErr::chain_err) method to convert the
223//! [`Result<T, E>`](Result) into a `Result<T, StrError>`. Of course
224//! you may choose to use a `Box<dyn std::error::Error>` instead of a
225//! [`StrError`](StrError) in the the return type of your function, in
226//! which case `?` will work for all error types.
227//!
228//! # `Deref`
229//!
230//! [`StrError`](StrError)s deref to a [`String`](String), so you can
231//! use many of the usual [`String`](String) methods.
232//!
233//! ```
234//! # use strerror::prelude::*;
235//! fn main() -> Result<(), StrError> {
236//! # || -> Result<(), StrError> {
237//!     let mut err = "This is".into_error();
238//!     *err += " an error";
239//!     err.push_str(" message");
240//!     Err(err)
241//! # }().unwrap_err(); Ok(())
242//! }
243//! ```
244//!
245//! gives output
246//!
247//! ```text
248//! Error: This is an error message
249//! ```
250//!
251//! # Iterating through the source chain
252//!
253//! A reference to a [`StrError`](StrError) can be iterated over to
254//! examine its chain of boxed sources.
255//!
256//! ```
257//! # use strerror::prelude::*;
258//! use std::io::Error as IoError;
259//!
260//! fn main() -> Result<(), StrError> {
261//!     let err = IoError::from_raw_os_error(5)
262//!         .chain_error("Failure reading disk")
263//!         .chain_error("Application error");
264//!     for e in &err {
265//!         println!(
266//!             "Error: {:31} Is StrError?: {}",
267//!             e,
268//!             e.downcast_ref::<StrError>().is_some()
269//!         );
270//!     }
271//!     Ok(())
272//! }
273//! ```
274//!
275//! gives output
276//!
277//! ```text
278//! Error: Application error               Is StrError?: true
279//! Error: Failure reading disk            Is StrError?: true
280//! Error: Input/output error (os error 5) Is StrError?: false
281//! ```
282
283use std::borrow::Cow;
284use std::error::Error;
285use std::fmt::Error as FmtError;
286use std::fmt::{Debug, Display, Formatter};
287use std::ops::{Deref, DerefMut};
288
289/// Reexports of [`eformat`](eformat), [`ChainErr`](ChainErr),
290/// [`ChainError`](ChainError), [`IntoErr`](IntoErr),
291/// [`IntoError`](IntoError) and [`StrError`](StrError).
292pub mod prelude {
293    pub use super::{
294        eformat, ChainErr, ChainError, IntoErr, IntoError, StrError,
295    };
296}
297
298/// A string-based error type implementing
299/// [`std::error::Error`](std::error::Error).
300///
301/// [`From`](From) conversions to [`StrError`](StrError) are
302/// implemented for most standard library error types, and for
303/// [`String`](String)s and [`&str`](str)s. [`Deref`](Deref) converts
304/// to a [`String`](String).
305///
306/// See crate level documentation for usage examples.
307pub struct StrError(Box<StrErrorInner>);
308
309struct StrErrorInner {
310    context: String,
311    source: Option<Box<dyn Error + Send + Sync>>,
312}
313
314impl StrError {
315    /// Create an iterator over a [`StrError`](StrError) and it's
316    /// sources. When using the iterator, the first item retrieved is
317    /// a reference to the [`StrError`](StrError) itself (as a trait
318    /// object). This is then followed by the chain of sources. You
319    /// can also use [`&StrError`](StrError)'s implementation of
320    /// [`IntoIterator`](IntoIterator) to obtain an iterator.
321    pub fn iter(&self) -> Iter<'_> {
322        Iter { next: Some(self) }
323    }
324
325    /// Destroy the [`StrError`](StrError), returning its raw
326    /// parts. These are its context string and its source error (if
327    /// it has one).
328    pub fn into_raw_parts(
329        self,
330    ) -> (String, Option<Box<dyn Error + Send + Sync>>) {
331        (self.0.context, self.0.source)
332    }
333}
334
335impl Error for StrError {
336    /// Return the lower-level source of this [`StrError`](StrError),
337    /// if any.
338    fn source(&self) -> Option<&(dyn Error + 'static)> {
339        self.0.source.as_ref().map(|e| &**e as &(dyn Error + 'static))
340    }
341}
342
343impl Display for StrError {
344    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
345        Display::fmt(&**self, f)
346    }
347}
348
349impl Debug for StrError {
350    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
351        write!(f, "{}", self)?;
352        for e in self.iter().skip(1) {
353            write!(f, "\nCaused by: {}", e)?;
354        }
355        Ok(())
356    }
357}
358
359/// An iterator producing a reference to a [`StrError`](StrError) (as
360/// a trait object), followed by its chain of sources.
361pub struct Iter<'a> {
362    next: Option<&'a (dyn Error + 'static)>,
363}
364
365impl<'a> Iterator for Iter<'a> {
366    type Item = &'a (dyn Error + 'static);
367
368    fn next(&mut self) -> Option<Self::Item> {
369        match &self.next {
370            None => None,
371            Some(e) => {
372                let next = self.next;
373                self.next = e.source();
374                next
375            }
376        }
377    }
378}
379
380impl<'a> IntoIterator for &'a StrError {
381    type Item = &'a (dyn Error + 'static);
382    type IntoIter = Iter<'a>;
383    fn into_iter(self) -> Iter<'a> {
384        self.iter()
385    }
386}
387
388/// Trait providing [`chain_error`](ChainError::chain_error) for
389/// converting an error of any type to a [`StrError`](StrError).
390///
391/// See crate level documentation for usage examples.
392pub trait ChainError {
393    /// Convert an error of any type to a [`StrError`](StrError), adding
394    /// context.
395    fn chain_error<C>(self, context: C) -> StrError
396    where
397        C: IntoChained;
398}
399
400impl<E> ChainError for E
401where
402    E: Into<Box<dyn Error + Send + Sync>>,
403{
404    fn chain_error<C>(self, context: C) -> StrError
405    where
406        C: IntoChained,
407    {
408        context.into_chained(self)
409    }
410}
411
412/// Trait providing [`chain_err`](ChainErr::chain_err) for mapping the
413/// [`Err`](Err) variant value within a [`Result`](Result) to a
414/// [`StrError`](StrError).
415///
416/// See crate level documentation for usage examples.
417pub trait ChainErr<T> {
418    /// Map the [`Err`](Err) variant value within a [`Result`](Result)
419    /// to a [`StrError`](StrError), adding context.
420    fn chain_err<C>(self, context: C) -> Result<T, StrError>
421    where
422        C: IntoChained;
423}
424
425impl<T, E> ChainErr<T> for Result<T, E>
426where
427    E: Into<Box<dyn Error + Send + Sync>>,
428{
429    fn chain_err<C>(self, context: C) -> Result<T, StrError>
430    where
431        C: IntoChained,
432    {
433        self.map_err(|e| context.into_chained(e))
434    }
435}
436
437/// Trait providing [`into_error`](IntoError::into_error) for
438/// converting a [`String`](String) or [`&str`](str) to a
439/// [`StrError`](StrError).
440///
441/// See crate level documentation for usage examples.
442pub trait IntoError {
443    /// Convert a [`String`](String) or [`&str`](str) to a
444    /// [`StrError`](StrError).
445    fn into_error(self) -> StrError;
446}
447
448impl<S> IntoError for S
449where
450    S: Into<StrError>,
451{
452    fn into_error(self) -> StrError {
453        self.into()
454    }
455}
456
457/// Trait providing [`into_err`](IntoErr::into_err) for converting a
458/// [`String`](String), [`&str`](str) or error to an [`Err`](Err)
459/// variant [`Result`](Result).
460///
461/// [`into_err`](IntoErr::into_err) has a very simple implementation:
462/// `Err(self.into())`, which is similar to how the `?` operator
463/// returns errors.
464///
465/// See crate level documentation for usage examples.
466pub trait IntoErr {
467    /// Convert a [`String`](String), [`&str`](str) or error to an
468    /// [`Err`](Err) variant [`Result`](Result).
469    fn into_err<T, E>(self) -> Result<T, E>
470    where
471        Self: Into<E>;
472}
473
474impl<C> IntoErr for C {
475    fn into_err<T, E>(self) -> Result<T, E>
476    where
477        Self: Into<E>,
478    {
479        Err(self.into())
480    }
481}
482
483/// A macro for creating a [`StrError`](StrError) using interpolation
484/// of runtime expressions (like [`format!`](format!)).
485///
486/// [`eformat!`](eformat!) is an extremely simple macro meant to save
487/// some keystrokes when creating [`StrError`](StrError)s. The name
488/// was chosen to mirror that of [`eprint!`](eprint!) in the standard
489/// library, which is an "error" version of [`print!`](print!).
490///
491/// Call [`eformat!`](eformat!) as you would call
492/// [`format!`](format!), but you will get a [`StrError`](StrError)
493/// instead of a [`String`](String).
494#[macro_export]
495macro_rules! eformat {
496    ($($arg:tt)*) => {
497        StrError::from(format!($($arg)*))
498    }
499}
500
501/// Trait providing [`into_chained`](IntoChained::into_chained) for
502/// converting context into a chained error.
503///
504/// You will likely not use this trait directly, and it is not in the
505/// prelude. It is used to implement [`ChainErr`](ChainErr) and
506/// [`ChainError`](ChainError), which you should use instead.
507pub trait IntoChained {
508    fn into_chained<S>(self, source: S) -> StrError
509    where
510        S: Into<Box<dyn Error + Send + Sync>>;
511}
512
513impl IntoChained for Box<str> {
514    fn into_chained<S>(self, source: S) -> StrError
515    where
516        S: Into<Box<dyn Error + Send + Sync>>,
517    {
518        self.into_string().into_chained(source)
519    }
520}
521impl IntoChained for Cow<'_, str> {
522    fn into_chained<S>(self, source: S) -> StrError
523    where
524        S: Into<Box<dyn Error + Send + Sync>>,
525    {
526        self.into_owned().into_chained(source)
527    }
528}
529impl IntoChained for &str {
530    fn into_chained<S>(self, source: S) -> StrError
531    where
532        S: Into<Box<dyn Error + Send + Sync>>,
533    {
534        self.to_owned().into_chained(source)
535    }
536}
537impl IntoChained for String {
538    fn into_chained<S>(self, source: S) -> StrError
539    where
540        S: Into<Box<dyn Error + Send + Sync>>,
541    {
542        StrError(Box::new(StrErrorInner {
543            context: self,
544            source: Some(source.into()),
545        }))
546    }
547}
548impl IntoChained for &String {
549    fn into_chained<S>(self, source: S) -> StrError
550    where
551        S: Into<Box<dyn Error + Send + Sync>>,
552    {
553        self.clone().into_chained(source)
554    }
555}
556impl<F, C> IntoChained for F
557where
558    F: FnOnce() -> C,
559    C: IntoChained,
560{
561    fn into_chained<S>(self, source: S) -> StrError
562    where
563        S: Into<Box<dyn Error + Send + Sync>>,
564    {
565        self().into_chained(source)
566    }
567}
568
569// Deref conversion for StrError.
570impl Deref for StrError {
571    type Target = String;
572
573    fn deref(&self) -> &String {
574        &self.0.context
575    }
576}
577impl DerefMut for StrError {
578    fn deref_mut(&mut self) -> &mut String {
579        &mut self.0.context
580    }
581}
582
583// From conversions for Strings,  &strs etc
584impl From<Box<str>> for StrError {
585    fn from(s: Box<str>) -> Self {
586        From::from(s.into_string())
587    }
588}
589impl From<Cow<'_, str>> for StrError {
590    fn from(s: Cow<'_, str>) -> Self {
591        From::from(s.into_owned())
592    }
593}
594impl From<&str> for StrError {
595    fn from(s: &str) -> Self {
596        From::from(s.to_owned())
597    }
598}
599impl From<String> for StrError {
600    fn from(s: String) -> Self {
601        StrError(Box::new(StrErrorInner { context: s, source: None }))
602    }
603}
604impl From<&String> for StrError {
605    fn from(s: &String) -> Self {
606        From::from(s.clone())
607    }
608}
609
610// From conversions for stdlib errors.
611macro_rules! impl_from {
612    ($from:ty) => {
613        impl From<$from> for StrError {
614            fn from(err: $from) -> Self {
615                stringify!($from).into_chained(err)
616            }
617        }
618    };
619    ($from:ty, $param:ident $(, $bound:ident)* ) => {
620        impl<$param> From<$from> for StrError
621        where
622            $param: Send + Sync + 'static $(+ $bound)*,
623        {
624            fn from(err: $from) -> Self {
625                stringify!($from).into_chained(err)
626            }
627        }
628    };
629}
630
631impl_from!(std::alloc::LayoutErr);
632impl_from!(std::array::TryFromSliceError);
633impl_from!(std::boxed::Box<T>, T, Error);
634impl_from!(std::cell::BorrowError);
635impl_from!(std::cell::BorrowMutError);
636impl_from!(std::char::CharTryFromError);
637impl_from!(std::char::DecodeUtf16Error);
638impl_from!(std::char::ParseCharError);
639impl_from!(std::env::JoinPathsError);
640impl_from!(std::env::VarError);
641impl_from!(std::ffi::FromBytesWithNulError);
642impl_from!(std::ffi::IntoStringError);
643impl_from!(std::ffi::NulError);
644impl_from!(std::fmt::Error);
645impl_from!(std::io::Error);
646impl_from!(std::io::IntoInnerError<T>, T, Debug);
647impl_from!(std::net::AddrParseError);
648impl_from!(std::num::ParseFloatError);
649impl_from!(std::num::ParseIntError);
650impl_from!(std::num::TryFromIntError);
651impl_from!(std::path::StripPrefixError);
652impl_from!(std::str::ParseBoolError);
653impl_from!(std::str::Utf8Error);
654impl_from!(std::string::FromUtf16Error);
655impl_from!(std::string::FromUtf8Error);
656impl_from!(std::string::ParseError);
657impl_from!(std::sync::PoisonError<T>, T);
658impl_from!(std::sync::TryLockError<T>, T);
659impl_from!(std::sync::mpsc::RecvError);
660impl_from!(std::sync::mpsc::RecvTimeoutError);
661impl_from!(std::sync::mpsc::SendError<T>, T);
662impl_from!(std::sync::mpsc::TryRecvError);
663impl_from!(std::sync::mpsc::TrySendError<T>, T);
664impl_from!(std::time::SystemTimeError);