fix: address review feedback for no_std support

- Use core::error::Error directly (MSRV 1.82 supports it)
- Remove build.rs (track_caller is always available with MSRV 1.82)
- Simplify feature detection in handler.rs syscall module
- Add clippy lints to catch std/core/alloc misuse
- Remove custom StdError trait, re-export core::error::Error
- Add __alloc module for derive macro compatibility
- Clean up verbose comments
- Remove extern crate alloc from tests (not needed with std)
- Fix derive macros to use miette::__alloc paths
This commit is contained in:
François Garillot 2026-01-14 15:38:54 -05:00
parent 09e1090b2e
commit c07cab4b60
No known key found for this signature in database
GPG Key ID: E7645C6B883A1E9A
31 changed files with 158 additions and 225 deletions

View File

@ -13,11 +13,11 @@ rust-version = "1.82.0"
exclude = ["images/", "tests/", "miette-derive/"] exclude = ["images/", "tests/", "miette-derive/"]
[dependencies] [dependencies]
thiserror = "2.0.11"
miette-derive = { path = "miette-derive", version = "=7.6.0", optional = true } miette-derive = { path = "miette-derive", version = "=7.6.0", optional = true }
unicode-width = { version = "0.2.0", default-features = false } unicode-width = { version = "0.2.0", default-features = false }
cfg-if = "1.0.0" cfg-if = "1.0.0"
spin = { version = "0.9", default-features = false, features = ["mutex", "spin_mutex", "lazy"] } # spin is needed for no_std environments (provides Once without std)
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,6 +31,7 @@ serde = { version = "1.0.196", features = ["derive"], optional = true }
syntect = { version = "5.1.0", optional = true } syntect = { version = "5.1.0", optional = true }
[dev-dependencies] [dev-dependencies]
thiserror = "2.0.11"
semver = "1.0.21" semver = "1.0.21"
futures = { version = "0.3", default-features = false } futures = { version = "0.3", default-features = false }
indenter = "0.3.3" indenter = "0.3.3"
@ -45,7 +46,7 @@ strip-ansi-escapes = "0.2.0"
[features] [features]
default = ["derive", "std"] default = ["derive", "std"]
std = ["thiserror/std", "fancy-no-syscall"] std = []
derive = ["dep:miette-derive"] derive = ["dep:miette-derive"]
no-format-args-capture = [] no-format-args-capture = []
fancy-base = [ fancy-base = [
@ -57,13 +58,14 @@ 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",
"dep:supports-unicode", "dep:supports-unicode",
] ]
fancy = ["fancy-no-backtrace", "dep:backtrace", "dep:backtrace-ext"] fancy = ["fancy-no-backtrace", "dep:backtrace", "dep:backtrace-ext"]
syntect-highlighter = ["fancy-no-backtrace", "dep:syntect", "std"] syntect-highlighter = ["fancy-no-backtrace", "dep:syntect"]
[workspace] [workspace]
members = ["miette-derive"] members = ["miette-derive"]

View File

@ -1,6 +0,0 @@
fn main() {
// track_caller is always available with our MSRV (Rust ≥ 1.82)
// Add check-cfg for conditional configurations
println!("cargo:rustc-check-cfg=cfg(doc_cfg)");
}

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 { .. } => core::option::Option::Some(alloc::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(..) => core::option::Option::Some(alloc::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 => core::option::Option::Some(alloc::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) -> core::option::Option<alloc::boxed::Box<dyn core::fmt::Display + '_>> { fn code(&self) -> core::option::Option<miette::__alloc::Box<dyn core::fmt::Display + '_>> {
core::option::Option::Some(alloc::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 => {
core::option::Option::Some(alloc::borrow::Borrow::borrow(#rel)) core::option::Option::Some(miette::__alloc::borrow::Borrow::borrow(#rel))
} }
} }
}) })
@ -71,7 +71,7 @@ impl DiagnosticSource {
let rel = &self.0; let rel = &self.0;
Some(quote! { Some(quote! {
fn diagnostic_source<'a>(&'a self) -> core::option::Option<&'a dyn miette::Diagnostic> { fn diagnostic_source<'a>(&'a self) -> core::option::Option<&'a dyn miette::Diagnostic> {
core::option::Option::Some(alloc::borrow::Borrow::borrow(&self.#rel)) core::option::Option::Some(miette::__alloc::borrow::Borrow::borrow(&self.#rel))
} }
}) })
} }

