fix: support no-std environments

This commit is contained in:
Paul Schoenfelder 2024-03-06 06:45:47 -05:00 committed by François Garillot
parent df7bcfa17d
commit 243ea534e8
No known key found for this signature in database
GPG Key ID: E7645C6B883A1E9A
31 changed files with 303 additions and 130 deletions

View File

@ -13,9 +13,11 @@ rust-version = "1.82.0"
exclude = ["images/", "tests/", "miette-derive/"]
[dependencies]
thiserror = "2.0.11"
miette-derive = { path = "miette-derive", version = "=7.6.0", optional = true }
unicode-width = "0.2.0"
unicode-width = { version = "0.2.0", default-features = false }
cfg-if = "1.0.0"
spin = { version = "0.9", default-features = false, features = ["mutex", "spin_mutex", "lazy"] }
owo-colors = { version = "4.0.0", optional = true }
textwrap = { version = "0.16.0", default-features = false, features = ["unicode-linebreak", "unicode-width"], optional = true }
@ -29,9 +31,11 @@ serde = { version = "1.0.196", features = ["derive"], optional = true }
syntect = { version = "5.1.0", optional = true }
[dev-dependencies]
thiserror = "2.0.11"
semver = "1.0.21"
[build-dependencies]
rustc_version = "0.2"
# Eyre devdeps
futures = { version = "0.3", default-features = false }
indenter = "0.3.3"
@ -45,7 +49,8 @@ serde_json = "1.0.113"
strip-ansi-escapes = "0.2.0"
[features]
default = ["derive"]
default = ["derive", "std"]
std = ["thiserror/std", "fancy-no-syscall"]
derive = ["dep:miette-derive"]
no-format-args-capture = []
fancy-base = [

View File

@ -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 { .. } => Option::Some(alloc::boxed::Box::new(#code)), }
}
syn::Fields::Unnamed(_) => {
quote! { Self::#ident(..) => std::option::Option::Some(std::boxed::Box::new(#code)), }
quote! { Self::#ident(..) => Option::Some(alloc::boxed::Box::new(#code)), }
}
syn::Fields::Unit => {
quote! { Self::#ident => std::option::Option::Some(std::boxed::Box::new(#code)), }
quote! { Self::#ident => Option::Some(alloc::boxed::Box::new(#code)), }
}
})
},
@ -72,8 +72,8 @@ impl Code {
pub(crate) fn gen_struct(&self) -> Option<TokenStream> {
let code = &self.0;
Some(quote! {
fn code(&self) -> std::option::Option<std::boxed::Box<dyn std::fmt::Display + '_>> {
std::option::Option::Some(std::boxed::Box::new(#code))
fn code(&self) -> Option<alloc::boxed::Box<dyn core::fmt::Display + '_>> {
Some(alloc::boxed::Box::new(#code))
}
})
}

View File

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

View File

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

View File

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

View File

@ -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<std::boxed::Box<dyn std::iter::Iterator<Item = miette::LabeledSpan> + '_>> {
fn labels(&self) -> Option<alloc::boxed::Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
use miette::macro_helpers::ToOption;
let Self #display_pat = self;
@ -236,7 +236,7 @@ impl Labels {
.into_iter()
#(#collections_chain)*;
std::option::Option::Some(Box::new(labels_iter.filter(Option::is_some).map(Option::unwrap)))
Option::Some(alloc::boxed::Box::new(labels_iter.filter(Option::is_some).map(Option::unwrap)))
}
})
}
@ -322,7 +322,7 @@ impl Labels {
]
.into_iter()
#(#collections_chain)*;
std::option::Option::Some(std::boxed::Box::new(labels_iter.filter(Option::is_some).map(Option::unwrap)))
Option::Some(alloc::boxed::Box::new(labels_iter.filter(Option::is_some).map(Option::unwrap)))
}
}),
}

View File

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

View File

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

View File

@ -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 {

View File

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

View File

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

View File

@ -2,7 +2,10 @@
Iterate over error `.diagnostic_source()` chains.
*/
extern crate alloc;
use crate::protocol::Diagnostic;
use alloc::string::ToString;
/// Iterator of a chain of cause errors.
#[derive(Clone, Default)]
@ -18,7 +21,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 +62,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 +77,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 +86,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),

View File

@ -1,10 +1,14 @@
/*!
Default trait implementations for [`Diagnostic`].
*/
extern crate alloc;
use std::{convert::Infallible, fmt::Display};
use core::{convert::Infallible, fmt::Display};
use alloc::boxed::Box;
use crate::{Diagnostic, LabeledSpan, Severity, SourceCode};
use crate::{Diagnostic, LabeledSpan, Severity, SourceCode, StdError};
impl StdError for Infallible {}
impl Diagnostic for Infallible {
fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {

View File

@ -1,8 +1,13 @@
use std::{
error::Error,
fmt::{self, Display},
io,
};
extern crate alloc;
#[cfg(feature = "std")]
use std::io;
#[cfg(feature = "std")]
use std::error::Error;
#[cfg(not(feature = "std"))]
use crate::StdError as Error;
use core::fmt::{self, Display};
use alloc::boxed::Box;
use crate::Diagnostic;
@ -11,8 +16,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 +29,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 +41,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<io::Error> for MietteError {
fn from(value: io::Error) -> Self {
Self::IoError(value)
@ -49,6 +58,7 @@ impl From<io::Error> for MietteError {
impl Diagnostic for MietteError {
fn code<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
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 +66,7 @@ impl Diagnostic for MietteError {
fn help<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
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,10 +77,11 @@ impl Diagnostic for MietteError {
fn url<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
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,
)))
@ -78,12 +90,18 @@ impl Diagnostic for MietteError {
#[cfg(test)]
pub(crate) mod tests {
use std::{error::Error, io::ErrorKind};
#[cfg(feature = "std")]
use std::io::ErrorKind;
#[cfg(not(feature = "std"))]
use crate::StdError as Error;
use super::*;
#[derive(Debug)]
#[cfg(feature = "std")]
pub(crate) struct TestError(pub(crate) io::Error);
#[cfg(not(feature = "std"))]
pub(crate) struct TestError(pub(crate) &'static str);
impl Display for TestError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@ -91,12 +109,21 @@ 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");

View File

@ -1,8 +1,15 @@
extern crate alloc;
use super::error::{ContextError, ErrorImpl};
use super::{Report, WrapErr};
use core::fmt::{self, Debug, Display, Write};
use core::convert::Infallible;
#[cfg(feature = "std")]
use std::error::Error as StdError;
#[cfg(not(feature = "std"))]
use crate::StdError as StdError;
use alloc::boxed::Box;
use crate::{Diagnostic, LabeledSpan};
@ -38,7 +45,7 @@ mod ext {
}
}
impl<T> WrapErr<T, std::convert::Infallible> for Option<T> {
impl<T> WrapErr<T, Infallible> for Option<T> {
fn wrap_err<D>(self, msg: D) -> Result<T, Report>
where
D: Display + Send + Sync + 'static,

View File

@ -1,8 +1,14 @@
extern crate alloc;
use core::any::TypeId;
use core::fmt::{self, Debug, Display};
use core::mem::ManuallyDrop;
use core::ptr::{self, NonNull};
#[cfg(feature = "std")]
use std::error::Error as StdError;
#[cfg(not(feature = "std"))]
use crate::StdError as StdError;
use alloc::boxed::Box;
use super::ptr::{Mut, Own, Ref};
use super::Report;
@ -429,7 +435,7 @@ impl Report {
/// Construct a [`Report`] directly from an error-like type
pub fn from_err<E>(err: E) -> Self
where
E: std::error::Error + Send + Sync + 'static,
E: StdError + Send + Sync + 'static,
{
super::DiagnosticError(Box::new(err)).into()
}
@ -816,8 +822,16 @@ impl AsRef<dyn StdError> for Report {
}
}
#[cfg(feature = "std")]
impl std::borrow::Borrow<dyn Diagnostic> for Report {
fn borrow(&self) -> &(dyn Diagnostic + 'static) {
self.as_ref()
}
}
#[cfg(not(feature = "std"))]
impl core::borrow::Borrow<dyn Diagnostic> for Report {
fn borrow(&self) -> &(dyn Diagnostic + 'static) {
self.as_ref()
}
}

View File

@ -1,14 +1,21 @@
use std::{error::Error, fmt::Display};
extern crate alloc;
#[cfg(feature = "std")]
use std::error::Error;
#[cfg(not(feature = "std"))]
use crate::StdError as Error;
use core::fmt::Display;
use alloc::boxed::Box;
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<dyn std::error::Error + Send + Sync + 'static>);
pub(crate) struct DiagnosticError(pub(crate) Box<dyn Error + Send + Sync + 'static>);
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}")
}
@ -38,20 +45,31 @@ pub trait IntoDiagnostic<T, E> {
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> {
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> {
fn into_diagnostic(self) -> Result<T, Report> {
self.map_err(|e| DiagnosticError(Box::new(e)).into())
}
}
#[cfg(test)]
mod tests {
#[cfg(feature = "std")]
use std::io::{self, ErrorKind};
use super::*;
#[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");

View File

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

View File

@ -4,10 +4,16 @@
clippy::new_ret_no_self,
clippy::wrong_self_convention
)]
use core::fmt::Display;
extern crate alloc;
use core::fmt::Display;
use alloc::boxed::Box;
#[cfg(feature = "std")]
use std::error::Error as StdError;
use std::sync::OnceLock;
#[cfg(not(feature = "std"))]
use crate::StdError as StdError;
use spin::Once;
#[allow(unreachable_pub)]
pub use into_diagnostic::*;
@ -61,7 +67,11 @@ unsafe impl Send for Report {}
pub type ErrorHook =
Box<dyn Fn(&(dyn Diagnostic + 'static)) -> Box<dyn ReportHandler> + Sync + Send + 'static>;
static HOOK: OnceLock<ErrorHook> = OnceLock::new();
static HOOK: Once<ErrorHook> = Once::new();
fn default_hook() -> ErrorHook {
Box::new(get_default_printer)
}
/// Error indicating that [`set_hook()`] was unable to install the provided
/// [`ErrorHook`].
@ -81,18 +91,23 @@ impl Diagnostic for InstallError {}
Set the error hook.
*/
pub fn set_hook(hook: ErrorHook) -> Result<(), InstallError> {
HOOK.set(hook).map_err(|_| InstallError)
HOOK.call_once(|| hook);
Ok(())
}
#[cfg_attr(track_caller, track_caller)]
#[cfg_attr(not(track_caller), allow(unused_mut))]
fn capture_handler(error: &(dyn Diagnostic + 'static)) -> Box<dyn ReportHandler> {
let hook = HOOK.get_or_init(|| Box::new(get_default_printer)).as_ref();
static DEFAULT: Once<ErrorHook> = Once::new();
let hook = HOOK.get().unwrap_or_else(|| {
DEFAULT.call_once(|| default_hook());
DEFAULT.get().unwrap()
});
#[cfg(track_caller)]
{
let mut handler = hook(error);
handler.track_caller(std::panic::Location::caller());
handler.track_caller(core::panic::Location::caller());
handler
}
#[cfg(not(track_caller))]
@ -193,7 +208,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<T, Report>`

View File

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

View File

@ -1,6 +1,14 @@
extern crate alloc;
use core::fmt::{self, Debug, Display};
#[cfg(feature = "std")]
use std::error::Error as StdError;
#[cfg(not(feature = "std"))]
use crate::StdError as StdError;
use alloc::boxed::Box;
use alloc::string::String;
use alloc::vec::Vec;
use crate::{Diagnostic, LabeledSpan, Report, SourceCode};
@ -107,16 +115,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<E, C> {

View File

@ -8,7 +8,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)]

View File

@ -1,4 +1,7 @@
use std::fmt;
extern crate alloc;
use core::fmt;
use alloc::vec::Vec;
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)?;

View File

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

View File

@ -1,10 +1,15 @@
use std::fmt;
extern crate alloc;
use core::fmt;
use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
use crate::diagnostic_chain::DiagnosticChain;
use crate::protocol::{Diagnostic, Severity};
use crate::{LabeledSpan, MietteError, ReportHandler, SourceCode, SourceSpan, SpanContents};
use alloc::string::String;
use alloc::vec::Vec;
use alloc::boxed::Box;
/**
[`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();

View File

@ -1,3 +1,5 @@
#![no_std]
#![deny(missing_docs, missing_debug_implementations, nonstandard_style)]
#![warn(unreachable_pub, rust_2018_idioms)]
#![allow(unexpected_cfgs)]
@ -815,6 +817,23 @@
//! 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;
#[cfg(feature = "std")]
pub use std::error::Error as StdError;
#[cfg(not(feature = "std"))]
/// Compatibility trait for error handling in no_std environments.
/// This trait provides a subset of `std::error::Error` functionality
/// suitable for no_std environments.
pub trait StdError: core::fmt::Debug + core::fmt::Display {
/// Returns the lower-level source of this error, if any.
fn source(&self) -> Option<&(dyn StdError + 'static)> {
None
}
}
#[cfg(feature = "derive")]
pub use miette_derive::*;

View File

@ -51,6 +51,7 @@ impl ToLabeledSpan<LabeledSpan> for ToLabelSpanWrapper {
span
}
}
#[cfg(not(feature = "std"))]
impl<T> ToLabeledSpan<T> for ToLabelSpanWrapper
where
T: Into<SourceSpan>,

View File

@ -1,7 +1,14 @@
use std::{
error::Error,
fmt::{Debug, Display},
};
extern crate alloc;
use core::fmt::{Debug, Display};
#[cfg(feature = "std")]
use std::error::Error;
#[cfg(not(feature = "std"))]
use crate::StdError as Error;
use alloc::string::String;
use alloc::vec::{self, Vec};
use alloc::format;
use alloc::boxed::Box;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
@ -39,7 +46,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 +190,7 @@ impl MietteDiagnostic {
/// assert_eq!(diag.labels, Some(vec![label]));
/// ```
pub fn with_label(mut self, label: impl Into<LabeledSpan>) -> Self {
self.labels = Some(vec![label.into()]);
self.labels = Some(Vec::from([label.into()]));
self
}

View File

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

View File

@ -3,11 +3,15 @@ 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 core::fmt::{self, Display};
#[cfg(feature = "std")]
use std::fs;
use core::panic::Location;
use core::ops;
use alloc::boxed::Box;
use alloc::string::String;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
@ -17,7 +21,7 @@ use crate::{DiagnosticError, 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 +76,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 +94,7 @@ box_error_impls! {
macro_rules! box_borrow_impls {
($($box_type:ty),*) => {
$(
impl std::borrow::Borrow<dyn Diagnostic> for $box_type {
impl core::borrow::Borrow<dyn Diagnostic> for $box_type {
fn borrow(&self) -> &(dyn Diagnostic + 'static) {
self.as_ref()
}
@ -152,7 +152,10 @@ impl From<String> for Box<dyn Diagnostic + Send + Sync> {
fn from(s: String) -> Self {
struct StringError(String);
#[cfg(feature = "std")]
impl std::error::Error for StringError {}
#[cfg(not(feature = "std"))]
impl crate::StdError for StringError {}
impl Diagnostic for StringError {}
impl Display for StringError {
@ -172,6 +175,7 @@ impl From<String> for Box<dyn Diagnostic + Send + Sync> {
}
}
#[cfg(feature = "std")]
impl From<Box<dyn std::error::Error + Send + Sync>> for Box<dyn Diagnostic + Send + Sync> {
fn from(s: Box<dyn std::error::Error + Send + Sync>) -> Self {
Box::new(DiagnosticError(s))
@ -594,8 +598,8 @@ impl From<(SourceOffset, usize)> for SourceSpan {
}
}
impl From<std::ops::Range<ByteOffset>> for SourceSpan {
fn from(range: std::ops::Range<ByteOffset>) -> Self {
impl From<ops::Range<ByteOffset>> for SourceSpan {
fn from(range: ops::Range<ByteOffset>) -> Self {
Self {
offset: range.start.into(),
length: range.len(),
@ -603,12 +607,12 @@ impl From<std::ops::Range<ByteOffset>> for SourceSpan {
}
}
impl From<std::ops::RangeInclusive<ByteOffset>> for SourceSpan {
impl From<ops::RangeInclusive<ByteOffset>> for SourceSpan {
/// # Panics
///
/// Panics 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<ByteOffset>) -> Self {
fn from(range: ops::RangeInclusive<ByteOffset>) -> Self {
let (start, end) = range.clone().into_inner();
Self {
offset: start.into(),
@ -715,6 +719,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((
@ -723,6 +728,15 @@ impl SourceOffset {
.map(|txt| Self::from_location(txt, loc.line() as usize, loc.column() as usize))?,
))
}
/// Returns both the filename that was given and the offset of the caller
/// as a [`SourceOffset`].
///
/// In no_std environments, this is not supported and will return an error.
#[cfg(not(feature = "std"))]
pub fn from_current_location() -> Result<(String, Self), MietteError> {
Err(MietteError::OutOfBounds)
}
}
impl From<ByteOffset> for SourceOffset {

View File

@ -1,7 +1,16 @@
/*!
Default trait implementations for [`SourceCode`].
*/
use std::{borrow::Cow, collections::VecDeque, fmt::Debug, sync::Arc};
extern crate alloc;
use core::fmt::Debug;
use alloc::borrow::Cow;
use alloc::borrow::ToOwned;
use alloc::collections::VecDeque;
use alloc::sync::Arc;
use alloc::string::String;
use alloc::vec::Vec;
use alloc::boxed::Box;
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());