| Crates.io | ohno |
| lib.rs | ohno |
| version | 0.2.1 |
| created_at | 2025-08-07 12:42:55.895367+00 |
| updated_at | 2026-01-19 14:50:19.062721+00 |
| description | High-quality Rust error handling. |
| homepage | https://github.com/microsoft/oxidizer |
| repository | https://github.com/microsoft/oxidizer |
| max_upload_size | |
| id | 1785222 |
| size | 182,167 |
High-quality error handling for Rust.
Ohno combines error wrapping, enrichment messages stacking, backtrace capture, and procedural macros into one ergonomic crate for comprehensive error handling.
#[derive(Error)]: Derive macro for automatic std::error::Error, Display, Debug implementations#[error]: Attribute macro for creating error types#[enrich_err("...")]: Attribute macro for automatic error enrichment with file and line information.ErrorExt: Trait that provides additional methods for ohno error types, it’s implemented automatically for all ohno error typesOhnoCore: Core error type that wraps source errors, captures backtraces, and holds enrichment entriesAppError: Application-level error type for general application errorsuse std::path::{Path, PathBuf};
#[ohno::error]
pub struct ConfigError(PathBuf);
#[ohno::enrich_err("failed to open file {}", path.as_ref().display())]
fn open_file(path: impl AsRef<Path>) -> Result<String, ConfigError> {
std::fs::read_to_string(path.as_ref())
.map_err(|e| ConfigError::caused_by(path.as_ref().to_path_buf(), e))
}
Derive macro for automatically implementing error traits.
When applied to a struct or enum containing an OhnoCore field,
this macro automatically implements std::error::Error, std::fmt::Display, std::fmt::Debug, and From conversions.
Note:
From<std::convert::Infallible>is implemented by default and calls viaunreachable!macro.
use ohno::{OhnoCore, Error};
#[derive(Error)]
pub struct MyError {
inner_error: OhnoCore,
}
ohno::errorThe #[ohno::error] attribute macro is a convenience wrapper that automatically adds a OhnoCore
field to your struct and applies #[derive(Error)]. This is the simplest way to create error types
without manually managing the error infrastructure.
// Simple error without extra fields
#[ohno::error]
pub struct ParseError;
// Error with multiple fields
#[ohno::error]
pub struct NetworkError {
host: String,
port: u16,
}
The #[display("...")] attribute allows you to customize the main error message
while preserving the underlying error as a cause in the error chain.
use std::path::PathBuf;
#[ohno::error]
#[display("Failed to read config with path: {path}")]
pub struct ConfigError {
pub path: String,
}
// Usage
let error = ConfigError::caused_by("/etc/config.toml", "file not found");
// Output: "Failed to read config with path: /etc/config.toml\nCaused by:\n\tfile not found"
The template string supports field interpolation using {field_name} syntax. The underlying
error (if any) is automatically shown as “Caused by:” in the error chain. If the inner error
has no source, only the custom message is displayed.
By default, #[derive(Error)] automatically generates new() and caused_by() constructor methods:
#[ohno::error]
struct ConfigError {
path: String,
}
// The derive macro automatically generates:
// - ConfigError::new(path: String) -> Self
// - ConfigError::caused_by(path: String, error: impl Into<Box<dyn Error...>>) -> Self
let error = ConfigError::new("/etc/config.toml");
let error_with_cause = ConfigError::caused_by("/etc/config.toml", "File not found");
Disabling Automatic Constructors:
Use #[no_constructors] to disable automatic generation when you need custom constructors:
use ohno::{Error, OhnoCore};
#[derive(Error)]
#[no_constructors]
struct CustomError {
inner_error: OhnoCore,
}
impl CustomError {
pub fn new(custom_logic: bool) -> Self {
// Your custom constructor logic here
Self { inner_error: OhnoCore::default() }
}
}
The #[from(Type1, Type2, ...)] attribute automatically generates From<Type> implementations
for the specified types. Other fields in the struct are defaulted using Default::default().
#[ohno::error]
#[derive(Default)]
#[from(std::io::Error, std::fmt::Error)]
struct MyError {
optional_field: Option<String>,
code: i32,
}
// This generates:
// impl From<std::io::Error> for MyError { ... }
// impl From<std::fmt::Error> for MyError { ... }
let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
let my_err: MyError = io_err.into(); // Works automatically
// optional_field = None, code = 0 (defaulted)
Note: Error’s fields must implement Default when using #[from] to ensure they can be properly initialized.
The #[enrich_err("message")] attribute macro adds error enrichment with file and line info to function errors.
Functions annotated with #[enrich_err("message")] automatically wrap any returned Result. If
the function returns an error, the macro injects a message, including file and line information, into the error chain.
Requirements:
map_err method (such as Result or Poll)Enrichable trait (automatically implemented for all ohno error types)Supported syntax patterns:
#[enrich_err("failed to process request")]
fn process() -> Result<(), MyError> { /* ... */ }
#[enrich_err("failed to read file: {path}")]
fn read_file(path: &str) -> Result<String, MyError> { /* ... */ }
use std::path::Path;
#[enrich_err("failed to read file: {}", path.display())]
fn read_file(path: &Path) -> Result<String, MyError> { /* ... */ }
#[enrich_err("processed {} items with total size {} bytes", items.len(), total_size)]
fn process_items(items: &[String], total_size: usize) -> Result<(), MyError> { /* ... */ }
#[enrich_err("user {user} failed operation with {} items", items.len())]
fn user_operation(user: &str, items: &[String]) -> Result<(), MyError> { /* ... */ }
All patterns include file and line information automatically:
#[ohno::error]
struct MyError;
#[ohno::enrich_err("failed to open file")]
fn open_file(path: &str) -> Result<String, MyError> {
std::fs::read_to_string(path)
.map_err(MyError::caused_by)
}
// Error output will include: "failed to open file (at src/main.rs:42)"
For applications that need a simple, catch-all error type, use AppError. It
automatically captures backtraces and can wrap any error type.
To avoid accidental usage in libraries, AppError is only available when the app-err
feature is enabled.
Example usage:
use ohno::AppError;
fn process() -> Result<(), AppError> {
std::fs::read_to_string("file.txt")?; // Automatically converts errors
Ok(())
}