aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel <kingdread@gmx.de>2020-05-01 14:38:37 +0200
committerDaniel <kingdread@gmx.de>2020-05-01 14:38:52 +0200
commited835ae90ea9f62dca0ac45adb7c4b0e0454da23 (patch)
treea52bcef88988930393efd72150549098441490c8
parent4479ceddd48ae062fa10879ceb199b771fa35add (diff)
downloadraidgrep-ed835ae90ea9f62dca0ac45adb7c4b0e0454da23.tar.gz
raidgrep-ed835ae90ea9f62dca0ac45adb7c4b0e0454da23.tar.bz2
raidgrep-ed835ae90ea9f62dca0ac45adb7c4b0e0454da23.zip
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.
-rw-r--r--src/fexpr/grammar.lalrpop2
-rw-r--r--src/fexpr/mod.rs48
-rw-r--r--src/main.rs13
3 files changed, 50 insertions, 13 deletions
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<T>(err: &ParseError<usize, T, FError>) -> 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<S: AsRef<str>, T: IntoIterator<Item = S>>(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(())