Expand description
§luhtwin - Horrible Error Handling for Rust
luhtwin provides a ergonomic error handling system that emphasizes
context accumulation, structured diagnostics, and flexible formatting.
Built around the AnyError type, it allows you to wrap any error with rich
metadata and progressively add context as errors bubble up through your application.
§Core Ideas
AnyError— The main error container that wraps anyErrortype with context chainsLuhTwin<T>— Type alias forResult<T, AnyError>, the primary result typeErrorContext— Structured metadata including messages, file/line info, docs, and severityWrap— Wrapping existing Results into LuhTwin.Twin— Transforming existing Results into LuhTwin.Encase— Encase existing LuhTwins in another layer of context.
§Getting Started
Add luhtwin to your Cargo.toml:
[dependencies]
luhtwin = "0.1.4"§Quick Example
ⓘ
use luhtwin::{LuhTwin, Twin, Wrap, Encase, at};
use std::fs;
use std::io;
fn read_config_file(path: &str) -> LuhTwin<String> {
fs::read_to_string(path)
.wrap(|| format!("failed to read config from {}", path))
}
fn parse_config(content: String) -> LuhTwin<Config> {
serde_json::from_str(&content)
.twin()
.encase(|| "failed to parse config as JSON")
}
fn load_config() -> LuhTwin<Config> {
let content = read_config_file("config.json")?;
parse_config(content)
.encase(|| "config loading failed")
}
fn main() -> LuhTwin<()> {
let config = load_config()?;
println!("Config loaded successfully!");
Ok(())
}§Error Output
When an error occurs, luhtwin provides detailed, layered context:
LUHTWIN_FULL=1 to see full errors <3
1: failed to read config from config.json: No such file or directory
2: failed to parse config as JSON
3: config loading failed
source: No such file or directory (os error 2)
backtrace:
disabled backtraceWith LUHTWIN_FULL=1, you get the full context including file/line info:
1: failed to read config from config.json: No such file or directory
- file: "src/main.rs"
- line: 8
2: failed to parse config as JSON
- file: "src/main.rs"
- line: 14
3: config loading failed
- file: "src/main.rs"
- line: 19§Common Patterns
§Converting foreign errors with .twin()
ⓘ
use std::fs::File;
fn open_file() -> LuhTwin<File> {
File::open("data.txt").twin()
}§Wrapping errors with context using .wrap()
ⓘ
fn read_user_data(id: u32) -> LuhTwin<UserData> {
read_from_db(id).wrap(|| format!("failed to load user {}", id))
}§Adding layers with .encase()
ⓘ
fn initialize() -> LuhTwin<()> {
load_config()
.encase(|| "initialization failed")?;
connect_db()
.encase(|| "initialization failed")?;
Ok(())
}§Using the at! macro for rich context
ⓘ
fn validate_input(input: &str) -> LuhTwin<()> {
if input.is_empty() {
return Err(at!("input validation failed")
.attach("input", input)
.attach("expected", "non-empty string")
.into());
}
Ok(())
}§Environment Variables
LUHTWIN_FULL=1- Show full error details with all attached argumentsRUST_BACKTRACE=1- Enable backtrace capture (standard Rust behavior)
Macros§
- at
- Creates an
ErrorContextwith automatic file and line information. - bail
- Early return with an error, similar to
anyhow::bail!. - ensure
- Assert a condition and bail with an error if it’s false.
Structs§
- AnyError
- AnyError
- Arg
- Arg
- Error
Context - ErrorContext
Traits§
Type Aliases§
- BigTwin
- BigTwin
- Error
Source - Just a base trait ease of refactoring <3
- LuhTwin
- LuhTwin