This commit is contained in:
François Garillot 2026-06-03 13:58:11 +09:00 committed by GitHub
commit f834b05a26
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
45 changed files with 562 additions and 258 deletions

View File

@ -68,6 +68,21 @@ jobs:
- name: Check wasm target - name: Check wasm target
run: cargo check --target wasm32-unknown-unknown --features fancy-no-syscall 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: miri:
name: Miri name: Miri
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -84,6 +99,20 @@ jobs:
MIRIFLAGS: -Zmiri-disable-isolation -Zmiri-strict-provenance MIRIFLAGS: -Zmiri-disable-isolation -Zmiri-strict-provenance
run: cargo miri test --all --verbose --features fancy run: cargo miri test --all --verbose --features fancy
feature-check:
name: Feature combinations check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
- name: Install cargo-hack
uses: taiki-e/install-action@cargo-hack
- name: Check feature powerset
run: cargo hack check --feature-powerset --no-dev-deps --skip default
minimal_versions: minimal_versions:
name: Minimal versions check name: Minimal versions check
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}

View File

@ -16,6 +16,7 @@ exclude = ["images/", "tests/", "miette-derive/"]
miette-derive = { path = "miette-derive", version = "=7.6.0", optional = true } miette-derive = { path = "miette-derive", version = "=7.6.0", optional = true }
unicode-width = "0.2.0" unicode-width = "0.2.0"
cfg-if = "1.0.0" cfg-if = "1.0.0"
spin = { version = "0.9", default-features = false, features = ["once"] }
owo-colors = { version = "4.0.0", optional = true } owo-colors = { version = "4.0.0", optional = true }
textwrap = { version = "0.16.0", default-features = false, features = ["unicode-linebreak", "unicode-width"], optional = true } textwrap = { version = "0.16.0", default-features = false, features = ["unicode-linebreak", "unicode-width"], optional = true }
@ -31,21 +32,20 @@ syntect = { version = "5.1.0", optional = true }
[dev-dependencies] [dev-dependencies]
thiserror = "2.0.11" thiserror = "2.0.11"
semver = "1.0.21" semver = "1.0.21"
# Eyre devdeps
futures = { version = "0.3", default-features = false } futures = { version = "0.3", default-features = false }
indenter = "0.3.3" indenter = "0.3.3"
rustversion = "1.0" rustversion = "1.0"
trybuild = { version = "1.0.89", features = ["diff"] } trybuild = { version = "1.0.89", features = ["diff"] }
syn = { version = "2.0.87", features = ["full"] } syn = { version = "2.0.87", features = ["full"] }
regex = "1.10" regex = "1.10"
serde = { version = "1.0.196", features = ["derive"] } serde = { version = "1.0.196", features = ["derive"] }
serde_json = "1.0.113" serde_json = "1.0.113"
strip-ansi-escapes = "0.2.0" strip-ansi-escapes = "0.2.0"
[features] [features]
default = ["derive"] default = ["derive", "std"]
std = []
derive = ["dep:miette-derive"] derive = ["dep:miette-derive"]
no-format-args-capture = [] no-format-args-capture = []
fancy-base = [ fancy-base = [
@ -57,6 +57,7 @@ fancy-no-syscall = [
] ]
fancy-no-backtrace = [ fancy-no-backtrace = [
"fancy-base", "fancy-base",
"std",
"dep:terminal_size", "dep:terminal_size",
"dep:supports-hyperlinks", "dep:supports-hyperlinks",
"dep:supports-color", "dep:supports-color",

View File

@ -58,8 +58,8 @@ diagnostic error code: ruget::api::bad_json
### Features ### Features
- Generic [`Diagnostic`] protocol, compatible (and dependent on) - Generic [`Diagnostic`] protocol, compatible with `std::error::Error`.
[`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`]. - Unique error codes on every [`Diagnostic`].
- Custom links to get more details on error codes. - Custom links to get more details on error codes.
- Super handy derive macro for defining diagnostic metadata. - Super handy derive macro for defining diagnostic metadata.
@ -93,6 +93,19 @@ If you want to use the fancy printer in all these screenshots:
$ cargo add miette --features fancy $ 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 ### Example
```rust ```rust

View File

@ -2,6 +2,8 @@
//! so the decoding source will be annotated with the decoding error, //! so the decoding source will be annotated with the decoding error,
//! providing contextual information about the error. //! providing contextual information about the error.
extern crate alloc;
use miette::{IntoDiagnostic, SourceOffset}; use miette::{IntoDiagnostic, SourceOffset};
use serde_json::{self, json}; use serde_json::{self, json};

View File

@ -56,13 +56,13 @@ impl Code {
let code = &code.as_ref()?.0; let code = &code.as_ref()?.0;
Some(match fields { Some(match fields {
syn::Fields::Named(_) => { syn::Fields::Named(_) => {
quote! { Self::#ident { .. } => std::option::Option::Some(std::boxed::Box::new(#code)), } quote! { Self::#ident { .. } => core::option::Option::Some(miette::__alloc::Box::new(#code)), }
} }
syn::Fields::Unnamed(_) => { syn::Fields::Unnamed(_) => {
quote! { Self::#ident(..) => std::option::Option::Some(std::boxed::Box::new(#code)), } quote! { Self::#ident(..) => core::option::Option::Some(miette::__alloc::Box::new(#code)), }
} }
syn::Fields::Unit => { syn::Fields::Unit => {
quote! { Self::#ident => std::option::Option::Some(std::boxed::Box::new(#code)), } quote! { Self::#ident => core::option::Option::Some(miette::__alloc::Box::new(#code)), }
} }
}) })
}, },
@ -72,8 +72,8 @@ impl Code {
pub(crate) fn gen_struct(&self) -> Option<TokenStream> { pub(crate) fn gen_struct(&self) -> Option<TokenStream> {
let code = &self.0; let code = &self.0;
Some(quote! { Some(quote! {
fn code(&self) -> std::option::Option<std::boxed::Box<dyn std::fmt::Display + '_>> { fn code(&self) -> core::option::Option<miette::__alloc::Box<dyn core::fmt::Display + '_>> {
std::option::Option::Some(std::boxed::Box::new(#code)) core::option::Option::Some(miette::__alloc::Box::new(#code))
} }
}) })
} }

View File

@ -59,7 +59,7 @@ impl DiagnosticSource {
}; };
quote! { quote! {
Self::#ident #display_pat => { Self::#ident #display_pat => {
std::option::Option::Some(std::borrow::Borrow::borrow(#rel)) core::option::Option::Some(miette::__alloc::borrow::Borrow::borrow(#rel))
} }
} }
}) })
@ -70,8 +70,8 @@ impl DiagnosticSource {
pub(crate) fn gen_struct(&self) -> Option<TokenStream> { pub(crate) fn gen_struct(&self) -> Option<TokenStream> {
let rel = &self.0; let rel = &self.0;
Some(quote! { Some(quote! {
fn diagnostic_source<'a>(&'a self) -> std::option::Option<&'a dyn miette::Diagnostic> { fn diagnostic_source<'a>(&'a self) -> core::option::Option<&'a dyn miette::Diagnostic> {
std::option::Option::Some(std::borrow::Borrow::borrow(&self.#rel)) core::option::Option::Some(miette::__alloc::borrow::Borrow::borrow(&self.#rel))
} }
}) })
} }

View File

@ -58,34 +58,34 @@ impl WhichFn {
pub fn signature(&self) -> TokenStream { pub fn signature(&self) -> TokenStream {
match self { match self {
Self::Code => quote! { Self::Code => quote! {
fn code(& self) -> std::option::Option<std::boxed::Box<dyn std::fmt::Display + '_>> fn code(& self) -> Option<miette::__alloc::Box<dyn core::fmt::Display + '_>>
}, },
Self::Help => quote! { Self::Help => quote! {
fn help(& self) -> std::option::Option<std::boxed::Box<dyn std::fmt::Display + '_>> fn help(& self) -> Option<miette::__alloc::Box<dyn core::fmt::Display + '_>>
}, },
Self::Url => quote! { Self::Url => quote! {
fn url(& self) -> std::option::Option<std::boxed::Box<dyn std::fmt::Display + '_>> fn url(& self) -> Option<miette::__alloc::Box<dyn core::fmt::Display + '_>>
}, },
Self::Severity => quote! { Self::Severity => quote! {
fn severity(&self) -> std::option::Option<miette::Severity> fn severity(&self) -> Option<miette::Severity>
}, },
Self::Related => quote! { Self::Related => quote! {
fn related(&self) -> std::option::Option<std::boxed::Box<dyn std::iter::Iterator<Item = &dyn miette::Diagnostic> + '_>> fn related(&self) -> Option<miette::__alloc::Box<dyn Iterator<Item = &dyn miette::Diagnostic> + '_>>
}, },
Self::Labels => quote! { Self::Labels => quote! {
fn labels(&self) -> std::option::Option<std::boxed::Box<dyn std::iter::Iterator<Item = miette::LabeledSpan> + '_>> fn labels(&self) -> Option<miette::__alloc::Box<dyn Iterator<Item = miette::LabeledSpan> + '_>>
}, },
Self::SourceCode => quote! { Self::SourceCode => quote! {
fn source_code(&self) -> std::option::Option<&dyn miette::SourceCode> fn source_code(&self) -> Option<&dyn miette::SourceCode>
}, },
Self::DiagnosticSource => quote! { 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 { pub fn catchall_arm(&self) -> TokenStream {
quote! { _ => std::option::Option::None } quote! { _ => Option::None }
} }
} }

View File

@ -94,7 +94,7 @@ impl Help {
Help::Display(display) => { Help::Display(display) => {
let (fmt, args) = display.expand_shorthand_cloned(&display_members); let (fmt, args) = display.expand_shorthand_cloned(&display_members);
Some(quote! { Some(quote! {
Self::#ident #display_pat => std::option::Option::Some(std::boxed::Box::new(format!(#fmt #args))), Self::#ident #display_pat => Option::Some(miette::__alloc::Box::new(format!(#fmt #args))),
}) })
} }
Help::Field(member, ty) => { Help::Field(member, ty) => {
@ -108,7 +108,7 @@ impl Help {
Some(quote! { Some(quote! {
Self::#ident #display_pat => { Self::#ident #display_pat => {
use miette::macro_helpers::ToOption; 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| -> miette::__alloc::Box<dyn core::fmt::Display + '_> { miette::__alloc::Box::new(format!("{}", #var)) })
}, },
}) })
} }
@ -123,21 +123,21 @@ impl Help {
Help::Display(display) => { Help::Display(display) => {
let (fmt, args) = display.expand_shorthand_cloned(&display_members); let (fmt, args) = display.expand_shorthand_cloned(&display_members);
Some(quote! { Some(quote! {
fn help(&self) -> std::option::Option<std::boxed::Box<dyn std::fmt::Display + '_>> { fn help(&self) -> Option<miette::__alloc::Box<dyn core::fmt::Display + '_>> {
#[allow(unused_variables, deprecated)] #[allow(unused_variables, deprecated)]
let Self #display_pat = self; let Self #display_pat = self;
std::option::Option::Some(std::boxed::Box::new(format!(#fmt #args))) Option::Some(miette::__alloc::Box::new(format!(#fmt #args)))
} }
}) })
} }
Help::Field(member, ty) => { Help::Field(member, ty) => {
let var = quote! { __miette_internal_var }; let var = quote! { __miette_internal_var };
Some(quote! { Some(quote! {
fn help(&self) -> std::option::Option<std::boxed::Box<dyn std::fmt::Display + '_>> { fn help(&self) -> Option<miette::__alloc::Box<dyn core::fmt::Display + '_>> {
#[allow(unused_variables, deprecated)] #[allow(unused_variables, deprecated)]
let Self #display_pat = self; let Self #display_pat = self;
use miette::macro_helpers::ToOption; 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| -> miette::__alloc::Box<dyn core::fmt::Display + '_> { miette::__alloc::Box::new(format!("{}", #var)) })
} }
}) })
} }

View File

@ -175,9 +175,9 @@ impl Labels {
let var = quote! { __miette_internal_var }; let var = quote! { __miette_internal_var };
let display = if let Some(display) = label { let display = if let Some(display) = label {
let (fmt, args) = display.expand_shorthand_cloned(&display_members); let (fmt, args) = display.expand_shorthand_cloned(&display_members);
quote! { std::option::Option::Some(format!(#fmt #args)) } quote! { Option::Some(format!(#fmt #args)) }
} else { } else {
quote! { std::option::Option::None } quote! { Option::None }
}; };
let ctor = if *lbl_ty == LabelType::Primary { let ctor = if *lbl_ty == LabelType::Primary {
quote! { miette::LabeledSpan::new_primary_with_span } quote! { miette::LabeledSpan::new_primary_with_span }
@ -205,9 +205,9 @@ impl Labels {
} }
let display = if let Some(display) = label { let display = if let Some(display) = label {
let (fmt, args) = display.expand_shorthand_cloned(&display_members); let (fmt, args) = display.expand_shorthand_cloned(&display_members);
quote! { std::option::Option::Some(format!(#fmt #args)) } quote! { Option::Some(format!(#fmt #args)) }
} else { } else {
quote! { std::option::Option::None } quote! { Option::None }
}; };
Some(quote! { Some(quote! {
.chain({ .chain({
@ -226,7 +226,7 @@ impl Labels {
Some(quote! { Some(quote! {
#[allow(unused_variables)] #[allow(unused_variables)]
fn labels(&self) -> std::option::Option<std::boxed::Box<dyn std::iter::Iterator<Item = miette::LabeledSpan> + '_>> { fn labels(&self) -> Option<miette::__alloc::Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
use miette::macro_helpers::ToOption; use miette::macro_helpers::ToOption;
let Self #display_pat = self; let Self #display_pat = self;
@ -236,7 +236,7 @@ impl Labels {
.into_iter() .into_iter()
#(#collections_chain)*; #(#collections_chain)*;
std::option::Option::Some(Box::new(labels_iter.filter(Option::is_some).map(Option::unwrap))) Option::Some(miette::__alloc::Box::new(labels_iter.filter(Option::is_some).map(Option::unwrap)))
} }
}) })
} }
@ -322,7 +322,7 @@ impl Labels {
] ]
.into_iter() .into_iter()
#(#collections_chain)*; #(#collections_chain)*;
std::option::Option::Some(std::boxed::Box::new(labels_iter.filter(Option::is_some).map(Option::unwrap))) Option::Some(miette::__alloc::Box::new(labels_iter.filter(Option::is_some).map(Option::unwrap)))
} }
}), }),
} }

View File

@ -55,7 +55,7 @@ impl Related {
}; };
quote! { quote! {
Self::#ident #display_pat => { Self::#ident #display_pat => {
std::option::Option::Some(std::boxed::Box::new( Option::Some(miette::__alloc::Box::new(
#rel.iter().map(|x| -> &(dyn miette::Diagnostic) { &*x }) #rel.iter().map(|x| -> &(dyn miette::Diagnostic) { &*x })
)) ))
} }
@ -68,9 +68,9 @@ impl Related {
pub(crate) fn gen_struct(&self) -> Option<TokenStream> { pub(crate) fn gen_struct(&self) -> Option<TokenStream> {
let rel = &self.0; let rel = &self.0;
Some(quote! { Some(quote! {
fn related<'a>(&'a self) -> std::option::Option<std::boxed::Box<dyn std::iter::Iterator<Item = &'a dyn miette::Diagnostic> + 'a>> { fn related<'a>(&'a self) -> Option<miette::__alloc::Box<dyn Iterator<Item = &'a dyn miette::Diagnostic> + 'a>> {
use ::core::borrow::Borrow; use miette::__alloc::borrow::Borrow;
std::option::Option::Some(std::boxed::Box::new( Option::Some(miette::__alloc::Box::new(
self.#rel.iter().map(|x| -> &(dyn miette::Diagnostic) { &*x.borrow() }) self.#rel.iter().map(|x| -> &(dyn miette::Diagnostic) { &*x.borrow() })
)) ))
} }

View File

@ -71,9 +71,7 @@ impl Severity {
syn::Fields::Unnamed(_) => quote! { (..) }, syn::Fields::Unnamed(_) => quote! { (..) },
syn::Fields::Unit => quote! {}, syn::Fields::Unit => quote! {},
}; };
Some( Some(quote! { Self::#ident #fields => Option::Some(miette::Severity::#severity), })
quote! { Self::#ident #fields => std::option::Option::Some(miette::Severity::#severity), },
)
}, },
) )
} }

View File

@ -74,7 +74,7 @@ impl SourceCode {
Some(quote! { Some(quote! {
#[allow(unused_variables)] #[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; let Self #display_pat = self;
#ret #ret
} }
@ -101,7 +101,7 @@ impl SourceCode {
} }
} else { } else {
quote! { quote! {
std::option::Option::Some(#field) Option::Some(#field)
} }
}; };
match &fields { match &fields {

View File

@ -96,7 +96,7 @@ impl Url {
} }
}; };
Some(quote! { Some(quote! {
Self::#ident #pat => std::option::Option::Some(std::boxed::Box::new(format!(#fmt #args))), Self::#ident #pat => Option::Some(miette::__alloc::Box::new(format!(#fmt #args))),
}) })
}, },
) )
@ -129,10 +129,10 @@ impl Url {
} }
}; };
Some(quote! { Some(quote! {
fn url(&self) -> std::option::Option<std::boxed::Box<dyn std::fmt::Display + '_>> { fn url(&self) -> Option<miette::__alloc::Box<dyn core::fmt::Display + '_>> {
#[allow(unused_variables, deprecated)] #[allow(unused_variables, deprecated)]
let Self #pat = self; let Self #pat = self;
std::option::Option::Some(std::boxed::Box::new(format!(#fmt #args))) Option::Some(miette::__alloc::Box::new(format!(#fmt #args)))
} }
}) })
} }

View File

@ -3,8 +3,10 @@ Iterate over error `.source()` chains.
NOTE: This module is taken wholesale from <https://crates.io/crates/eyre>. NOTE: This module is taken wholesale from <https://crates.io/crates/eyre>.
*/ */
use std::error::Error as StdError; extern crate alloc;
use std::vec;
use crate::StdError;
use alloc::vec::{self, Vec};
use ChainState::*; use ChainState::*;

View File

@ -18,7 +18,7 @@ impl<'a> DiagnosticChain<'a> {
} }
} }
pub(crate) fn from_stderror(head: &'a (dyn std::error::Error + 'static)) -> Self { pub(crate) fn from_stderror(head: &'a (dyn crate::StdError + 'static)) -> Self {
DiagnosticChain { DiagnosticChain {
state: Some(ErrorKind::StdError(head)), state: Some(ErrorKind::StdError(head)),
} }
@ -59,7 +59,7 @@ impl ExactSizeIterator for DiagnosticChain<'_> {
#[derive(Clone)] #[derive(Clone)]
pub(crate) enum ErrorKind<'a> { pub(crate) enum ErrorKind<'a> {
Diagnostic(&'a dyn Diagnostic), Diagnostic(&'a dyn Diagnostic),
StdError(&'a (dyn std::error::Error + 'static)), StdError(&'a (dyn crate::StdError + 'static)),
} }
impl<'a> ErrorKind<'a> { impl<'a> ErrorKind<'a> {
@ -74,8 +74,8 @@ impl<'a> ErrorKind<'a> {
} }
} }
impl std::fmt::Debug for ErrorKind<'_> { impl core::fmt::Debug for ErrorKind<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self { match self {
ErrorKind::Diagnostic(d) => d.fmt(f), ErrorKind::Diagnostic(d) => d.fmt(f),
ErrorKind::StdError(e) => e.fmt(f), ErrorKind::StdError(e) => e.fmt(f),
@ -83,8 +83,8 @@ impl std::fmt::Debug for ErrorKind<'_> {
} }
} }
impl std::fmt::Display for ErrorKind<'_> { impl core::fmt::Display for ErrorKind<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self { match self {
ErrorKind::Diagnostic(d) => d.fmt(f), ErrorKind::Diagnostic(d) => d.fmt(f),
ErrorKind::StdError(e) => e.fmt(f), ErrorKind::StdError(e) => e.fmt(f),

