Skip to main content

idc/
lib.rs

1//!# idc: A simple crate for error propagation  
2//!Idc is a simple crate for propagating errors that implement std’s Error trait.  
3//!Idc also supports no_std with the same functionality, but you have to provide a global allocator and disable default features.  
4//!  
5//!## examples:  
6//!1. propagating multiple different errors:  
7//!  
8//!```ignore
9//!use std::fs;
10//!use idc::*;
11//!use serde_json::Value;
12//!
13//!fn main() -> Result<()> {
14//!    let foo = fs::read_to_string("foo.json").context("failed to read foo.", "maybe it doesn't exist?".into())?;
15//!    let json: Value = serde_json::from_str(&foo).context("failed to turn foo into json.", "make sure foo.json is valid json.".into())?;
16//!    println!("{}", json["important item"]);
17//!    Ok(())
18//!}
19//!
20//!```
21//!  
22//!2. returning an one-time error:  
23//!  
24//!```no_run
25//!use std::env;
26//!use idc::*;
27//!
28//!fn main() -> Result<()> {
29//!    let args: Vec<String> = env::args().collect();
30//!    if args.len() < 2 {
31//!        bail!("no argument provided!");
32//!    }
33//!    //...
34//!    Ok(())
35//!}
36//!```
37#![no_std]
38#![deny(missing_docs)]
39extern crate alloc;
40#[cfg(not(feature = "std"))]
41use alloc::string::String;
42#[cfg(not(feature = "std"))]
43use alloc::string::ToString;
44#[cfg(feature = "std")]
45use std::string::ToString;
46#[cfg(feature = "std")]
47extern crate std;
48#[cfg(feature = "std")]
49use std::string::String;
50#[cfg(not(feature = "std"))]
51use core::error::Error as StdError;
52#[cfg(feature = "std")]
53use std::error::Error as StdError;
54use core::fmt;
55
56///An error type for errors that implement std's Error trait.  
57/// 
58///See top-level documentation for usage examples.
59pub struct Error {
60    msg: String,
61    context: Option<String>,
62    hint: Option<String>
63}
64impl Error {
65    ///Create a new Error without context.  
66    ///You'll probaly never need to use this function.  
67    ///For one time errors, see the [`bail!()`] macro.  
68    /// 
69    ///## example:  
70    ///```  
71    ///use idc::*;  
72    ///fn main() -> Result<()> {  
73    ///    let err = Error::new("I failed");
74    ///    assert_eq!(&format!("{}", err), "I failed");  
75    ///    Ok(())  
76    ///}  
77    ///```  
78    pub fn new(msg: &str) -> Self {
79        Error {
80            msg: msg.to_string(),
81            context: None,
82            hint: None
83        }
84    }
85}
86impl<E: StdError> From<E> for Error {
87    fn from(value: E) -> Self {
88        Self {
89            msg: value.to_string(),
90            context: None,
91            hint: None
92        }
93    }
94}
95impl fmt::Display for Error {
96    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97        if let Some(context) = &self.context {
98            if let Some(hint) = &self.hint {
99                write!(f, "{context}\n\nCaused by:\n\t{}\n\nHint: {hint}", self.msg)
100            }
101            else {
102                write!(f, "{context}\n\nCaused by:\n\t{}", self.msg)
103            }
104        }
105        else {
106            write!(f, "{}", self.msg)
107        }
108    }
109}
110impl fmt::Debug for Error {
111    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112        fmt::Display::fmt(&self, f)
113    }
114}
115///Shoter version of Result<T, idc::Error>.  
116/// 
117///You can use it the same way as you would use [`std::result::Result<T, E>`].
118pub type Result<T, E = Error> = core::result::Result<T, E>;
119///Trait for adding context and an optional hint to errors.  
120/// 
121///## example:  
122///```no_run  
123///use idc::*;  
124///use std::fs;  
125///fn main() -> Result<()> {  
126///    let foo = fs::read_to_string("foo").context("failed to read foo.", "maybe it doesn't exist?".into())?;  
127///    println!("{}", foo);  
128///    Ok(())  
129///}
130pub trait Context<T, E: StdError> {
131    ///Function for adding context and an option hint to an error.  
132    ///
133    ///See [`Context`] for an exmaple.
134    fn context(self, context: &str, hint: Option<&str>) -> Result<T>;
135}
136impl<T, E: StdError> Context<T, E> for core::result::Result<T, E> {
137    fn context(self, context: &str, hint: Option<&str>) -> Result<T> {
138        self.map_err(|e| {
139            let mut error = Error::from(e);
140            error.context = Some(context.to_string());
141            error.hint = hint.map(|hint| hint.to_string());
142            error
143        })
144    }
145}
146#[macro_export]
147///Macro for one time errors.  
148/// 
149///## example:  
150///```no_run
151///use std::env;
152///use idc::*;
153///
154///fn main() -> Result<()> {
155///    let args: Vec<String> = env::args().collect();
156///    if args.len() < 2 {
157///        bail!("no argument provided!");
158///    }
159///    //...
160///    Ok(())
161///}
162///```
163macro_rules! bail {
164    ($($arg:tt)*) => {
165        extern crate alloc;
166        return Err($crate::Error::new(&alloc::format!($($arg)*)));
167    };
168}