View File

@ -58,22 +58,22 @@ 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) -> Option<alloc::boxed::Box<dyn core::fmt::Display + '_>> fn code(& self) -> Option<miette::__alloc::Box<dyn core::fmt::Display + '_>>
}, },
Self::Help => quote! { Self::Help => quote! {
fn help(& self) -> Option<alloc::boxed::Box<dyn core::fmt::Display + '_>> fn help(& self) -> Option<miette::__alloc::Box<dyn core::fmt::Display + '_>>
}, },
Self::Url => quote! { Self::Url => quote! {
fn url(& self) -> Option<alloc::boxed::Box<dyn core::fmt::Display + '_>> fn url(& self) -> Option<miette::__alloc::Box<dyn core::fmt::Display + '_>>
}, },
Self::Severity => quote! { Self::Severity => quote! {
fn severity(&self) -> Option<miette::Severity> fn severity(&self) -> Option<miette::Severity>
}, },
Self::Related => quote! { Self::Related => quote! {
fn related(&self) -> Option<alloc::boxed::Box<dyn 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) -> Option<alloc::boxed::Box<dyn 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) -> Option<&dyn miette::SourceCode> fn source_code(&self) -> Option<&dyn miette::SourceCode>

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 => Option::Some(alloc::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| -> alloc::boxed::Box<dyn core::fmt::Display + '_> { alloc::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) -> Option<alloc::boxed::Box<dyn core::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;
Option::Some(alloc::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) -> Option<alloc::boxed::Box<dyn core::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| -> alloc::boxed::Box<dyn core::fmt::Display + '_> { alloc::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

@ -226,7 +226,7 @@ impl Labels {
Some(quote! { Some(quote! {
#[allow(unused_variables)] #[allow(unused_variables)]
fn labels(&self) -> Option<alloc::boxed::Box<dyn 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)*;
Option::Some(alloc::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)))
} }
}) })
} }
@ -322,7 +322,7 @@ impl Labels {
] ]
.into_iter() .into_iter()
#(#collections_chain)*; #(#collections_chain)*;
Option::Some(alloc::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 => {
Option::Some(alloc::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) -> Option<alloc::boxed::Box<dyn 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 alloc::borrow::Borrow; use miette::__alloc::borrow::Borrow;
Option::Some(alloc::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

@ -96,7 +96,7 @@ impl Url {
} }
}; };
Some(quote! { Some(quote! {
Self::#ident #pat => Option::Some(alloc::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) -> Option<alloc::boxed::Box<dyn core::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;
Option::Some(alloc::boxed::Box::new(format!(#fmt #args))) Option::Some(miette::__alloc::Box::new(format!(#fmt #args)))
} }
}) })
} }

View File

@ -8,8 +8,8 @@ use core::{convert::Infallible, fmt::Display};
use crate::{Diagnostic, LabeledSpan, Severity, SourceCode}; use crate::{Diagnostic, LabeledSpan, Severity, SourceCode};
#[cfg(not(feature = "std"))] // Note: Infallible implements core::error::Error since Rust 1.81,
impl crate::StdError for Infallible {} // 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>> {

View File

