resext 0.9.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,ignore
//! use resext::resext;
//!
//! #[resext]
//! enum AppError {
//!     Io(std::io::Error),
//!     Parse(std::num::ParseIntError),
//! }
//!
//! fn read_config() -> Res<String> {
//!     let content = std::fs::read_to_string("config.toml")
//!         .context("Failed to read config file")?;
//!     
//!     let value: i32 = content.trim().parse()
//!         .context("Failed to parse config value")?;
//!     
//!     Ok(content)
//! }
//! ```
//!
//! ---
//!
//! # Proc Macro
//!
//! The proc macro provides clean syntax with full customization:
//!
//! ```rust,ignore
//! use resext::resext;
//!
//! #[resext(
//!     prefix = "ERROR: ",
//!     delimiter = " -> ",
//!     include_variant = true,
//!     alias = AppResult
//! )]
//! enum MyError {
//!     Network(reqwest::Error),
//!     Database { error: sqlx::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)
//!
//! ---
//!
//! # Context Methods
//!
//! ## `.context(msg: &str)`
//!
//! Add static context to an error:
//!
//! ```rust,ignore
//! use resext::resext;
//!
//! #[resext] enum E { Io(std::io::Error) }
//!
//! std::fs::read("file.txt")
//!     .context("Failed to read file")?;
//! Ok::<(), ResErr>(())
//! ```
//!
//! ## `.with_context(args: core::fmt::Arguments<'_>)`
//!
//! Add dynamic context:
//!
//! ```rust,ignore
//! use resext::resext;
//!
//! #[resext] enum E { Io(std::io::Error) }
//!
//! let path = "file.txt";
//! std::fs::read(path)
//!     .with_context(|| format!("Failed to read {}", path))?;
//! Ok::<(), ResErr>(())
//! ```
//!
//! ## `.or_exit(code: i32)`
//!
//! Print error to Stderr and exit process with given code on error:
//!
//! ```rust,ignore
//! use resext::resext;
//!
//! #[resext] enum E { Io(std::io::Error) }
//!
//! fn load_config() -> Res<()> { Ok(()) }
//!
//! let config = load_config().or_exit(1);
//! ```
//!
//! ## `.better_expect(msg: FnOnce() -> impl std::fmt::Display, code: i32)`
//!
//! Like `or_exit` but with custom message:
//!
//! ```rust,ignore
//! use resext::resext;
//!
//! #[resext] enum E { Io(std::io::Error) }
//!
//! fn load_critical_data() -> Res<()> { Ok(()) }
//!
//! let data = load_critical_data()
//!     .better_expect(|| "FATAL: Cannot start without data", 1);
//! ```
//!
//! ---
//!
//! # 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,ignore
//! use resext::resext;
//!
//! #[resext]
//! enum ConfigError {
//!     Io(std::io::Error),
//!     Parse(toml::de::Error),
//! }
//!
//! fn load_config(path: &str) -> Res<Config> {
//!     let content = std::fs::read_to_string(path)
//!         .context("Failed to read config")?;
//!     
//!     toml::from_str(&content)
//!         .with_context(format_args!("Failed to parse {}", path))
//! }
//! ```
//!
//! ## Multiple Error Types
//!
//! ```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;

/// Panic with message if `condition` is true
///
/// Accepts a message as any type that implements `std::fmt::Display`
///
/// ## Examples
///
/// ```rust,ignore
/// use resext::panic_if;
///
/// let x = 5;
///
/// panic_if!(x > 10, "x is too big", 1);
/// panic_if!(x > 10, format!("x={} is too big", x), 1);
/// ```
#[macro_export]
macro_rules! panic_if {
    ($condition:expr, $msg:expr, $code:expr) => {
        if $condition {
            eprintln!("{}", $msg);
            std::process::exit($code);
        }
    };
}