mantid_grep 0.0.1

mantid_grep provides grep functionality for use within the mantid ecosystem
Documentation
mod error;

use error::Error;
use mantid_core::{constants, Boxed};

use regex::{Regex, RegexBuilder};
use std::io::BufRead;

const RED_OUTPUT: &str = "\x1b[33m";
const NO_COLOUR_OUTPUT: &str = "\x1b[0m";

pub enum MatchesInput<'a> {
    Expression(&'a str),
    RE(&'a Regex),
}

/// grep can accept either an expression or a prebuilt Regex
pub fn matches<'a>(
    test: &'a str,
    input: MatchesInput,
    case_insensitive: bool,
) -> Result<Vec<regex::Match<'a>>, Error> {
    let get_match = |re: &Regex| {
        let result = re.find_iter(test).collect::<Vec<regex::Match>>();
        Ok(result)
    };
    match input {
        MatchesInput::Expression(expression) => get_match(
            &RegexBuilder::new(expression)
                .case_insensitive(case_insensitive)
                .build()?,
        ),
        MatchesInput::RE(input_re) => get_match(input_re),
    }
}

pub fn grep_cli(
    expression: &str,
    case_insensitive: bool,
    only_matching: bool,
    hash: bool,
) -> Boxed {
    let tested_expression = if hash {
        // require specific formatting as to test lengthiest hashes first
        format!(
            "{}|{}|{}",
            constants::HASH_EXP
                .get("SHA-256")
                .ok_or(Error::HashRetrieve)?,
            constants::HASH_EXP
                .get("SHA-1")
                .ok_or(Error::HashRetrieve)?,
            constants::HASH_EXP.get("MD5").ok_or(Error::HashRetrieve)?
        )
    } else {
        expression.to_string()
    };

    // dbg!(&tested_expression);

    let re = RegexBuilder::new(&tested_expression)
        .case_insensitive(case_insensitive)
        .build()?;

    for test in std::io::stdin().lock().lines() {
        let test = test?;
        let matches = matches(&test, MatchesInput::RE(&re), case_insensitive)?;

        if only_matching {
            for m in matches {
                println!("{}", m.as_str());
            }
        } else {
            match matches.first() {
                Some(m) => {
                    let ss = test
                        .get(m.start()..m.end()) // take slice with match index to length of target
                        .ok_or(Error::InvalidSlice)?;
                    println!(
                        "{}",
                        test.replace(ss, &format!("{}{}{}", RED_OUTPUT, ss, NO_COLOUR_OUTPUT))
                    )
                }
                None => {}
            }
        }
    }

    Ok(())
}