Thiserror Typed Error Enums

Model domain errors as thiserror enums with variant payloads, source chaining, and From conversions for ergonomic propagation.

RustResilience

Code

snippet
use thiserror::Error;

#[derive(Debug, Error)]
pub enum InventoryError {
    #[error("sku {sku} not found in catalog")]
    NotFound { sku: String },

    #[error("insufficient stock: have {available}, requested {requested}")]
    InsufficientStock { available: u32, requested: u32 },

    #[error("warehouse service unreachable")]
    Unreachable(#[from] std::io::Error),

    #[error("serialization of manifest failed")]
    Serialization(#[from] serde_json::Error),

    #[error("reservation expired after {seconds}s")]
    Expired { seconds: u64 },
}

pub type InventoryResult<T> = Result<T, InventoryError>;

fn reserve(sku: &str, requested: u32) -> InventoryResult<u32> {
    let stock: u32 = if sku == "SKU-9" { 2 } else { 120 };

    if sku == "SKU-0" {
        return Err(InventoryError::NotFound { sku: sku.into() });
    }
    if requested > stock {
        return Err(InventoryError::InsufficientStock {
            available: stock,
            requested,
        });
    }
    Ok(stock - requested)
}

fn fetch_manifest(path: &str) -> InventoryResult<String> {
    let raw = std::fs::read_to_string(path)?; // io::Error 自动 From 转换
    let value: serde_json::Value = serde_json::from_str(&raw)?;
    Ok(value.to_string())
}

fn main() {
    match reserve("SKU-9", 5) {
        Ok(left) => println!("reserved, {left} left"),
        Err(err) => println!("reserve failed: {err}"),
    }

    match reserve("SKU-0", 1) {
        Ok(_) => unreachable!(),
        Err(InventoryError::NotFound { sku }) => println!("missing sku: {sku}"),
        Err(other) => println!("other: {other}"),
    }

    println!("{:?}", fetch_manifest("missing.json").map(|s| s.len()));
}