From ed835ae90ea9f62dca0ac45adb7c4b0e0454da23 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 1 May 2020 14:38:37 +0200 Subject: improve requoting heuristic First of all, this allows : to be part of a word. This has been added because the account names start with a colon, so -player :Dunje should work. Furthermore, the re-quoting now also quotes strings that contain a .+*, as those are characters usually used in regular expressions. A command line like raidgrep -- -player "G.dric" should work, so we either have to re-quote words with a dot, or allow the dot to be part of a (lexical) word as well. For now, we're re-quoting it, but if it turns out to be too troublesome, we might change that. --- src/fexpr/grammar.lalrpop | 2 +- src/fexpr/mod.rs | 48 +++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 13 +------------ 3 files changed, 50 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/fexpr/grammar.lalrpop b/src/fexpr/grammar.lalrpop index 4cb1849..c0165ce 100644 --- a/src/fexpr/grammar.lalrpop +++ b/src/fexpr/grammar.lalrpop @@ -173,7 +173,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"[\w:]+" => word, r#""[^"]*""# => string, _ diff --git a/src/fexpr/mod.rs b/src/fexpr/mod.rs index 391b739..2bdbfe7 100644 --- a/src/fexpr/mod.rs +++ b/src/fexpr/mod.rs @@ -7,6 +7,7 @@ use super::{filters, FightOutcome, SearchField}; use std::{error, fmt}; +use itertools::Itertools; use lalrpop_util::{lalrpop_mod, lexer::Token, ParseError}; use thiserror::Error; @@ -64,3 +65,50 @@ pub fn location(err: &ParseError) -> usize { ParseError::User { ref error } => error.location, } } + +/// "Re-quotes" a list of string pieces to a long, whitespace separated string. +/// +/// This function is needed because the shell already does some argument parsing, so if the user +/// specifies `-player "godric gobbledygook"` on the command line, we will get `["-player", "godric +/// gobbledygook"]` as the arguments. Howvever, our parser expects a single string, so we re-join +/// the pieces and apply the quotes where necessary. +/// +/// Note that this works on a "best guess" method, as we cannot reconstruct the shell's quotes 1:1. +/// This means that some things that work on the command line won't work in the REPL, and vice +/// versa. +/// +/// ``` +/// assert_eq!( +/// requote(&["-player", "godric gobbledygook"]), +/// r#"-player "godric gobbledygook""#, +/// ); +/// ``` +pub fn requote, T: IntoIterator>(items: T) -> String { + const SPECIAL_CHARS: &[char] = &[' ', '.', '^', '$', '+', '+']; + items + .into_iter() + .map(|part| { + let part = part.as_ref(); + if part.contains(SPECIAL_CHARS) { + format!(r#""{}""#, part) + } else { + part.into() + } + }) + .join(" ") +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_requote() { + assert_eq!( + requote(&["-player", "godric gobbledygook"]), + r#"-player "godric gobbledygook""#, + ); + assert_eq!(requote(&["-player", "godric"]), r#"-player godric"#,); + assert_eq!(requote(&["-player", "g.dric"]), r#"-player "g.dric""#,); + } +} diff --git a/src/main.rs b/src/main.rs index 33b3c45..cb67968 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,7 +9,6 @@ use std::str::FromStr; use anyhow::{anyhow, Error, Result}; use chrono::{DateTime, TimeZone, Utc}; use colored::Colorize; -use itertools::Itertools; use log::debug; use regex::Regex; use rustyline::Editor; @@ -284,17 +283,7 @@ fn single(opt: &Opt) -> Result<()> { return grep(opt, &*maybe_filter?); } - let expr_string = opt - .expression - .iter() - .map(|part| { - if part.contains(' ') { - format!(r#""{}""#, part) - } else { - part.into() - } - }) - .join(" "); + let expr_string = fexpr::requote(&opt.expression); let filter = build_filter(&expr_string)?; grep(&opt, &*filter)?; Ok(()) -- cgit v1.2.3