Skip to content

Commit

Permalink
feat: added upstream miette support (#1038)
Browse files Browse the repository at this point in the history
* feat: conditionally implement `miette::Diagnostic` for `pest::error::Error`

* fix: use `into_miette` method for turning `Error` into `miette::Diagnostic`

* chore: clean up `parens` example

* Added test for miette support

* Update miette version

* Fixes and cargo fmt

* Fixed dependencies for miette

---------

Co-authored-by: Bobbbay <[email protected]>
  • Loading branch information
prsabahrami and bobbbay authored Sep 11, 2024
1 parent da3fcfe commit 65e5b2b
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 2 deletions.
4 changes: 4 additions & 0 deletions pest/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,20 @@ std = ["ucd-trie/std", "dep:thiserror"]
pretty-print = ["dep:serde", "dep:serde_json"]
# Enable const fn constructor for `PrecClimber`
const_prec_climber = []
# Enable miette error
miette-error = ["std", "pretty-print", "dep:miette", "dep:thiserror"]

[dependencies]
ucd-trie = { version = "0.1.5", default-features = false }
serde = { version = "1.0.145", optional = true }
serde_json = { version = "1.0.85", optional = true }
thiserror = { version = "1.0.37", optional = true }
memchr = { version = "2", optional = true }
miette = { version = "7.2.0", optional = true, features = ["fancy"] }

[dev-dependencies]
criterion = { version = "0.5.1", features = ["html_reports"] }
miette = { version = "7.2.0", features = ["fancy"] }

[[bench]]
name = "stack"
Expand Down
15 changes: 13 additions & 2 deletions pest/examples/parens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,20 @@ 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-error")]
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),
// To print pest errors, use Display formatting.
#[cfg(not(feature = "miette-error"))]
Err(e) => eprintln!("\n{}", e),
// To print miette errors, use Debug formatting.
#[cfg(feature = "miette-error")]
Err(e) => eprintln!("\n{:?}", e),
};
}
}
77 changes: 77 additions & 0 deletions pest/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,12 @@ impl<R: RuleType> Error<R> {
)
}
}

#[cfg(feature = "miette-error")]
/// Turns an error into a [miette](crates.io/miette) Diagnostic.
pub fn into_miette(self) -> impl ::miette::Diagnostic {
miette_adapter::MietteAdapter(self)
}
}

impl<R: RuleType> ErrorVariant<R> {
Expand Down Expand Up @@ -728,6 +734,47 @@ fn visualize_whitespace(input: &str) -> String {
input.to_owned().replace('\r', "␍").replace('\n', "␊")
}

#[cfg(feature = "miette-error")]
mod miette_adapter {
use alloc::string::ToString;
use std::boxed::Box;

use crate::error::LineColLocation;

use super::{Error, RuleType};

use miette::{Diagnostic, LabeledSpan, SourceCode};

#[derive(thiserror::Error, Debug)]
#[error("Failure to parse at {:?}", self.0.line_col)]
pub(crate) struct MietteAdapter<R: RuleType>(pub(crate) Error<R>);

impl<R: RuleType> Diagnostic for MietteAdapter<R> {
fn source_code(&self) -> Option<&dyn SourceCode> {
Some(&self.0.line)
}

fn labels(&self) -> Option<Box<dyn Iterator<Item = LabeledSpan>>> {
let message = self.0.variant.message().to_string();

let (offset, length) = match self.0.line_col {
LineColLocation::Pos((_, c)) => (c - 1, 1),
LineColLocation::Span((_, start_c), (_, end_c)) => {
(start_c - 1, end_c - start_c + 1)
}
};

let span = LabeledSpan::new(Some(message), offset, length);

Some(Box::new(std::iter::once(span)))
}

fn help<'a>(&'a self) -> Option<Box<dyn core::fmt::Display + 'a>> {
Some(Box::new(self.0.message()))
}
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -1104,4 +1151,34 @@ mod tests {
span.into()
);
}

#[cfg(feature = "miette-error")]
#[test]
fn miette_error() {
let input = "abc\ndef";
let pos = Position::new(input, 4).unwrap();
let error: Error<u32> = Error::new_from_pos(
ErrorVariant::ParsingError {
positives: vec![1, 2, 3],
negatives: vec![4, 5, 6],
},
pos,
);

let miette_error = miette::Error::new(error.into_miette());

assert_eq!(
format!("{:?}", miette_error),
vec![
" \u{1b}[31m×\u{1b}[0m Failure to parse at (2, 1)",
" ╭────",
" \u{1b}[2m1\u{1b}[0m │ def",
" · \u{1b}[35;1m┬\u{1b}[0m",
" · \u{1b}[35;1m╰── \u{1b}[35;1munexpected 4, 5, or 6; expected 1, 2, or 3\u{1b}[0m\u{1b}[0m",
" ╰────",
"\u{1b}[36m help: \u{1b}[0munexpected 4, 5, or 6; expected 1, 2, or 3\n"
]
.join("\n")
);
}
}

0 comments on commit 65e5b2b

Please sign in to comment.