View File

@ -1,11 +1,16 @@
/*! /*!
Default trait implementations for [`Diagnostic`]. Default trait implementations for [`Diagnostic`].
*/ */
extern crate alloc;
use std::{convert::Infallible, fmt::Display}; use alloc::boxed::Box;
use core::{convert::Infallible, fmt::Display};
use crate::{Diagnostic, LabeledSpan, Severity, SourceCode}; use crate::{Diagnostic, LabeledSpan, Severity, SourceCode};
// Note: Infallible implements core::error::Error since Rust 1.81,
// so we don't need to provide our own StdError impl.
impl Diagnostic for Infallible { impl Diagnostic for Infallible {
fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> { fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
match *self {} match *self {}

View File

@ -1,8 +1,10 @@
use std::{ extern crate alloc;
error::Error,
fmt::{self, Display}, use alloc::boxed::Box;
io, use core::error::Error;
}; use core::fmt::{self, Display};
#[cfg(feature = "std")]
use std::io;
use crate::Diagnostic; use crate::Diagnostic;
@ -11,8 +13,9 @@ Error enum for miette. Used by certain operations in the protocol.
*/ */
#[derive(Debug)] #[derive(Debug)]
pub enum MietteError { pub enum MietteError {
/// Wrapper around [`std::io::Error`]. This is returned when something went /// Wrapper around [`io::Error`]. This is returned when something went
/// wrong while reading a [`SourceCode`](crate::SourceCode). /// wrong while reading a [`SourceCode`](crate::SourceCode).
#[cfg(feature = "std")]
IoError(io::Error), IoError(io::Error),
/// Returned when a [`SourceSpan`](crate::SourceSpan) extends beyond the /// Returned when a [`SourceSpan`](crate::SourceSpan) extends beyond the
@ -23,6 +26,7 @@ pub enum MietteError {
impl Display for MietteError { impl Display for MietteError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
#[cfg(feature = "std")]
MietteError::IoError(error) => write!(f, "{error}"), MietteError::IoError(error) => write!(f, "{error}"),
MietteError::OutOfBounds => { MietteError::OutOfBounds => {
write!(f, "The given offset is outside the bounds of its Source") write!(f, "The given offset is outside the bounds of its Source")
@ -34,12 +38,14 @@ impl Display for MietteError {
impl Error for MietteError { impl Error for MietteError {
fn source(&self) -> Option<&(dyn Error + 'static)> { fn source(&self) -> Option<&(dyn Error + 'static)> {
match self { match self {
#[cfg(feature = "std")]
MietteError::IoError(error) => error.source(), MietteError::IoError(error) => error.source(),
MietteError::OutOfBounds => None, MietteError::OutOfBounds => None,
} }
} }
} }
#[cfg(feature = "std")]
impl From<io::Error> for MietteError { impl From<io::Error> for MietteError {
fn from(value: io::Error) -> Self { fn from(value: io::Error) -> Self {
Self::IoError(value) Self::IoError(value)
@ -49,6 +55,7 @@ impl From<io::Error> for MietteError {
impl Diagnostic for MietteError { impl Diagnostic for MietteError {
fn code<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> { fn code<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
match self { match self {
#[cfg(feature = "std")]
MietteError::IoError(_) => Some(Box::new("miette::io_error")), MietteError::IoError(_) => Some(Box::new("miette::io_error")),
MietteError::OutOfBounds => Some(Box::new("miette::span_out_of_bounds")), MietteError::OutOfBounds => Some(Box::new("miette::span_out_of_bounds")),
} }
@ -56,6 +63,7 @@ impl Diagnostic for MietteError {
fn help<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> { fn help<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
match self { match self {
#[cfg(feature = "std")]
MietteError::IoError(_) => None, MietteError::IoError(_) => None,
MietteError::OutOfBounds => Some(Box::new( MietteError::OutOfBounds => Some(Box::new(
"Double-check your spans. Do you have an off-by-one error?", "Double-check your spans. Do you have an off-by-one error?",
@ -66,24 +74,32 @@ impl Diagnostic for MietteError {
fn url<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> { fn url<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
let crate_version = env!("CARGO_PKG_VERSION"); let crate_version = env!("CARGO_PKG_VERSION");
let variant = match self { let variant = match self {
#[cfg(feature = "std")]
MietteError::IoError(_) => "#variant.IoError", MietteError::IoError(_) => "#variant.IoError",
MietteError::OutOfBounds => "#variant.OutOfBounds", MietteError::OutOfBounds => "#variant.OutOfBounds",
}; };
Some(Box::new(format!( Some(Box::new(alloc::format!(
"https://docs.rs/miette/{}/miette/enum.MietteError.html{}", "https://docs.rs/miette/{}/miette/enum.MietteError.html{}",
crate_version, variant, crate_version,
variant,
))) )))
} }
} }
#[cfg(test)] #[cfg(test)]
pub(crate) mod tests { pub(crate) mod tests {
use std::{error::Error, io::ErrorKind}; #[cfg(not(feature = "std"))]
use crate::StdError as Error;
use alloc::string::ToString;
use super::*; use super::*;
#[derive(Debug)] #[derive(Debug)]
#[cfg(feature = "std")]
pub(crate) struct TestError(pub(crate) io::Error); pub(crate) struct TestError(pub(crate) io::Error);
#[cfg(not(feature = "std"))]
#[derive(Debug)]
pub(crate) struct TestError(pub(crate) &'static str);
impl Display for TestError { impl Display for TestError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@ -91,17 +107,26 @@ pub(crate) mod tests {
} }
} }
#[cfg(feature = "std")]
impl Error for TestError { impl Error for TestError {
fn source(&self) -> Option<&(dyn Error + 'static)> { fn source(&self) -> Option<&(dyn Error + 'static)> {
Some(&self.0) Some(&self.0)
} }
} }
#[cfg(not(feature = "std"))]
impl Error for TestError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
None
}
}
#[cfg(feature = "std")]
#[test] #[test]
fn io_error() { fn io_error() {
let inner_error = io::Error::new(ErrorKind::Other, "halt and catch fire"); let inner_error = io::Error::other("halt and catch fire");
let outer_error = TestError(inner_error); let outer_error = TestError(inner_error);
let io_error = io::Error::new(ErrorKind::Other, outer_error); let io_error = io::Error::other(outer_error);
let miette_error = MietteError::from(io_error); let miette_error = MietteError::from(io_error);

View File

@ -1,8 +1,12 @@
extern crate alloc;
use super::error::{ContextError, ErrorImpl}; use super::error::{ContextError, ErrorImpl};
use super::{Report, WrapErr}; use super::{Report, WrapErr};
use core::convert::Infallible;
use core::fmt::{self, Debug, Display, Write}; use core::fmt::{self, Debug, Display, Write};
use std::error::Error as StdError; use crate::StdError;
use alloc::boxed::Box;
use crate::{Diagnostic, LabeledSpan}; use crate::{Diagnostic, LabeledSpan};
@ -10,7 +14,7 @@ mod ext {
use super::*; use super::*;
pub trait Diag { pub trait Diag {
#[cfg_attr(track_caller, track_caller)] #[track_caller]
fn ext_report<D>(self, msg: D) -> Report fn ext_report<D>(self, msg: D) -> Report
where where
D: Display + Send + Sync + 'static; D: Display + Send + Sync + 'static;
@ -20,6 +24,7 @@ mod ext {
where where
E: Diagnostic + Send + Sync + 'static, E: Diagnostic + Send + Sync + 'static,
{ {
#[track_caller]
fn ext_report<D>(self, msg: D) -> Report fn ext_report<D>(self, msg: D) -> Report
where where
D: Display + Send + Sync + 'static, D: Display + Send + Sync + 'static,
@ -29,6 +34,7 @@ mod ext {
} }
impl Diag for Report { impl Diag for Report {
#[track_caller]
fn ext_report<D>(self, msg: D) -> Report fn ext_report<D>(self, msg: D) -> Report
where where
D: Display + Send + Sync + 'static, D: Display + Send + Sync + 'static,
@ -38,7 +44,8 @@ mod ext {
} }
} }
impl<T> WrapErr<T, std::convert::Infallible> for Option<T> { impl<T> WrapErr<T, Infallible> for Option<T> {
#[track_caller]
fn wrap_err<D>(self, msg: D) -> Result<T, Report> fn wrap_err<D>(self, msg: D) -> Result<T, Report>
where where
D: Display + Send + Sync + 'static, D: Display + Send + Sync + 'static,
@ -49,6 +56,7 @@ impl<T> WrapErr<T, std::convert::Infallible> for Option<T> {
} }
} }
#[track_caller]
fn wrap_err_with<D, F>(self, msg: F) -> Result<T, Report> fn wrap_err_with<D, F>(self, msg: F) -> Result<T, Report>
where where
D: Display + Send + Sync + 'static, D: Display + Send + Sync + 'static,
@ -60,6 +68,7 @@ impl<T> WrapErr<T, std::convert::Infallible> for Option<T> {
} }
} }
#[track_caller]
fn context<D>(self, msg: D) -> Result<T, Report> fn context<D>(self, msg: D) -> Result<T, Report>
where where
D: Display + Send + Sync + 'static, D: Display + Send + Sync + 'static,
@ -67,6 +76,7 @@ impl<T> WrapErr<T, std::convert::Infallible> for Option<T> {
self.wrap_err(msg) self.wrap_err(msg)
} }
#[track_caller]
fn with_context<D, F>(self, msg: F) -> Result<T, Report> fn with_context<D, F>(self, msg: F) -> Result<T, Report>
where where
D: Display + Send + Sync + 'static, D: Display + Send + Sync + 'static,
@ -80,6 +90,7 @@ impl<T, E> WrapErr<T, E> for Result<T, E>
where where
E: ext::Diag + Send + Sync + 'static, E: ext::Diag + Send + Sync + 'static,
{ {
#[track_caller]
fn wrap_err<D>(self, msg: D) -> Result<T, Report> fn wrap_err<D>(self, msg: D) -> Result<T, Report>
where where
D: Display + Send + Sync + 'static, D: Display + Send + Sync + 'static,
@ -90,6 +101,7 @@ where
} }
} }
#[track_caller]
fn wrap_err_with<D, F>(self, msg: F) -> Result<T, Report> fn wrap_err_with<D, F>(self, msg: F) -> Result<T, Report>
where where
D: Display + Send + Sync + 'static, D: Display + Send + Sync + 'static,
@ -101,6 +113,7 @@ where
} }
} }
#[track_caller]
fn context<D>(self, msg: D) -> Result<T, Report> fn context<D>(self, msg: D) -> Result<T, Report>
where where
D: Display + Send + Sync + 'static, D: Display + Send + Sync + 'static,
@ -108,6 +121,7 @@ where
self.wrap_err(msg) self.wrap_err(msg)
} }
#[track_caller]
fn with_context<D, F>(self, msg: F) -> Result<T, Report> fn with_context<D, F>(self, msg: F) -> Result<T, Report>
where where
D: Display + Send + Sync + 'static, D: Display + Send + Sync + 'static,

View File

@ -1,8 +1,11 @@
extern crate alloc;
use crate::StdError;
use alloc::boxed::Box;
use core::any::TypeId; use core::any::TypeId;
use core::fmt::{self, Debug, Display}; use core::fmt::{self, Debug, Display};
use core::mem::ManuallyDrop; use core::mem::ManuallyDrop;
use core::ptr::{self, NonNull}; use core::ptr::{self, NonNull};
use std::error::Error as StdError;
use super::ptr::{Mut, Own, Ref}; use super::ptr::{Mut, Own, Ref};
use super::Report; use super::Report;
@ -20,7 +23,7 @@ impl Report {
/// ///
/// If the error type does not provide a backtrace, a backtrace will be /// If the error type does not provide a backtrace, a backtrace will be
/// created here to ensure that a backtrace exists. /// created here to ensure that a backtrace exists.
#[cfg_attr(track_caller, track_caller)] #[track_caller]
#[cold] #[cold]
pub fn new<E>(error: E) -> Self pub fn new<E>(error: E) -> Self
where where
@ -66,7 +69,7 @@ impl Report {
/// .await /// .await
/// } /// }
/// ``` /// ```
#[cfg_attr(track_caller, track_caller)] #[track_caller]
#[cold] #[cold]
pub fn msg<M>(message: M) -> Self pub fn msg<M>(message: M) -> Self
where where
@ -82,12 +85,11 @@ impl Report {
/// ///
/// Boxed `Diagnostic`s don't implement `Diagnostic` themselves due to trait coherence issues. /// Boxed `Diagnostic`s don't implement `Diagnostic` themselves due to trait coherence issues.
/// This method allows you to create a `Report` from a boxed `Diagnostic`. /// This method allows you to create a `Report` from a boxed `Diagnostic`.
#[cfg_attr(track_caller, track_caller)] #[track_caller]
pub fn new_boxed(error: Box<dyn Diagnostic + Send + Sync + 'static>) -> Self { pub fn new_boxed(error: Box<dyn Diagnostic + Send + Sync + 'static>) -> Self {
Report::from_boxed(error) Report::from_boxed(error)
} }
#[cfg_attr(track_caller, track_caller)]
#[cold] #[cold]
pub(crate) fn from_std<E>(error: E) -> Self pub(crate) fn from_std<E>(error: E) -> Self
where where
@ -109,7 +111,7 @@ impl Report {
unsafe { Report::construct(error, vtable, handler) } unsafe { Report::construct(error, vtable, handler) }
} }
#[cfg_attr(track_caller, track_caller)] #[track_caller]
#[cold] #[cold]
pub(crate) fn from_adhoc<M>(message: M) -> Self pub(crate) fn from_adhoc<M>(message: M) -> Self
where where
@ -134,7 +136,7 @@ impl Report {
unsafe { Report::construct(error, vtable, handler) } unsafe { Report::construct(error, vtable, handler) }
} }
#[cfg_attr(track_caller, track_caller)] #[track_caller]
#[cold] #[cold]
pub(crate) fn from_msg<D, E>(msg: D, error: E) -> Self pub(crate) fn from_msg<D, E>(msg: D, error: E) -> Self
where where
@ -154,12 +156,12 @@ impl Report {
}; };
// Safety: passing vtable that operates on the right type. // Safety: passing vtable that operates on the right type.
let handler = Some(super::capture_handler(&error)); let handler = Some(super::capture_handler_with_location(&error));
unsafe { Report::construct(error, vtable, handler) } unsafe { Report::construct(error, vtable, handler) }
} }
#[cfg_attr(track_caller, track_caller)] #[track_caller]
#[cold] #[cold]
pub(crate) fn from_boxed(error: Box<dyn Diagnostic + Send + Sync>) -> Self { pub(crate) fn from_boxed(error: Box<dyn Diagnostic + Send + Sync>) -> Self {
use super::wrapper::BoxedError; use super::wrapper::BoxedError;
@ -429,7 +431,7 @@ impl Report {
/// Construct a [`Report`] directly from an error-like type /// Construct a [`Report`] directly from an error-like type
pub fn from_err<E>(err: E) -> Self pub fn from_err<E>(err: E) -> Self
where where
E: std::error::Error + Send + Sync + 'static, E: StdError + Send + Sync + 'static,
{ {
super::DiagnosticError(Box::new(err)).into() super::DiagnosticError(Box::new(err)).into()
} }
@ -439,7 +441,6 @@ impl<E> From<E> for Report
where where
E: Diagnostic + Send + Sync + 'static, E: Diagnostic + Send + Sync + 'static,
{ {
#[cfg_attr(track_caller, track_caller)]
#[cold] #[cold]
fn from(error: E) -> Self { fn from(error: E) -> Self {
Report::from_std(error) Report::from_std(error)
@ -816,7 +817,7 @@ impl AsRef<dyn StdError> for Report {
} }
} }
impl std::borrow::Borrow<dyn Diagnostic> for Report { impl core::borrow::Borrow<dyn Diagnostic> for Report {
fn borrow(&self) -> &(dyn Diagnostic + 'static) { fn borrow(&self) -> &(dyn Diagnostic + 'static) {
self.as_ref() self.as_ref()
} }

View File

@ -1,14 +1,17 @@
use std::{error::Error, fmt::Display}; extern crate alloc;
use alloc::boxed::Box;
use core::error::Error;
use crate::{Diagnostic, Report}; use crate::{Diagnostic, Report};
/// Convenience [`Diagnostic`] that can be used as an "anonymous" wrapper for /// Convenience [`Diagnostic`] that can be used as an "anonymous" wrapper for
/// Errors. This is intended to be paired with [`IntoDiagnostic`]. /// Errors. This is intended to be paired with [`IntoDiagnostic`].
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct DiagnosticError(pub(crate) Box<dyn std::error::Error + Send + Sync + 'static>); pub(crate) struct DiagnosticError(pub(crate) Box<dyn Error + Send + Sync + 'static>);
impl Display for DiagnosticError { impl core::fmt::Display for DiagnosticError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let msg = &self.0; let msg = &self.0;
write!(f, "{msg}") write!(f, "{msg}")
} }
@ -35,10 +38,12 @@ inaccessible. If you have a type implementing [`Diagnostic`] consider simply ret
pub trait IntoDiagnostic<T, E> { pub trait IntoDiagnostic<T, E> {
/// Converts [`Result`] types that return regular [`std::error::Error`]s /// Converts [`Result`] types that return regular [`std::error::Error`]s
/// into a [`Result`] that returns a [`Diagnostic`]. /// into a [`Result`] that returns a [`Diagnostic`].
#[track_caller]
fn into_diagnostic(self) -> Result<T, Report>; fn into_diagnostic(self) -> Result<T, Report>;
} }
impl<T, E: std::error::Error + Send + Sync + 'static> IntoDiagnostic<T, E> for Result<T, E> { impl<T, E: Error + Send + Sync + 'static> IntoDiagnostic<T, E> for Result<T, E> {
#[track_caller]
fn into_diagnostic(self) -> Result<T, Report> { fn into_diagnostic(self) -> Result<T, Report> {
self.map_err(|e| DiagnosticError(Box::new(e)).into()) self.map_err(|e| DiagnosticError(Box::new(e)).into())
} }
@ -46,15 +51,21 @@ impl<T, E: std::error::Error + Send + Sync + 'static> IntoDiagnostic<T, E> for R
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::io::{self, ErrorKind}; extern crate alloc;
use super::*; use alloc::string::ToString;
use super::IntoDiagnostic;
#[cfg(feature = "std")]
use std::io;
#[cfg(feature = "std")]
use crate::error::tests::TestError; use crate::error::tests::TestError;
#[cfg(feature = "std")]
#[test] #[test]
fn diagnostic_error() { fn diagnostic_error() {
let inner_error = io::Error::new(ErrorKind::Other, "halt and catch fire"); let inner_error = io::Error::other("halt and catch fire");
let outer_error: Result<(), _> = Err(TestError(inner_error)); let outer_error: Result<(), _> = Err(TestError(inner_error));
let diagnostic_error = outer_error.into_diagnostic().unwrap_err(); let diagnostic_error = outer_error.into_diagnostic().unwrap_err();

View File

@ -45,9 +45,12 @@
// let error = $msg; // let error = $msg;
// (&error).miette_kind().new(error) // (&error).miette_kind().new(error)
use super::Report; extern crate alloc;
use alloc::boxed::Box;
use core::fmt::{Debug, Display}; use core::fmt::{Debug, Display};
use super::Report;
use crate::Diagnostic; use crate::Diagnostic;
pub struct Adhoc; pub struct Adhoc;
@ -62,7 +65,7 @@ pub trait AdhocKind: Sized {
impl<T> AdhocKind for &T where T: ?Sized + Display + Debug + Send + Sync + 'static {} impl<T> AdhocKind for &T where T: ?Sized + Display + Debug + Send + Sync + 'static {}
impl Adhoc { impl Adhoc {
#[cfg_attr(track_caller, track_caller)] #[track_caller]
#[cold] #[cold]
pub fn new<M>(self, message: M) -> Report pub fn new<M>(self, message: M) -> Report
where where
@ -84,7 +87,7 @@ pub trait TraitKind: Sized {
impl<E> TraitKind for E where E: Into<Report> {} impl<E> TraitKind for E where E: Into<Report> {}
impl Trait { impl Trait {
#[cfg_attr(track_caller, track_caller)] #[track_caller]
#[cold] #[cold]
pub fn new<E>(self, error: E) -> Report pub fn new<E>(self, error: E) -> Report
where where
@ -106,7 +109,7 @@ pub trait BoxedKind: Sized {
impl BoxedKind for Box<dyn Diagnostic + Send + Sync> {} impl BoxedKind for Box<dyn Diagnostic + Send + Sync> {}
impl Boxed { impl Boxed {
#[cfg_attr(track_caller, track_caller)] #[track_caller]
#[cold] #[cold]
pub fn new(self, error: Box<dyn Diagnostic + Send + Sync>) -> Report { pub fn new(self, error: Box<dyn Diagnostic + Send + Sync>) -> Report {
Report::from_boxed(error) Report::from_boxed(error)

View File

@ -4,9 +4,13 @@
clippy::new_ret_no_self, clippy::new_ret_no_self,
clippy::wrong_self_convention clippy::wrong_self_convention
)] )]
extern crate alloc;
use alloc::boxed::Box;
use core::fmt::Display; use core::fmt::Display;
use std::error::Error as StdError; use core::error::Error as StdError;
#[cfg(feature = "std")]
use std::sync::OnceLock; use std::sync::OnceLock;
#[allow(unreachable_pub)] #[allow(unreachable_pub)]
@ -61,8 +65,16 @@ unsafe impl Send for Report {}
pub type ErrorHook = pub type ErrorHook =
Box<dyn Fn(&(dyn Diagnostic + 'static)) -> Box<dyn ReportHandler> + Sync + Send + 'static>; Box<dyn Fn(&(dyn Diagnostic + 'static)) -> Box<dyn ReportHandler> + Sync + Send + 'static>;
#[cfg(feature = "std")]
static HOOK: OnceLock<ErrorHook> = OnceLock::new(); static HOOK: OnceLock<ErrorHook> = OnceLock::new();
#[cfg(not(feature = "std"))]
static HOOK: spin::Once<ErrorHook> = spin::Once::new();
fn default_hook() -> ErrorHook {
Box::new(get_default_printer)
}
/// Error indicating that [`set_hook()`] was unable to install the provided /// Error indicating that [`set_hook()`] was unable to install the provided
/// [`ErrorHook`]. /// [`ErrorHook`].
#[derive(Debug)] #[derive(Debug)]
@ -77,28 +89,51 @@ impl core::fmt::Display for InstallError {
impl StdError for InstallError {} impl StdError for InstallError {}
impl Diagnostic for InstallError {} impl Diagnostic for InstallError {}
/** /// Set the error hook.
Set the error hook. #[cfg(feature = "std")]
*/
pub fn set_hook(hook: ErrorHook) -> Result<(), InstallError> { pub fn set_hook(hook: ErrorHook) -> Result<(), InstallError> {
HOOK.set(hook).map_err(|_| InstallError) HOOK.set(hook).map_err(|_| InstallError)
} }
#[cfg_attr(track_caller, track_caller)] /// Set the error hook.
#[cfg_attr(not(track_caller), allow(unused_mut))] #[cfg(not(feature = "std"))]
fn capture_handler(error: &(dyn Diagnostic + 'static)) -> Box<dyn ReportHandler> { pub fn set_hook(hook: ErrorHook) -> Result<(), InstallError> {
let hook = HOOK.get_or_init(|| Box::new(get_default_printer)).as_ref(); HOOK.call_once(|| hook);
Ok(())
}
#[cfg(track_caller)] #[cfg(feature = "std")]
{ pub(crate) fn capture_handler(error: &(dyn Diagnostic + 'static)) -> Box<dyn ReportHandler> {
let mut handler = hook(error); let hook = HOOK.get_or_init(default_hook);
handler.track_caller(std::panic::Location::caller()); hook(error)
handler }
}
#[cfg(not(track_caller))] #[cfg(not(feature = "std"))]
{ pub(crate) fn capture_handler(error: &(dyn Diagnostic + 'static)) -> Box<dyn ReportHandler> {
hook(error) let hook = HOOK.call_once(default_hook);
} hook(error)
}
#[track_caller]
#[cfg(feature = "std")]
pub(crate) fn capture_handler_with_location(
error: &(dyn Diagnostic + 'static),
) -> Box<dyn ReportHandler> {
let hook = HOOK.get_or_init(default_hook);
let mut handler = hook(error);
handler.track_caller(core::panic::Location::caller());
handler
}
#[track_caller]
#[cfg(not(feature = "std"))]
pub(crate) fn capture_handler_with_location(
error: &(dyn Diagnostic + 'static),
) -> Box<dyn ReportHandler> {
let hook = HOOK.call_once(default_hook);
let mut handler = hook(error);
handler.track_caller(core::panic::Location::caller());
handler
} }
fn get_default_printer(_err: &(dyn Diagnostic + 'static)) -> Box<dyn ReportHandler + 'static> { fn get_default_printer(_err: &(dyn Diagnostic + 'static)) -> Box<dyn ReportHandler + 'static> {
@ -193,7 +228,7 @@ pub trait ReportHandler: core::any::Any + Send + Sync {
/// Store the location of the caller who constructed this error report /// Store the location of the caller who constructed this error report
#[allow(unused_variables)] #[allow(unused_variables)]
fn track_caller(&mut self, location: &'static std::panic::Location<'static>) {} fn track_caller(&mut self, location: &'static core::panic::Location<'static>) {}
} }
/// type alias for `Result<T, Report>` /// type alias for `Result<T, Report>`
@ -428,27 +463,27 @@ pub type Result<T, E = Report> = core::result::Result<T, E>;
/// ``` /// ```
pub trait WrapErr<T, E>: context::private::Sealed { pub trait WrapErr<T, E>: context::private::Sealed {
/// Wrap the error value with a new adhoc error /// Wrap the error value with a new adhoc error
#[cfg_attr(track_caller, track_caller)] #[track_caller]
fn wrap_err<D>(self, msg: D) -> Result<T, Report> fn wrap_err<D>(self, msg: D) -> Result<T, Report>
where where
D: Display + Send + Sync + 'static; D: Display + Send + Sync + 'static;
/// Wrap the error value with a new adhoc error that is evaluated lazily /// Wrap the error value with a new adhoc error that is evaluated lazily
/// only once an error does occur. /// only once an error does occur.
#[cfg_attr(track_caller, track_caller)] #[track_caller]
fn wrap_err_with<D, F>(self, f: F) -> Result<T, Report> fn wrap_err_with<D, F>(self, f: F) -> Result<T, Report>
where where
D: Display + Send + Sync + 'static, D: Display + Send + Sync + 'static,
F: FnOnce() -> D; F: FnOnce() -> D;
/// Compatibility re-export of `wrap_err()` for interop with `anyhow` /// Compatibility re-export of `wrap_err()` for interop with `anyhow`
#[cfg_attr(track_caller, track_caller)] #[track_caller]
fn context<D>(self, msg: D) -> Result<T, Report> fn context<D>(self, msg: D) -> Result<T, Report>
where where
D: Display + Send + Sync + 'static; D: Display + Send + Sync + 'static;
/// Compatibility re-export of `wrap_err_with()` for interop with `anyhow` /// Compatibility re-export of `wrap_err_with()` for interop with `anyhow`
#[cfg_attr(track_caller, track_caller)] #[track_caller]
fn with_context<D, F>(self, f: F) -> Result<T, Report> fn with_context<D, F>(self, f: F) -> Result<T, Report>
where where
D: Display + Send + Sync + 'static, D: Display + Send + Sync + 'static,
@ -470,7 +505,7 @@ pub mod private {
pub use super::super::kind::BoxedKind; pub use super::super::kind::BoxedKind;
} }
#[cfg_attr(track_caller, track_caller)] #[track_caller]
#[cold] #[cold]
pub fn new_adhoc<M>(message: M) -> Report pub fn new_adhoc<M>(message: M) -> Report
where where

View File

@ -1,4 +1,8 @@
use std::{marker::PhantomData, ptr::NonNull}; extern crate alloc;
use alloc::boxed::Box;
use core::marker::PhantomData;
use core::ptr::NonNull;
#[repr(transparent)] #[repr(transparent)]
/// A raw pointer that owns its pointee /// A raw pointer that owns its pointee

View File

@ -1,6 +1,9 @@
extern crate alloc;
use core::fmt::{self, Debug, Display}; use core::fmt::{self, Debug, Display};
use std::error::Error as StdError; use crate::StdError;
use alloc::boxed::Box;
use crate::{Diagnostic, LabeledSpan, Report, SourceCode}; use crate::{Diagnostic, LabeledSpan, Report, SourceCode};
@ -107,16 +110,6 @@ impl StdError for BoxedError {
fn source(&self) -> Option<&(dyn StdError + 'static)> { fn source(&self) -> Option<&(dyn StdError + 'static)> {
self.0.source() self.0.source()
} }
fn description(&self) -> &str {
#[allow(deprecated)]
self.0.description()
}
fn cause(&self) -> Option<&dyn StdError> {
#[allow(deprecated)]
self.0.cause()
}
} }
pub(crate) struct WithSourceCode<E, C> { pub(crate) struct WithSourceCode<E, C> {
@ -216,8 +209,16 @@ impl<C> StdError for WithSourceCode<Report, C> {
} }
} }
#[cfg(test)] #[cfg(all(test, feature = "std"))]
mod tests { mod tests {
extern crate alloc;
use alloc::boxed::Box;
#[cfg(feature = "fancy")]
use alloc::format;
use alloc::string::{String, ToString};
#[cfg(feature = "fancy")]
use alloc::{vec, vec::Vec};
use thiserror::Error; use thiserror::Error;
use crate::{Diagnostic, LabeledSpan, Report, SourceCode, SourceSpan}; use crate::{Diagnostic, LabeledSpan, Report, SourceCode, SourceSpan};
@ -231,7 +232,7 @@ mod tests {
impl Diagnostic for Inner { impl Diagnostic for Inner {
fn labels(&self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + '_>> { fn labels(&self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + '_>> {
Some(Box::new(std::iter::once(LabeledSpan::underline(self.at)))) Some(Box::new(core::iter::once(LabeledSpan::underline(self.at))))
} }
fn source_code(&self) -> Option<&dyn SourceCode> { fn source_code(&self) -> Option<&dyn SourceCode> {

View File

@ -1,3 +1,7 @@
extern crate alloc;
use alloc::boxed::Box;
use alloc::string::String;
use crate::highlighters::Highlighter; use crate::highlighters::Highlighter;
use crate::highlighters::MietteHighlighter; use crate::highlighters::MietteHighlighter;
use crate::protocol::Diagnostic; use crate::protocol::Diagnostic;
@ -8,7 +12,7 @@ use crate::ReportHandler;
use crate::ThemeCharacters; use crate::ThemeCharacters;
use crate::ThemeStyles; use crate::ThemeStyles;
use cfg_if::cfg_if; use cfg_if::cfg_if;
use std::fmt; use core::fmt;
/// Settings to control the color format used for graphical rendering. /// Settings to control the color format used for graphical rendering.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)] #[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
@ -341,15 +345,26 @@ impl MietteHandlerOpts {
} }
} }
#[allow(clippy::manual_unwrap_or)]
pub(crate) fn is_graphical(&self) -> bool { pub(crate) fn is_graphical(&self) -> bool {
if let Some(force_narrated) = self.force_narrated { if let Some(force_narrated) = self.force_narrated {
!force_narrated !force_narrated
} else if let Some(force_graphical) = self.force_graphical { } else if let Some(force_graphical) = self.force_graphical {
force_graphical force_graphical
} else if let Ok(env) = std::env::var("NO_GRAPHICS") {
env == "0"
} else { } else {
true #[cfg(feature = "fancy-no-backtrace")]
{
if let Ok(env) = std::env::var("NO_GRAPHICS") {
env == "0"
} else {
true
}
}
#[cfg(not(feature = "fancy-no-backtrace"))]
{
// fancy-base or fancy-no-syscall without std: default to graphical
true
}
} }
} }
@ -464,60 +479,63 @@ impl From<HighlighterOption> for MietteHighlighter {
} }
mod syscall { mod syscall {
use cfg_if::cfg_if;
#[inline] #[inline]
pub(super) fn terminal_width() -> Option<usize> { pub(super) fn terminal_width() -> Option<usize> {
cfg_if! { #[cfg(all(feature = "fancy-no-backtrace", not(miri)))]
if #[cfg(any(feature = "fancy-no-syscall", miri))] { {
None terminal_size::terminal_size().map(|size| size.0 .0 as usize)
} else { }
terminal_size::terminal_size().map(|size| size.0 .0 as usize) #[cfg(any(not(feature = "fancy-no-backtrace"), miri))]
} {
None
} }
} }
#[inline] #[inline]
pub(super) fn supports_hyperlinks() -> bool { pub(super) fn supports_hyperlinks() -> bool {
cfg_if! { #[cfg(feature = "fancy-no-backtrace")]
if #[cfg(feature = "fancy-no-syscall")] { {
false supports_hyperlinks::on(supports_hyperlinks::Stream::Stderr)
} else { }
supports_hyperlinks::on(supports_hyperlinks::Stream::Stderr) #[cfg(not(feature = "fancy-no-backtrace"))]
} {
false
} }
} }
#[inline] #[inline]
pub(super) fn supports_color() -> bool { pub(super) fn supports_color() -> bool {
cfg_if! { #[cfg(feature = "fancy-no-backtrace")]
if #[cfg(feature = "fancy-no-syscall")] { {
false supports_color::on(supports_color::Stream::Stderr).is_some()
} else { }
supports_color::on(supports_color::Stream::Stderr).is_some() #[cfg(not(feature = "fancy-no-backtrace"))]
} {
false
} }
} }
#[inline] #[inline]
pub(super) fn supports_color_has_16m() -> Option<bool> { pub(super) fn supports_color_has_16m() -> Option<bool> {
cfg_if! { #[cfg(feature = "fancy-no-backtrace")]
if #[cfg(feature = "fancy-no-syscall")] { {
None supports_color::on(supports_color::Stream::Stderr).map(|color| color.has_16m)
} else { }
supports_color::on(supports_color::Stream::Stderr).map(|color| color.has_16m) #[cfg(not(feature = "fancy-no-backtrace"))]
} {
None
} }
} }
#[inline] #[inline]
pub(super) fn supports_unicode() -> bool { pub(super) fn supports_unicode() -> bool {
cfg_if! { #[cfg(feature = "fancy-no-backtrace")]
if #[cfg(feature = "fancy-no-syscall")] { {
false supports_unicode::on(supports_unicode::Stream::Stderr)
} else { }
supports_unicode::on(supports_unicode::Stream::Stderr) #[cfg(not(feature = "fancy-no-backtrace"))]
} {
false
} }
} }
} }

View File

@ -1,4 +1,7 @@
use std::fmt; extern crate alloc;
use alloc::vec::Vec;
use core::fmt;
use crate::{protocol::Diagnostic, ReportHandler}; use crate::{protocol::Diagnostic, ReportHandler};
@ -34,25 +37,25 @@ impl DebugReportHandler {
diagnostic: &dyn Diagnostic, diagnostic: &dyn Diagnostic,
) -> fmt::Result { ) -> fmt::Result {
let mut diag = f.debug_struct("Diagnostic"); let mut diag = f.debug_struct("Diagnostic");
diag.field("message", &format!("{}", diagnostic)); diag.field("message", &alloc::format!("{}", diagnostic));
if let Some(code) = diagnostic.code() { if let Some(code) = diagnostic.code() {
diag.field("code", &code.to_string()); diag.field("code", &alloc::format!("{}", code));
} }
if let Some(severity) = diagnostic.severity() { if let Some(severity) = diagnostic.severity() {
diag.field("severity", &format!("{:?}", severity)); diag.field("severity", &alloc::format!("{:?}", severity));
} }
if let Some(url) = diagnostic.url() { if let Some(url) = diagnostic.url() {
diag.field("url", &url.to_string()); diag.field("url", &alloc::format!("{}", url));
} }
if let Some(help) = diagnostic.help() { if let Some(help) = diagnostic.help() {
diag.field("help", &help.to_string()); diag.field("help", &alloc::format!("{}", help));
} }
if let Some(labels) = diagnostic.labels() { if let Some(labels) = diagnostic.labels() {
let labels: Vec<_> = labels.collect(); let labels: Vec<_> = labels.collect();
diag.field("labels", &format!("{:?}", labels)); diag.field("labels", &alloc::format!("{:?}", labels));
} }
if let Some(cause) = diagnostic.diagnostic_source() { if let Some(cause) = diagnostic.diagnostic_source() {
diag.field("caused by", &format!("{:?}", cause)); diag.field("caused by", &alloc::format!("{:?}", cause));
} }
diag.finish()?; diag.finish()?;
writeln!(f)?; writeln!(f)?;

View File

@ -1,4 +1,10 @@
use std::fmt::{self, Write}; extern crate alloc;
use alloc::boxed::Box;
use alloc::format;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use core::fmt::{self, Write};
use owo_colors::{OwoColorize, Style, StyledList}; use owo_colors::{OwoColorize, Style, StyledList};
use unicode_width::{UnicodeWidthChar, UnicodeWidthStr}; use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
@ -582,7 +588,7 @@ impl GraphicalReportHandler {
// The snippets will overlap, so we create one Big Chunky Boi // The snippets will overlap, so we create one Big Chunky Boi
let left_end = left.offset() + left.len(); let left_end = left.offset() + left.len();
let right_end = right.offset() + right.len(); let right_end = right.offset() + right.len();
let new_end = std::cmp::max(left_end, right_end); let new_end = core::cmp::max(left_end, right_end);
let new_span = LabeledSpan::new( let new_span = LabeledSpan::new(
left.label().map(String::from), left.label().map(String::from),
@ -649,7 +655,7 @@ impl GraphicalReportHandler {
num_highlights += 1; num_highlights += 1;
} }
} }
max_gutter = std::cmp::max(max_gutter, num_highlights); max_gutter = core::cmp::max(max_gutter, num_highlights);
} }
// Oh and one more thing: We need to figure out how much room our line // Oh and one more thing: We need to figure out how much room our line
@ -1173,7 +1179,7 @@ impl GraphicalReportHandler {
.style(hl.style) .style(hl.style)
.to_string(), .to_string(),
); );
highest = std::cmp::max(highest, end); highest = core::cmp::max(highest, end);
(hl, vbar_offset) (hl, vbar_offset)
}) })

View File

@ -1,4 +1,7 @@
use std::fmt::{self, Write}; extern crate alloc;
use alloc::string::ToString;
use core::fmt::{self, Write};
use crate::{ use crate::{
diagnostic_chain::DiagnosticChain, protocol::Diagnostic, ReportHandler, Severity, SourceCode, diagnostic_chain::DiagnosticChain, protocol::Diagnostic, ReportHandler, Severity, SourceCode,

View File

@ -1,10 +1,15 @@
use std::fmt; extern crate alloc;
use core::fmt;
use unicode_width::{UnicodeWidthChar, UnicodeWidthStr}; use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
use crate::diagnostic_chain::DiagnosticChain; use crate::diagnostic_chain::DiagnosticChain;
use crate::protocol::{Diagnostic, Severity}; use crate::protocol::{Diagnostic, Severity};
use crate::{LabeledSpan, MietteError, ReportHandler, SourceCode, SourceSpan, SpanContents}; use crate::{LabeledSpan, MietteError, ReportHandler, SourceCode, SourceSpan, SpanContents};
use alloc::boxed::Box;
use alloc::string::String;
use alloc::vec::Vec;
/** /**
[`ReportHandler`] that renders plain text and avoids extraneous graphics. [`ReportHandler`] that renders plain text and avoids extraneous graphics.
@ -290,7 +295,7 @@ impl NarratableReportHandler {
let context_data = source let context_data = source
.read_span(context_span, self.context_lines, self.context_lines) .read_span(context_span, self.context_lines, self.context_lines)
.map_err(|_| fmt::Error)?; .map_err(|_| fmt::Error)?;
let context = std::str::from_utf8(context_data.data()).expect("Bad utf8 detected"); let context = core::str::from_utf8(context_data.data()).expect("Bad utf8 detected");
let mut line = context_data.line(); let mut line = context_data.line();
let mut column = context_data.column(); let mut column = context_data.column();
let mut offset = context_data.span().offset(); let mut offset = context_data.span().offset();

View File

@ -1,5 +1,7 @@
use std::io::IsTerminal; extern crate alloc;
use alloc::string::String;
use alloc::vec;
use alloc::vec::Vec;
use owo_colors::Style; use owo_colors::Style;
/** /**
@ -69,12 +71,20 @@ impl GraphicalTheme {
impl Default for GraphicalTheme { impl Default for GraphicalTheme {
fn default() -> Self { fn default() -> Self {
match std::env::var("NO_COLOR") { #[cfg(feature = "fancy-no-backtrace")]
_ if !std::io::stdout().is_terminal() || !std::io::stderr().is_terminal() => { {
Self::none() use std::io::IsTerminal;
match std::env::var("NO_COLOR") {
_ if !std::io::stdout().is_terminal() || !std::io::stderr().is_terminal() => {
Self::none()
}
Ok(string) if string != "0" => Self::unicode_nocolor(),
_ => Self::unicode(),
} }
Ok(string) if string != "0" => Self::unicode_nocolor(), }
_ => Self::unicode(), #[cfg(not(feature = "fancy-no-backtrace"))]
{
Self::unicode_nocolor()
} }
} }
} }

View File

@ -1,3 +1,8 @@
extern crate alloc;
use alloc::boxed::Box;
use alloc::vec;
use alloc::vec::Vec;
use owo_colors::Style; use owo_colors::Style;
use crate::SpanContents; use crate::SpanContents;

View File

@ -11,7 +11,12 @@
//! * `syntect-highlighter` - Enables [`syntect`](https://docs.rs/syntect/latest/syntect/) syntax highlighting support via the [`SyntectHighlighter`] //! * `syntect-highlighter` - Enables [`syntect`](https://docs.rs/syntect/latest/syntect/) syntax highlighting support via the [`SyntectHighlighter`]
//! //!
use std::{ops::Deref, sync::Arc}; use core::ops::Deref;
extern crate alloc;
use alloc::boxed::Box;
use alloc::sync::Arc;
use alloc::vec::Vec;
use crate::SpanContents; use crate::SpanContents;
use owo_colors::Styled; use owo_colors::Styled;
@ -78,21 +83,22 @@ impl MietteHighlighter {
} }
impl Default for MietteHighlighter { impl Default for MietteHighlighter {
#[cfg(feature = "syntect-highlighter")]
fn default() -> Self { fn default() -> Self {
use std::io::IsTerminal; #[cfg(feature = "syntect-highlighter")]
match std::env::var("NO_COLOR") { {
_ if !std::io::stdout().is_terminal() || !std::io::stderr().is_terminal() => { use std::io::IsTerminal;
//TODO: should use ANSI styling instead of 24-bit truecolor here match std::env::var("NO_COLOR") {
Self(Arc::new(SyntectHighlighter::default())) _ if !std::io::stdout().is_terminal() || !std::io::stderr().is_terminal() => {
Self(Arc::new(SyntectHighlighter::default()))
}
Ok(string) if string != "0" => MietteHighlighter::nocolor(),
_ => Self(Arc::new(SyntectHighlighter::default())),
} }
Ok(string) if string != "0" => MietteHighlighter::nocolor(),
_ => Self(Arc::new(SyntectHighlighter::default())),
} }
} #[cfg(not(feature = "syntect-highlighter"))]
#[cfg(not(feature = "syntect-highlighter"))] {
fn default() -> Self { MietteHighlighter::nocolor()
MietteHighlighter::nocolor() }
} }
} }
@ -102,8 +108,8 @@ impl<T: Highlighter + Send + Sync + 'static> From<T> for MietteHighlighter {
} }
} }
impl std::fmt::Debug for MietteHighlighter { impl core::fmt::Debug for MietteHighlighter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "MietteHighlighter(...)") write!(f, "MietteHighlighter(...)")
} }
} }

View File

@ -1,5 +1,11 @@
use core::str;
use std::path::Path; use std::path::Path;
extern crate alloc;
use alloc::boxed::Box;
use alloc::vec;
use alloc::vec::Vec;
// all syntect imports are explicitly qualified, but their paths are shortened for convenience // all syntect imports are explicitly qualified, but their paths are shortened for convenience
#[allow(clippy::module_inception)] #[allow(clippy::module_inception)]
mod syntect { mod syntect {
@ -97,7 +103,7 @@ impl SyntectHighlighter {
} }
// finally, attempt to guess syntax based on first line // finally, attempt to guess syntax based on first line
self.syntax_set.find_syntax_by_first_line( self.syntax_set.find_syntax_by_first_line(
std::str::from_utf8(contents.data()) core::str::from_utf8(contents.data())
.ok()? .ok()?
.split('\n') .split('\n')
.next()?, .next()?,

View File

@ -1,6 +1,13 @@
#![no_std]
#![deny(missing_docs, missing_debug_implementations, nonstandard_style)] #![deny(missing_docs, missing_debug_implementations, nonstandard_style)]
#![warn(unreachable_pub, rust_2018_idioms)] #![warn(unreachable_pub, rust_2018_idioms)]
#![deny(
clippy::alloc_instead_of_core,
clippy::std_instead_of_core,
clippy::std_instead_of_alloc
)]
#![allow(unexpected_cfgs)] #![allow(unexpected_cfgs)]
//! You run miette? You run her code like the software? Oh. Oh! Error code for //! You run miette? You run her code like the software? Oh. Oh! Error code for
//! coder! Error code for One Thousand Lines! //! coder! Error code for One Thousand Lines!
//! //!
@ -58,8 +65,8 @@
//! //!
//! ## Features //! ## Features
//! //!
//! - Generic [`Diagnostic`] protocol, compatible (and dependent on) //! - Generic [`Diagnostic`] protocol, compatible with `std::error::Error`.
//! [`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`]. //! - Unique error codes on every [`Diagnostic`].
//! - Custom links to get more details on error codes. //! - Custom links to get more details on error codes.
//! - Super handy derive macro for defining diagnostic metadata. //! - Super handy derive macro for defining diagnostic metadata.
@ -93,9 +100,23 @@
//! $ cargo add miette --features fancy //! $ 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 //! ## Example
//! //!
//! ```rust //! ```rust
//! # extern crate alloc;
//! /* //! /*
//! You can derive a `Diagnostic` from any `std::error::Error` type. //! You can derive a `Diagnostic` from any `std::error::Error` type.
//! //!
@ -193,6 +214,7 @@
//! the trait directly, just like with `std::error::Error`. //! the trait directly, just like with `std::error::Error`.
//! //!
//! ```rust //! ```rust
//! # extern crate alloc;
//! // lib/error.rs //! // lib/error.rs
//! use miette::{Diagnostic, SourceSpan}; //! use miette::{Diagnostic, SourceSpan};
//! use thiserror::Error; //! use thiserror::Error;
@ -360,6 +382,7 @@
//! attribute: //! attribute:
//! //!
//! ```rust //! ```rust
//! # extern crate alloc;
//! use miette::Diagnostic; //! use miette::Diagnostic;
//! use thiserror::Error; //! use thiserror::Error;
//! //!
@ -380,6 +403,7 @@
//! (very high quality and detailed!) documentation on this diagnostic: //! (very high quality and detailed!) documentation on this diagnostic:
//! //!
//! ```rust //! ```rust
//! # extern crate alloc;
//! use miette::Diagnostic; //! use miette::Diagnostic;
//! use thiserror::Error; //! use thiserror::Error;
//! //!
@ -410,6 +434,7 @@
//! `derive(Diagnostic)` macro: //! `derive(Diagnostic)` macro:
//! //!
//! ```rust //! ```rust
//! # extern crate alloc;
//! use miette::{Diagnostic, SourceSpan}; //! use miette::{Diagnostic, SourceSpan};
//! use thiserror::Error; //! use thiserror::Error;
//! //!
@ -448,6 +473,7 @@
//! enum variants: //! enum variants:
//! //!
//! ```rust //! ```rust
//! # extern crate alloc;
//! use miette::Diagnostic; //! use miette::Diagnostic;
//! use thiserror::Error; //! use thiserror::Error;
//! //!
@ -461,6 +487,7 @@
//! your diagnostic: //! your diagnostic:
//! //!
//! ```rust //! ```rust
//! # extern crate alloc;
//! use miette::Diagnostic; //! use miette::Diagnostic;
//! use thiserror::Error; //! use thiserror::Error;
//! //!
@ -499,6 +526,7 @@
//! `Diagnostic` type: //! `Diagnostic` type:
//! //!
//! ```rust //! ```rust
//! # extern crate alloc;
//! use miette::Diagnostic; //! use miette::Diagnostic;
//! use thiserror::Error; //! use thiserror::Error;
//! //!
@ -517,6 +545,7 @@
//! method for that: //! method for that:
//! //!
//! ```rust,no_run //! ```rust,no_run
//! # extern crate alloc;
//! use miette::{Diagnostic, SourceSpan}; //! use miette::{Diagnostic, SourceSpan};
//! use thiserror::Error; //! use thiserror::Error;
//! //!
@ -549,6 +578,7 @@
//! emitted at the same time: //! emitted at the same time:
//! //!
//! ```rust,no_run //! ```rust,no_run
//! # extern crate alloc;
//! use miette::{Diagnostic, Report, SourceSpan}; //! use miette::{Diagnostic, Report, SourceSpan};
//! use thiserror::Error; //! use thiserror::Error;
//! //!
@ -610,6 +640,7 @@
//! will likely want to use _both_: //! will likely want to use _both_:
//! //!
//! ```rust //! ```rust
//! # extern crate alloc;
//! use miette::Diagnostic; //! use miette::Diagnostic;
//! use thiserror::Error; //! use thiserror::Error;
//! //!
@ -815,6 +846,19 @@
//! and some from [`thiserror`](https://github.com/dtolnay/thiserror), also //! and some from [`thiserror`](https://github.com/dtolnay/thiserror), also
//! under the Apache License. Some code is taken from //! under the Apache License. Some code is taken from
//! [`ariadne`](https://github.com/zesterer/ariadne), which is MIT licensed. //! [`ariadne`](https://github.com/zesterer/ariadne), which is MIT licensed.
#[cfg(feature = "std")]
extern crate std;
pub use core::error::Error as StdError;
#[doc(hidden)]
pub mod __alloc {
extern crate alloc;
pub use alloc::borrow;
pub use alloc::boxed::Box;
}
#[cfg(feature = "derive")] #[cfg(feature = "derive")]
pub use miette_derive::*; pub use miette_derive::*;

View File

@ -1,7 +1,10 @@
use std::{ extern crate alloc;
error::Error,
fmt::{Debug, Display}, use alloc::boxed::Box;
}; use alloc::string::String;
use alloc::vec::Vec;
use core::error::Error;
use core::fmt::{Debug, Display};
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -39,7 +42,7 @@ pub struct MietteDiagnostic {
} }
impl Display for MietteDiagnostic { impl Display for MietteDiagnostic {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", &self.message) write!(f, "{}", &self.message)
} }
} }
@ -183,7 +186,7 @@ impl MietteDiagnostic {
/// assert_eq!(diag.labels, Some(vec![label])); /// assert_eq!(diag.labels, Some(vec![label]));
/// ``` /// ```
pub fn with_label(mut self, label: impl Into<LabeledSpan>) -> Self { pub fn with_label(mut self, label: impl Into<LabeledSpan>) -> Self {
self.labels = Some(vec![label.into()]); self.labels = Some(Vec::from([label.into()]));
self self
} }
@ -261,6 +264,7 @@ impl MietteDiagnostic {
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
#[test] #[test]
fn test_serialize_miette_diagnostic() { fn test_serialize_miette_diagnostic() {
use alloc::format;
use serde_json::json; use serde_json::json;
use crate::diagnostic; use crate::diagnostic;
@ -311,6 +315,7 @@ fn test_serialize_miette_diagnostic() {
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
#[test] #[test]
fn test_deserialize_miette_diagnostic() { fn test_deserialize_miette_diagnostic() {
use alloc::format;
use serde_json::json; use serde_json::json;
use crate::diagnostic; use crate::diagnostic;

View File

@ -10,8 +10,14 @@ pub struct NamedSource<S: SourceCode + 'static> {
language: Option<String>, language: Option<String>,
} }
impl<S: SourceCode> std::fmt::Debug for NamedSource<S> { extern crate alloc;
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use alloc::boxed::Box;
use alloc::string::String;
use alloc::string::ToString;
impl<S: SourceCode> core::fmt::Debug for NamedSource<S> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("NamedSource") f.debug_struct("NamedSource")
.field("name", &self.name) .field("name", &self.name)
.field("source", &"<redacted>") .field("source", &"<redacted>")

View File

@ -1,10 +1,24 @@
use std::{error::Error, fmt::Display}; // `fancy` feature requires std, so std imports are fine here.
#![allow(
clippy::std_instead_of_core,
clippy::std_instead_of_alloc,
clippy::alloc_instead_of_core
)]
use std::boxed::Box;
use std::{
eprintln,
error::Error,
fmt::Display,
format,
string::{String, ToString},
};
use backtrace::Backtrace; use backtrace::Backtrace;
use crate::{Context, Diagnostic, Result}; use crate::{Context, Diagnostic, Result};
/// Tells miette to render panics using its rendering engine. /// Makes miette show pretty error messages when your program crashes.
pub fn set_panic_hook() { pub fn set_panic_hook() {
std::panic::set_hook(Box::new(move |info| { std::panic::set_hook(Box::new(move |info| {
let mut message = "Something went wrong".to_string(); let mut message = "Something went wrong".to_string();
@ -104,7 +118,7 @@ impl Panic {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::error::Error; use std::{borrow::ToOwned, error::Error};
use super::*; use super::*;

View File

@ -3,21 +3,26 @@ This module defines the core of the miette protocol: a series of types and
traits that you can implement to get access to miette's (and related library's) traits that you can implement to get access to miette's (and related library's)
full reporting and such features. full reporting and such features.
*/ */
use std::{ extern crate alloc;
fmt::{self, Display},
fs, use alloc::boxed::Box;
panic::Location, use alloc::string::String;
}; use core::fmt::{self, Display};
use core::ops;
#[cfg(feature = "std")]
use core::panic::Location;
#[cfg(feature = "std")]
use std::fs;
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{DiagnosticError, MietteError}; use crate::MietteError;
/// Adds rich metadata to your Error that can be used by /// Adds rich metadata to your Error that can be used by
/// [`Report`](crate::Report) to print really nice and human-friendly error /// [`Report`](crate::Report) to print really nice and human-friendly error
/// messages. /// messages.
pub trait Diagnostic: std::error::Error { pub trait Diagnostic: crate::StdError {
/// Unique diagnostic code that can be used to look up more information /// Unique diagnostic code that can be used to look up more information
/// about this `Diagnostic`. Ideally also globally unique, and documented /// about this `Diagnostic`. Ideally also globally unique, and documented
/// in the toplevel crate's documentation for easy searching. Rust path /// in the toplevel crate's documentation for easy searching. Rust path
@ -72,14 +77,10 @@ pub trait Diagnostic: std::error::Error {
macro_rules! box_error_impls { macro_rules! box_error_impls {
($($box_type:ty),*) => { ($($box_type:ty),*) => {
$( $(
impl std::error::Error for $box_type { impl crate::StdError for $box_type {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { fn source(&self) -> Option<&(dyn crate::StdError + 'static)> {
(**self).source() (**self).source()
} }
fn cause(&self) -> Option<&dyn std::error::Error> {
self.source()
}
} }
)* )*
} }
@ -94,7 +95,7 @@ box_error_impls! {
macro_rules! box_borrow_impls { macro_rules! box_borrow_impls {
($($box_type:ty),*) => { ($($box_type:ty),*) => {
$( $(
impl std::borrow::Borrow<dyn Diagnostic> for $box_type { impl core::borrow::Borrow<dyn Diagnostic> for $box_type {
fn borrow(&self) -> &(dyn Diagnostic + 'static) { fn borrow(&self) -> &(dyn Diagnostic + 'static) {
self.as_ref() self.as_ref()
} }
@ -152,7 +153,7 @@ impl From<String> for Box<dyn Diagnostic + Send + Sync> {
fn from(s: String) -> Self { fn from(s: String) -> Self {
struct StringError(String); struct StringError(String);
impl std::error::Error for StringError {} impl crate::StdError for StringError {}
impl Diagnostic for StringError {} impl Diagnostic for StringError {}
impl Display for StringError { impl Display for StringError {
@ -172,8 +173,12 @@ impl From<String> for Box<dyn Diagnostic + Send + Sync> {
} }
} }
impl From<Box<dyn std::error::Error + Send + Sync>> for Box<dyn Diagnostic + Send + Sync> { #[cfg(feature = "std")]
fn from(s: Box<dyn std::error::Error + Send + Sync>) -> Self { use crate::DiagnosticError;
#[cfg(feature = "std")]
impl From<Box<dyn core::error::Error + Send + Sync>> for Box<dyn Diagnostic + Send + Sync> {
fn from(s: Box<dyn core::error::Error + Send + Sync>) -> Self {
Box::new(DiagnosticError(s)) Box::new(DiagnosticError(s))
} }
} }
@ -369,6 +374,7 @@ impl LabeledSpan {
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
#[test] #[test]
fn test_serialize_labeled_span() { fn test_serialize_labeled_span() {
use alloc::string::ToString;
use serde_json::json; use serde_json::json;
assert_eq!( assert_eq!(
@ -392,6 +398,7 @@ fn test_serialize_labeled_span() {
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
#[test] #[test]
fn test_deserialize_labeled_span() { fn test_deserialize_labeled_span() {
use alloc::string::ToString;
use serde_json::json; use serde_json::json;
let span: LabeledSpan = serde_json::from_value(json!({ let span: LabeledSpan = serde_json::from_value(json!({
@ -594,8 +601,8 @@ impl From<(SourceOffset, usize)> for SourceSpan {
} }
} }
impl From<std::ops::Range<ByteOffset>> for SourceSpan { impl From<ops::Range<ByteOffset>> for SourceSpan {
fn from(range: std::ops::Range<ByteOffset>) -> Self { fn from(range: ops::Range<ByteOffset>) -> Self {
Self { Self {
offset: range.start.into(), offset: range.start.into(),
length: range.len(), length: range.len(),
@ -603,12 +610,12 @@ impl From<std::ops::Range<ByteOffset>> for SourceSpan {
} }
} }
impl From<std::ops::RangeInclusive<ByteOffset>> for SourceSpan { impl From<ops::RangeInclusive<ByteOffset>> for SourceSpan {
/// # Panics /// # Panics
/// ///
/// Panics if the total length of the inclusive range would overflow a /// Panics if the total length of the inclusive range would overflow a
/// `usize`. This will only occur with the range `0..=usize::MAX`. /// `usize`. This will only occur with the range `0..=usize::MAX`.
fn from(range: std::ops::RangeInclusive<ByteOffset>) -> Self { fn from(range: ops::RangeInclusive<ByteOffset>) -> Self {
let (start, end) = range.clone().into_inner(); let (start, end) = range.clone().into_inner();
Self { Self {
offset: start.into(), offset: start.into(),
@ -715,6 +722,7 @@ impl SourceOffset {
/// you're shipping binaries for your application, you'll want to ignore /// you're shipping binaries for your application, you'll want to ignore
/// the Err case or otherwise report it. /// the Err case or otherwise report it.
#[track_caller] #[track_caller]
#[cfg(feature = "std")]
pub fn from_current_location() -> Result<(String, Self), MietteError> { pub fn from_current_location() -> Result<(String, Self), MietteError> {
let loc = Location::caller(); let loc = Location::caller();
Ok(( Ok((

View File

@ -1,7 +1,16 @@
/*! /*!
Default trait implementations for [`SourceCode`]. Default trait implementations for [`SourceCode`].
*/ */
use std::{borrow::Cow, collections::VecDeque, fmt::Debug, sync::Arc}; extern crate alloc;
use alloc::borrow::Cow;
use alloc::borrow::ToOwned;
use alloc::boxed::Box;
use alloc::collections::VecDeque;
use alloc::string::String;
use alloc::sync::Arc;
use alloc::vec::Vec;
use core::fmt::Debug;
use crate::{MietteError, MietteSpanContents, SourceCode, SourceSpan, SpanContents}; use crate::{MietteError, MietteSpanContents, SourceCode, SourceSpan, SpanContents};
@ -205,7 +214,7 @@ mod tests {
fn basic() -> Result<(), MietteError> { fn basic() -> Result<(), MietteError> {
let src = String::from("foo\n"); let src = String::from("foo\n");
let contents = src.read_span(&(0, 4).into(), 0, 0)?; let contents = src.read_span(&(0, 4).into(), 0, 0)?;
assert_eq!("foo\n", std::str::from_utf8(contents.data()).unwrap()); assert_eq!("foo\n", core::str::from_utf8(contents.data()).unwrap());
assert_eq!(0, contents.line()); assert_eq!(0, contents.line());
assert_eq!(0, contents.column()); assert_eq!(0, contents.column());
Ok(()) Ok(())
@ -215,7 +224,7 @@ mod tests {
fn shifted() -> Result<(), MietteError> { fn shifted() -> Result<(), MietteError> {
let src = String::from("foobar"); let src = String::from("foobar");
let contents = src.read_span(&(3, 3).into(), 1, 1)?; let contents = src.read_span(&(3, 3).into(), 1, 1)?;
assert_eq!("foobar", std::str::from_utf8(contents.data()).unwrap()); assert_eq!("foobar", core::str::from_utf8(contents.data()).unwrap());
assert_eq!(0, contents.line()); assert_eq!(0, contents.line());
assert_eq!(0, contents.column()); assert_eq!(0, contents.column());
Ok(()) Ok(())
@ -225,7 +234,7 @@ mod tests {
fn middle() -> Result<(), MietteError> { fn middle() -> Result<(), MietteError> {
let src = String::from("foo\nbar\nbaz\n"); let src = String::from("foo\nbar\nbaz\n");
let contents = src.read_span(&(4, 4).into(), 0, 0)?; let contents = src.read_span(&(4, 4).into(), 0, 0)?;
assert_eq!("bar\n", std::str::from_utf8(contents.data()).unwrap()); assert_eq!("bar\n", core::str::from_utf8(contents.data()).unwrap());
assert_eq!(1, contents.line()); assert_eq!(1, contents.line());
assert_eq!(0, contents.column()); assert_eq!(0, contents.column());
Ok(()) Ok(())
@ -235,7 +244,7 @@ mod tests {
fn middle_of_line() -> Result<(), MietteError> { fn middle_of_line() -> Result<(), MietteError> {
let src = String::from("foo\nbarbar\nbaz\n"); let src = String::from("foo\nbarbar\nbaz\n");
let contents = src.read_span(&(7, 4).into(), 0, 0)?; let contents = src.read_span(&(7, 4).into(), 0, 0)?;
assert_eq!("bar\n", std::str::from_utf8(contents.data()).unwrap()); assert_eq!("bar\n", core::str::from_utf8(contents.data()).unwrap());
assert_eq!(1, contents.line()); assert_eq!(1, contents.line());
assert_eq!(3, contents.column()); assert_eq!(3, contents.column());
Ok(()) Ok(())
@ -245,7 +254,7 @@ mod tests {
fn with_crlf() -> Result<(), MietteError> { fn with_crlf() -> Result<(), MietteError> {
let src = String::from("foo\r\nbar\r\nbaz\r\n"); let src = String::from("foo\r\nbar\r\nbaz\r\n");
let contents = src.read_span(&(5, 5).into(), 0, 0)?; let contents = src.read_span(&(5, 5).into(), 0, 0)?;
assert_eq!("bar\r\n", std::str::from_utf8(contents.data()).unwrap()); assert_eq!("bar\r\n", core::str::from_utf8(contents.data()).unwrap());
assert_eq!(1, contents.line()); assert_eq!(1, contents.line());
assert_eq!(0, contents.column()); assert_eq!(0, contents.column());
Ok(()) Ok(())
@ -257,7 +266,7 @@ mod tests {
let contents = src.read_span(&(8, 3).into(), 1, 1)?; let contents = src.read_span(&(8, 3).into(), 1, 1)?;
assert_eq!( assert_eq!(
"foo\nbar\nbaz\n", "foo\nbar\nbaz\n",
std::str::from_utf8(contents.data()).unwrap() core::str::from_utf8(contents.data()).unwrap()
); );
assert_eq!(1, contents.line()); assert_eq!(1, contents.line());
assert_eq!(0, contents.column()); assert_eq!(0, contents.column());
@ -270,7 +279,7 @@ mod tests {
let contents = src.read_span(&(9, 11).into(), 1, 1)?; let contents = src.read_span(&(9, 11).into(), 1, 1)?;
assert_eq!( assert_eq!(
"\nfoo\nbar\nbaz\n\n", "\nfoo\nbar\nbaz\n\n",
std::str::from_utf8(contents.data()).unwrap() core::str::from_utf8(contents.data()).unwrap()
); );
assert_eq!(2, contents.line()); assert_eq!(2, contents.line());
assert_eq!(0, contents.column()); assert_eq!(0, contents.column());
@ -285,7 +294,7 @@ mod tests {
let contents = src.read_span(&(2, 0).into(), 2, 2)?; let contents = src.read_span(&(2, 0).into(), 2, 2)?;
assert_eq!( assert_eq!(
"one\ntwo\n\n", "one\ntwo\n\n",
std::str::from_utf8(contents.data()).unwrap() core::str::from_utf8(contents.data()).unwrap()
); );
assert_eq!(0, contents.line()); assert_eq!(0, contents.line());
assert_eq!(0, contents.column()); assert_eq!(0, contents.column());

View File

@ -83,7 +83,9 @@ fn check_colors<F: Fn(MietteHandlerOpts) -> MietteHandlerOpts>(
// //
// Since environment variables are shared for the entire process, we need // Since environment variables are shared for the entire process, we need
// to ensure that only one test that modifies these env vars runs at a time. // to ensure that only one test that modifies these env vars runs at a time.
let lock = COLOR_ENV_VARS.lock().unwrap(); let lock = COLOR_ENV_VARS
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
let guards = ( let guards = (
EnvVarGuard::new("NO_COLOR"), EnvVarGuard::new("NO_COLOR"),

View File

@ -10,5 +10,5 @@ pub fn bail_fmt() -> Result<()> {
} }
pub fn bail_error() -> Result<()> { pub fn bail_error() -> Result<()> {
bail!(io::Error::new(io::ErrorKind::Other, "oh no!")); bail!(io::Error::other("oh no!"));
} }

View File

@ -1,3 +1,5 @@
#![allow(unused_assignments)] // some fields unused when feature="fancy"
use miette::{Diagnostic, Report, Severity, SourceSpan}; use miette::{Diagnostic, Report, Severity, SourceSpan};
use thiserror::Error; use thiserror::Error;
@ -244,6 +246,7 @@ fn help_field() {
#[diagnostic()] #[diagnostic()]
struct Foo<'a> { struct Foo<'a> {
#[help] #[help]
#[allow(unused_assignments)]
do_this: Option<&'a str>, do_this: Option<&'a str>,
} }
@ -293,6 +296,7 @@ fn test_snippet_named_struct() {
#[error("welp")] #[error("welp")]
#[diagnostic(code(foo::bar::baz))] #[diagnostic(code(foo::bar::baz))]
#[allow(dead_code)] #[allow(dead_code)]
#[allow(unused_assignments)]
struct Foo<'a> { struct Foo<'a> {
#[source_code] #[source_code]
src: &'a str, src: &'a str,
@ -401,6 +405,7 @@ const SNIPPET_TEXT: &str = "hello from miette";
help("help"), help("help"),
severity(Warning) severity(Warning)
)] )]
#[allow(unused_assignments)]
struct ForwardsTo { struct ForwardsTo {
#[source_code] #[source_code]
src: String, src: String,
@ -501,6 +506,7 @@ fn test_forward_struct_named() {
help("{help}"), help("{help}"),
forward(span) forward(span)
)] )]
#[allow(unused_assignments)]
struct Struct<'a> { struct Struct<'a> {
span: ForwardsTo, span: ForwardsTo,
help: &'a str, help: &'a str,
@ -534,6 +540,7 @@ fn test_forward_enum_named() {
enum Enum<'a> { enum Enum<'a> {
#[error("help: {help_text}")] #[error("help: {help_text}")]
#[diagnostic(code(foo::bar::overridden), help("{help_text}"), forward(span))] #[diagnostic(code(foo::bar::overridden), help("{help_text}"), forward(span))]
#[allow(unused_assignments)]
Variant { Variant {
span: ForwardsTo, span: ForwardsTo,
help_text: &'a str, help_text: &'a str,
@ -595,6 +602,7 @@ fn test_unit_enum_display() {
fn test_optional_source_code() { fn test_optional_source_code() {
#[derive(Debug, Diagnostic, Error)] #[derive(Debug, Diagnostic, Error)]
#[error("struct with optional source")] #[error("struct with optional source")]
#[allow(unused_assignments)]
struct Struct { struct Struct {
#[source_code] #[source_code]
src: Option<String>, src: Option<String>,
@ -607,6 +615,7 @@ fn test_optional_source_code() {
.is_some()); .is_some());
#[derive(Debug, Diagnostic, Error)] #[derive(Debug, Diagnostic, Error)]
#[allow(unused_assignments)]
enum Enum { enum Enum {
#[error("variant1 with optional source")] #[error("variant1 with optional source")]
Variant1 { Variant1 {

View File

@ -42,13 +42,13 @@ fn test_boxed_str_stderr() {
#[test] #[test]
fn test_boxed_thiserror() { fn test_boxed_thiserror() {
let error = MyError { let error = MyError {
source: io::Error::new(io::ErrorKind::Other, "oh no!"), source: io::Error::other("oh no!"),
}; };
let report: Report = miette!(error); let report: Report = miette!(error);
assert_eq!("oh no!", report.source().unwrap().to_string()); assert_eq!("oh no!", report.source().unwrap().to_string());
let error = MyError { let error = MyError {
source: io::Error::new(io::ErrorKind::Other, "oh no!!!!"), source: io::Error::other("oh no!!!!"),
}; };
let error: Box<dyn Diagnostic + Send + Sync + 'static> = Box::new(error); let error: Box<dyn Diagnostic + Send + Sync + 'static> = Box::new(error);
let report = Report::new_boxed(error); let report = Report::new_boxed(error);
@ -203,7 +203,7 @@ fn test_boxed_custom_diagnostic() {
let related = CustomDiagnostic::new(); let related = CustomDiagnostic::new();
let main_diagnostic = CustomDiagnostic::new() let main_diagnostic = CustomDiagnostic::new()
.with_source(io::Error::new(io::ErrorKind::Other, "oh no!")) .with_source(io::Error::other("oh no!"))
.with_related(related); .with_related(related);
let report = Report::new_boxed(Box::new(main_diagnostic)); let report = Report::new_boxed(Box::new(main_diagnostic));
@ -211,7 +211,7 @@ fn test_boxed_custom_diagnostic() {
let related = CustomDiagnostic::new(); let related = CustomDiagnostic::new();
let main_diagnostic = CustomDiagnostic::new() let main_diagnostic = CustomDiagnostic::new()
.with_source(io::Error::new(io::ErrorKind::Other, "oh no!")) .with_source(io::Error::other("oh no!"))
.with_related(related); .with_related(related);
let main_diagnostic = Box::new(main_diagnostic) as Box<dyn Diagnostic + Send + Sync + 'static>; let main_diagnostic = Box::new(main_diagnostic) as Box<dyn Diagnostic + Send + Sync + 'static>;
let report = miette!(main_diagnostic); let report = miette!(main_diagnostic);
@ -228,7 +228,7 @@ fn test_boxed_custom_diagnostic() {
#[test] #[test]
fn test_boxed_sources() { fn test_boxed_sources() {
let error = MyError { let error = MyError {
source: io::Error::new(io::ErrorKind::Other, "oh no!"), source: io::Error::other("oh no!"),
}; };
let error = Box::<dyn Diagnostic + Send + Sync>::from(error); let error = Box::<dyn Diagnostic + Send + Sync>::from(error);
let error: Report = miette!(error).wrap_err("it failed"); let error: Report = miette!(error).wrap_err("it failed");

View File

@ -1,4 +1,5 @@
// Testing of the `diagnostic` attr used by derive(Diagnostic) // Testing of the `diagnostic` attr used by derive(Diagnostic)
use miette::{Diagnostic, LabeledSpan, NamedSource, SourceSpan}; use miette::{Diagnostic, LabeledSpan, NamedSource, SourceSpan};
use thiserror::Error; use thiserror::Error;

View File

@ -50,7 +50,7 @@ fn test_fmt_source() {
#[test] #[test]
#[ignore = "Again with the io::Error source issue?"] #[ignore = "Again with the io::Error source issue?"]
fn test_io_source() { fn test_io_source() {
let io = io::Error::new(io::ErrorKind::Other, "oh no!"); let io = io::Error::other("oh no!");
let error: Report = miette!(TestError::Io(io)); let error: Report = miette!(TestError::Io(io));
assert_eq!("oh no!", error.source().unwrap().to_string()); assert_eq!("oh no!", error.source().unwrap().to_string());
} }