When writing a library, it is best practice to define concrete error types using thiserror and implement the miette::Diagnostic trait. This ensures compatibility with std::error::Error for consumers who do not use miette. You can use #[diagnostic] attributes to add metadata like error codes, URLs, and help text. Use #[diagnostic(transparent)] to wrap another diagnostic without losing its labels, or #[diagnostic(forward(field_name))] to forward the diagnostic to a specific field.
// lib/error.rs
use miette::{Diagnostic, SourceSpan};
use thiserror::Error;
#[derive(Error, Diagnostic, Debug)]
pub enum MyLibError {
#[error(transparent)]
#[diagnostic(code(my_lib::io_error))]
IoError(#[from] std::io::Error),
#[error("Oops it blew up")]
#[diagnostic(code(my_lib::bad_code))]
BadThingHappened,
#[error(transparent)]
// Use `#[diagnostic(transparent)]` to wrap another `Diagnostic`. You won't see labels otherwise
#[diagnostic(transparent)]
AnotherError(#[from] AnotherError),
/// Forward the diagnostic to a particular field.
#[error("other error")]
#[diagnostic(forward(the_actual_diagnostic))]
EvenMoreData {
unrelated_field_1: String,
unrelated_field_2: usize,
#[source]
the_actual_diagnostic: AnotherError,
}
}
#[derive(Error, Diagnostic, Debug)]
#[error("another error")]
pub struct AnotherError {
#[label("here")]
pub at: SourceSpan
}