From f02642eabc11e17eee93c318e70bc4b88a15df8e Mon Sep 17 00:00:00 2001 From: Bobbbay Date: Sun, 24 Jul 2022 18:15:41 +0000 Subject: [PATCH 1/3] feat: conditionally implement `miette::Diagnostic` for `pest::error::Error` --- pest/Cargo.toml | 6 ++++++ pest/examples/parens.rs | 19 +++++++++++++++++++ pest/src/error.rs | 42 +++++++++++++++++++++++++++++++++++++++-- 3 files changed, 65 insertions(+), 2 deletions(-) diff --git a/pest/Cargo.toml b/pest/Cargo.toml index abf1939f..6bb9d3c8 100644 --- a/pest/Cargo.toml +++ b/pest/Cargo.toml @@ -21,9 +21,15 @@ std = ["ucd-trie/std", "thiserror"] pretty-print = ["serde", "serde_json"] # Enable const fn constructor for `PrecClimber` const_prec_climber = [] +# Enables error compatibility with miette +miette = ["dep:miette", "std"] [dependencies] ucd-trie = { version = "0.1.1", default-features = false } serde = { version = "1.0.89", optional = true } serde_json = { version = "1.0.39", optional = true} thiserror = { version = "1.0.31", optional = true } +miette = { version = "5.1.1", optional = true } + +[dev-dependencies] +miette = { version = "5.1.1", features = ["fancy"] } diff --git a/pest/examples/parens.rs b/pest/examples/parens.rs index 566c119b..fe6f6ec9 100644 --- a/pest/examples/parens.rs +++ b/pest/examples/parens.rs @@ -2,6 +2,9 @@ extern crate pest; use std::io::{self, Write}; +#[cfg(feature = "miette")] +use miette::IntoDiagnostic; + use pest::error::Error; use pest::iterators::Pairs; use pest::{state, ParseResult, Parser, ParserState}; @@ -57,6 +60,7 @@ fn expr(pairs: Pairs) -> Vec { .collect() } +#[cfg(not(feature = "miette"))] fn main() { loop { let mut line = String::new(); @@ -73,3 +77,18 @@ fn main() { }; } } + +#[cfg(feature = "miette")] +fn main() -> miette::Result<()> { + loop { + let mut line = String::new(); + + print!("> "); + io::stdout().flush().into_diagnostic()?; + + io::stdin().read_line(&mut line).into_diagnostic()?; + line.pop(); + + ParenParser::parse(Rule::expr, &line)?; + } +} diff --git a/pest/src/error.rs b/pest/src/error.rs index c2ab8066..d270f4fe 100644 --- a/pest/src/error.rs +++ b/pest/src/error.rs @@ -9,6 +9,9 @@ //! Types for different kinds of parsing failures. +#[cfg(feature = "miette")] +use std::boxed::Box; + use alloc::borrow::Cow; use alloc::borrow::ToOwned; use alloc::format; @@ -16,6 +19,7 @@ use alloc::string::String; use alloc::string::ToString; use alloc::vec::Vec; use core::cmp; +use core::fmt; use core::mem; use crate::position::Position; @@ -25,7 +29,8 @@ use crate::RuleType; /// Parse-related error type. #[derive(Clone, Debug, Eq, Hash, PartialEq)] #[cfg_attr(feature = "std", derive(thiserror::Error))] -#[cfg_attr(feature = "std", error("{}", self.format()))] +#[cfg_attr(all(feature = "std", not(feature = "miette")), error("{}", self.format()))] +#[cfg_attr(all(feature = "std", feature = "miette"), error("Failed to parse at {}", self.line_col))] pub struct Error { /// Variant of the error pub variant: ErrorVariant, @@ -38,6 +43,30 @@ pub struct Error { continued_line: Option, } +#[cfg(feature = "miette")] +impl miette::Diagnostic for Error { + fn source_code(&self) -> Option<&dyn miette::SourceCode> { + Some(&self.line) + } + + fn labels(&self) -> Option>> { + let message = self.variant.message().to_string(); + + let (offset, length) = match self.location { + InputLocation::Pos(x) => (x, 0), + InputLocation::Span((x, y)) => (x, y), + }; + + let span = miette::LabeledSpan::new(Some(message), offset, length); + + Some(Box::new(std::iter::once(span))) + } + + fn help<'a>(&'a self) -> Option> { + Some(Box::new(self.variant.message())) + } +} + /// Different kinds of parsing errors. #[derive(Clone, Debug, Eq, Hash, PartialEq)] #[cfg_attr(feature = "std", derive(thiserror::Error))] @@ -76,6 +105,15 @@ pub enum LineColLocation { Span((usize, usize), (usize, usize)), } +impl fmt::Display for LineColLocation { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Pos((l, c)) => write!(f, "({}, {})", l, c), + Self::Span((ls, cs), (le, ce)) => write!(f, "({}, {}) to ({}, {})", ls, cs, le, ce), + } + } +} + impl Error { /// Creates `Error` from `ErrorVariant` and `Position`. /// @@ -411,7 +449,7 @@ impl Error { } } - pub(crate) fn format(&self) -> String { + pub fn format(&self) -> String { let spacing = self.spacing(); let path = self .path From 0bd281f5fc6b2cf8f14aa0508e1b5915def2ed35 Mon Sep 17 00:00:00 2001 From: Bobbbay Date: Mon, 25 Jul 2022 09:47:36 +0000 Subject: [PATCH 2/3] fix: use `into_miette` method for turning `Error` into `miette::Diagnostic` --- pest/Cargo.toml | 2 -- pest/examples/parens.rs | 26 +++++---------- pest/src/error.rs | 73 +++++++++++++++++++++++++---------------- 3 files changed, 52 insertions(+), 49 deletions(-) diff --git a/pest/Cargo.toml b/pest/Cargo.toml index 6bb9d3c8..b4d19da0 100644 --- a/pest/Cargo.toml +++ b/pest/Cargo.toml @@ -21,8 +21,6 @@ std = ["ucd-trie/std", "thiserror"] pretty-print = ["serde", "serde_json"] # Enable const fn constructor for `PrecClimber` const_prec_climber = [] -# Enables error compatibility with miette -miette = ["dep:miette", "std"] [dependencies] ucd-trie = { version = "0.1.1", default-features = false } diff --git a/pest/examples/parens.rs b/pest/examples/parens.rs index fe6f6ec9..be1f63b1 100644 --- a/pest/examples/parens.rs +++ b/pest/examples/parens.rs @@ -60,7 +60,6 @@ fn expr(pairs: Pairs) -> Vec { .collect() } -#[cfg(not(feature = "miette"))] fn main() { loop { let mut line = String::new(); @@ -71,24 +70,15 @@ fn main() { io::stdin().read_line(&mut line).unwrap(); line.pop(); - match ParenParser::parse(Rule::expr, &line) { + let parsed = ParenParser::parse(Rule::expr, &line); + #[cfg(feature = "miette")] + let parsed = parsed + .map_err(Error::into_miette) + .map_err(miette::Report::from); + + match parsed { Ok(pairs) => println!("{:?}", expr(pairs)), - Err(e) => println!("\n{}", e), + Err(e) => eprintln!("\n{:?}", e), }; } } - -#[cfg(feature = "miette")] -fn main() -> miette::Result<()> { - loop { - let mut line = String::new(); - - print!("> "); - io::stdout().flush().into_diagnostic()?; - - io::stdin().read_line(&mut line).into_diagnostic()?; - line.pop(); - - ParenParser::parse(Rule::expr, &line)?; - } -} diff --git a/pest/src/error.rs b/pest/src/error.rs index d270f4fe..cb816614 100644 --- a/pest/src/error.rs +++ b/pest/src/error.rs @@ -9,9 +9,6 @@ //! Types for different kinds of parsing failures. -#[cfg(feature = "miette")] -use std::boxed::Box; - use alloc::borrow::Cow; use alloc::borrow::ToOwned; use alloc::format; @@ -29,8 +26,7 @@ use crate::RuleType; /// Parse-related error type. #[derive(Clone, Debug, Eq, Hash, PartialEq)] #[cfg_attr(feature = "std", derive(thiserror::Error))] -#[cfg_attr(all(feature = "std", not(feature = "miette")), error("{}", self.format()))] -#[cfg_attr(all(feature = "std", feature = "miette"), error("Failed to parse at {}", self.line_col))] +#[cfg_attr(feature = "std", error("{}", self.format()))] pub struct Error { /// Variant of the error pub variant: ErrorVariant, @@ -43,30 +39,6 @@ pub struct Error { continued_line: Option, } -#[cfg(feature = "miette")] -impl miette::Diagnostic for Error { - fn source_code(&self) -> Option<&dyn miette::SourceCode> { - Some(&self.line) - } - - fn labels(&self) -> Option>> { - let message = self.variant.message().to_string(); - - let (offset, length) = match self.location { - InputLocation::Pos(x) => (x, 0), - InputLocation::Span((x, y)) => (x, y), - }; - - let span = miette::LabeledSpan::new(Some(message), offset, length); - - Some(Box::new(std::iter::once(span))) - } - - fn help<'a>(&'a self) -> Option> { - Some(Box::new(self.variant.message())) - } -} - /// Different kinds of parsing errors. #[derive(Clone, Debug, Eq, Hash, PartialEq)] #[cfg_attr(feature = "std", derive(thiserror::Error))] @@ -520,6 +492,12 @@ impl Error { ) } } + + #[cfg(feature = "miette")] + /// Turns an error into a [miette](crates.io/miette) Diagnostic. + pub fn into_miette(self) -> impl ::miette::Diagnostic { + miette::MietteAdapter(self) + } } impl ErrorVariant { @@ -562,6 +540,43 @@ fn visualize_whitespace(input: &str) -> String { input.to_owned().replace('\r', "␍").replace('\n', "␊") } +#[cfg(feature = "miette")] +mod miette { + use alloc::string::ToString; + use std::boxed::Box; + + use super::{Error, InputLocation, RuleType}; + + use miette::{Diagnostic, LabeledSpan, SourceCode}; + + #[derive(thiserror::Error, Debug)] + #[error("Failure to parse at {}", self.0.line_col)] + pub(crate) struct MietteAdapter(pub(crate) Error); + + impl Diagnostic for MietteAdapter { + fn source_code(&self) -> Option<&dyn SourceCode> { + Some(&self.0.line) + } + + fn labels(&self) -> Option>> { + let message = self.0.variant.message().to_string(); + + let (offset, length) = match self.0.location { + InputLocation::Pos(x) => (x, 0), + InputLocation::Span((x, y)) => (x, y), + }; + + let span = LabeledSpan::new(Some(message), offset, length); + + Some(Box::new(std::iter::once(span))) + } + + fn help<'a>(&'a self) -> Option> { + Some(Box::new(self.0.variant.message())) + } + } +} + #[cfg(test)] mod tests { use super::super::position; From 3dac71fc8fc29264f825d2e7ba7893b4a6b098e5 Mon Sep 17 00:00:00 2001 From: Bobbbay Date: Mon, 25 Jul 2022 09:52:23 +0000 Subject: [PATCH 3/3] chore: clean up `parens` example --- pest/examples/parens.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pest/examples/parens.rs b/pest/examples/parens.rs index be1f63b1..aad43706 100644 --- a/pest/examples/parens.rs +++ b/pest/examples/parens.rs @@ -2,9 +2,6 @@ extern crate pest; use std::io::{self, Write}; -#[cfg(feature = "miette")] -use miette::IntoDiagnostic; - use pest::error::Error; use pest::iterators::Pairs; use pest::{state, ParseResult, Parser, ParserState}; @@ -78,6 +75,11 @@ fn main() { match parsed { Ok(pairs) => println!("{:?}", expr(pairs)), + // To print pest errors, use Display formatting. + #[cfg(not(feature = "miette"))] + Err(e) => eprintln!("\n{}", e), + // To print miette errors, use Debug formatting. + #[cfg(feature = "miette")] Err(e) => eprintln!("\n{:?}", e), }; }