mirror of https://github.com/zkat/miette.git
fix(deps): miette can now be used without syn (#436)
* style(clippy): quickly fix up clippy lints * refactor(miette): remove `thiserror` from `error.rs` * fix(fancy): `fancy` no longer depends on `derive` Before this change, the `fancy` feature required the `derive` feature to compile. Despite this, `derive` was not listed as a required feature to use `fancy`. This meant that building with: `cargo build --no-default-features --features fancy` would result in a compilation error! Now `fancy` can be used without `derive` enabled, and another use of `thiserror` has been removed! * refactor(miette): remove `thiserror` from `into_diagnostic.rs` * refactor(miette): reuse `DiagnosticError` in `protocol.rs` * refactor(miette): make `thiserror` a dev-dependency * fix(miette): correctly forward error sources * fix(miette): match `TestError` visibility with `mod tests` * fix(miette): maintain 1.70 MSRV * fix(miette): another fix for MSRV 1.70 * docs(miette): sync README and `rustdoc`
This commit is contained in:
parent
59c81617de
commit
521ef91f77
|
|
@ -13,7 +13,6 @@ rust-version = "1.70.0"
|
||||||
exclude = ["images/", "tests/", "miette-derive/"]
|
exclude = ["images/", "tests/", "miette-derive/"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
thiserror = "2.0.11"
|
|
||||||
miette-derive = { path = "miette-derive", version = "=7.5.0", optional = true }
|
miette-derive = { path = "miette-derive", version = "=7.5.0", optional = true }
|
||||||
unicode-width = "0.1.11"
|
unicode-width = "0.1.11"
|
||||||
cfg-if = "1.0.0"
|
cfg-if = "1.0.0"
|
||||||
|
|
@ -30,6 +29,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"
|
||||||
|
|
||||||
# Eyre devdeps
|
# Eyre devdeps
|
||||||
|
|
|
||||||
88
src/error.rs
88
src/error.rs
|
|
@ -1,25 +1,51 @@
|
||||||
use std::{fmt, io};
|
use std::{
|
||||||
|
error::Error,
|
||||||
use thiserror::Error;
|
fmt::{self, Display},
|
||||||
|
io,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::Diagnostic;
|
use crate::Diagnostic;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Error enum for miette. Used by certain operations in the protocol.
|
Error enum for miette. Used by certain operations in the protocol.
|
||||||
*/
|
*/
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug)]
|
||||||
pub enum MietteError {
|
pub enum MietteError {
|
||||||
/// Wrapper around [`std::io::Error`]. This is returned when something went
|
/// Wrapper around [`std::io::Error`]. This is returned when something went
|
||||||
/// wrong while reading a [`SourceCode`](crate::SourceCode).
|
/// wrong while reading a [`SourceCode`](crate::SourceCode).
|
||||||
#[error(transparent)]
|
IoError(io::Error),
|
||||||
IoError(#[from] io::Error),
|
|
||||||
|
|
||||||
/// Returned when a [`SourceSpan`](crate::SourceSpan) extends beyond the
|
/// Returned when a [`SourceSpan`](crate::SourceSpan) extends beyond the
|
||||||
/// bounds of a given [`SourceCode`](crate::SourceCode).
|
/// bounds of a given [`SourceCode`](crate::SourceCode).
|
||||||
#[error("The given offset is outside the bounds of its Source")]
|
|
||||||
OutOfBounds,
|
OutOfBounds,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for MietteError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
MietteError::IoError(error) => write!(f, "{error}"),
|
||||||
|
MietteError::OutOfBounds => {
|
||||||
|
write!(f, "The given offset is outside the bounds of its Source")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for MietteError {
|
||||||
|
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||||
|
match self {
|
||||||
|
MietteError::IoError(error) => error.source(),
|
||||||
|
MietteError::OutOfBounds => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<io::Error> for MietteError {
|
||||||
|
fn from(value: io::Error) -> Self {
|
||||||
|
Self::IoError(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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 {
|
||||||
|
|
@ -49,3 +75,51 @@ impl Diagnostic for MietteError {
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) mod tests {
|
||||||
|
use std::{error::Error, io::ErrorKind};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct TestError(pub(crate) io::Error);
|
||||||
|
|
||||||
|
impl Display for TestError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "testing, testing...")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for TestError {
|
||||||
|
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||||
|
Some(&self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn io_error() {
|
||||||
|
let inner_error = io::Error::new(ErrorKind::Other, "halt and catch fire");
|
||||||
|
let outer_error = TestError(inner_error);
|
||||||
|
let io_error = io::Error::new(ErrorKind::Other, outer_error);
|
||||||
|
|
||||||
|
let miette_error = MietteError::from(io_error);
|
||||||
|
|
||||||
|
assert_eq!(miette_error.to_string(), "testing, testing...");
|
||||||
|
assert_eq!(
|
||||||
|
miette_error.source().unwrap().to_string(),
|
||||||
|
"halt and catch fire"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn out_of_bounds() {
|
||||||
|
let miette_error = MietteError::OutOfBounds;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
miette_error.to_string(),
|
||||||
|
"The given offset is outside the bounds of its Source"
|
||||||
|
);
|
||||||
|
assert_eq!(miette_error.source().map(ToString::to_string), None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -280,7 +280,7 @@ impl Report {
|
||||||
/// The root cause is the last error in the iterator produced by
|
/// The root cause is the last error in the iterator produced by
|
||||||
/// [`chain()`](Report::chain).
|
/// [`chain()`](Report::chain).
|
||||||
pub fn root_cause(&self) -> &(dyn StdError + 'static) {
|
pub fn root_cause(&self) -> &(dyn StdError + 'static) {
|
||||||
self.chain().last().unwrap()
|
self.chain().next_back().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if `E` is the type held by this error object.
|
/// Returns true if `E` is the type held by this error object.
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,24 @@
|
||||||
use thiserror::Error;
|
use std::{error::Error, fmt::Display};
|
||||||
|
|
||||||
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, Error)]
|
#[derive(Debug)]
|
||||||
#[error(transparent)]
|
|
||||||
pub(crate) struct DiagnosticError(pub(crate) Box<dyn std::error::Error + Send + Sync + 'static>);
|
pub(crate) struct DiagnosticError(pub(crate) Box<dyn std::error::Error + Send + Sync + 'static>);
|
||||||
|
|
||||||
|
impl Display for DiagnosticError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let msg = &self.0;
|
||||||
|
write!(f, "{msg}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Error for DiagnosticError {
|
||||||
|
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||||
|
self.0.source()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Diagnostic for DiagnosticError {}
|
impl Diagnostic for DiagnosticError {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -31,3 +43,26 @@ impl<T, E: std::error::Error + Send + Sync + 'static> IntoDiagnostic<T, E> for R
|
||||||
self.map_err(|e| DiagnosticError(Box::new(e)).into())
|
self.map_err(|e| DiagnosticError(Box::new(e)).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::io::{self, ErrorKind};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use crate::error::tests::TestError;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn diagnostic_error() {
|
||||||
|
let inner_error = io::Error::new(ErrorKind::Other, "halt and catch fire");
|
||||||
|
let outer_error: Result<(), _> = Err(TestError(inner_error));
|
||||||
|
|
||||||
|
let diagnostic_error = outer_error.into_diagnostic().unwrap_err();
|
||||||
|
|
||||||
|
assert_eq!(diagnostic_error.to_string(), "testing, testing...");
|
||||||
|
assert_eq!(
|
||||||
|
diagnostic_error.source().unwrap().to_string(),
|
||||||
|
"halt and catch fire"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -424,7 +424,7 @@ impl HighlighterOption {
|
||||||
highlighter: Option<MietteHighlighter>,
|
highlighter: Option<MietteHighlighter>,
|
||||||
supports_color: bool,
|
supports_color: bool,
|
||||||
) -> HighlighterOption {
|
) -> HighlighterOption {
|
||||||
if color == Some(false) || (color == None && !supports_color) {
|
if color == Some(false) || (color.is_none() && !supports_color) {
|
||||||
return HighlighterOption::Disable;
|
return HighlighterOption::Disable;
|
||||||
}
|
}
|
||||||
highlighter
|
highlighter
|
||||||
|
|
@ -433,6 +433,9 @@ impl HighlighterOption {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: This is manually implemented so that it's clearer what's going on with
|
||||||
|
// the conditional compilation — clippy isn't picking up the `cfg` stuff here
|
||||||
|
#[allow(clippy::derivable_impls)]
|
||||||
impl Default for HighlighterOption {
|
impl Default for HighlighterOption {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
cfg_if! {
|
cfg_if! {
|
||||||
|
|
|
||||||
|
|
@ -96,12 +96,12 @@ impl SyntectHighlighter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// finally, attempt to guess syntax based on first line
|
// finally, attempt to guess syntax based on first line
|
||||||
return self.syntax_set.find_syntax_by_first_line(
|
self.syntax_set.find_syntax_by_first_line(
|
||||||
std::str::from_utf8(contents.data())
|
std::str::from_utf8(contents.data())
|
||||||
.ok()?
|
.ok()?
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.next()?,
|
.next()?,
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -115,7 +115,7 @@ pub(crate) struct SyntectHighlighterState<'h> {
|
||||||
use_bg_color: bool,
|
use_bg_color: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'h> HighlighterState for SyntectHighlighterState<'h> {
|
impl HighlighterState for SyntectHighlighterState<'_> {
|
||||||
fn highlight_line<'s>(&mut self, line: &'s str) -> Vec<Styled<&'s str>> {
|
fn highlight_line<'s>(&mut self, line: &'s str) -> Vec<Styled<&'s str>> {
|
||||||
if let Ok(ops) = self.parse_state.parse_line(line, self.syntax_set) {
|
if let Ok(ops) = self.parse_state.parse_line(line, self.syntax_set) {
|
||||||
let use_bg_color = self.use_bg_color;
|
let use_bg_color = self.use_bg_color;
|
||||||
|
|
|
||||||
32
src/lib.rs
32
src/lib.rs
|
|
@ -51,6 +51,7 @@
|
||||||
//! - [... handler options](#-handler-options)
|
//! - [... handler options](#-handler-options)
|
||||||
//! - [... dynamic diagnostics](#-dynamic-diagnostics)
|
//! - [... dynamic diagnostics](#-dynamic-diagnostics)
|
||||||
//! - [... syntax highlighting](#-syntax-highlighting)
|
//! - [... syntax highlighting](#-syntax-highlighting)
|
||||||
|
//! - [... primary label](#-primary-label)
|
||||||
//! - [... collection of labels](#-collection-of-labels)
|
//! - [... collection of labels](#-collection-of-labels)
|
||||||
//! - [Acknowledgements](#acknowledgements)
|
//! - [Acknowledgements](#acknowledgements)
|
||||||
//! - [License](#license)
|
//! - [License](#license)
|
||||||
|
|
@ -690,6 +691,37 @@
|
||||||
//! [`with_syntax_highlighting`](MietteHandlerOpts::with_syntax_highlighting)
|
//! [`with_syntax_highlighting`](MietteHandlerOpts::with_syntax_highlighting)
|
||||||
//! method. See the [`highlighters`] module docs for more details.
|
//! method. See the [`highlighters`] module docs for more details.
|
||||||
//!
|
//!
|
||||||
|
//! ### ... primary label
|
||||||
|
//!
|
||||||
|
//! You can use the `primary` parameter to `label` to indicate that the label
|
||||||
|
//! is the primary label.
|
||||||
|
//!
|
||||||
|
//! ```rust,ignore
|
||||||
|
//! #[derive(Debug, Diagnostic, Error)]
|
||||||
|
//! #[error("oops!")]
|
||||||
|
//! struct MyError {
|
||||||
|
//! #[label(primary, "main issue")]
|
||||||
|
//! primary_span: SourceSpan,
|
||||||
|
//!
|
||||||
|
//! #[label("other label")]
|
||||||
|
//! other_span: SourceSpan,
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! The `primary` parameter can be used at most once:
|
||||||
|
//!
|
||||||
|
//! ```rust,ignore
|
||||||
|
//! #[derive(Debug, Diagnostic, Error)]
|
||||||
|
//! #[error("oops!")]
|
||||||
|
//! struct MyError {
|
||||||
|
//! #[label(primary, "main issue")]
|
||||||
|
//! primary_span: SourceSpan,
|
||||||
|
//!
|
||||||
|
//! #[label(primary, "other label")] // Error: Cannot have more than one primary label.
|
||||||
|
//! other_span: SourceSpan,
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
//! ### ... collection of labels
|
//! ### ... collection of labels
|
||||||
//!
|
//!
|
||||||
//! When the number of labels is unknown, you can use a collection of `SourceSpan`
|
//! When the number of labels is unknown, you can use a collection of `SourceSpan`
|
||||||
|
|
|
||||||
58
src/panic.rs
58
src/panic.rs
|
|
@ -1,7 +1,8 @@
|
||||||
use backtrace::Backtrace;
|
use std::{error::Error, fmt::Display};
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
use crate::{self as miette, Context, Diagnostic, Result};
|
use backtrace::Backtrace;
|
||||||
|
|
||||||
|
use crate::{Context, Diagnostic, Result};
|
||||||
|
|
||||||
/// Tells miette to render panics using its rendering engine.
|
/// Tells miette to render panics using its rendering engine.
|
||||||
pub fn set_panic_hook() {
|
pub fn set_panic_hook() {
|
||||||
|
|
@ -25,11 +26,27 @@ pub fn set_panic_hook() {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error, Diagnostic)]
|
#[derive(Debug)]
|
||||||
#[error("{0}{panic}", panic = Panic::backtrace())]
|
|
||||||
#[diagnostic(help("set the `RUST_BACKTRACE=1` environment variable to display a backtrace."))]
|
|
||||||
struct Panic(String);
|
struct Panic(String);
|
||||||
|
|
||||||
|
impl Display for Panic {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let msg = &self.0;
|
||||||
|
let panic = Panic::backtrace();
|
||||||
|
write!(f, "{msg}{panic}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for Panic {}
|
||||||
|
|
||||||
|
impl Diagnostic for Panic {
|
||||||
|
fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
|
||||||
|
Some(Box::new(
|
||||||
|
"set the `RUST_BACKTRACE=1` environment variable to display a backtrace.",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Panic {
|
impl Panic {
|
||||||
fn backtrace() -> String {
|
fn backtrace() -> String {
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
@ -84,3 +101,32 @@ impl Panic {
|
||||||
"".into()
|
"".into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn panic() {
|
||||||
|
let panic = Panic("ruh roh raggy".to_owned());
|
||||||
|
|
||||||
|
assert_eq!(panic.to_string(), "ruh roh raggy");
|
||||||
|
assert!(panic.source().is_none());
|
||||||
|
assert!(panic.code().is_none());
|
||||||
|
assert!(panic.severity().is_none());
|
||||||
|
assert_eq!(
|
||||||
|
panic.help().map(|h| h.to_string()),
|
||||||
|
Some(
|
||||||
|
"set the `RUST_BACKTRACE=1` environment variable to display a backtrace."
|
||||||
|
.to_owned()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
assert!(panic.url().is_none());
|
||||||
|
assert!(panic.source_code().is_none());
|
||||||
|
assert!(panic.labels().is_none());
|
||||||
|
assert!(panic.related().is_none());
|
||||||
|
assert!(panic.diagnostic_source().is_none());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ use std::{
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::MietteError;
|
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
|
||||||
|
|
@ -174,18 +174,7 @@ impl From<String> for Box<dyn Diagnostic + Send + Sync> {
|
||||||
|
|
||||||
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 {
|
||||||
#[derive(thiserror::Error)]
|
Box::new(DiagnosticError(s))
|
||||||
#[error(transparent)]
|
|
||||||
struct BoxedDiagnostic(Box<dyn std::error::Error + Send + Sync>);
|
|
||||||
impl fmt::Debug for BoxedDiagnostic {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
fmt::Debug::fmt(&self.0, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Diagnostic for BoxedDiagnostic {}
|
|
||||||
|
|
||||||
Box::new(BoxedDiagnostic(s))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue