diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7d10924..161aecc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 @@ -84,6 +99,20 @@ jobs: MIRIFLAGS: -Zmiri-disable-isolation -Zmiri-strict-provenance 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: name: Minimal versions check runs-on: ${{ matrix.os }} diff --git a/Cargo.toml b/Cargo.toml index f89f14b..ae6292e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ exclude = ["images/", "tests/", "miette-derive/"] miette-derive = { path = "miette-derive", version = "=7.6.0", optional = true } unicode-width = "0.2.0" cfg-if = "1.0.0" +spin = { version = "0.9", default-features = false, features = ["once"] } owo-colors = { version = "4.0.0", 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] 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" + [features] -default = ["derive"] +default = ["derive", "std"] +std = [] derive = ["dep:miette-derive"] no-format-args-capture = [] fancy-base = [ @@ -57,6 +57,7 @@ fancy-no-syscall = [ ] fancy-no-backtrace = [ "fancy-base", + "std", "dep:terminal_size", "dep:supports-hyperlinks", "dep:supports-color", diff --git a/README.md b/README.md index 8ecc16d..084ce0d 100644 --- a/README.md +++ b/README.md @@ -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. @@ -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 diff --git a/examples/serde_json.rs b/examples/serde_json.rs index d57a76a..be3fbe1 100644 --- a/examples/serde_json.rs +++ b/examples/serde_json.rs @@ -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}; diff --git a/miette-derive/src/code.rs b/miette-derive/src/code.rs index 22dc795..04b655e 100644 --- a/miette-derive/src/code.rs +++ b/miette-derive/src/code.rs @@ -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(miette::__alloc::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(miette::__alloc::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(miette::__alloc::Box::new(#code)), } } }) }, @@ -72,8 +72,8 @@ impl Code { pub(crate) fn gen_struct(&self) -> Option { let code = &self.0; Some(quote! { - fn code(&self) -> std::option::Option> { - std::option::Option::Some(std::boxed::Box::new(#code)) + fn code(&self) -> core::option::Option> { + core::option::Option::Some(miette::__alloc::Box::new(#code)) } }) } diff --git a/miette-derive/src/diagnostic_source.rs b/miette-derive/src/diagnostic_source.rs index 1104eb7..fff97b1 100644 --- a/miette-derive/src/diagnostic_source.rs +++ b/miette-derive/src/diagnostic_source.rs @@ -59,7 +59,7 @@ impl DiagnosticSource { }; quote! { 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 { 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(miette::__alloc::borrow::Borrow::borrow(&self.#rel)) } }) } diff --git a/miette-derive/src/forward.rs b/miette-derive/src/forward.rs index d170366..278d6ca 100644 --- a/miette-derive/src/forward.rs +++ b/miette-derive/src/forward.rs @@ -58,34 +58,34 @@ impl WhichFn { pub fn signature(&self) -> TokenStream { match self { Self::Code => quote! { - fn code(& self) -> std::option::Option> + fn code(& self) -> Option> }, Self::Help => quote! { - fn help(& self) -> std::option::Option> + fn help(& self) -> Option> }, Self::Url => quote! { - fn url(& self) -> std::option::Option> + fn url(& self) -> Option> }, Self::Severity => quote! { - fn severity(&self) -> std::option::Option + fn severity(&self) -> Option }, Self::Related => quote! { - fn related(&self) -> std::option::Option + '_>> + fn related(&self) -> Option + '_>> }, Self::Labels => quote! { - fn labels(&self) -> std::option::Option + '_>> + fn labels(&self) -> Option + '_>> }, 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 } } } diff --git a/miette-derive/src/help.rs b/miette-derive/src/help.rs index 1c21054..685537c 100644 --- a/miette-derive/src/help.rs +++ b/miette-derive/src/help.rs @@ -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(miette::__alloc::Box::new(format!(#fmt #args))), }) } Help::Field(member, ty) => { @@ -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 { std::boxed::Box::new(format!("{}", #var)) }) + miette::macro_helpers::OptionalWrapper::<#ty>::new().to_option(&#help).as_ref().map(|#var| -> miette::__alloc::Box { miette::__alloc::Box::new(format!("{}", #var)) }) }, }) } @@ -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> { + fn help(&self) -> Option> { #[allow(unused_variables, deprecated)] 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) => { let var = quote! { __miette_internal_var }; Some(quote! { - fn help(&self) -> std::option::Option> { + fn help(&self) -> Option> { #[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 { std::boxed::Box::new(format!("{}", #var)) }) + miette::macro_helpers::OptionalWrapper::<#ty>::new().to_option(&self.#member).as_ref().map(|#var| -> miette::__alloc::Box { miette::__alloc::Box::new(format!("{}", #var)) }) } }) } diff --git a/miette-derive/src/label.rs b/miette-derive/src/label.rs index ab2ceac..ff3738a 100644 --- a/miette-derive/src/label.rs +++ b/miette-derive/src/label.rs @@ -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 } @@ -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({ @@ -226,7 +226,7 @@ impl Labels { Some(quote! { #[allow(unused_variables)] - fn labels(&self) -> std::option::Option + '_>> { + fn labels(&self) -> Option + '_>> { use miette::macro_helpers::ToOption; let Self #display_pat = self; @@ -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(miette::__alloc::Box::new(labels_iter.filter(Option::is_some).map(Option::unwrap))) } }) } @@ -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(miette::__alloc::Box::new(labels_iter.filter(Option::is_some).map(Option::unwrap))) } }), } diff --git a/miette-derive/src/related.rs b/miette-derive/src/related.rs index 9b7f9e1..6c0a276 100644 --- a/miette-derive/src/related.rs +++ b/miette-derive/src/related.rs @@ -55,7 +55,7 @@ impl Related { }; quote! { 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 }) )) } @@ -68,9 +68,9 @@ impl Related { pub(crate) fn gen_struct(&self) -> Option { let rel = &self.0; Some(quote! { - fn related<'a>(&'a self) -> std::option::Option + 'a>> { - use ::core::borrow::Borrow; - std::option::Option::Some(std::boxed::Box::new( + fn related<'a>(&'a self) -> Option + 'a>> { + use miette::__alloc::borrow::Borrow; + Option::Some(miette::__alloc::Box::new( self.#rel.iter().map(|x| -> &(dyn miette::Diagnostic) { &*x.borrow() }) )) } diff --git a/miette-derive/src/severity.rs b/miette-derive/src/severity.rs index 4f26e4e..a6f664a 100644 --- a/miette-derive/src/severity.rs +++ b/miette-derive/src/severity.rs @@ -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), }) }, ) } diff --git a/miette-derive/src/source_code.rs b/miette-derive/src/source_code.rs index e1b85ab..f130953 100644 --- a/miette-derive/src/source_code.rs +++ b/miette-derive/src/source_code.rs @@ -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 } @@ -101,7 +101,7 @@ impl SourceCode { } } else { quote! { - std::option::Option::Some(#field) + Option::Some(#field) } }; match &fields { diff --git a/miette-derive/src/url.rs b/miette-derive/src/url.rs index 734d1a4..156315b 100644 --- a/miette-derive/src/url.rs +++ b/miette-derive/src/url.rs @@ -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(miette::__alloc::Box::new(format!(#fmt #args))), }) }, ) @@ -129,10 +129,10 @@ impl Url { } }; Some(quote! { - fn url(&self) -> std::option::Option> { + fn url(&self) -> Option> { #[allow(unused_variables, deprecated)] let Self #pat = self; - std::option::Option::Some(std::boxed::Box::new(format!(#fmt #args))) + Option::Some(miette::__alloc::Box::new(format!(#fmt #args))) } }) } diff --git a/src/chain.rs b/src/chain.rs index 369ff7d..6880678 100644 --- a/src/chain.rs +++ b/src/chain.rs @@ -3,8 +3,10 @@ Iterate over error `.source()` chains. NOTE: This module is taken wholesale from . */ -use std::error::Error as StdError; -use std::vec; +extern crate alloc; + +use crate::StdError; +use alloc::vec::{self, Vec}; use ChainState::*; diff --git a/src/diagnostic_chain.rs b/src/diagnostic_chain.rs index 996fdba..7fcfa20 100644 --- a/src/diagnostic_chain.rs +++ b/src/diagnostic_chain.rs @@ -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 { state: Some(ErrorKind::StdError(head)), } @@ -59,7 +59,7 @@ impl ExactSizeIterator for DiagnosticChain<'_> { #[derive(Clone)] pub(crate) enum ErrorKind<'a> { Diagnostic(&'a dyn Diagnostic), - StdError(&'a (dyn std::error::Error + 'static)), + StdError(&'a (dyn crate::StdError + 'static)), } impl<'a> ErrorKind<'a> { @@ -74,8 +74,8 @@ impl<'a> ErrorKind<'a> { } } -impl std::fmt::Debug for ErrorKind<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl core::fmt::Debug for ErrorKind<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { ErrorKind::Diagnostic(d) => d.fmt(f), ErrorKind::StdError(e) => e.fmt(f), @@ -83,8 +83,8 @@ impl std::fmt::Debug for ErrorKind<'_> { } } -impl std::fmt::Display for ErrorKind<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl core::fmt::Display for ErrorKind<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { ErrorKind::Diagnostic(d) => d.fmt(f), ErrorKind::StdError(e) => e.fmt(f), diff --git a/src/diagnostic_impls.rs b/src/diagnostic_impls.rs index aebe56b..fc635d4 100644 --- a/src/diagnostic_impls.rs +++ b/src/diagnostic_impls.rs @@ -1,11 +1,16 @@ /*! 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}; +// 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 { fn code<'a>(&'a self) -> Option> { match *self {} diff --git a/src/error.rs b/src/error.rs index f77ce1e..1f34865 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,8 +1,10 @@ -use std::{ - error::Error, - fmt::{self, Display}, - io, -}; +extern crate alloc; + +use alloc::boxed::Box; +use core::error::Error; +use core::fmt::{self, Display}; +#[cfg(feature = "std")] +use std::io; use crate::Diagnostic; @@ -11,8 +13,9 @@ Error enum for miette. Used by certain operations in the protocol. */ #[derive(Debug)] 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). + #[cfg(feature = "std")] IoError(io::Error), /// Returned when a [`SourceSpan`](crate::SourceSpan) extends beyond the @@ -23,6 +26,7 @@ pub enum MietteError { impl Display for MietteError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { + #[cfg(feature = "std")] MietteError::IoError(error) => write!(f, "{error}"), MietteError::OutOfBounds => { write!(f, "The given offset is outside the bounds of its Source") @@ -34,12 +38,14 @@ impl Display for MietteError { impl Error for MietteError { fn source(&self) -> Option<&(dyn Error + 'static)> { match self { + #[cfg(feature = "std")] MietteError::IoError(error) => error.source(), MietteError::OutOfBounds => None, } } } +#[cfg(feature = "std")] impl From for MietteError { fn from(value: io::Error) -> Self { Self::IoError(value) @@ -49,6 +55,7 @@ impl From for MietteError { impl Diagnostic for MietteError { fn code<'a>(&'a self) -> Option> { match self { + #[cfg(feature = "std")] MietteError::IoError(_) => Some(Box::new("miette::io_error")), MietteError::OutOfBounds => Some(Box::new("miette::span_out_of_bounds")), } @@ -56,6 +63,7 @@ impl Diagnostic for MietteError { fn help<'a>(&'a self) -> Option> { match self { + #[cfg(feature = "std")] MietteError::IoError(_) => None, MietteError::OutOfBounds => Some(Box::new( "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> { let crate_version = env!("CARGO_PKG_VERSION"); let variant = match self { + #[cfg(feature = "std")] MietteError::IoError(_) => "#variant.IoError", MietteError::OutOfBounds => "#variant.OutOfBounds", }; - Some(Box::new(format!( + Some(Box::new(alloc::format!( "https://docs.rs/miette/{}/miette/enum.MietteError.html{}", - crate_version, variant, + crate_version, + variant, ))) } } #[cfg(test)] 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::*; #[derive(Debug)] + #[cfg(feature = "std")] 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 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -91,17 +107,26 @@ pub(crate) mod tests { } } + #[cfg(feature = "std")] impl Error for TestError { fn source(&self) -> Option<&(dyn Error + 'static)> { Some(&self.0) } } + #[cfg(not(feature = "std"))] + impl Error for TestError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + None + } + } + + #[cfg(feature = "std")] #[test] 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 io_error = io::Error::new(ErrorKind::Other, outer_error); + let io_error = io::Error::other(outer_error); let miette_error = MietteError::from(io_error); diff --git a/src/eyreish/context.rs b/src/eyreish/context.rs index ee1fa08..c92d651 100644 --- a/src/eyreish/context.rs +++ b/src/eyreish/context.rs @@ -1,8 +1,12 @@ +extern crate alloc; + use super::error::{ContextError, ErrorImpl}; use super::{Report, WrapErr}; +use core::convert::Infallible; use core::fmt::{self, Debug, Display, Write}; -use std::error::Error as StdError; +use crate::StdError; +use alloc::boxed::Box; use crate::{Diagnostic, LabeledSpan}; @@ -10,7 +14,7 @@ mod ext { use super::*; pub trait Diag { - #[cfg_attr(track_caller, track_caller)] + #[track_caller] fn ext_report(self, msg: D) -> Report where D: Display + Send + Sync + 'static; @@ -20,6 +24,7 @@ mod ext { where E: Diagnostic + Send + Sync + 'static, { + #[track_caller] fn ext_report(self, msg: D) -> Report where D: Display + Send + Sync + 'static, @@ -29,6 +34,7 @@ mod ext { } impl Diag for Report { + #[track_caller] fn ext_report(self, msg: D) -> Report where D: Display + Send + Sync + 'static, @@ -38,7 +44,8 @@ mod ext { } } -impl WrapErr for Option { +impl WrapErr for Option { + #[track_caller] fn wrap_err(self, msg: D) -> Result where D: Display + Send + Sync + 'static, @@ -49,6 +56,7 @@ impl WrapErr for Option { } } + #[track_caller] fn wrap_err_with(self, msg: F) -> Result where D: Display + Send + Sync + 'static, @@ -60,6 +68,7 @@ impl WrapErr for Option { } } + #[track_caller] fn context(self, msg: D) -> Result where D: Display + Send + Sync + 'static, @@ -67,6 +76,7 @@ impl WrapErr for Option { self.wrap_err(msg) } + #[track_caller] fn with_context(self, msg: F) -> Result where D: Display + Send + Sync + 'static, @@ -80,6 +90,7 @@ impl WrapErr for Result where E: ext::Diag + Send + Sync + 'static, { + #[track_caller] fn wrap_err(self, msg: D) -> Result where D: Display + Send + Sync + 'static, @@ -90,6 +101,7 @@ where } } + #[track_caller] fn wrap_err_with(self, msg: F) -> Result where D: Display + Send + Sync + 'static, @@ -101,6 +113,7 @@ where } } + #[track_caller] fn context(self, msg: D) -> Result where D: Display + Send + Sync + 'static, @@ -108,6 +121,7 @@ where self.wrap_err(msg) } + #[track_caller] fn with_context(self, msg: F) -> Result where D: Display + Send + Sync + 'static, diff --git a/src/eyreish/error.rs b/src/eyreish/error.rs index 706470e..ac84975 100644 --- a/src/eyreish/error.rs +++ b/src/eyreish/error.rs @@ -1,8 +1,11 @@ +extern crate alloc; + +use crate::StdError; +use alloc::boxed::Box; use core::any::TypeId; use core::fmt::{self, Debug, Display}; use core::mem::ManuallyDrop; use core::ptr::{self, NonNull}; -use std::error::Error as StdError; use super::ptr::{Mut, Own, Ref}; use super::Report; @@ -20,7 +23,7 @@ impl Report { /// /// If the error type does not provide a backtrace, a backtrace will be /// created here to ensure that a backtrace exists. - #[cfg_attr(track_caller, track_caller)] + #[track_caller] #[cold] pub fn new(error: E) -> Self where @@ -66,7 +69,7 @@ impl Report { /// .await /// } /// ``` - #[cfg_attr(track_caller, track_caller)] + #[track_caller] #[cold] pub fn msg(message: M) -> Self where @@ -82,12 +85,11 @@ impl Report { /// /// 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`. - #[cfg_attr(track_caller, track_caller)] + #[track_caller] pub fn new_boxed(error: Box) -> Self { Report::from_boxed(error) } - #[cfg_attr(track_caller, track_caller)] #[cold] pub(crate) fn from_std(error: E) -> Self where @@ -109,7 +111,7 @@ impl Report { unsafe { Report::construct(error, vtable, handler) } } - #[cfg_attr(track_caller, track_caller)] + #[track_caller] #[cold] pub(crate) fn from_adhoc(message: M) -> Self where @@ -134,7 +136,7 @@ impl Report { unsafe { Report::construct(error, vtable, handler) } } - #[cfg_attr(track_caller, track_caller)] + #[track_caller] #[cold] pub(crate) fn from_msg(msg: D, error: E) -> Self where @@ -154,12 +156,12 @@ impl Report { }; // 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) } } - #[cfg_attr(track_caller, track_caller)] + #[track_caller] #[cold] pub(crate) fn from_boxed(error: Box) -> Self { use super::wrapper::BoxedError; @@ -429,7 +431,7 @@ impl Report { /// Construct a [`Report`] directly from an error-like type pub fn from_err(err: E) -> Self where - E: std::error::Error + Send + Sync + 'static, + E: StdError + Send + Sync + 'static, { super::DiagnosticError(Box::new(err)).into() } @@ -439,7 +441,6 @@ impl From for Report where E: Diagnostic + Send + Sync + 'static, { - #[cfg_attr(track_caller, track_caller)] #[cold] fn from(error: E) -> Self { Report::from_std(error) @@ -816,7 +817,7 @@ impl AsRef for Report { } } -impl std::borrow::Borrow for Report { +impl core::borrow::Borrow for Report { fn borrow(&self) -> &(dyn Diagnostic + 'static) { self.as_ref() } diff --git a/src/eyreish/into_diagnostic.rs b/src/eyreish/into_diagnostic.rs index 1f90c34..758b0e1 100644 --- a/src/eyreish/into_diagnostic.rs +++ b/src/eyreish/into_diagnostic.rs @@ -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}; /// Convenience [`Diagnostic`] that can be used as an "anonymous" wrapper for /// Errors. This is intended to be paired with [`IntoDiagnostic`]. #[derive(Debug)] -pub(crate) struct DiagnosticError(pub(crate) Box); +pub(crate) struct DiagnosticError(pub(crate) Box); -impl Display for DiagnosticError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl core::fmt::Display for DiagnosticError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let msg = &self.0; write!(f, "{msg}") } @@ -35,10 +38,12 @@ inaccessible. If you have a type implementing [`Diagnostic`] consider simply ret pub trait IntoDiagnostic { /// Converts [`Result`] types that return regular [`std::error::Error`]s /// into a [`Result`] that returns a [`Diagnostic`]. + #[track_caller] fn into_diagnostic(self) -> Result; } -impl IntoDiagnostic for Result { +impl IntoDiagnostic for Result { + #[track_caller] fn into_diagnostic(self) -> Result { self.map_err(|e| DiagnosticError(Box::new(e)).into()) } @@ -46,15 +51,21 @@ impl IntoDiagnostic for R #[cfg(test)] 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; + #[cfg(feature = "std")] #[test] 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 diagnostic_error = outer_error.into_diagnostic().unwrap_err(); diff --git a/src/eyreish/kind.rs b/src/eyreish/kind.rs index 6b0f5ec..7f78df8 100644 --- a/src/eyreish/kind.rs +++ b/src/eyreish/kind.rs @@ -45,9 +45,12 @@ // let error = $msg; // (&error).miette_kind().new(error) -use super::Report; +extern crate alloc; + +use alloc::boxed::Box; use core::fmt::{Debug, Display}; +use super::Report; use crate::Diagnostic; pub struct Adhoc; @@ -62,7 +65,7 @@ pub trait AdhocKind: Sized { impl AdhocKind for &T where T: ?Sized + Display + Debug + Send + Sync + 'static {} impl Adhoc { - #[cfg_attr(track_caller, track_caller)] + #[track_caller] #[cold] pub fn new(self, message: M) -> Report where @@ -84,7 +87,7 @@ pub trait TraitKind: Sized { impl TraitKind for E where E: Into {} impl Trait { - #[cfg_attr(track_caller, track_caller)] + #[track_caller] #[cold] pub fn new(self, error: E) -> Report where @@ -106,7 +109,7 @@ pub trait BoxedKind: Sized { impl BoxedKind for Box {} impl Boxed { - #[cfg_attr(track_caller, track_caller)] + #[track_caller] #[cold] pub fn new(self, error: Box) -> Report { Report::from_boxed(error) diff --git a/src/eyreish/mod.rs b/src/eyreish/mod.rs index 3f35103..3667b73 100644 --- a/src/eyreish/mod.rs +++ b/src/eyreish/mod.rs @@ -4,9 +4,13 @@ clippy::new_ret_no_self, clippy::wrong_self_convention )] +extern crate alloc; + +use alloc::boxed::Box; use core::fmt::Display; -use std::error::Error as StdError; +use core::error::Error as StdError; +#[cfg(feature = "std")] use std::sync::OnceLock; #[allow(unreachable_pub)] @@ -61,8 +65,16 @@ unsafe impl Send for Report {} pub type ErrorHook = Box Box + Sync + Send + 'static>; +#[cfg(feature = "std")] static HOOK: OnceLock = OnceLock::new(); +#[cfg(not(feature = "std"))] +static HOOK: spin::Once = spin::Once::new(); + +fn default_hook() -> ErrorHook { + Box::new(get_default_printer) +} + /// Error indicating that [`set_hook()`] was unable to install the provided /// [`ErrorHook`]. #[derive(Debug)] @@ -77,28 +89,51 @@ impl core::fmt::Display for InstallError { impl StdError 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> { HOOK.set(hook).map_err(|_| InstallError) } -#[cfg_attr(track_caller, track_caller)] -#[cfg_attr(not(track_caller), allow(unused_mut))] -fn capture_handler(error: &(dyn Diagnostic + 'static)) -> Box { - let hook = HOOK.get_or_init(|| Box::new(get_default_printer)).as_ref(); +/// Set the error hook. +#[cfg(not(feature = "std"))] +pub fn set_hook(hook: ErrorHook) -> Result<(), InstallError> { + HOOK.call_once(|| hook); + Ok(()) +} - #[cfg(track_caller)] - { - let mut handler = hook(error); - handler.track_caller(std::panic::Location::caller()); - handler - } - #[cfg(not(track_caller))] - { - hook(error) - } +#[cfg(feature = "std")] +pub(crate) fn capture_handler(error: &(dyn Diagnostic + 'static)) -> Box { + let hook = HOOK.get_or_init(default_hook); + hook(error) +} + +#[cfg(not(feature = "std"))] +pub(crate) fn capture_handler(error: &(dyn Diagnostic + 'static)) -> Box { + 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 { + 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 { + 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 { @@ -193,7 +228,7 @@ pub trait ReportHandler: core::any::Any + Send + Sync { /// Store the location of the caller who constructed this error report #[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` @@ -428,27 +463,27 @@ pub type Result = core::result::Result; /// ``` pub trait WrapErr: context::private::Sealed { /// Wrap the error value with a new adhoc error - #[cfg_attr(track_caller, track_caller)] + #[track_caller] fn wrap_err(self, msg: D) -> Result where D: Display + Send + Sync + 'static; /// Wrap the error value with a new adhoc error that is evaluated lazily /// only once an error does occur. - #[cfg_attr(track_caller, track_caller)] + #[track_caller] fn wrap_err_with(self, f: F) -> Result where D: Display + Send + Sync + 'static, F: FnOnce() -> D; /// Compatibility re-export of `wrap_err()` for interop with `anyhow` - #[cfg_attr(track_caller, track_caller)] + #[track_caller] fn context(self, msg: D) -> Result where D: Display + Send + Sync + 'static; /// Compatibility re-export of `wrap_err_with()` for interop with `anyhow` - #[cfg_attr(track_caller, track_caller)] + #[track_caller] fn with_context(self, f: F) -> Result where D: Display + Send + Sync + 'static, @@ -470,7 +505,7 @@ pub mod private { pub use super::super::kind::BoxedKind; } - #[cfg_attr(track_caller, track_caller)] + #[track_caller] #[cold] pub fn new_adhoc(message: M) -> Report where diff --git a/src/eyreish/ptr.rs b/src/eyreish/ptr.rs index 40c9c7b..9852b69 100644 --- a/src/eyreish/ptr.rs +++ b/src/eyreish/ptr.rs @@ -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)] /// A raw pointer that owns its pointee diff --git a/src/eyreish/wrapper.rs b/src/eyreish/wrapper.rs index bab7320..fd8af1b 100644 --- a/src/eyreish/wrapper.rs +++ b/src/eyreish/wrapper.rs @@ -1,6 +1,9 @@ +extern crate alloc; + 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}; @@ -107,16 +110,6 @@ impl StdError for BoxedError { fn source(&self) -> Option<&(dyn StdError + 'static)> { 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 { @@ -216,8 +209,16 @@ impl StdError for WithSourceCode { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] 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 crate::{Diagnostic, LabeledSpan, Report, SourceCode, SourceSpan}; @@ -231,7 +232,7 @@ mod tests { impl Diagnostic for Inner { fn labels(&self) -> Option + '_>> { - 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> { diff --git a/src/handler.rs b/src/handler.rs index 5cd5a53..21ab0e7 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -1,3 +1,7 @@ +extern crate alloc; +use alloc::boxed::Box; +use alloc::string::String; + use crate::highlighters::Highlighter; use crate::highlighters::MietteHighlighter; use crate::protocol::Diagnostic; @@ -8,7 +12,7 @@ use crate::ReportHandler; use crate::ThemeCharacters; use crate::ThemeStyles; use cfg_if::cfg_if; -use std::fmt; +use core::fmt; /// Settings to control the color format used for graphical rendering. #[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 { if let Some(force_narrated) = self.force_narrated { !force_narrated } else if let Some(force_graphical) = self.force_graphical { force_graphical - } else if let Ok(env) = std::env::var("NO_GRAPHICS") { - env == "0" } 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 for MietteHighlighter { } mod syscall { - use cfg_if::cfg_if; - #[inline] pub(super) fn terminal_width() -> Option { - cfg_if! { - if #[cfg(any(feature = "fancy-no-syscall", miri))] { - None - } else { - terminal_size::terminal_size().map(|size| size.0 .0 as usize) - } + #[cfg(all(feature = "fancy-no-backtrace", not(miri)))] + { + terminal_size::terminal_size().map(|size| size.0 .0 as usize) + } + #[cfg(any(not(feature = "fancy-no-backtrace"), miri))] + { + None } } #[inline] pub(super) fn supports_hyperlinks() -> bool { - cfg_if! { - if #[cfg(feature = "fancy-no-syscall")] { - false - } else { - supports_hyperlinks::on(supports_hyperlinks::Stream::Stderr) - } + #[cfg(feature = "fancy-no-backtrace")] + { + supports_hyperlinks::on(supports_hyperlinks::Stream::Stderr) + } + #[cfg(not(feature = "fancy-no-backtrace"))] + { + false } } #[inline] pub(super) fn supports_color() -> bool { - cfg_if! { - if #[cfg(feature = "fancy-no-syscall")] { - false - } else { - supports_color::on(supports_color::Stream::Stderr).is_some() - } + #[cfg(feature = "fancy-no-backtrace")] + { + supports_color::on(supports_color::Stream::Stderr).is_some() + } + #[cfg(not(feature = "fancy-no-backtrace"))] + { + false } } #[inline] pub(super) fn supports_color_has_16m() -> Option { - cfg_if! { - if #[cfg(feature = "fancy-no-syscall")] { - None - } else { - supports_color::on(supports_color::Stream::Stderr).map(|color| color.has_16m) - } + #[cfg(feature = "fancy-no-backtrace")] + { + supports_color::on(supports_color::Stream::Stderr).map(|color| color.has_16m) + } + #[cfg(not(feature = "fancy-no-backtrace"))] + { + None } } #[inline] pub(super) fn supports_unicode() -> bool { - cfg_if! { - if #[cfg(feature = "fancy-no-syscall")] { - false - } else { - supports_unicode::on(supports_unicode::Stream::Stderr) - } + #[cfg(feature = "fancy-no-backtrace")] + { + supports_unicode::on(supports_unicode::Stream::Stderr) + } + #[cfg(not(feature = "fancy-no-backtrace"))] + { + false } } } diff --git a/src/handlers/debug.rs b/src/handlers/debug.rs index 629908b..8e9ff3c 100644 --- a/src/handlers/debug.rs +++ b/src/handlers/debug.rs @@ -1,4 +1,7 @@ -use std::fmt; +extern crate alloc; + +use alloc::vec::Vec; +use core::fmt; use crate::{protocol::Diagnostic, ReportHandler}; @@ -34,25 +37,25 @@ impl DebugReportHandler { diagnostic: &dyn Diagnostic, ) -> fmt::Result { let mut diag = f.debug_struct("Diagnostic"); - diag.field("message", &format!("{}", diagnostic)); + diag.field("message", &alloc::format!("{}", diagnostic)); if let Some(code) = diagnostic.code() { - diag.field("code", &code.to_string()); + diag.field("code", &alloc::format!("{}", code)); } if let Some(severity) = diagnostic.severity() { - diag.field("severity", &format!("{:?}", severity)); + diag.field("severity", &alloc::format!("{:?}", severity)); } if let Some(url) = diagnostic.url() { - diag.field("url", &url.to_string()); + diag.field("url", &alloc::format!("{}", url)); } if let Some(help) = diagnostic.help() { - diag.field("help", &help.to_string()); + diag.field("help", &alloc::format!("{}", help)); } if let Some(labels) = diagnostic.labels() { let labels: Vec<_> = labels.collect(); - diag.field("labels", &format!("{:?}", labels)); + diag.field("labels", &alloc::format!("{:?}", labels)); } if let Some(cause) = diagnostic.diagnostic_source() { - diag.field("caused by", &format!("{:?}", cause)); + diag.field("caused by", &alloc::format!("{:?}", cause)); } diag.finish()?; writeln!(f)?; diff --git a/src/handlers/graphical.rs b/src/handlers/graphical.rs index 37b6bf8..74fb261 100644 --- a/src/handlers/graphical.rs +++ b/src/handlers/graphical.rs @@ -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 unicode_width::{UnicodeWidthChar, UnicodeWidthStr}; @@ -582,7 +588,7 @@ impl GraphicalReportHandler { // The snippets will overlap, so we create one Big Chunky Boi let left_end = left.offset() + left.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( left.label().map(String::from), @@ -649,7 +655,7 @@ impl GraphicalReportHandler { 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 @@ -1173,7 +1179,7 @@ impl GraphicalReportHandler { .style(hl.style) .to_string(), ); - highest = std::cmp::max(highest, end); + highest = core::cmp::max(highest, end); (hl, vbar_offset) }) diff --git a/src/handlers/json.rs b/src/handlers/json.rs index e37064c..a5d5708 100644 --- a/src/handlers/json.rs +++ b/src/handlers/json.rs @@ -1,4 +1,7 @@ -use std::fmt::{self, Write}; +extern crate alloc; + +use alloc::string::ToString; +use core::fmt::{self, Write}; use crate::{ diagnostic_chain::DiagnosticChain, protocol::Diagnostic, ReportHandler, Severity, SourceCode, diff --git a/src/handlers/narratable.rs b/src/handlers/narratable.rs index 8dead98..418b9ef 100644 --- a/src/handlers/narratable.rs +++ b/src/handlers/narratable.rs @@ -1,10 +1,15 @@ -use std::fmt; +extern crate alloc; + +use core::fmt; use unicode_width::{UnicodeWidthChar, UnicodeWidthStr}; use crate::diagnostic_chain::DiagnosticChain; use crate::protocol::{Diagnostic, Severity}; 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. @@ -290,7 +295,7 @@ impl NarratableReportHandler { let context_data = source .read_span(context_span, self.context_lines, self.context_lines) .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 column = context_data.column(); let mut offset = context_data.span().offset(); diff --git a/src/handlers/theme.rs b/src/handlers/theme.rs index 360b2da..4f61c3b 100644 --- a/src/handlers/theme.rs +++ b/src/handlers/theme.rs @@ -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; /** @@ -69,12 +71,20 @@ impl GraphicalTheme { impl Default for GraphicalTheme { fn default() -> Self { - match std::env::var("NO_COLOR") { - _ if !std::io::stdout().is_terminal() || !std::io::stderr().is_terminal() => { - Self::none() + #[cfg(feature = "fancy-no-backtrace")] + { + 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() } } } diff --git a/src/highlighters/blank.rs b/src/highlighters/blank.rs index 50a9c65..f32f9df 100644 --- a/src/highlighters/blank.rs +++ b/src/highlighters/blank.rs @@ -1,3 +1,8 @@ +extern crate alloc; + +use alloc::boxed::Box; +use alloc::vec; +use alloc::vec::Vec; use owo_colors::Style; use crate::SpanContents; diff --git a/src/highlighters/mod.rs b/src/highlighters/mod.rs index 0af1aa2..437aac9 100644 --- a/src/highlighters/mod.rs +++ b/src/highlighters/mod.rs @@ -11,7 +11,12 @@ //! * `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 owo_colors::Styled; @@ -78,21 +83,22 @@ impl MietteHighlighter { } impl Default for MietteHighlighter { - #[cfg(feature = "syntect-highlighter")] fn default() -> Self { - use std::io::IsTerminal; - match std::env::var("NO_COLOR") { - _ if !std::io::stdout().is_terminal() || !std::io::stderr().is_terminal() => { - //TODO: should use ANSI styling instead of 24-bit truecolor here - Self(Arc::new(SyntectHighlighter::default())) + #[cfg(feature = "syntect-highlighter")] + { + use std::io::IsTerminal; + match std::env::var("NO_COLOR") { + _ 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"))] - fn default() -> Self { - MietteHighlighter::nocolor() + #[cfg(not(feature = "syntect-highlighter"))] + { + MietteHighlighter::nocolor() + } } } @@ -102,8 +108,8 @@ impl From for MietteHighlighter { } } -impl std::fmt::Debug for MietteHighlighter { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl core::fmt::Debug for MietteHighlighter { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "MietteHighlighter(...)") } } diff --git a/src/highlighters/syntect.rs b/src/highlighters/syntect.rs index 75759fc..6d31eff 100644 --- a/src/highlighters/syntect.rs +++ b/src/highlighters/syntect.rs @@ -1,5 +1,11 @@ +use core::str; 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 #[allow(clippy::module_inception)] mod syntect { @@ -97,7 +103,7 @@ impl SyntectHighlighter { } // finally, attempt to guess syntax based on first line self.syntax_set.find_syntax_by_first_line( - std::str::from_utf8(contents.data()) + core::str::from_utf8(contents.data()) .ok()? .split('\n') .next()?, diff --git a/src/lib.rs b/src/lib.rs index 21ad61d..d646ebe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,13 @@ +#![no_std] #![deny(missing_docs, missing_debug_implementations, nonstandard_style)] #![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)] + //! You run miette? You run her code like the software? Oh. Oh! Error code for //! coder! Error code for One Thousand Lines! //! @@ -58,8 +65,8 @@ //! //! ## 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. @@ -93,9 +100,23 @@ //! $ 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 +//! # extern crate alloc; //! /* //! You can derive a `Diagnostic` from any `std::error::Error` type. //! @@ -193,6 +214,7 @@ //! the trait directly, just like with `std::error::Error`. //! //! ```rust +//! # extern crate alloc; //! // lib/error.rs //! use miette::{Diagnostic, SourceSpan}; //! use thiserror::Error; @@ -360,6 +382,7 @@ //! attribute: //! //! ```rust +//! # extern crate alloc; //! use miette::Diagnostic; //! use thiserror::Error; //! @@ -380,6 +403,7 @@ //! (very high quality and detailed!) documentation on this diagnostic: //! //! ```rust +//! # extern crate alloc; //! use miette::Diagnostic; //! use thiserror::Error; //! @@ -410,6 +434,7 @@ //! `derive(Diagnostic)` macro: //! //! ```rust +//! # extern crate alloc; //! use miette::{Diagnostic, SourceSpan}; //! use thiserror::Error; //! @@ -448,6 +473,7 @@ //! enum variants: //! //! ```rust +//! # extern crate alloc; //! use miette::Diagnostic; //! use thiserror::Error; //! @@ -461,6 +487,7 @@ //! your diagnostic: //! //! ```rust +//! # extern crate alloc; //! use miette::Diagnostic; //! use thiserror::Error; //! @@ -499,6 +526,7 @@ //! `Diagnostic` type: //! //! ```rust +//! # extern crate alloc; //! use miette::Diagnostic; //! use thiserror::Error; //! @@ -517,6 +545,7 @@ //! method for that: //! //! ```rust,no_run +//! # extern crate alloc; //! use miette::{Diagnostic, SourceSpan}; //! use thiserror::Error; //! @@ -549,6 +578,7 @@ //! emitted at the same time: //! //! ```rust,no_run +//! # extern crate alloc; //! use miette::{Diagnostic, Report, SourceSpan}; //! use thiserror::Error; //! @@ -610,6 +640,7 @@ //! will likely want to use _both_: //! //! ```rust +//! # extern crate alloc; //! use miette::Diagnostic; //! use thiserror::Error; //! @@ -815,6 +846,19 @@ //! and some from [`thiserror`](https://github.com/dtolnay/thiserror), also //! under the Apache License. Some code is taken from //! [`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")] pub use miette_derive::*; diff --git a/src/miette_diagnostic.rs b/src/miette_diagnostic.rs index 9863e88..d56391a 100644 --- a/src/miette_diagnostic.rs +++ b/src/miette_diagnostic.rs @@ -1,7 +1,10 @@ -use std::{ - error::Error, - fmt::{Debug, Display}, -}; +extern crate alloc; + +use alloc::boxed::Box; +use alloc::string::String; +use alloc::vec::Vec; +use core::error::Error; +use core::fmt::{Debug, Display}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -39,7 +42,7 @@ pub struct 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) } } @@ -183,7 +186,7 @@ impl MietteDiagnostic { /// assert_eq!(diag.labels, Some(vec![label])); /// ``` pub fn with_label(mut self, label: impl Into) -> Self { - self.labels = Some(vec![label.into()]); + self.labels = Some(Vec::from([label.into()])); self } @@ -261,6 +264,7 @@ impl MietteDiagnostic { #[cfg(feature = "serde")] #[test] fn test_serialize_miette_diagnostic() { + use alloc::format; use serde_json::json; use crate::diagnostic; @@ -311,6 +315,7 @@ fn test_serialize_miette_diagnostic() { #[cfg(feature = "serde")] #[test] fn test_deserialize_miette_diagnostic() { + use alloc::format; use serde_json::json; use crate::diagnostic; diff --git a/src/named_source.rs b/src/named_source.rs index ea11cd2..0f2804e 100644 --- a/src/named_source.rs +++ b/src/named_source.rs @@ -10,8 +10,14 @@ pub struct NamedSource { language: Option, } -impl std::fmt::Debug for NamedSource { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +extern crate alloc; + +use alloc::boxed::Box; +use alloc::string::String; +use alloc::string::ToString; + +impl core::fmt::Debug for NamedSource { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("NamedSource") .field("name", &self.name) .field("source", &"") diff --git a/src/panic.rs b/src/panic.rs index b1dfd4a..3437ef8 100644 --- a/src/panic.rs +++ b/src/panic.rs @@ -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 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() { std::panic::set_hook(Box::new(move |info| { let mut message = "Something went wrong".to_string(); @@ -104,7 +118,7 @@ impl Panic { #[cfg(test)] mod tests { - use std::error::Error; + use std::{borrow::ToOwned, error::Error}; use super::*; diff --git a/src/protocol.rs b/src/protocol.rs index 96ae9a3..d7e3855 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -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) full reporting and such features. */ -use std::{ - fmt::{self, Display}, - fs, - panic::Location, -}; +extern crate alloc; + +use alloc::boxed::Box; +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")] use serde::{Deserialize, Serialize}; -use crate::{DiagnosticError, MietteError}; +use crate::MietteError; /// Adds rich metadata to your Error that can be used by /// [`Report`](crate::Report) to print really nice and human-friendly error /// messages. -pub trait Diagnostic: std::error::Error { +pub trait Diagnostic: crate::StdError { /// Unique diagnostic code that can be used to look up more information /// about this `Diagnostic`. Ideally also globally unique, and documented /// 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 { ($($box_type:ty),*) => { $( - impl std::error::Error for $box_type { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + impl crate::StdError for $box_type { + fn source(&self) -> Option<&(dyn crate::StdError + 'static)> { (**self).source() } - - fn cause(&self) -> Option<&dyn std::error::Error> { - self.source() - } } )* } @@ -94,7 +95,7 @@ box_error_impls! { macro_rules! box_borrow_impls { ($($box_type:ty),*) => { $( - impl std::borrow::Borrow for $box_type { + impl core::borrow::Borrow for $box_type { fn borrow(&self) -> &(dyn Diagnostic + 'static) { self.as_ref() } @@ -152,7 +153,7 @@ impl From for Box { fn from(s: String) -> Self { struct StringError(String); - impl std::error::Error for StringError {} + impl crate::StdError for StringError {} impl Diagnostic for StringError {} impl Display for StringError { @@ -172,8 +173,12 @@ impl From for Box { } } -impl From> for Box { - fn from(s: Box) -> Self { +#[cfg(feature = "std")] +use crate::DiagnosticError; + +#[cfg(feature = "std")] +impl From> for Box { + fn from(s: Box) -> Self { Box::new(DiagnosticError(s)) } } @@ -369,6 +374,7 @@ impl LabeledSpan { #[cfg(feature = "serde")] #[test] fn test_serialize_labeled_span() { + use alloc::string::ToString; use serde_json::json; assert_eq!( @@ -392,6 +398,7 @@ fn test_serialize_labeled_span() { #[cfg(feature = "serde")] #[test] fn test_deserialize_labeled_span() { + use alloc::string::ToString; use serde_json::json; let span: LabeledSpan = serde_json::from_value(json!({ @@ -594,8 +601,8 @@ impl From<(SourceOffset, usize)> for SourceSpan { } } -impl From> for SourceSpan { - fn from(range: std::ops::Range) -> Self { +impl From> for SourceSpan { + fn from(range: ops::Range) -> Self { Self { offset: range.start.into(), length: range.len(), @@ -603,12 +610,12 @@ impl From> for SourceSpan { } } -impl From> for SourceSpan { +impl From> for SourceSpan { /// # Panics /// /// Panics if the total length of the inclusive range would overflow a /// `usize`. This will only occur with the range `0..=usize::MAX`. - fn from(range: std::ops::RangeInclusive) -> Self { + fn from(range: ops::RangeInclusive) -> Self { let (start, end) = range.clone().into_inner(); Self { offset: start.into(), @@ -715,6 +722,7 @@ impl SourceOffset { /// you're shipping binaries for your application, you'll want to ignore /// the Err case or otherwise report it. #[track_caller] + #[cfg(feature = "std")] pub fn from_current_location() -> Result<(String, Self), MietteError> { let loc = Location::caller(); Ok(( diff --git a/src/source_impls.rs b/src/source_impls.rs index 9c26b71..efa415c 100644 --- a/src/source_impls.rs +++ b/src/source_impls.rs @@ -1,7 +1,16 @@ /*! 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}; @@ -205,7 +214,7 @@ mod tests { fn basic() -> Result<(), MietteError> { let src = String::from("foo\n"); 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.column()); Ok(()) @@ -215,7 +224,7 @@ mod tests { fn shifted() -> Result<(), MietteError> { let src = String::from("foobar"); 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.column()); Ok(()) @@ -225,7 +234,7 @@ mod tests { fn middle() -> Result<(), MietteError> { let src = String::from("foo\nbar\nbaz\n"); 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!(0, contents.column()); Ok(()) @@ -235,7 +244,7 @@ mod tests { fn middle_of_line() -> Result<(), MietteError> { let src = String::from("foo\nbarbar\nbaz\n"); 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!(3, contents.column()); Ok(()) @@ -245,7 +254,7 @@ mod tests { fn with_crlf() -> Result<(), MietteError> { let src = String::from("foo\r\nbar\r\nbaz\r\n"); 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!(0, contents.column()); Ok(()) @@ -257,7 +266,7 @@ mod tests { let contents = src.read_span(&(8, 3).into(), 1, 1)?; assert_eq!( "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!(0, contents.column()); @@ -270,7 +279,7 @@ mod tests { let contents = src.read_span(&(9, 11).into(), 1, 1)?; assert_eq!( "\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!(0, contents.column()); @@ -285,7 +294,7 @@ mod tests { let contents = src.read_span(&(2, 0).into(), 2, 2)?; assert_eq!( "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.column()); diff --git a/tests/color_format.rs b/tests/color_format.rs index 4e95856..682f54d 100644 --- a/tests/color_format.rs +++ b/tests/color_format.rs @@ -83,7 +83,9 @@ fn check_colors MietteHandlerOpts>( // // 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. - let lock = COLOR_ENV_VARS.lock().unwrap(); + let lock = COLOR_ENV_VARS + .lock() + .unwrap_or_else(|poisoned| poisoned.into_inner()); let guards = ( EnvVarGuard::new("NO_COLOR"), diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 223810c..8c48253 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -10,5 +10,5 @@ pub fn bail_fmt() -> Result<()> { } pub fn bail_error() -> Result<()> { - bail!(io::Error::new(io::ErrorKind::Other, "oh no!")); + bail!(io::Error::other("oh no!")); } diff --git a/tests/derive.rs b/tests/derive.rs index aa631dc..88b46eb 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -1,3 +1,5 @@ +#![allow(unused_assignments)] // some fields unused when feature="fancy" + use miette::{Diagnostic, Report, Severity, SourceSpan}; use thiserror::Error; @@ -244,6 +246,7 @@ fn help_field() { #[diagnostic()] struct Foo<'a> { #[help] + #[allow(unused_assignments)] do_this: Option<&'a str>, } @@ -293,6 +296,7 @@ fn test_snippet_named_struct() { #[error("welp")] #[diagnostic(code(foo::bar::baz))] #[allow(dead_code)] + #[allow(unused_assignments)] struct Foo<'a> { #[source_code] src: &'a str, @@ -401,6 +405,7 @@ const SNIPPET_TEXT: &str = "hello from miette"; help("help"), severity(Warning) )] +#[allow(unused_assignments)] struct ForwardsTo { #[source_code] src: String, @@ -501,6 +506,7 @@ fn test_forward_struct_named() { help("{help}"), forward(span) )] + #[allow(unused_assignments)] struct Struct<'a> { span: ForwardsTo, help: &'a str, @@ -534,6 +540,7 @@ fn test_forward_enum_named() { enum Enum<'a> { #[error("help: {help_text}")] #[diagnostic(code(foo::bar::overridden), help("{help_text}"), forward(span))] + #[allow(unused_assignments)] Variant { span: ForwardsTo, help_text: &'a str, @@ -595,6 +602,7 @@ fn test_unit_enum_display() { fn test_optional_source_code() { #[derive(Debug, Diagnostic, Error)] #[error("struct with optional source")] + #[allow(unused_assignments)] struct Struct { #[source_code] src: Option, @@ -607,6 +615,7 @@ fn test_optional_source_code() { .is_some()); #[derive(Debug, Diagnostic, Error)] + #[allow(unused_assignments)] enum Enum { #[error("variant1 with optional source")] Variant1 { diff --git a/tests/test_boxed.rs b/tests/test_boxed.rs index 9f9f4e2..db8d214 100644 --- a/tests/test_boxed.rs +++ b/tests/test_boxed.rs @@ -42,13 +42,13 @@ fn test_boxed_str_stderr() { #[test] fn test_boxed_thiserror() { let error = MyError { - source: io::Error::new(io::ErrorKind::Other, "oh no!"), + source: io::Error::other("oh no!"), }; let report: Report = miette!(error); assert_eq!("oh no!", report.source().unwrap().to_string()); let error = MyError { - source: io::Error::new(io::ErrorKind::Other, "oh no!!!!"), + source: io::Error::other("oh no!!!!"), }; let error: Box = Box::new(error); let report = Report::new_boxed(error); @@ -203,7 +203,7 @@ fn test_boxed_custom_diagnostic() { let related = 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); let report = Report::new_boxed(Box::new(main_diagnostic)); @@ -211,7 +211,7 @@ fn test_boxed_custom_diagnostic() { let related = 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); let main_diagnostic = Box::new(main_diagnostic) as Box; let report = miette!(main_diagnostic); @@ -228,7 +228,7 @@ fn test_boxed_custom_diagnostic() { #[test] fn test_boxed_sources() { let error = MyError { - source: io::Error::new(io::ErrorKind::Other, "oh no!"), + source: io::Error::other("oh no!"), }; let error = Box::::from(error); let error: Report = miette!(error).wrap_err("it failed"); diff --git a/tests/test_derive_attr.rs b/tests/test_derive_attr.rs index f1b0f3d..f2b9ddf 100644 --- a/tests/test_derive_attr.rs +++ b/tests/test_derive_attr.rs @@ -1,4 +1,5 @@ // Testing of the `diagnostic` attr used by derive(Diagnostic) + use miette::{Diagnostic, LabeledSpan, NamedSource, SourceSpan}; use thiserror::Error; diff --git a/tests/test_source.rs b/tests/test_source.rs index 9511bdb..303c2f9 100644 --- a/tests/test_source.rs +++ b/tests/test_source.rs @@ -50,7 +50,7 @@ fn test_fmt_source() { #[test] #[ignore = "Again with the io::Error source issue?"] 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)); assert_eq!("oh no!", error.source().unwrap().to_string()); }