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
//! Error propagation tracing in Rust.
//!
//! This crate provides [`propagate::Result`], a replacement for the standard
//! library result type that automatically tracks the propagation of error
//! results using the `?` operator.
//!
//!
//! # Examples
//!
//! Consider the following custom error type:
//!
//! ```
//! use std::io;
//!
//! enum MyError {
//! Unlucky,
//! Io(io::Error),
//! TooSmall(u64),
//! }
//!
//! impl From<io::Error> for MyError {
//! fn from(e: io::Error) -> Self {
//! Self::Io(e)
//! }
//! }
//! ```
//!
//! The following two examples show how [`propagate::Result`] might be used for
//! error handling.
//!
//! ## With try blocks
//!
//! This example makes use of [`try` blocks]. This makes the resulting code
//! cleaner compared to not using `try` blocks.
//!
//! ```
//! #![feature(try_blocks)]
//! # use std::io;
//! # enum MyError {
//! # Unlucky,
//! # Io(io::Error),
//! # TooSmall(u64),
//! # }
//! # impl From<io::Error> for MyError {
//! # fn from(e: io::Error) -> Self {
//! # Self::Io(e)
//! # }
//! # }
//! use std::fs::File;
//!
//! fn file_size(path: &str) -> propagate::Result<u64, MyError> {
//! try {
//! // The `?` operator coerces `std::result::Result<_, io::Error>`
//! // into `propagate::Result<_, MyError>`.
//! let size = File::open(path)?.metadata()?.len();
//!
//! if size < 1024 {
//! Err(MyError::TooSmall(size))?
//! }
//!
//! size
//! }
//! }
//!
//! fn maybe_file_size(path: &str) -> propagate::Result<u64, MyError> {
//! let lucky = (path.len() % 2) == 0;
//!
//! try {
//! if !lucky {
//! Err(MyError::Unlucky)?
//! }
//!
//! file_size(path)?
//! }
//! }
//!
//! # fn main() {
//! # let result = maybe_file_size("foo.txt");
//! # match result {
//! # propagate::Ok(size) => println!("File size: {} KiB", size / 1024),
//! # propagate::Err(err, trace) => {
//! # match err {
//! # MyError::Unlucky => println!("Not this time!"),
//! # MyError::Io(e) => println!("I/O error: {}", e),
//! # MyError::TooSmall(size) => println!("File too small: {} bytes", size),
//! # }
//! # println!("Return trace: {}", trace);
//! # }
//! # }
//! # }
//! ```
//!
//! ## Without try blocks
//!
//! This example is the same as the one above, except it does not make use of
//! [`try` blocks]. This requires a bit more boilerplate, and also requires the
//! user to remember to properly forward results (see [`propagate::Result`] for
//! more information).
//!
//! ```
//! # use std::io;
//! # enum MyError {
//! # Unlucky,
//! # Io(io::Error),
//! # TooSmall(u64),
//! # }
//! # impl From<io::Error> for MyError {
//! # fn from(e: io::Error) -> Self {
//! # Self::Io(e)
//! # }
//! # }
//! use propagate::ErrorTrace;
//! use std::fs::File;
//!
//! fn file_size(path: &str) -> propagate::Result<u64, MyError> {
//! // The `?` operator coerces `std::result::Result<_, io::Error>`
//! // into `propagate::Result<_, MyError>`.
//! let size = File::open(path)?.metadata()?.len();
//!
//! if size < 1024 {
//! // Option 1: Coerce a `std::result::Result` to a `propagate::Result`
//! // using `?`.
//! Err(MyError::TooSmall(size))?
//! }
//!
//! propagate::Ok(size)
//! }
//!
//! fn maybe_file_size(path: &str) -> propagate::Result<u64, MyError> {
//! let lucky = (path.len() % 2) == 0;
//!
//! if !lucky {
//! // Option 2: Directly construct a `propagate::Result`
//! // using `ErrorTrace::new()`.
//! return propagate::Err(MyError::Unlucky, ErrorTrace::new())
//! }
//!
//! // Must remember to surround with `Ok(..?)`.
//! propagate::Ok(file_size(path)?)
//! }
//! ```
//!
//! See [`propagate::Result`] for more information.
//!
//! [`propagate::Result`]: crate::Result
//! [`try` blocks]: https://doc.rust-lang.org/beta/unstable-book/language-features/try-blocks.html
#![feature(try_trait_v2)]
#![feature(control_flow_enum)]
#![feature(termination_trait_lib)]
// TODO:
// * Add a feature flag to fall back to standard library results.
// * Massage `CodeLocation` and `ErrorTrace` a bit.
// * Improve crate-level docs a bit.
// * Put more thought into the Result interface.
// - i.e., should more methods preserve the error trace?
// * Put `MyError` into shared example module?
pub mod result;
pub mod trace;
#[doc(inline)]
pub use self::{
result::Result,
trace::{CodeLocation, ErrorTrace, Traced},
};
pub use self::result::Result::{Err, Ok};
#[cfg(test)]
mod test;
// Test that all code snippets in README.md compile.
#[cfg(doctest)]
#[doc = include_str!("../README.md")]
pub struct ReadmeDoctests;