Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,21 @@ jobs:
- name: Check wasm target
run: cargo check --target wasm32-unknown-unknown --features fancy-no-syscall

no-std:
name: Check no-std build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
targets: wasm32-unknown-unknown
- name: Check no-std core build
run: cargo check --target wasm32-unknown-unknown --no-default-features
- name: Check no-std with fancy-no-syscall
run: cargo check --target wasm32-unknown-unknown --no-default-features --features fancy-no-syscall

miri:
name: Miri
runs-on: ubuntu-latest
Expand Down
16 changes: 9 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ rust-version = "1.82.0"
exclude = ["images/", "tests/", "miette-derive/"]

[dependencies]
thiserror = "2.0.11"
miette-derive = { path = "miette-derive", version = "=7.6.0", optional = true }
unicode-width = "0.2.0"
unicode-width = { version = "0.2.0", default-features = false }
cfg-if = "1.0.0"
spin = { version = "0.9", default-features = false, features = ["mutex", "spin_mutex", "lazy"] }
Copy link
Contributor

@cgettys-microsoft cgettys-microsoft Nov 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't seem like it should be a dependency for std environments. I see the simplicity benefit, but adding a dependency for consumers for whom OnceLock is an option, doesn't seem like it makes sense.
Also, do you need all those features?

Copy link
Author

@huitseeker huitseeker Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spin is optional = true and only activated when not(feature = "std") (see the cfg guards in src/eyreish/mod.rs). Std users don't pull in spin at all. The minimal features (once only) are intentional to keep the dependency small for no_std users who need it.


owo-colors = { version = "4.0.0", optional = true }
textwrap = { version = "0.16.0", default-features = false, features = ["unicode-linebreak", "unicode-width"], optional = true }
Expand All @@ -29,23 +31,23 @@ serde = { version = "1.0.196", features = ["derive"], optional = true }
syntect = { version = "5.1.0", optional = true }

[dev-dependencies]
thiserror = "2.0.11"
semver = "1.0.21"

# Eyre devdeps
futures = { version = "0.3", default-features = false }
indenter = "0.3.3"
rustversion = "1.0"
trybuild = { version = "1.0.89", features = ["diff"] }
syn = { version = "2.0.87", features = ["full"] }
regex = "1.10"

serde = { version = "1.0.196", features = ["derive"] }
serde_json = "1.0.113"
strip-ansi-escapes = "0.2.0"

[build-dependencies]
rustc_version = "0.2"

