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://wisagan.github.io/simple-error/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`. The underlying
8//! is a `String` 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 self::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 self::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 self::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 self::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<'a> From<&'a 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 only be used in functions that return `Result<_, SimpleError>`.
127///
128///
129/// # Examples
130///
131/// ```
132/// # #[macro_use] extern crate simple_error;
133/// # fn main() {
134/// use self::simple_error::SimpleError;
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 only be used in functions that return `Result<_, SimpleError>`.
190///
191///
192/// # Examples
193///
194/// ```
195/// # #[macro_use] extern crate simple_error;
196/// # fn main() {
197/// use self::simple_error::SimpleError;
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 returning a `SimpleError` constructed from either a `Into<SimpleError>`, a
251/// string slice, or a formatted string.
252///
253/// # Examples
254///
255/// ```
256/// # #[macro_use] extern crate simple_error;
257/// # fn main() {
258/// use self::simple_error::SimpleError;
259/// use std::error::Error;
260/// // Use with a `Into<SimpleError>`
261///
262/// struct ErrorSeed;
263///
264/// impl From<ErrorSeed> for SimpleError {
265/// fn from(_: ErrorSeed) -> SimpleError {
266/// SimpleError::new(".")
267/// }
268/// }
269///
270/// fn bail_block_into(es: ErrorSeed) -> Result<(), SimpleError> {
271/// bail!(es);
272/// }
273///
274/// // Use with a string slice
275/// fn bail_block_str(s: &str) -> Result<(), SimpleError> {
276/// bail!(s);
277/// }
278///
279/// // Use with a formatted string
280/// fn bail_block_format(s: &str) -> Result<(), SimpleError> {
281/// bail!("reason: {}", s);
282/// }
283///
284/// // Use with a formatted string to a boxed error
285/// fn bail_block_format_to_box_error(s: &str) -> Result<(), Box<dyn Error>> {
286/// bail!("reason: {}", s);
287/// }
288/// # }
289/// ```
290#[macro_export]
291macro_rules! bail {
292 ($fmt:literal) => {
293 return Err(::std::convert::From::from($crate::SimpleError::new(format!($fmt))));
294 };
295 ($e:expr) => {
296 return Err(::std::convert::From::from($e));
297 };
298 ($fmt:literal, $($arg:tt)+) => {
299 return Err(::std::convert::From::from($crate::SimpleError::new(format!($fmt, $($arg)+))));
300 };
301}
302
303/// Construct an ad-hoc `SimpleError` from a string.
304///
305/// It can take either just a string, or a format string with arguments.
306///
307/// # Example
308///
309/// ```
310/// # #[macro_use] extern crate simple_error;
311/// # fn main() {
312/// use self::simple_error::SimpleResult;
313///
314/// fn add_reason(r: Result<(), ()>) -> SimpleResult<()> {
315/// // Use with a string slice
316/// r.map_err(|_| simple_error!("no reason"))
317/// }
318///
319/// fn add_reason_with_str(r: Result<(), ()>, s: &str) -> SimpleResult<()> {
320/// // Use with a formatted string
321/// r.map_err(|_| simple_error!("reason: {}", s))
322/// }
323/// # }
324/// ```
325#[macro_export]
326macro_rules! simple_error {
327 ($fmt:literal) => {
328 $crate::SimpleError::new(format!($fmt))
329 };
330 ($e:expr) => {
331 $crate::SimpleError::new($e)
332 };
333 ($fmt:literal, $($arg:tt)+) => {
334 $crate::SimpleError::new(format!($fmt, $($arg)+))
335 };
336}
337
338/// Map a result type error to simple error with format
339///
340/// It can take either just a string, or a format string with arguments.
341///
342/// # Example
343///
344/// ```
345/// # #[macro_use] extern crate simple_error;
346/// # fn main() {
347/// use self::simple_error::SimpleResult;
348///
349/// fn map_err_with_reason(r: Result<(), std::io::Error>) -> SimpleResult<()> {
350/// // Use with a string slice
351/// map_err_with!(r, "no reason")
352/// }
353///
354/// fn map_err_with_reason_with_str(r: Result<(), std::io::Error>, s: &str) -> SimpleResult<()> {
355/// // Use with a formatted string
356/// map_err_with!(r, "no reason: {}", s)
357/// }
358/// # }
359/// ```
360#[macro_export]
361macro_rules! map_err_with {
362 ($r: expr, $fmt:literal) => {
363 $r.map_err(|e| $crate::SimpleError::with(&format!($fmt), e))
364 };
365 ($r: expr, $str: expr) => {
366 $r.map_err(|e| $crate::SimpleError::with($str.as_ref(), e))
367 };
368 ($r: expr, $fmt:literal, $($arg:tt)+) => {
369 $r.map_err(|e| $crate::SimpleError::with(&format!($fmt, $($arg)+), e))
370 };
371}
372
373
374#[cfg(test)]
375mod tests {
376 use super::SimpleError;
377 use std::error::Error;
378 use std::io;
379
380 pub struct ErrorSeed;
381
382 impl From<ErrorSeed> for SimpleError {
383 fn from(_: ErrorSeed) -> SimpleError {
384 SimpleError::new(".")
385 }
386 }
387
388 #[test]
389 fn new_from_string() {
390 let err = SimpleError::new(String::from("an error from String"));
391 assert_eq!("an error from String", format!("{err}"));
392 }
393
394 #[test]
395 fn new_from_str() {
396 let err = SimpleError::new("an error from str");
397 assert_eq!("an error from str", format!("{err}"));
398 }
399
400 #[test]
401 fn new_from_simple_error_macro() {
402 let err = SimpleError::new("an error from str");
403 assert_eq!(SimpleError::new("an error from str"), simple_error!("{err}"));
404 }
405
406 #[test]
407 fn map_error_with_macro() {
408 let err = crate::SimpleResult::<()>::Err(SimpleError::new("an error from str"));
409 let additional_reason = "error in current function";
410 assert_eq!(Err(SimpleError::new("error in current function, an error from str")), map_err_with!(err, "{additional_reason}"));
411 }
412
413 #[test]
414 fn from_io_error() {
415 let err = SimpleError::from(io::Error::new(io::ErrorKind::Other, "oh no"));
416 assert_eq!("oh no", format!("{err}"));
417 }
418
419 fn try_block(result: Result<(), SimpleError>, s: &str) -> Result<(), SimpleError> {
420 Ok(try_with!(result, s))
421 }
422
423 fn try_block_format(result: Result<(), SimpleError>, s: &str) -> Result<(), SimpleError> {
424 Ok(try_with!(result, "with {}", s))
425 }
426
427 fn try_block_format_with_capture(result: Result<(), SimpleError>, s: &str) -> Result<(), SimpleError> {
428 Ok(try_with!(result, "with {s}"))
429 }
430
431 #[test]
432 fn macro_try_with() {
433 assert_eq!(Ok(()), try_block(Ok(()), ""));
434 assert_eq!(Err(SimpleError::new("try block error, error foo")), try_block(Err(SimpleError::new("error foo")), "try block error"));
435 assert_eq!(Err(SimpleError::new("with try block error, error foo")), try_block_format(Err(SimpleError::new("error foo")), "try block error"));
436 assert_eq!(Err(SimpleError::new("with try block error, error foo")), try_block_format_with_capture(Err(SimpleError::new("error foo")), "try block error"));
437 }
438
439 fn require_block(option: Option<()>, s: &str) -> Result<(), SimpleError> {
440 Ok(require_with!(option, s))
441 }
442
443 fn require_block_str_as_ref(option: Option<()>, s: &String) -> Result<(), SimpleError> {
444 Ok(require_with!(option, s))
445 }
446
447 fn require_block_format(maybe: Option<()>, s: &str) -> Result<(), SimpleError> {
448 Ok(require_with!(maybe, "with {}", s))
449 }
450
451 fn require_block_format_with_capture(maybe: Option<()>, s: &str) -> Result<(), SimpleError> {
452 Ok(require_with!(maybe, "with {s}"))
453 }
454
455 #[test]
456 fn macro_require_with() {
457 assert_eq!(Ok(()), require_block(Some(()), ""));
458 assert_eq!(Err(SimpleError::new("require block error")), require_block(None, "require block error"));
459 assert_eq!(Err(SimpleError::new("require block error")), require_block_str_as_ref(None, &"require block error".to_owned()));
460 assert_eq!(Err(SimpleError::new("with require block error")), require_block_format(None, "require block error"));
461 assert_eq!(Err(SimpleError::new("with require block error")), require_block_format_with_capture(None, "require block error"));
462 }
463
464 fn bail_block_into(es: ErrorSeed) -> Result<(), SimpleError> {
465 bail!(es);
466 }
467
468 fn bail_block_str(s: &str) -> Result<(), SimpleError> {
469 bail!(s);
470 }
471
472 fn bail_block_format(s: &str) -> Result<(), SimpleError> {
473 bail!("reason: {}", s);
474 }
475
476 fn bail_block_format_with_capture(s: &str) -> Result<(), SimpleError> {
477 bail!("reason: {s}");
478 }
479
480 fn bail_block_format_to_box_error(s: &str) -> Result<(), Box<dyn Error>> {
481 bail!("reason: {}", s);
482 }
483
484 #[test]
485 fn macro_bail() {
486 assert_eq!(Err(SimpleError::new(".")), bail_block_into(ErrorSeed));
487 assert_eq!(Err(SimpleError::new("no reason")), bail_block_str("no reason"));
488 assert_eq!(Err(SimpleError::new("reason: plane crashed")), bail_block_format("plane crashed"));
489 assert!(bail_block_format_to_box_error("plane crashed").is_err());
490 }
491
492 #[test]
493 fn inline_format_arg_capture() {
494 assert_eq!(Err(SimpleError::new("reason: plane crashed")), bail_block_format_with_capture("plane crashed"));
495 }
496}