diff options
| -rw-r--r-- | src/fexpr/grammar.lalrpop | 2 | ||||
| -rw-r--r-- | src/fexpr/mod.rs | 48 | ||||
| -rw-r--r-- | src/main.rs | 13 | 
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(()) | 