@ -1,12 +1,9 @@
extern crate alloc; extern crate alloc;
#[cfg(not(feature = "std"))]
use crate::StdError as Error;
use alloc::boxed::Box; use alloc::boxed::Box;
use core::error::Error;
use core::fmt::{self, Display}; use core::fmt::{self, Display};
#[cfg(feature = "std")] #[cfg(feature = "std")]
use std::error::Error;
#[cfg(feature = "std")]
use std::io; use std::io;
use crate::Diagnostic; use crate::Diagnostic;
@ -93,10 +90,7 @@ impl Diagnostic for MietteError {
pub(crate) mod tests { pub(crate) mod tests {
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
use crate::StdError as Error; use crate::StdError as Error;
#[cfg(not(feature = "std"))]
use alloc::string::ToString; use alloc::string::ToString;
#[cfg(feature = "std")]
use std::string::ToString;
use super::*; use super::*;

View File

@ -1,7 +1,7 @@
extern crate alloc; extern crate alloc;
use crate::StdError as Error;
use alloc::boxed::Box; use alloc::boxed::Box;
use core::error::Error;
use crate::{Diagnostic, Report}; use crate::{Diagnostic, Report};
@ -42,15 +42,6 @@ pub trait IntoDiagnostic<T, E> {
fn into_diagnostic(self) -> Result<T, Report>; fn into_diagnostic(self) -> Result<T, Report>;
} }
#[cfg(feature = "std")]
impl<T, E: std::error::Error + Send + Sync + 'static> IntoDiagnostic<T, E> for Result<T, E> {
#[track_caller]
fn into_diagnostic(self) -> Result<T, Report> {
self.map_err(|e| DiagnosticError(Box::new(e)).into())
}
}
#[cfg(not(feature = "std"))]
impl<T, E: 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] #[track_caller]
fn into_diagnostic(self) -> Result<T, Report> { fn into_diagnostic(self) -> Result<T, Report> {
@ -60,11 +51,13 @@ impl<T, E: Error + Send + Sync + 'static> IntoDiagnostic<T, E> for Result<T, E>
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
extern crate alloc;
use alloc::string::ToString;
use super::IntoDiagnostic; use super::IntoDiagnostic;
#[cfg(feature = "std")] #[cfg(feature = "std")]
use std::io::{self}; use std::io;
#[cfg(feature = "std")]
use std::string::ToString;
#[cfg(feature = "std")] #[cfg(feature = "std")]
use crate::error::tests::TestError; use crate::error::tests::TestError;

View File

@ -45,20 +45,14 @@
// let error = $msg; // let error = $msg;
// (&error).miette_kind().new(error) // (&error).miette_kind().new(error)
#[cfg(not(feature = "std"))]
extern crate alloc; extern crate alloc;
use super::Report; use alloc::boxed::Box;
use core::fmt::{Debug, Display}; use core::fmt::{Debug, Display};
use super::Report;
use crate::Diagnostic; use crate::Diagnostic;
#[cfg(not(feature = "std"))]
use alloc::boxed::Box;
#[cfg(feature = "std")]
use std::boxed::Box;
pub struct Adhoc; pub struct Adhoc;
pub trait AdhocKind: Sized { pub trait AdhocKind: Sized {

View File

@ -9,11 +9,9 @@ extern crate alloc;
use alloc::boxed::Box; use alloc::boxed::Box;
use core::fmt::Display; use core::fmt::Display;
#[cfg(not(feature = "std"))] use core::error::Error as StdError;
use crate::StdError;
use spin::Once;
#[cfg(feature = "std")] #[cfg(feature = "std")]
use std::error::Error as StdError; use std::sync::OnceLock;
#[allow(unreachable_pub)] #[allow(unreachable_pub)]
pub use into_diagnostic::*; pub use into_diagnostic::*;
@ -67,7 +65,11 @@ 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>;
static HOOK: Once<ErrorHook> = Once::new(); #[cfg(feature = "std")]
static HOOK: OnceLock<ErrorHook> = OnceLock::new();
#[cfg(not(feature = "std"))]
static HOOK: spin::Once<ErrorHook> = spin::Once::new();
fn default_hook() -> ErrorHook { fn default_hook() -> ErrorHook {
Box::new(get_default_printer) Box::new(get_default_printer)
@ -87,39 +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> {
HOOK.set(hook).map_err(|_| InstallError)
}
/// Set the error hook.
#[cfg(not(feature = "std"))]
pub fn set_hook(hook: ErrorHook) -> Result<(), InstallError> { pub fn set_hook(hook: ErrorHook) -> Result<(), InstallError> {
HOOK.call_once(|| hook); HOOK.call_once(|| hook);
Ok(()) Ok(())
} }
#[cfg(feature = "std")]
pub(crate) fn capture_handler(error: &(dyn Diagnostic + 'static)) -> Box<dyn ReportHandler> { pub(crate) fn capture_handler(error: &(dyn Diagnostic + 'static)) -> Box<dyn ReportHandler> {
static DEFAULT: Once<ErrorHook> = Once::new(); let hook = HOOK.get_or_init(default_hook);
let hook = HOOK.get().unwrap_or_else(|| { hook(error)
DEFAULT.call_once(|| default_hook()); }
DEFAULT.get().unwrap()
});
#[cfg(not(feature = "std"))]
pub(crate) fn capture_handler(error: &(dyn Diagnostic + 'static)) -> Box<dyn ReportHandler> {
let hook = HOOK.call_once(default_hook);
hook(error) hook(error)
} }
#[track_caller] #[track_caller]
#[cfg(feature = "std")]
pub(crate) fn capture_handler_with_location( pub(crate) fn capture_handler_with_location(
error: &(dyn Diagnostic + 'static), error: &(dyn Diagnostic + 'static),
) -> Box<dyn ReportHandler> { ) -> Box<dyn ReportHandler> {
static DEFAULT: Once<ErrorHook> = Once::new(); let hook = HOOK.get_or_init(default_hook);
let hook = HOOK.get().unwrap_or_else(|| { let mut handler = hook(error);
DEFAULT.call_once(|| default_hook()); handler.track_caller(core::panic::Location::caller());
DEFAULT.get().unwrap() handler
}); }
{ #[track_caller]
let mut handler = hook(error); #[cfg(not(feature = "std"))]
handler.track_caller(core::panic::Location::caller()); pub(crate) fn capture_handler_with_location(
handler 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> {

View File

@ -211,14 +211,14 @@ impl<C> StdError for WithSourceCode<Report, C> {
#[cfg(all(test, feature = "std"))] #[cfg(all(test, feature = "std"))]
mod tests { mod tests {
extern crate alloc;
use alloc::boxed::Box;
#[cfg(feature = "fancy")] #[cfg(feature = "fancy")]
use std::format; use alloc::format;
use std::{ use alloc::string::{String, ToString};
boxed::Box,
string::{String, ToString},
};
#[cfg(feature = "fancy")] #[cfg(feature = "fancy")]
use std::{vec, vec::Vec}; 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};
@ -232,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

@ -352,25 +352,18 @@ impl MietteHandlerOpts {
} else if let Some(force_graphical) = self.force_graphical { } else if let Some(force_graphical) = self.force_graphical {
force_graphical force_graphical
} else { } else {
#[cfg(feature = "fancy-no-syscall")] #[cfg(all(feature = "fancy-no-syscall", not(feature = "fancy-no-backtrace")))]
{ {
// In no-std environment, assume graphics are available
true true
} }
#[cfg(all(not(feature = "fancy-no-syscall"), feature = "std"))] #[cfg(feature = "fancy-no-backtrace")]
{ {
// In std environment, check NO_GRAPHICS env var
if let Ok(env) = std::env::var("NO_GRAPHICS") { if let Ok(env) = std::env::var("NO_GRAPHICS") {
env == "0" env == "0"
} else { } else {
true true
} }
} }
#[cfg(all(not(feature = "fancy-no-syscall"), not(feature = "std")))]
{
// In no-std environment without fancy-no-syscall, default to true
true
}
} }
} }
@ -485,77 +478,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 if #[cfg(feature = "fancy-no-backtrace")] { }
terminal_size::terminal_size().map(|size| size.0 .0 as usize) #[cfg(any(not(feature = "fancy-no-backtrace"), miri))]
} else { {
None 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 if #[cfg(feature = "fancy-no-backtrace")] { }
supports_hyperlinks::on(supports_hyperlinks::Stream::Stderr) #[cfg(not(feature = "fancy-no-backtrace"))]
} else { {
false false
}
} }
} }
#[inline] #[inline]
pub(super) fn supports_color() -> bool { pub(super) fn supports_color() -> bool {
cfg_if! { #[cfg(feature = "fancy-no-backtrace")]
if #[cfg(all(feature = "fancy", not(feature = "fancy-no-syscall")))] { {
// Standard fancy mode with full std support supports_color::on(supports_color::Stream::Stderr).is_some()
supports_color::on(supports_color::Stream::Stderr).is_some() }
} else if #[cfg(all(feature = "fancy", feature = "fancy-no-backtrace"))] { #[cfg(not(feature = "fancy-no-backtrace"))]
// Fancy mode without backtrace but with color support {
supports_color::on(supports_color::Stream::Stderr).is_some() false
} else if #[cfg(not(feature = "std"))] {
// No-std environment - no color support by default
false
} else {
// All other cases - no color support
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-backtrace")] { {
supports_color::on(supports_color::Stream::Stderr).map(|color| color.has_16m) supports_color::on(supports_color::Stream::Stderr).map(|color| color.has_16m)
} else if #[cfg(feature = "fancy-no-syscall")] { }
// In no-std environment without color support, default to no RGB color support #[cfg(not(feature = "fancy-no-backtrace"))]
Some(false) {
} else { None
Some(true) // Fallback to assuming RGB support
}
} }
} }
#[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 if #[cfg(feature = "fancy-no-backtrace")] { }
supports_unicode::on(supports_unicode::Stream::Stderr) #[cfg(not(feature = "fancy-no-backtrace"))]
} else { {
false false
}
} }
} }
} }

