use super::{ FError, FightOutcome, filters, SearchField, Weekday, }; use evtclib::statistics::gamedata::Boss; use std::collections::HashSet; use lalrpop_util::ParseError; use chrono::NaiveDateTime; use regex::Regex; grammar; extern { type Error = FError; } pub LogFilter: Box<dyn filters::log::LogFilter> = { Disjunction<LogPredicate>, } PlayerFilter: Box<dyn filters::player::PlayerFilter> = { Disjunction<PlayerPredicate>, } Disjunction<T>: T = { <a:Conjunction<T>> "or" <b:Conjunction<T>> => a | b, Conjunction<T>, } Conjunction<T>: T = { <a:Negation<T>> "and"? <b:Negation<T>> => a & b, Negation<T>, } Negation<T>: T = { "not" <T> => ! <>, "!" <T> => ! <>, T, } LogPredicate: Box<dyn filters::log::LogFilter> = { "-success" => filters::log::OutcomeFilter::success(), "-wipe" => filters::log::OutcomeFilter::wipe(), "-outcome" <Comma<FightOutcome>> => filters::log::OutcomeFilter::new(<>), "-weekday" <Comma<Weekday>> => filters::log::WeekdayFilter::new(<>), "-before" <Date> => filters::log::TimeFilter::new(None, Some(<>)), "-after" <Date> => filters::log::TimeFilter::new(Some(<>), None), "-boss" <Comma<Boss>> => filters::log::BossFilter::new(<>), "-player" <Regex> => filters::player::any( filters::player::NameFilter::new(SearchField::Character, <>.clone()) | filters::player::NameFilter::new(SearchField::Account, <>) ), "all" "(" "player" ":" <PlayerFilter> ")" => filters::player::all(<>), "any" "(" "player" ":" <PlayerFilter> ")" => filters::player::any(<>), "exists" "(" "player" ":" <PlayerFilter> ")" => filters::player::any(<>), "(" <LogFilter> ")", } PlayerPredicate: Box<dyn filters::player::PlayerFilter> = { "-character" <Regex> => filters::player::NameFilter::new(SearchField::Character, <>), "-account" <Regex> => filters::player::NameFilter::new(SearchField::Account, <>), "-name" <Regex> => filters::player::NameFilter::new(SearchField::Account, <>.clone()) | filters::player::NameFilter::new(SearchField::Character, <>), "(" <PlayerFilter> ")", } Regex: Regex = { <s:r#""[^"]*""#> =>? Regex::new(&s[1..s.len() - 1]).map_err(|_| ParseError::User { error: FError::InvalidRegex(s.into()), }), <s:word> =>? Regex::new(s).map_err(|e| ParseError::User { error: FError::InvalidRegex(s.into()), }), } FightOutcome: FightOutcome = { <word> =>? <>.parse().map_err(|_| ParseError::User { error: FError::InvalidFightOutcome(<>.into()), }), } Weekday: Weekday = { <word> =>? <>.parse().map_err(|_| ParseError::User { error: FError::InvalidWeekday(<>.into()), }), } Boss: Boss = { <word> =>? <>.parse().map_err(|_| ParseError::User { error: FError::InvalidBoss(<>.into()), }), } Date: NaiveDateTime = { <datetime> =>? NaiveDateTime::parse_from_str(<>, "%Y-%m-%d %H:%M:%S") .map_err(|_| ParseError::User { error: FError::InvalidTimestamp(<>.into()), }), <date> =>? NaiveDateTime::parse_from_str(&format!("{} 00:00:00", <>), "%Y-%m-%d %H:%M:%S") .map_err(|_| ParseError::User { error: FError::InvalidTimestamp(<>.into()), }), } Comma<T>: HashSet<T> = { <v:(<T> ",")*> <e:T> => { let mut result = v.into_iter().collect::<HashSet<_>>(); result.insert(e); result }, } match { "player" => "player", "not" => "not", "any" => "any", "all" => "all", "exists" => "exists", 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, _ }