diff options
| -rw-r--r-- | src/fexpr/mod.rs | 6 | ||||
| -rw-r--r-- | src/main.rs | 40 | 
2 files changed, 33 insertions, 13 deletions
| diff --git a/src/fexpr/mod.rs b/src/fexpr/mod.rs index 5754d94..5610aba 100644 --- a/src/fexpr/mod.rs +++ b/src/fexpr/mod.rs @@ -46,9 +46,9 @@ pub enum FErrorKind {  }  /// Shortcut to create a new parser and parse the given input. -pub fn parse_logfilter( -    input: &str, -) -> Result<Box<dyn filters::log::LogFilter>, ParseError<usize, Token, FError>> { +pub fn parse_logfilter<'a>( +    input: &'a str, +) -> Result<Box<dyn filters::log::LogFilter>, ParseError<usize, Token<'a>, FError>> {      grammar::LogFilterParser::new().parse(input)  } diff --git a/src/main.rs b/src/main.rs index 2e0c82f..ff882e5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@  use std::collections::HashMap;  use std::fmt;  use std::fs::File; -use std::io::{BufReader, Read, Seek}; +use std::io::{BufReader, Read, Seek, Write};  use std::path::PathBuf;  use std::str::FromStr; @@ -58,6 +58,10 @@ pub struct Opt {      #[structopt(long = "guilds")]      guilds: bool, +    /// Run the REPL. +    #[structopt(long)] +    repl: bool, +      /// The filter expression.      expression: Vec<String>,  } @@ -211,9 +215,13 @@ fn run() -> Result<()> {          guilds::prepare_cache();      } -    let filter = build_filter(&opt)?; - -    grep(&opt, &*filter)?; +    if !opt.repl { +        let expr_string = opt.expression.join(" "); +        let filter = build_filter(&expr_string)?; +        grep(&opt, &*filter)?; +    } else { +        repl(&opt)?; +    }      if opt.guilds {          guilds::save_cache(); @@ -222,6 +230,22 @@ fn run() -> Result<()> {      Ok(())  } +fn repl(opt: &Opt) -> Result<()> { +    let stdin = std::io::stdin(); +    loop { +        print!("Query> "); +        std::io::stdout().flush()?; +        let mut line = String::new(); +        stdin.read_line(&mut line)?; +        let line = line.trim(); +        let parsed = build_filter(&line); +        match parsed { +            Ok(filter) => grep(&opt, &*filter)?, +            Err(err) => display_error(&err.into()), +        } +    } +} +  /// Check if the given entry represents a log file, based on the file name.  fn is_log_file(entry: &DirEntry) -> bool {      entry @@ -231,12 +255,8 @@ fn is_log_file(entry: &DirEntry) -> bool {          .unwrap_or(false)  } -fn build_filter(opt: &Opt) -> Result<Box<dyn LogFilter>> { -    // Our error needs access to the string, so we make our lives easier by just leaking it into a -    // 'static lifetime. Otherwise we'd need to build this string in main() and pass it in. -    // We're fine with the small memory leak, as we're only dealing with a small string in a -    // short-lived program. -    let expr_string = Box::leak(Box::new(opt.expression.join(" "))); +/// Small wrapper around `fexpr::parse_logfilter` to convert the returned `Err` to be `'static'. +fn build_filter(expr_string: &str) -> Result<Box<dyn LogFilter>> {      if expr_string.trim().is_empty() {          return Err(anyhow!("Expected a filter to be given"));      } | 
