resext 1.0.0

A simple, lightweight error handling crate for Rust
Documentation
//! **Context-rich error handling for Rust with zero-cost abstractions and zero allocations**
//!
//! This is the primary interface for ResExt. It re-exports the proc-macro as well as other helpers
//! provided by ResExt.
//!
//! # Quick Start
//!
//! ```rust
//! use resext::resext;
//!
//! #[resext(alias = AppRes)]
//! enum AppError {
//!     Io(std::io::Error),
//!     Parse(std::num::ParseIntError),
//! }
//!
//! fn read_config() -> AppRes<String> {
//!     let content = std::fs::read_to_string("config.toml")
//!         .context("Failed to read config file")?;
//!     
//!     let value: i32 = content.trim().parse::<i32>()
//!         .context("Failed to parse config value")?;
//!     
//!     Ok(content)
//! }
//! ```
//!
//! ---
//!
//! # Proc Macro
//!
//! The proc macro provides clean syntax with full customization:
//!
//! ```rust
//! use resext::resext;
//!
//! #[resext(
//!     prefix = "ERROR: ",
//!     delimiter = " -> ",
//!     include_variant = true,
//! )]
//! enum MyError {
//!     Io(std::io::Error),
//!     Fmt { error: std::fmt::Error },
//! }
//! ```
//!
//! ## Attribute Options
//!
//! - `prefix` - String prepended to entire error message
//! - `suffix` - String appended to entire error message
//! - `msg_prefix` - String prepended to each context message
//! - `msg_suffix` - String appended to each context message
//! - `delimiter` - Separator between context messages (default: " - ")
//! - `source_prefix` - String prepended to source error (default: "Error: ")
//! - `include_variant` - Include variant name in Display output (default: false)
//! - `alias` - Custom type alias name which is used for getting the names for other items generated by the proc-macro (default: `Res`)
//! - `buf_size` - Size for the context message byte buffer (default: 64)
//! - `std` - Enable std-only features like `.or_exit()` macro
//! - `alloc` Enable heap-spilling if context exceeds `buf_size`, this attribute requires a global allocator / importing `extern crate alloc`
//!
//! ---
//!
//! # Context Methods
//!
//! ## `.context(self, msg: &str)`
//!
//! Add static context to an error:
//!
//! ```rust,ignore
//! std::fs::read("file.txt")
//!     .context("Failed to read file")?;
//! Ok::<(), ResErr>(())
//! ```
//!
//! ## `.with_context(self, args: core::fmt::Arguments<'_>)`
//!
//! Add dynamic context:
//!
//! ```rust,ignore
//! let path = "file.txt";
//!
//! std::fs::read(path)
//!     .with_context(|| format!("Failed to read {}", path))?;
//! Ok::<(), ResErr>(())
//! ```
//!
//! ## `.or_exit(self, code: i32)`
//!
//! Print error to Stderr and exit process with given code on error, this is an std-only method:
//!
//! ```rust,ignore
//! fn load_config() -> Res<()> { Ok(()) }
//!
//! let config = load_config().or_exit(1);
//! ```
//!
//! ## `.better_expect(self, msg: FnOnce() -> impl std::fmt::Display, code: i32)`
//!
//! Like `or_exit` but with custom message, this is an std-only method:
//!
//! ```rust,ignore
//! fn load_critical_data() -> Res<()> { Ok(()) }
//!
//! let data = load_critical_data()
//!     .better_expect(|| "FATAL: Cannot start without data", 1);
//! ```
//!
//! ## `.write_log<W: std::io::Write>(self, writer: &mut W)`
//!
//! Write error to writer on error and return `None` or return `Some(T)`
//!
//! Writer needs to implement `std::io::Write`, so it's an std-only method:
//!
//! ```rust,ignore
//! fn write_output_entry() -> Res<()> { Ok(()) }
//!
//! let mut wtr = BufWriter::new(Vec::new());
//! let opt = write_output_entry().write_log(&mut wtr);
//!
//! match opt {
//!     Some(_) => {}
//!     None => eprintln!("Failed to write output entry"),
//! }
//! ```
//!
//! ## `.fmt_log<F: core::fmt::Write>(self, writer: &mut F)`
//!
//! Write error to writer on error and return `None` or return `Some(T)`:
//!
//! ```rust,ignore
//! fn write_output_entry() -> Res<()> { Ok(()) }
//!
//! let mut wtr = String::new();
//! let opt = write_output_entry().write_log(&mut wtr);
//!
//! match opt {
//!     Some(_) => {}
//!     None => eprintln!("Failed to write output entry"),
//! }
//! ```
//!
//! ---
//!
//! # Error Display Format
//!
//! Errors are displayed with context chains:
//!
//! ```text
//! Failed to load application
//!  - Failed to read config file
//!  - Failed to open file
//! Error: No such file or directory
//! ```
//!
//! With `include_variant = true`:
//!
//! ```text
//! Failed to load application
//!  - Failed to read config file
//! Error: Io: No such file or directory
//! ```
//!
//! ---
//!
//! # Examples
//!
//! ## Basic Error Handling
//!
//! ```rust
//! use resext::resext;
//!
//! #[resext]
//! enum ConfigError {
//!     Io(std::io::Error),
//!     Utf8(std::string::FromUtf8Error),
//! }
//!
//! fn load_config(path: &str) -> Res<String> {
//!     let content = std::fs::read(path)
//!         .context("Failed to read config")?;
//!     
//!     std::string::String::from_utf8(content)
//!         .with_context(format_args!("Failed to parse {}", path))
//! }
//! ```
//!
//! ## Multiple Error Types
//!
//! **Note:** This example is not tested as it's an example of errors from external crates
//!
//! ```rust,ignore
//! use resext::resext;
//!
//! #[resext(alias = ApiResult)]
//! enum ApiError {
//!     Network(reqwest::Error),
//!     Database(sqlx::Error),
//!     Json(serde_json::Error),
//! }
//!
//! async fn fetch_user(id: u64) -> ApiResult<User> {
//!     let response = reqwest::get(format!("/users/{}", id))
//!         .await
//!         .context("Failed to fetch user")?;
//!     
//!     let user = response.json()
//!         .await
//!         .context("Failed to parse user data")?;
//!     
//!     Ok(user)
//! }
//! ```
//!

pub use resext_macro::resext;