[features]
default = ["derive"]
default = ["derive", "std"]
std = ["thiserror/std", "fancy-no-syscall"]
derive = ["dep:miette-derive"]
no-format-args-capture = []
fancy-base = [
Expand All @@ -63,7 +65,7 @@ fancy-no-backtrace = [
"dep:supports-unicode",
]
fancy = ["fancy-no-backtrace", "dep:backtrace", "dep:backtrace-ext"]
syntect-highlighter = ["fancy-no-backtrace", "dep:syntect"]
syntect-highlighter = ["fancy-no-backtrace", "dep:syntect", "std"]

[workspace]
members = ["miette-derive"]
Expand Down
17 changes: 15 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ diagnostic error code: ruget::api::bad_json

### Features

- Generic [`Diagnostic`] protocol, compatible (and dependent on)
[`std::error::Error`].
- Generic [`Diagnostic`] protocol, compatible with `std::error::Error`.
Works without the standard library: Just turn off the default `std` feature and you can use `miette` in places like embedded systems or web browsers that don't have the full standard library. You still need `alloc` for memory management.
- Unique error codes on every [`Diagnostic`].
- Custom links to get more details on error codes.
- Super handy derive macro for defining diagnostic metadata.
Expand Down Expand Up @@ -93,6 +93,19 @@ If you want to use the fancy printer in all these screenshots:
$ cargo add miette --features fancy
```

For computers without the standard library (like microcontrollers or web browsers):

```sh
$ cargo add miette --no-default-features --features derive
```

Available features you can turn on or off:
- `std` (on by default): Use the standard library
- `derive`: Lets you automatically create error types
- `fancy`: Shows pretty error messages with colors
- `fancy-no-syscall`: Pretty errors without using system calls
- `fancy-no-backtrace`: Pretty errors without showing the call stack

### Example

```rust
Expand Down
15 changes: 15 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use rustc_version::{version_meta, Channel};

fn main() {
if let Channel::Nightly = version_meta().unwrap().channel {
println!("cargo:rustc-cfg=nightly")
}

// track_caller is stable since Rust 1.46 (2020), so no version check needed
println!("cargo:rustc-cfg=track_caller");

// Add check-cfg for conditional configurations
println!("cargo:rustc-check-cfg=cfg(doc_cfg)");
println!("cargo:rustc-check-cfg=cfg(track_caller)");
println!("cargo:rustc-check-cfg=cfg(nightly)");
}
2 changes: 2 additions & 0 deletions examples/serde_json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
//! so the decoding source will be annotated with the decoding error,
//! providing contextual information about the error.

extern crate alloc;

use miette::{IntoDiagnostic, SourceOffset};
use serde_json::{self, json};

Expand Down
10 changes: 5 additions & 5 deletions miette-derive/src/code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,13 @@ impl Code {
let code = &code.as_ref()?.0;
Some(match fields {
syn::Fields::Named(_) => {
quote! { Self::#ident { .. } => std::option::Option::Some(std::boxed::Box::new(#code)), }
quote! { Self::#ident { .. } => core::option::Option::Some(alloc::boxed::Box::new(#code)), }
}
syn::Fields::Unnamed(_) => {
quote! { Self::#ident(..) => std::option::Option::Some(std::boxed::Box::new(#code)), }
quote! { Self::#ident(..) => core::option::Option::Some(alloc::boxed::Box::new(#code)), }
}
syn::Fields::Unit => {
quote! { Self::#ident => std::option::Option::Some(std::boxed::Box::new(#code)), }
quote! { Self::#ident => core::option::Option::Some(alloc::boxed::Box::new(#code)), }
}
})
},
Expand All @@ -72,8 +72,8 @@ impl Code {
pub(crate) fn gen_struct(&self) -> Option<TokenStream> {
let code = &self.0;
Some(quote! {
fn code(&self) -> std::option::Option<std::boxed::Box<dyn std::fmt::Display + '_>> {
std::option::Option::Some(std::boxed::Box::new(#code))
fn code(&self) -> core::option::Option<alloc::boxed::Box<dyn core::fmt::Display + '_>> {
core::option::Option::Some(alloc::boxed::Box::new(#code))
}
})
}
Expand Down
6 changes: 3 additions & 3 deletions miette-derive/src/diagnostic_source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ impl DiagnosticSource {
};
quote! {
Self::#ident #display_pat => {
std::option::Option::Some(std::borrow::Borrow::borrow(#rel))
core::option::Option::Some(alloc::borrow::Borrow::borrow(#rel))
}
}
})
Expand All @@ -70,8 +70,8 @@ impl DiagnosticSource {
pub(crate) fn gen_struct(&self) -> Option<TokenStream> {
let rel = &self.0;
Some(quote! {
fn diagnostic_source<'a>(&'a self) -> std::option::Option<&'a dyn miette::Diagnostic> {
std::option::Option::Some(std::borrow::Borrow::borrow(&self.#rel))
fn diagnostic_source<'a>(&'a self) -> core::option::Option<&'a dyn miette::Diagnostic> {
core::option::Option::Some(alloc::borrow::Borrow::borrow(&self.#rel))
}
})
}
Expand Down
18 changes: 9 additions & 9 deletions miette-derive/src/forward.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,34 +58,34 @@ impl WhichFn {
pub fn signature(&self) -> TokenStream {
match self {
Self::Code => quote! {
fn code(& self) -> std::option::Option<std::boxed::Box<dyn std::fmt::Display + '_>>
fn code(& self) -> Option<alloc::boxed::Box<dyn core::fmt::Display + '_>>
},
Self::Help => quote! {
fn help(& self) -> std::option::Option<std::boxed::Box<dyn std::fmt::Display + '_>>
fn help(& self) -> Option<alloc::boxed::Box<dyn core::fmt::Display + '_>>
},
Self::Url => quote! {
fn url(& self) -> std::option::Option<std::boxed::Box<dyn std::fmt::Display + '_>>
fn url(& self) -> Option<alloc::boxed::Box<dyn core::fmt::Display + '_>>
},
Self::Severity => quote! {
fn severity(&self) -> std::option::Option<miette::Severity>
fn severity(&self) -> Option<miette::Severity>
},
Self::Related => quote! {
fn related(&self) -> std::option::Option<std::boxed::Box<dyn std::iter::Iterator<Item = &dyn miette::Diagnostic> + '_>>
fn related(&self) -> Option<alloc::boxed::Box<dyn Iterator<Item = &dyn miette::Diagnostic> + '_>>
},
Self::Labels => quote! {
fn labels(&self) -> std::option::Option<std::boxed::Box<dyn std::iter::Iterator<Item = miette::LabeledSpan> + '_>>
fn labels(&self) -> Option<alloc::boxed::Box<dyn Iterator<Item = miette::LabeledSpan> + '_>>
},
Self::SourceCode => quote! {
fn source_code(&self) -> std::option::Option<&dyn miette::SourceCode>
fn source_code(&self) -> Option<&dyn miette::SourceCode>
},
Self::DiagnosticSource => quote! {
fn diagnostic_source(&self) -> std::option::Option<&dyn miette::Diagnostic>
fn diagnostic_source(&self) -> Option<&dyn miette::Diagnostic>
},
}
}

pub fn catchall_arm(&self) -> TokenStream {
quote! { _ => std::option::Option::None }
quote! { _ => Option::None }
}
}

Expand Down
12 changes: 6 additions & 6 deletions miette-derive/src/help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ impl Help {
Help::Display(display) => {
let (fmt, args) = display.expand_shorthand_cloned(&display_members);
Some(quote! {
Self::#ident #display_pat => std::option::Option::Some(std::boxed::Box::new(format!(#fmt #args))),
Self::#ident #display_pat => Option::Some(alloc::boxed::Box::new(format!(#fmt #args))),
})
}
Help::Field(member, ty) => {
Expand All @@ -108,7 +108,7 @@ impl Help {
Some(quote! {
Self::#ident #display_pat => {
use miette::macro_helpers::ToOption;
miette::macro_helpers::OptionalWrapper::<#ty>::new().to_option(&#help).as_ref().map(|#var| -> std::boxed::Box<dyn std::fmt::Display + '_> { std::boxed::Box::new(format!("{}", #var)) })
miette::macro_helpers::OptionalWrapper::<#ty>::new().to_option(&#help).as_ref().map(|#var| -> alloc::boxed::Box<dyn core::fmt::Display + '_> { alloc::boxed::Box::new(format!("{}", #var)) })
},
})
}
Expand All @@ -123,21 +123,21 @@ impl Help {
Help::Display(display) => {
let (fmt, args) = display.expand_shorthand_cloned(&display_members);
Some(quote! {
fn help(&self) -> std::option::Option<std::boxed::Box<dyn std::fmt::Display + '_>> {
fn help(&self) -> Option<alloc::boxed::Box<dyn core::fmt::Display + '_>> {
#[allow(unused_variables, deprecated)]
let Self #display_pat = self;
std::option::Option::Some(std::boxed::Box::new(format!(#fmt #args)))
Option::Some(alloc::boxed::Box::new(format!(#fmt #args)))
}
})
}
Help::Field(member, ty) => {
let var = quote! { __miette_internal_var };
Some(quote! {
fn help(&self) -> std::option::Option<std::boxed::Box<dyn std::fmt::Display + '_>> {
fn help(&self) -> Option<alloc::boxed::Box<dyn core::fmt::Display + '_>> {
#[allow(unused_variables, deprecated)]
let Self #display_pat = self;
use miette::macro_helpers::ToOption;
miette::macro_helpers::OptionalWrapper::<#ty>::new().to_option(&self.#member).as_ref().map(|#var| -> std::boxed::Box<dyn std::fmt::Display + '_> { std::boxed::Box::new(format!("{}", #var)) })
miette::macro_helpers::OptionalWrapper::<#ty>::new().to_option(&self.#member).as_ref().map(|#var| -> alloc::boxed::Box<dyn core::fmt::Display + '_> { alloc::boxed::Box::new(format!("{}", #var)) })
}
})
}
Expand Down
14 changes: 7 additions & 7 deletions miette-derive/src/label.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,9 +175,9 @@ impl Labels {
let var = quote! { __miette_internal_var };
let display = if let Some(display) = label {
let (fmt, args) = display.expand_shorthand_cloned(&display_members);
quote! { std::option::Option::Some(format!(#fmt #args)) }
quote! { Option::Some(format!(#fmt #args)) }
} else {
quote! { std::option::Option::None }
quote! { Option::None }
};
let ctor = if *lbl_ty == LabelType::Primary {
quote! { miette::LabeledSpan::new_primary_with_span }
Expand Down Expand Up @@ -205,9 +205,9 @@ impl Labels {
}
let display = if let Some(display) = label {
let (fmt, args) = display.expand_shorthand_cloned(&display_members);
quote! { std::option::Option::Some(format!(#fmt #args)) }
quote! { Option::Some(format!(#fmt #args)) }
} else {
quote! { std::option::Option::None }
quote! { Option::None }
};
Some(quote! {
.chain({
Expand All @@ -226,7 +226,7 @@ impl Labels {

Some(quote! {
#[allow(unused_variables)]
fn labels(&self) -> std::option::Option<std::boxed::Box<dyn std::iter::Iterator<Item = miette::LabeledSpan> + '_>> {
fn labels(&self) -> Option<alloc::boxed::Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
use miette::macro_helpers::ToOption;
let Self #display_pat = self;

Expand All @@ -236,7 +236,7 @@ impl Labels {
.into_iter()
#(#collections_chain)*;

std::option::Option::Some(Box::new(labels_iter.filter(Option::is_some).map(Option::unwrap)))
Option::Some(alloc::boxed::Box::new(labels_iter.filter(Option::is_some).map(Option::unwrap)))
}
})
}
Expand Down Expand Up @@ -322,7 +322,7 @@ impl Labels {
]
.into_iter()
#(#collections_chain)*;
std::option::Option::Some(std::boxed::Box::new(labels_iter.filter(Option::is_some).map(Option::unwrap)))
Option::Some(alloc::boxed::Box::new(labels_iter.filter(Option::is_some).map(Option::unwrap)))
}
}),
}
Expand Down
8 changes: 4 additions & 4 deletions miette-derive/src/related.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ impl Related {
};
quote! {
Self::#ident #display_pat => {
std::option::Option::Some(std::boxed::Box::new(
Option::Some(alloc::boxed::Box::new(
#rel.iter().map(|x| -> &(dyn miette::Diagnostic) { &*x })
))
}
Expand All @@ -68,9 +68,9 @@ impl Related {
pub(crate) fn gen_struct(&self) -> Option<TokenStream> {
let rel = &self.0;
Some(quote! {
fn related<'a>(&'a self) -> std::option::Option<std::boxed::Box<dyn std::iter::Iterator<Item = &'a dyn miette::Diagnostic> + 'a>> {
use ::core::borrow::Borrow;
std::option::Option::Some(std::boxed::Box::new(
fn related<'a>(&'a self) -> Option<alloc::boxed::Box<dyn Iterator<Item = &'a dyn miette::Diagnostic> + 'a>> {
use alloc::borrow::Borrow;
Option::Some(alloc::boxed::Box::new(
self.#rel.iter().map(|x| -> &(dyn miette::Diagnostic) { &*x.borrow() })
))
}
Expand Down
4 changes: 1 addition & 3 deletions miette-derive/src/severity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,7 @@ impl Severity {
syn::Fields::Unnamed(_) => quote! { (..) },
syn::Fields::Unit => quote! {},
};
Some(
quote! { Self::#ident #fields => std::option::Option::Some(miette::Severity::#severity), },
)
Some(quote! { Self::#ident #fields => Option::Some(miette::Severity::#severity), })
},
)
}
Expand Down
4 changes: 2 additions & 2 deletions miette-derive/src/source_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ impl SourceCode {

Some(quote! {
#[allow(unused_variables)]
fn source_code(&self) -> std::option::Option<&dyn miette::SourceCode> {
fn source_code(&self) -> Option<&dyn miette::SourceCode> {
let Self #display_pat = self;
#ret
}
Expand All @@ -101,7 +101,7 @@ impl SourceCode {
}
} else {
quote! {
std::option::Option::Some(#field)
Option::Some(#field)
}
};
match &fields {
Expand Down
6 changes: 3 additions & 3 deletions miette-derive/src/url.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ impl Url {
}
};
Some(quote! {
Self::#ident #pat => std::option::Option::Some(std::boxed::Box::new(format!(#fmt #args))),
Self::#ident #pat => Option::Some(alloc::boxed::Box::new(format!(#fmt #args))),
})
},
)
Expand Down Expand Up @@ -129,10 +129,10 @@ impl Url {
}
};
Some(quote! {
fn url(&self) -> std::option::Option<std::boxed::Box<dyn std::fmt::Display + '_>> {
fn url(&self) -> Option<alloc::boxed::Box<dyn core::fmt::Display + '_>> {
#[allow(unused_variables, deprecated)]
let Self #pat = self;
std::option::Option::Some(std::boxed::Box::new(format!(#fmt #args)))
Option::Some(alloc::boxed::Box::new(format!(#fmt #args)))
}
})
}
Expand Down
Loading