View File

@ -71,13 +71,9 @@ impl GraphicalTheme {
impl Default for GraphicalTheme { impl Default for GraphicalTheme {
fn default() -> Self { fn default() -> Self {
#[cfg(feature = "fancy-no-syscall")] #[cfg(feature = "fancy-no-backtrace")]
{
// In no-std environments, default to no-color mode
Self::unicode_nocolor()
}
#[cfg(all(not(feature = "fancy-no-syscall"), feature = "std"))]
{ {
use std::io::IsTerminal;
match std::env::var("NO_COLOR") { match std::env::var("NO_COLOR") {
_ if !std::io::stdout().is_terminal() || !std::io::stderr().is_terminal() => { _ if !std::io::stdout().is_terminal() || !std::io::stderr().is_terminal() => {
Self::none() Self::none()
@ -86,10 +82,9 @@ impl Default for GraphicalTheme {
_ => Self::unicode(), _ => Self::unicode(),
} }
} }
#[cfg(all(not(feature = "fancy-no-syscall"), not(feature = "std")))] #[cfg(not(feature = "fancy-no-backtrace"))]
{ {
// In no-std environment, default to unicode theme Self::unicode_nocolor()
Self::unicode()
} }
} }
} }

View File

@ -84,23 +84,17 @@ impl MietteHighlighter {
impl Default for MietteHighlighter { impl Default for MietteHighlighter {
fn default() -> Self { fn default() -> Self {
#[cfg(all(feature = "syntect-highlighter", not(feature = "fancy-no-syscall")))] #[cfg(feature = "syntect-highlighter")]
{ {
use std::io::IsTerminal; use std::io::IsTerminal;
match std::env::var("NO_COLOR") { match std::env::var("NO_COLOR") {
_ if !std::io::stdout().is_terminal() || !std::io::stderr().is_terminal() => { _ 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())) Self(Arc::new(SyntectHighlighter::default()))
} }
Ok(string) if string != "0" => MietteHighlighter::nocolor(), Ok(string) if string != "0" => MietteHighlighter::nocolor(),
_ => Self(Arc::new(SyntectHighlighter::default())), _ => Self(Arc::new(SyntectHighlighter::default())),
} }
} }
#[cfg(all(feature = "syntect-highlighter", feature = "fancy-no-syscall"))]
{
// In no-std environment, use syntect but without terminal detection
Self(Arc::new(SyntectHighlighter::default()))
}
#[cfg(not(feature = "syntect-highlighter"))] #[cfg(not(feature = "syntect-highlighter"))]
{ {
MietteHighlighter::nocolor() MietteHighlighter::nocolor()

View File

@ -103,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,11 @@
#![no_std] #![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)]
#![warn(
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
@ -845,18 +850,13 @@
#[cfg(feature = "std")] #[cfg(feature = "std")]
extern crate std; extern crate std;
#[cfg(feature = "std")] pub use core::error::Error as StdError;
pub use std::error::Error as StdError;
#[cfg(not(feature = "std"))] #[doc(hidden)]
/// Compatibility trait for error handling in no_std environments. pub mod __alloc {
/// This trait provides a subset of `std::error::Error` functionality extern crate alloc;
/// suitable for no_std environments. pub use alloc::borrow;
pub trait StdError: core::fmt::Debug + core::fmt::Display { pub use alloc::boxed::Box;
/// Returns the lower-level source of this error, if any.
fn source(&self) -> Option<&(dyn StdError + 'static)> {
None
}
} }
#[cfg(feature = "derive")] #[cfg(feature = "derive")]

View File

@ -1,13 +1,10 @@
extern crate alloc; extern crate alloc;
#[cfg(not(feature = "std"))]
use crate::StdError as Error;
use alloc::boxed::Box; use alloc::boxed::Box;
use alloc::string::String; use alloc::string::String;
use alloc::vec::Vec; use alloc::vec::Vec;
use core::error::Error;
use core::fmt::{Debug, Display}; use core::fmt::{Debug, Display};
#[cfg(feature = "std")]
use std::error::Error;
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -267,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;
@ -317,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

@ -1,6 +1,11 @@
#[cfg(feature = "std")] // `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::boxed::Box;
#[cfg(feature = "std")]
use std::{ use std::{
eprintln, eprintln,
error::Error, error::Error,
@ -9,14 +14,11 @@ use std::{
string::{String, ToString}, string::{String, ToString},
}; };
#[cfg(feature = "std")]
use backtrace::Backtrace; use backtrace::Backtrace;
#[cfg(feature = "std")]
use crate::{Context, Diagnostic, Result}; use crate::{Context, Diagnostic, Result};
/// Makes miette show pretty error messages when your program crashes. /// Makes miette show pretty error messages when your program crashes.
#[cfg(feature = "std")]
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();
@ -38,20 +40,9 @@ pub fn set_panic_hook() {
})); }));
} }
/// Makes miette show pretty error messages when your program crashes.
///
/// On computers without the standard library, this function does nothing
/// because crash hooks need the standard library to work.
#[cfg(not(feature = "std"))]
pub fn set_panic_hook() {
// Does nothing on computers without the standard library
}
#[derive(Debug)] #[derive(Debug)]
#[cfg(feature = "std")]
struct Panic(String); struct Panic(String);
#[cfg(feature = "std")]
impl Display for Panic { impl Display for Panic {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let msg = &self.0; let msg = &self.0;
@ -60,10 +51,8 @@ impl Display for Panic {
} }
} }
#[cfg(feature = "std")]
impl Error for Panic {} impl Error for Panic {}
#[cfg(feature = "std")]
impl Diagnostic for Panic { impl Diagnostic for Panic {
fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> { fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
Some(Box::new( Some(Box::new(
@ -72,7 +61,6 @@ impl Diagnostic for Panic {
} }
} }
#[cfg(feature = "std")]
impl Panic { impl Panic {
fn backtrace() -> String { fn backtrace() -> String {
use std::fmt::Write; use std::fmt::Write;
@ -129,7 +117,6 @@ impl Panic {
} }
#[cfg(test)] #[cfg(test)]
#[cfg(feature = "std")]
mod tests { mod tests {
use std::{borrow::ToOwned, error::Error}; use std::{borrow::ToOwned, error::Error};

View File

@ -177,8 +177,8 @@ impl From<String> for Box<dyn Diagnostic + Send + Sync> {
use crate::DiagnosticError; use crate::DiagnosticError;
#[cfg(feature = "std")] #[cfg(feature = "std")]
impl From<Box<dyn std::error::Error + Send + Sync>> for Box<dyn Diagnostic + Send + Sync> { impl From<Box<dyn core::error::Error + Send + Sync>> for Box<dyn Diagnostic + Send + Sync> {
fn from(s: Box<dyn std::error::Error + Send + Sync>) -> Self { fn from(s: Box<dyn core::error::Error + Send + Sync>) -> Self {
Box::new(DiagnosticError(s)) Box::new(DiagnosticError(s))
} }
} }
@ -374,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!(
@ -397,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!({

View File

@ -1,7 +1,5 @@
#![cfg(feature = "fancy-no-backtrace")] #![cfg(feature = "fancy-no-backtrace")]
extern crate alloc;
use miette::{Diagnostic, MietteHandler, MietteHandlerOpts, ReportHandler, RgbColors}; use miette::{Diagnostic, MietteHandler, MietteHandlerOpts, ReportHandler, RgbColors};
use regex::Regex; use regex::Regex;
use std::ffi::OsString; use std::ffi::OsString;

View File

@ -1,5 +1,4 @@
#[allow(unused_assignments)] // some fields unused when feature="fancy" #![allow(unused_assignments)] // some fields unused when feature="fancy"
extern crate alloc;
use miette::{Diagnostic, Report, Severity, SourceSpan}; use miette::{Diagnostic, Report, Severity, SourceSpan};
use thiserror::Error; use thiserror::Error;

View File

@ -1,7 +1,5 @@
#![cfg(feature = "fancy-no-backtrace")] #![cfg(feature = "fancy-no-backtrace")]
extern crate alloc;
use miette::{ use miette::{
Diagnostic, GraphicalReportHandler, GraphicalTheme, MietteError, NamedSource, Diagnostic, GraphicalReportHandler, GraphicalTheme, MietteError, NamedSource,
NarratableReportHandler, Report, SourceSpan, NarratableReportHandler, Report, SourceSpan,

View File

@ -1,7 +1,5 @@
#![cfg(feature = "fancy-no-backtrace")] #![cfg(feature = "fancy-no-backtrace")]
extern crate alloc;
use miette::{Diagnostic, MietteError, NamedSource, NarratableReportHandler, Report, SourceSpan}; use miette::{Diagnostic, MietteError, NamedSource, NarratableReportHandler, Report, SourceSpan};
use miette::{GraphicalReportHandler, GraphicalTheme}; use miette::{GraphicalReportHandler, GraphicalTheme};

View File

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

View File

@ -1,5 +1,3 @@
extern crate alloc;
use std::{ use std::{
collections::{LinkedList, VecDeque}, collections::{LinkedList, VecDeque},
ops::Range, ops::Range,

View File

@ -1,5 +1,3 @@
extern crate alloc;
use miette::Diagnostic; use miette::Diagnostic;
#[derive(Debug, miette::Diagnostic, thiserror::Error)] #[derive(Debug, miette::Diagnostic, thiserror::Error)]

View File

@ -1,6 +1,4 @@
mod json_report_handler { mod json_report_handler {
extern crate alloc;
use miette::{Diagnostic, MietteError, NamedSource, Report, SourceSpan}; use miette::{Diagnostic, MietteError, NamedSource, Report, SourceSpan};
use miette::JSONReportHandler; use miette::JSONReportHandler;