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/"]
|
||||
|
||||
[dependencies]
|
||||
thiserror = "2.0.11"
|
||||
miette-derive = { path = "miette-derive", version = "=7.5.0", optional = true }
|
||||
unicode-width = "0.1.11"
|
||||
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 }
|
||||
|
||||
[dev-dependencies]
|
||||
thiserror = "2.0.11"
|
||||
semver = "1.0.21"
|
||||
|
||||
# Eyre devdeps
|
||||
|
|
|
|||
88
src/error.rs
88
src/error.rs
|
|
@ -1,25 +1,51 @@
|
|||
use std::{fmt, io};
|
||||
|
||||
use thiserror::Error;
|
||||
use std::{
|
||||
error::Error,
|
||||
fmt::{self, Display},
|
||||
io,
|
||||
};
|
||||
|
||||
use crate::Diagnostic;
|
||||
|
||||
/**
|
||||
Error enum for miette. Used by certain operations in the protocol.
|
||||
*/
|
||||
#[derive(Debug, Error)]
|
||||
#[derive(Debug)]
|
||||
pub enum MietteError {
|
||||
/// Wrapper around [`std::io::Error`]. This is returned when something went
|
||||
/// wrong while reading a [`SourceCode`](crate::SourceCode).
|
||||
#[error(transparent)]
|
||||
IoError(#[from] io::Error),
|
||||
IoError(io::Error),
|
||||
|
||||
/// Returned when a [`SourceSpan`](crate::SourceSpan) extends beyond the
|
||||
/// bounds of a given [`SourceCode`](crate::SourceCode).
|
||||
#[error("The given offset is outside the bounds of its Source")]
|
||||
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 {
|
||||
fn code<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
|
||||
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
|
||||
/// [`chain()`](Report::chain).
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -1,12 +1,24 @@
|
|||
use thiserror::Error;
|
||||
use std::{error::Error, fmt::Display};
|
||||
|
||||
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, Error)]
|
||||
#[error(transparent)]
|
||||
#[derive(Debug)]
|
||||
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 {}
|
||||
|
||||
/**
|
||||
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
#[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>,
|
||||
supports_color: bool,
|
||||
) -> HighlighterOption {
|
||||
if color == Some(false) || (color == None && !supports_color) {
|
||||
if color == Some(false) || (color.is_none() && !supports_color) {
|
||||
return HighlighterOption::Disable;
|
||||
}
|
||||
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 {
|
||||
fn default() -> Self {
|
||||
cfg_if! {
|
||||
|
|
|
|||
|
|
@ -96,12 +96,12 @@ impl SyntectHighlighter {
|
|||
}
|
||||
}
|
||||
// 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())
|
||||
.ok()?
|
||||
.split('\n')
|
||||
.next()?,
|
||||
);
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -115,7 +115,7 @@ pub(crate) struct SyntectHighlighterState<'h> {
|
|||
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>> {
|
||||
if let Ok(ops) = self.parse_state.parse_line(line, self.syntax_set) {
|
||||
let use_bg_color = self.use_bg_color;
|
||||
|
|
|
|||
32
src/lib.rs
32
src/lib.rs
|
|
@ -51,6 +51,7 @@
|
|||
//! - [... handler options](#-handler-options)
|
||||
//! - [... dynamic diagnostics](#-dynamic-diagnostics)
|
||||
//! - [... syntax highlighting](#-syntax-highlighting)
|
||||
//! - [... primary label](#-primary-label)
|
||||
//! - [... collection of labels](#-collection-of-labels)
|
||||
//! - [Acknowledgements](#acknowledgements)
|
||||
//! - [License](#license)
|
||||
|
|
@ -690,6 +691,37 @@
|
|||
//! [`with_syntax_highlighting`](MietteHandlerOpts::with_syntax_highlighting)
|
||||
//! 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
|
||||
//!
|
||||
//! 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 thiserror::Error;
|
||||
use std::{error::Error, fmt::Display};
|
||||
|
||||
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.
|
||||
pub fn set_panic_hook() {
|
||||
|
|
@ -25,11 +26,27 @@ pub fn set_panic_hook() {
|
|||
}));
|
||||
}
|
||||
|
||||
#[derive(Debug, Error, Diagnostic)]
|
||||
#[error("{0}{panic}", panic = Panic::backtrace())]
|
||||
#[diagnostic(help("set the `RUST_BACKTRACE=1` environment variable to display a backtrace."))]
|
||||
#[derive(Debug)]
|
||||
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 {
|
||||
fn backtrace() -> String {
|
||||
use std::fmt::Write;
|
||||
|
|
@ -84,3 +101,32 @@ impl Panic {
|
|||
"".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")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::MietteError;
|
||||
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
|
||||
|
|
@ -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> {
|
||||
fn from(s: Box<dyn std::error::Error + Send + Sync>) -> Self {
|
||||
#[derive(thiserror::Error)]
|
||||
#[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))
|
||||
Box::new(DiagnosticError(s))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue