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/"] 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 = "0.2.0" 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"] }
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 }
@ -29,9 +31,11 @@ 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"
[build-dependencies]
rustc_version = "0.2"
# Eyre devdeps # Eyre devdeps
futures = { version = "0.3", default-features = false } futures = { version = "0.3", default-features = false }
indenter = "0.3.3" indenter = "0.3.3"
@ -45,7 +49,8 @@ serde_json = "1.0.113"
strip-ansi-escapes = "0.2.0" strip-ansi-escapes = "0.2.0"
[features] [features]
default = ["derive"] default = ["derive", "std"]
std = ["thiserror/std", "fancy-no-syscall"]
derive = ["dep:miette-derive"] derive = ["dep:miette-derive"]
no-format-args-capture = [] no-format-args-capture = []
fancy-base = [ fancy-base = [

View File

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

View File

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

View File

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

View File

@ -94,7 +94,7 @@ impl Help {
Help::Display(display) => { Help::Display(display) => {
let (fmt, args) = display.expand_shorthand_cloned(&display_members); let (fmt, args) = display.expand_shorthand_cloned(&display_members);
Some(quote! { Some(quote! {
Self::#ident #display_pat => std::option::Option::Some(std::boxed::Box::new(format!(#fmt #args))), Self::#ident #display_pat => Option::Some(alloc::boxed::Box::new(format!(#fmt #args))),
}) })
} }
Help::Field(member, ty) => { Help::Field(member, ty) => {
@ -108,7 +108,7 @@ impl Help {
Some(quote! { Some(quote! {
Self::#ident #display_pat => { Self::#ident #display_pat => {
use miette::macro_helpers::ToOption; use miette::macro_helpers::ToOption;
miette::macro_helpers::OptionalWrapper::<#ty>::new().to_option(&#help).as_ref().map(|#var| -> std::boxed::Box<dyn std::fmt::Display + '_> { std::boxed::Box::new(format!("{}", #var)) }) miette::macro_helpers::OptionalWrapper::<#ty>::new().to_option(&#help).as_ref().map(|#var| -> alloc::boxed::Box<dyn core::fmt::Display + '_> { alloc::boxed::Box::new(format!("{}", #var)) })
}, },
}) })
} }
@ -123,21 +123,21 @@ impl Help {
Help::Display(display) => { Help::Display(display) => {
let (fmt, args) = display.expand_shorthand_cloned(&display_members); let (fmt, args) = display.expand_shorthand_cloned(&display_members);
Some(quote! { Some(quote! {
fn help(&self) -> std::option::Option<std::boxed::Box<dyn std::fmt::Display + '_>> { fn help(&self) -> Option<alloc::boxed::Box<dyn core::fmt::Display + '_>> {
#[allow(unused_variables, deprecated)] #[allow(unused_variables, deprecated)]
let Self #display_pat = self; let Self #display_pat = self;
std::option::Option::Some(std::boxed::Box::new(format!(#fmt #args))) Option::Some(alloc::boxed::Box::new(format!(#fmt #args)))
} }
}) })
} }
Help::Field(member, ty) => { Help::Field(member, ty) => {
let var = quote! { __miette_internal_var }; let var = quote! { __miette_internal_var };
Some(quote! { Some(quote! {
fn help(&self) -> std::option::Option<std::boxed::Box<dyn std::fmt::Display + '_>> { fn help(&self) -> Option<alloc::boxed::Box<dyn core::fmt::Display + '_>> {
#[allow(unused_variables, deprecated)] #[allow(unused_variables, deprecated)]
let Self #display_pat = self; let Self #display_pat = self;
use miette::macro_helpers::ToOption; use miette::macro_helpers::ToOption;
miette::macro_helpers::OptionalWrapper::<#ty>::new().to_option(&self.#member).as_ref().map(|#var| -> std::boxed::Box<dyn std::fmt::Display + '_> { std::boxed::Box::new(format!("{}", #var)) }) miette::macro_helpers::OptionalWrapper::<#ty>::new().to_option(&self.#member).as_ref().map(|#var| -> 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 var = quote! { __miette_internal_var };
let display = if let Some(display) = label { let display = if let Some(display) = label {
let (fmt, args) = display.expand_shorthand_cloned(&display_members); let (fmt, args) = display.expand_shorthand_cloned(&display_members);
quote! { std::option::Option::Some(format!(#fmt #args)) } quote! { Option::Some(format!(#fmt #args)) }
} else { } else {
quote! { std::option::Option::None } quote! { Option::None }
}; };
let ctor = if *lbl_ty == LabelType::Primary { let ctor = if *lbl_ty == LabelType::Primary {
quote! { miette::LabeledSpan::new_primary_with_span } quote! { miette::LabeledSpan::new_primary_with_span }
@ -205,9 +205,9 @@ impl Labels {
} }
let display = if let Some(display) = label { let display = if let Some(display) = label {
let (fmt, args) = display.expand_shorthand_cloned(&display_members); let (fmt, args) = display.expand_shorthand_cloned(&display_members);
quote! { std::option::Option::Some(format!(#fmt #args)) } quote! { Option::Some(format!(#fmt #args)) }
} else { } else {
quote! { std::option::Option::None } quote! { Option::None }
}; };
Some(quote! { Some(quote! {
.chain({ .chain({
@ -226,7 +226,7 @@ impl Labels {
Some(quote! { Some(quote! {
#[allow(unused_variables)] #[allow(unused_variables)]
fn labels(&self) -> std::option::Option<std::boxed::Box<dyn std::iter::Iterator<Item = miette::LabeledSpan> + '_>> { fn labels(&self) -> Option<alloc::boxed::Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
use miette::macro_helpers::ToOption; use miette::macro_helpers::ToOption;
let Self #display_pat = self; let Self #display_pat = self;
@ -236,7 +236,7 @@ impl Labels {
.into_iter() .into_iter()
#(#collections_chain)*; #(#collections_chain)*;
std::option::Option::Some(Box::new(labels_iter.filter(Option::is_some).map(Option::unwrap))) Option::Some(alloc::boxed::Box::new(labels_iter.filter(Option::is_some).map(Option::unwrap)))
} }
}) })
} }
@ -322,7 +322,7 @@ impl Labels {
] ]
.into_iter() .into_iter()
#(#collections_chain)*; #(#collections_chain)*;
std::option::Option::Some(std::boxed::Box::new(labels_iter.filter(Option::is_some).map(Option::unwrap))) Option::Some(alloc::boxed::Box::new(labels_iter.filter(Option::is_some).map(Option::unwrap)))
} }
}), }),
} }

View File

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

View File

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

View File

@ -96,7 +96,7 @@ impl Url {
} }
}; };
Some(quote! { Some(quote! {
Self::#ident #pat => std::option::Option::Some(std::boxed::Box::new(format!(#fmt #args))), Self::#ident #pat => Option::Some(alloc::boxed::Box::new(format!(#fmt #args))),
}) })
}, },
) )
@ -129,10 +129,10 @@ impl Url {
} }
}; };
Some(quote! { Some(quote! {
fn url(&self) -> std::option::Option<std::boxed::Box<dyn std::fmt::Display + '_>> { fn url(&self) -> Option<alloc::boxed::Box<dyn core::fmt::Display + '_>> {
#[allow(unused_variables, deprecated)] #[allow(unused_variables, deprecated)]
let Self #pat = self; let Self #pat = self;
std::option::Option::Some(std::boxed::Box::new(format!(#fmt #args))) Option::Some(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>. 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::error::Error as StdError;
use std::vec; #[cfg(not(feature = "std"))]
use crate::StdError;
use alloc::vec::{self, Vec};
use ChainState::*; use ChainState::*;

View File

@ -2,7 +2,10 @@
Iterate over error `.diagnostic_source()` chains. Iterate over error `.diagnostic_source()` chains.
*/ */
extern crate alloc;
use crate::protocol::Diagnostic; use crate::protocol::Diagnostic;
use alloc::string::ToString;
/// Iterator of a chain of cause errors. /// Iterator of a chain of cause errors.
#[derive(Clone, Default)] #[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 { DiagnosticChain {
state: Some(ErrorKind::StdError(head)), state: Some(ErrorKind::StdError(head)),
} }
@ -59,7 +62,7 @@ impl ExactSizeIterator for DiagnosticChain<'_> {
#[derive(Clone)] #[derive(Clone)]
pub(crate) enum ErrorKind<'a> { pub(crate) enum ErrorKind<'a> {
Diagnostic(&'a dyn Diagnostic), Diagnostic(&'a dyn Diagnostic),
StdError(&'a (dyn std::error::Error + 'static)), StdError(&'a (dyn crate::StdError + 'static)),
} }
impl<'a> ErrorKind<'a> { impl<'a> ErrorKind<'a> {
@ -74,8 +77,8 @@ impl<'a> ErrorKind<'a> {
} }
} }
impl std::fmt::Debug for ErrorKind<'_> { impl core::fmt::Debug for ErrorKind<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self { match self {
ErrorKind::Diagnostic(d) => d.fmt(f), ErrorKind::Diagnostic(d) => d.fmt(f),
ErrorKind::StdError(e) => e.fmt(f), ErrorKind::StdError(e) => e.fmt(f),
@ -83,8 +86,8 @@ impl std::fmt::Debug for ErrorKind<'_> {
} }
} }
impl std::fmt::Display for ErrorKind<'_> { impl core::fmt::Display for ErrorKind<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self { match self {
ErrorKind::Diagnostic(d) => d.fmt(f), ErrorKind::Diagnostic(d) => d.fmt(f),
ErrorKind::StdError(e) => e.fmt(f), ErrorKind::StdError(e) => e.fmt(f),

View File

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

View File

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

View File

@ -1,8 +1,14 @@
extern crate alloc;
use core::any::TypeId; use core::any::TypeId;
use core::fmt::{self, Debug, Display}; use core::fmt::{self, Debug, Display};
use core::mem::ManuallyDrop; use core::mem::ManuallyDrop;
use core::ptr::{self, NonNull}; use core::ptr::{self, NonNull};
#[cfg(feature = "std")]
use std::error::Error as StdError; 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::ptr::{Mut, Own, Ref};
use super::Report; use super::Report;
@ -429,7 +435,7 @@ impl Report {
/// Construct a [`Report`] directly from an error-like type /// Construct a [`Report`] directly from an error-like type
pub fn from_err<E>(err: E) -> Self pub fn from_err<E>(err: E) -> Self
where where
E: std::error::Error + Send + Sync + 'static, E: StdError + Send + Sync + 'static,
{ {
super::DiagnosticError(Box::new(err)).into() super::DiagnosticError(Box::new(err)).into()
} }
@ -816,8 +822,16 @@ impl AsRef<dyn StdError> for Report {
} }
} }
#[cfg(feature = "std")]
impl std::borrow::Borrow<dyn Diagnostic> for Report { impl std::borrow::Borrow<dyn Diagnostic> for Report {
fn borrow(&self) -> &(dyn Diagnostic + 'static) { fn borrow(&self) -> &(dyn Diagnostic + 'static) {
self.as_ref() 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}; use crate::{Diagnostic, Report};
/// Convenience [`Diagnostic`] that can be used as an "anonymous" wrapper for /// Convenience [`Diagnostic`] that can be used as an "anonymous" wrapper for
/// Errors. This is intended to be paired with [`IntoDiagnostic`]. /// Errors. This is intended to be paired with [`IntoDiagnostic`].
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct DiagnosticError(pub(crate) Box<dyn std::error::Error + Send + Sync + 'static>); pub(crate) struct DiagnosticError(pub(crate) Box<dyn Error + Send + Sync + 'static>);
impl Display for DiagnosticError { impl core::fmt::Display for DiagnosticError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let msg = &self.0; let msg = &self.0;
write!(f, "{msg}") write!(f, "{msg}")
} }
@ -38,20 +45,31 @@ 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> { impl<T, E: std::error::Error + Send + Sync + 'static> IntoDiagnostic<T, E> for Result<T, E> {
fn into_diagnostic(self) -> Result<T, Report> { fn into_diagnostic(self) -> Result<T, Report> {
self.map_err(|e| DiagnosticError(Box::new(e)).into()) self.map_err(|e| DiagnosticError(Box::new(e)).into())
} }
} }
#[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)] #[cfg(test)]
mod tests { mod tests {
#[cfg(feature = "std")]
use std::io::{self, ErrorKind}; use std::io::{self, ErrorKind};
use super::*; use super::*;
#[cfg(feature = "std")]
use crate::error::tests::TestError; use crate::error::tests::TestError;
#[cfg(feature = "std")]
#[test] #[test]
fn diagnostic_error() { fn diagnostic_error() {
let inner_error = io::Error::new(ErrorKind::Other, "halt and catch fire"); let inner_error = io::Error::new(ErrorKind::Other, "halt and catch fire");

View File

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

View File

@ -4,10 +4,16 @@
clippy::new_ret_no_self, clippy::new_ret_no_self,
clippy::wrong_self_convention 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::error::Error as StdError;
use std::sync::OnceLock; #[cfg(not(feature = "std"))]
use crate::StdError as StdError;
use spin::Once;
#[allow(unreachable_pub)] #[allow(unreachable_pub)]
pub use into_diagnostic::*; pub use into_diagnostic::*;
@ -61,7 +67,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: 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 /// Error indicating that [`set_hook()`] was unable to install the provided
/// [`ErrorHook`]. /// [`ErrorHook`].
@ -81,18 +91,23 @@ impl Diagnostic for InstallError {}
Set the error hook. Set the error hook.
*/ */
pub fn set_hook(hook: ErrorHook) -> Result<(), InstallError> { 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(track_caller, track_caller)]
#[cfg_attr(not(track_caller), allow(unused_mut))] #[cfg_attr(not(track_caller), allow(unused_mut))]
fn capture_handler(error: &(dyn Diagnostic + 'static)) -> Box<dyn ReportHandler> { 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)] #[cfg(track_caller)]
{ {
let mut handler = hook(error); let mut handler = hook(error);
handler.track_caller(std::panic::Location::caller()); handler.track_caller(core::panic::Location::caller());
handler handler
} }
#[cfg(not(track_caller))] #[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 /// Store the location of the caller who constructed this error report
#[allow(unused_variables)] #[allow(unused_variables)]
fn track_caller(&mut self, location: &'static std::panic::Location<'static>) {} fn track_caller(&mut self, location: &'static core::panic::Location<'static>) {}
} }
/// type alias for `Result<T, Report>` /// type alias for `Result<T, Report>`

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)] #[repr(transparent)]
/// A raw pointer that owns its pointee /// A raw pointer that owns its pointee

View File

@ -1,6 +1,14 @@
extern crate alloc;
use core::fmt::{self, Debug, Display}; use core::fmt::{self, Debug, Display};
#[cfg(feature = "std")]
use std::error::Error as StdError; 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}; use crate::{Diagnostic, LabeledSpan, Report, SourceCode};
@ -107,16 +115,6 @@ impl StdError for BoxedError {
fn source(&self) -> Option<&(dyn StdError + 'static)> { fn source(&self) -> Option<&(dyn StdError + 'static)> {
self.0.source() self.0.source()
} }
fn description(&self) -> &str {
#[allow(deprecated)]
self.0.description()
}
fn cause(&self) -> Option<&dyn StdError> {
#[allow(deprecated)]
self.0.cause()
}
} }
pub(crate) struct WithSourceCode<E, C> { pub(crate) struct WithSourceCode<E, C> {

View File

@ -8,7 +8,7 @@ use crate::ReportHandler;
use crate::ThemeCharacters; use crate::ThemeCharacters;
use crate::ThemeStyles; use crate::ThemeStyles;
use cfg_if::cfg_if; use cfg_if::cfg_if;
use std::fmt; use core::fmt;
/// Settings to control the color format used for graphical rendering. /// Settings to control the color format used for graphical rendering.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)] #[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]

View File

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

View File

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

View File

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

View File

@ -1,3 +1,5 @@
#![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)]
#![allow(unexpected_cfgs)] #![allow(unexpected_cfgs)]
@ -815,6 +817,23 @@
//! and some from [`thiserror`](https://github.com/dtolnay/thiserror), also //! and some from [`thiserror`](https://github.com/dtolnay/thiserror), also
//! under the Apache License. Some code is taken from //! under the Apache License. Some code is taken from
//! [`ariadne`](https://github.com/zesterer/ariadne), which is MIT licensed. //! [`ariadne`](https://github.com/zesterer/ariadne), which is MIT licensed.
#[cfg(feature = "std")]
extern crate std;
#[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")] #[cfg(feature = "derive")]
pub use miette_derive::*; pub use miette_derive::*;

View File

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

View File

@ -1,7 +1,14 @@
use std::{ extern crate alloc;
error::Error,
fmt::{Debug, Display}, 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")] #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -39,7 +46,7 @@ pub struct MietteDiagnostic {
} }
impl Display for MietteDiagnostic { impl Display for MietteDiagnostic {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", &self.message) write!(f, "{}", &self.message)
} }
} }
@ -183,7 +190,7 @@ impl MietteDiagnostic {
/// assert_eq!(diag.labels, Some(vec![label])); /// assert_eq!(diag.labels, Some(vec![label]));
/// ``` /// ```
pub fn with_label(mut self, label: impl Into<LabeledSpan>) -> Self { pub fn with_label(mut self, label: impl Into<LabeledSpan>) -> Self {
self.labels = Some(vec![label.into()]); self.labels = Some(Vec::from([label.into()]));
self self
} }

View File

@ -10,8 +10,14 @@ pub struct NamedSource<S: SourceCode + 'static> {
language: Option<String>, language: Option<String>,
} }
impl<S: SourceCode> std::fmt::Debug for NamedSource<S> { extern crate alloc;
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use alloc::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") f.debug_struct("NamedSource")
.field("name", &self.name) .field("name", &self.name)
.field("source", &"<redacted>") .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) traits that you can implement to get access to miette's (and related library's)
full reporting and such features. full reporting and such features.
*/ */
use std::{ extern crate alloc;
fmt::{self, Display},
fs, use core::fmt::{self, Display};
panic::Location, #[cfg(feature = "std")]
}; use std::fs;
use core::panic::Location;
use core::ops;
use alloc::boxed::Box;
use alloc::string::String;
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -17,7 +21,7 @@ use crate::{DiagnosticError, MietteError};
/// Adds rich metadata to your Error that can be used by /// Adds rich metadata to your Error that can be used by
/// [`Report`](crate::Report) to print really nice and human-friendly error /// [`Report`](crate::Report) to print really nice and human-friendly error
/// messages. /// messages.
pub trait Diagnostic: std::error::Error { pub trait Diagnostic: crate::StdError {
/// Unique diagnostic code that can be used to look up more information /// Unique diagnostic code that can be used to look up more information
/// about this `Diagnostic`. Ideally also globally unique, and documented /// about this `Diagnostic`. Ideally also globally unique, and documented
/// in the toplevel crate's documentation for easy searching. Rust path /// in the toplevel crate's documentation for easy searching. Rust path
@ -72,14 +76,10 @@ pub trait Diagnostic: std::error::Error {
macro_rules! box_error_impls { macro_rules! box_error_impls {
($($box_type:ty),*) => { ($($box_type:ty),*) => {
$( $(
impl std::error::Error for $box_type { impl crate::StdError for $box_type {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { fn source(&self) -> Option<&(dyn crate::StdError + 'static)> {
(**self).source() (**self).source()
} }
fn cause(&self) -> Option<&dyn std::error::Error> {
self.source()
}
} }
)* )*
} }
@ -94,7 +94,7 @@ box_error_impls! {
macro_rules! box_borrow_impls { macro_rules! box_borrow_impls {
($($box_type:ty),*) => { ($($box_type:ty),*) => {
$( $(
impl std::borrow::Borrow<dyn Diagnostic> for $box_type { impl core::borrow::Borrow<dyn Diagnostic> for $box_type {
fn borrow(&self) -> &(dyn Diagnostic + 'static) { fn borrow(&self) -> &(dyn Diagnostic + 'static) {
self.as_ref() self.as_ref()
} }
@ -152,7 +152,10 @@ impl From<String> for Box<dyn Diagnostic + Send + Sync> {
fn from(s: String) -> Self { fn from(s: String) -> Self {
struct StringError(String); struct StringError(String);
#[cfg(feature = "std")]
impl std::error::Error for StringError {} impl std::error::Error for StringError {}
#[cfg(not(feature = "std"))]
impl crate::StdError for StringError {}
impl Diagnostic for StringError {} impl Diagnostic for StringError {}
impl Display 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> { 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 { fn from(s: Box<dyn std::error::Error + Send + Sync>) -> Self {
Box::new(DiagnosticError(s)) Box::new(DiagnosticError(s))
@ -594,8 +598,8 @@ impl From<(SourceOffset, usize)> for SourceSpan {
} }
} }
impl From<std::ops::Range<ByteOffset>> for SourceSpan { impl From<ops::Range<ByteOffset>> for SourceSpan {
fn from(range: std::ops::Range<ByteOffset>) -> Self { fn from(range: ops::Range<ByteOffset>) -> Self {
Self { Self {
offset: range.start.into(), offset: range.start.into(),
length: range.len(), length: range.len(),
@ -603,12 +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
/// ///
/// Panics if the total length of the inclusive range would overflow a /// Panics if the total length of the inclusive range would overflow a
/// `usize`. This will only occur with the range `0..=usize::MAX`. /// `usize`. This will only occur with the range `0..=usize::MAX`.
fn from(range: std::ops::RangeInclusive<ByteOffset>) -> Self { fn from(range: ops::RangeInclusive<ByteOffset>) -> Self {
let (start, end) = range.clone().into_inner(); let (start, end) = range.clone().into_inner();
Self { Self {
offset: start.into(), offset: start.into(),
@ -715,6 +719,7 @@ impl SourceOffset {
/// you're shipping binaries for your application, you'll want to ignore /// you're shipping binaries for your application, you'll want to ignore
/// the Err case or otherwise report it. /// the Err case or otherwise report it.
#[track_caller] #[track_caller]
#[cfg(feature = "std")]
pub fn from_current_location() -> Result<(String, Self), MietteError> { pub fn from_current_location() -> Result<(String, Self), MietteError> {
let loc = Location::caller(); let loc = Location::caller();
Ok(( Ok((
@ -723,6 +728,15 @@ impl SourceOffset {
.map(|txt| Self::from_location(txt, loc.line() as usize, loc.column() as usize))?, .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 { impl From<ByteOffset> for SourceOffset {

View File

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