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}