Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added upstream miette support #1038

Merged
merged 8 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion pest/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ readme = "_README.md"
rust-version = "1.61"

[features]
default = ["std", "memchr"]
tomtau marked this conversation as resolved.
Show resolved Hide resolved
# Implements `std::error::Error` for the `Error` type
std = ["ucd-trie/std", "dep:thiserror"]
# Enables the `to_json` function for `Pair` and `Pairs`
Expand All @@ -28,9 +27,11 @@ 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")]
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"))]
Err(e) => eprintln!("\n{}", e),
// To print miette errors, use Debug formatting.
#[cfg(feature = "miette")]
Err(e) => eprintln!("\n{:?}", e),
};
}
}
79 changes: 78 additions & 1 deletion pest/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,7 @@ impl<R: RuleType> Error<R> {
}
}

pub(crate) fn format(&self) -> String {
pub fn format(&self) -> String {
tomtau marked this conversation as resolved.
Show resolved Hide resolved
let spacing = self.spacing();
let path = self
.path
Expand Down Expand Up @@ -671,6 +671,12 @@ impl<R: RuleType> Error<R> {
)
}
}

#[cfg(feature = "miette")]
/// 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,45 @@ fn visualize_whitespace(input: &str) -> String {
input.to_owned().replace('\r', "␍").replace('\n', "␊")
}

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

use crate::error::LineColLocation;

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<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((r, c)) => (c - 1, 1),
LineColLocation::Span((start_r, start_c), (end_r, 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 +1149,36 @@ mod tests {
span.into()
);
}

#[cfg(feature = "miette")]
#[test]
fn miette_error() {
use miette::{MietteDiagnostic, LabeledSpan, Diagnostic};

let input = "abc\ndef";
let pos = position::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")
);
}
}
Loading