1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
//! # Non-fatal, strongly typed errors
//!
//! By default, errors in Rust are handled using [`Result<T, E>`], which contains either a value
//! or an error. But sometimes, you need to return a value alongside with one or many errors. In this
//! case, you may need to use `wurm`.
//!
//! Think of non-fatal errors as compiler warnings. The compiler will give you the result (i.e. the
//! compiled binary) even if there are tons of warnings. You also receive the warnings and can inspect
//! them to decide what to do.
//!
//! As an alternative, you may just use a logger to yield such non-fatal errors, but you lose flexibility,
//! because your errors will be just strings sent into a logger, and inspecting them from code can be
//! problematic.
//!
//! # Basic usage
//!
//! Suppose you have a function, to which you want to add non-fatal error support:
//!
//! ```no_test
//! fn foo(first: u32, second: u32) -> u32;
//! ```
//!
//! Then, just add one more argument `warn: &mut impl Warn<Error>` where `Error` is the desired error type.
//! By convention, it is recommended to add the extra arg to the end of the argument list:
//!
//! ```no_test
//! fn foo(first: u32, second: u32, warn: &mut impl Warn<Error>) -> u32;
//! ```
//!
//! Then, you can yield non-fatal errors via [`Warn::warn`] or convert them from regular ones via [`OptionExt`]
//! or [`ResultExt`]. See below for details.
//!
//! # Motivating example
//!
//! ```
//! use thiserror::Error;
//! use wurm::prelude::*;
//! use wurm::CollectAll;
//!
//! // First error type
//! #[derive(Debug, Error, PartialEq, Eq)]
//! #[error("first error")]
//! struct FooError;
//!
//! // Second error type, which is converible from `FooError`
//! #[derive(Debug, Error, PartialEq, Eq)]
//! #[error("second error: {0}")]
//! struct BarError(#[from] FooError);
//!
//! // Ordinary function, which can return a simple `Result`
//! fn simple_func() -> Result<u32, FooError> {
//! Err(FooError)
//! }
//!
//! // Function which can yield non-fatal errors of type `FooError`
//! fn foo(arg: u32, warn: &mut impl Warn<FooError>) -> u32 {
//! // Just yield a non-fatal error via `Warn::warn`
//! warn.warn(FooError);
//! // You can also use `ResultExt::or_warn()` to push the error from `Result`
//! // into `warn`.
//! //
//! // Explicit type hint is added for extra clarity and is not needed actually.
//! let opt: Option<u32> = simple_func().or_warn(warn);
//! arg + opt.unwrap_or(1)
//! }
//!
//! // Function which can yield non-fatal errors of type `BarError`. It calls `foo()`
//! // internally and converts the errors from `FooError` to `BarError` via the adapter.
//! fn bar(first: u32, second: u32, warn: &mut impl Warn<BarError>) -> u32 {
//! // We pass `&mut warn.adapt()` to convert between different error types.
//! let x = foo(first, &mut warn.adapt());
//! let y = foo(second, &mut warn.adapt());
//! x * y
//! }
//!
//! // Create a sink for non-fatal errors.
//! //
//! // Explicit type hint is added for extra clarity and is not needed actually.
//! let mut warn: CollectAll<BarError> = CollectAll::default();
//!
//! // Call `bar()` with the created sink. It must yield four errors: two from each
//! // `foo()` subcall.
//! let value = bar(2, 3, &mut warn);
//! assert_eq!(value, 12);
//! assert_eq!(warn.0.len(), 4);
//! ```
mod base;
mod ext;
pub mod sink;
pub use base::{Adapt, AdaptMap, Warn, WarnExt};
pub use ext::{OptionExt, ResultExt};
#[allow(deprecated)]
pub use sink::{All, CollectAll, Ignore, Stderr};
/// The most important types to use
///
/// This prelude re-exports common, most frequently used types from the crate. It is intended to
/// reduce the amount of imports, glob-importing the prelude instead:
///
/// ```
/// use wurm::prelude::*;
/// ```
pub mod prelude {
pub use crate::{OptionExt, ResultExt, Warn, WarnExt};
}
#[cfg(test)]
mod tests {
use super::*;
use thiserror::Error;
#[derive(Debug, Error, Eq, PartialEq)]
#[error("first: {value}")]
struct ErrFirst {
value: usize,
}
#[derive(Debug, Error, Eq, PartialEq)]
#[error("second: {0}")]
struct ErrSecond(#[from] ErrFirst);
fn recursive(n: usize, warn: &mut impl Warn<ErrFirst>) {
if n == 0 {
return;
}
recursive(n - 1, warn);
warn.warn(ErrFirst { value: n });
recursive(n - 1, warn);
}
#[test]
fn test_recursive() {
let mut warn = CollectAll::default();
recursive(3, &mut warn);
let res = vec![
ErrFirst { value: 1 },
ErrFirst { value: 2 },
ErrFirst { value: 1 },
ErrFirst { value: 3 },
ErrFirst { value: 1 },
ErrFirst { value: 2 },
ErrFirst { value: 1 },
];
assert_eq!(warn.0, res);
}
fn inner(warn: &mut impl Warn<ErrFirst>) {
warn.warn(ErrFirst { value: 1 });
}
fn outer(warn: &mut impl Warn<ErrSecond>) {
inner(&mut warn.adapt());
warn.warn(ErrSecond(ErrFirst { value: 2 }));
}
#[test]
fn test_adapt() {
let mut warn = CollectAll::default();
outer(&mut warn);
let res = vec![
ErrSecond(ErrFirst { value: 1 }),
ErrSecond(ErrFirst { value: 2 }),
];
assert_eq!(warn.0, res);
}
#[test]
fn test_exts() {
let mut warn = CollectAll::default();
let value = Some(42);
assert_eq!(
value.or_warn_with(ErrSecond(ErrFirst { value: 1 }), &mut warn),
Some(42)
);
assert_eq!(warn.0.len(), 0);
let value: Option<isize> = None;
assert_eq!(
value.or_warn_with(ErrSecond(ErrFirst { value: 1 }), &mut warn),
None
);
assert_eq!(warn.0.len(), 1);
let value: Result<_, ErrSecond> = Ok(42);
assert_eq!(value.or_warn(&mut warn), Some(42));
assert_eq!(warn.0.len(), 1);
let value: Result<usize, _> = Err(ErrFirst { value: 2 });
assert_eq!(value.or_warn(&mut warn), None);
assert_eq!(warn.0.len(), 2);
}
}