From 185a5b2f802f9d05c3eb40f807c0488f168c6661 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 21 Apr 2020 13:59:28 +0200 Subject: better error outputs --- src/fexpr/grammar.lalrpop | 62 +++++++++++++++++++++++++++++++++++------------ src/fexpr/mod.rs | 57 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 92 insertions(+), 27 deletions(-) (limited to 'src/fexpr') diff --git a/src/fexpr/grammar.lalrpop b/src/fexpr/grammar.lalrpop index 48349a1..4e6ac89 100644 --- a/src/fexpr/grammar.lalrpop +++ b/src/fexpr/grammar.lalrpop @@ -1,5 +1,6 @@ use super::{ FError, + FErrorKind, FightOutcome, filters, SearchField, @@ -76,40 +77,68 @@ PlayerPredicate: Box = { } Regex: Regex = { - =>? Regex::new(&s[1..s.len() - 1]).map_err(|_| ParseError::User { - error: FError::InvalidRegex(s.into()), + =>? Regex::new(&s[1..s.len() - 1]).map_err(|error| ParseError::User { + error: FError { + location: l, + data: s.to_string(), + kind: error.into(), + } }), - =>? Regex::new(s).map_err(|e| ParseError::User { - error: FError::InvalidRegex(s.into()), + =>? Regex::new(s).map_err(|error| ParseError::User { + error: FError { + location: l, + data: s.to_string(), + kind: error.into(), + } }), } FightOutcome: FightOutcome = { - =>? <>.parse().map_err(|_| ParseError::User { - error: FError::InvalidFightOutcome(<>.into()), + =>? w.parse().map_err(|_| ParseError::User { + error: FError { + location: l, + data: w.into(), + kind: FErrorKind::InvalidFightOutcome, + } }), } Weekday: Weekday = { - =>? <>.parse().map_err(|_| ParseError::User { - error: FError::InvalidWeekday(<>.into()), + =>? w.parse().map_err(|_| ParseError::User { + error: FError { + location: l, + data: w.into(), + kind: FErrorKind::InvalidWeekday, + } }), } Boss: Boss = { - =>? <>.parse().map_err(|_| ParseError::User { - error: FError::InvalidBoss(<>.into()), + =>? w.parse().map_err(|_| ParseError::User { + error: FError { + location: l, + data: w.into(), + kind: FErrorKind::InvalidBoss, + } }), } Date: NaiveDateTime = { - =>? NaiveDateTime::parse_from_str(<>, "%Y-%m-%d %H:%M:%S") - .map_err(|_| ParseError::User { - error: FError::InvalidTimestamp(<>.into()), + =>? NaiveDateTime::parse_from_str(d, "%Y-%m-%d %H:%M:%S") + .map_err(|error| ParseError::User { + error: FError { + location: l, + data: d.into(), + kind: error.into(), + } }), - =>? NaiveDateTime::parse_from_str(&format!("{} 00:00:00", <>), "%Y-%m-%d %H:%M:%S") - .map_err(|_| ParseError::User { - error: FError::InvalidTimestamp(<>.into()), + =>? NaiveDateTime::parse_from_str(&format!("{} 00:00:00", d), "%Y-%m-%d %H:%M:%S") + .map_err(|error| ParseError::User { + error: FError { + location: l, + data: d.into(), + kind: error.into(), + } }), } @@ -131,6 +160,7 @@ match { r"\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d" => datetime, r"\d\d\d\d-\d\d-\d\d" => date, r"\w+" => word, + r#""[^"]*""# => regex, _ } diff --git a/src/fexpr/mod.rs b/src/fexpr/mod.rs index aafdea7..5754d94 100644 --- a/src/fexpr/mod.rs +++ b/src/fexpr/mod.rs @@ -4,28 +4,63 @@ //! type and convert it to a [`Filter`][super::filters::Filter]. // Make it available in the grammar mod. use super::{filters, FightOutcome, SearchField, Weekday}; -use lalrpop_util::{lalrpop_mod, lexer::Token, ParseError}; +use std::{error, fmt}; + +use lalrpop_util::{lalrpop_mod, lexer::Token, ParseError}; use thiserror::Error; -lalrpop_mod!(pub grammar, "/fexpr/grammar.rs"); +lalrpop_mod!(#[allow(clippy::all)] pub grammar, "/fexpr/grammar.rs"); + +#[derive(Debug)] +pub struct FError { + location: usize, + data: String, + kind: FErrorKind, +} + +impl fmt::Display for FError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{} (at {})", self.kind, self.location) + } +} + +impl error::Error for FError { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + Some(&self.kind) + } +} #[derive(Debug, Error)] -pub enum FError { +pub enum FErrorKind { #[error("invalid regular expression: {0}")] - InvalidRegex(String), - #[error("invalid fight outcome: {0}")] - InvalidFightOutcome(String), - #[error("invalid weekday: {0}")] - InvalidWeekday(String), + InvalidRegex(#[from] regex::Error), + #[error("invalid fight outcome")] + InvalidFightOutcome, + #[error("invalid weekday")] + InvalidWeekday, #[error("invalid timestamp: {0}")] - InvalidTimestamp(String), - #[error("invalid boss name: {0}")] - InvalidBoss(String), + InvalidTimestamp(#[from] chrono::format::ParseError), + #[error("invalid boss name")] + InvalidBoss, } +/// Shortcut to create a new parser and parse the given input. pub fn parse_logfilter( input: &str, ) -> Result, ParseError> { grammar::LogFilterParser::new().parse(input) } + +/// Extract the location from the given error. +pub fn location(err: &ParseError) -> usize { + match *err { + ParseError::InvalidToken { location } => location, + ParseError::UnrecognizedEOF { location, .. } => location, + ParseError::UnrecognizedToken { + token: (l, _, _), .. + } => l, + ParseError::ExtraToken { token: (l, _, _) } => l, + ParseError::User { ref error } => error.location, + } +} -- cgit v1.2.3