use crate::client::{Client, HglibError, Runner};
use crate::version;
use crate::{runcommand, MkArg};
pub struct Arg<'a> {
pub pattern: &'a str,
pub files: &'a [&'a str],
pub all: bool,
pub text: bool,
pub follow: bool,
pub ignorecase: bool,
pub fileswithmatches: bool,
pub line: bool,
pub user: bool,
pub date: bool,
pub include: &'a [&'a str],
pub exclude: &'a [&'a str],
}
impl<'a> Default for Arg<'a> {
fn default() -> Self {
Self {
pattern: "",
files: &[],
all: false,
text: false,
follow: false,
ignorecase: false,
fileswithmatches: false,
line: false,
user: false,
date: false,
include: &[],
exclude: &[],
}
}
}
impl<'a> Arg<'a> {
fn run(&self, client: &mut Client) -> Result<(Vec<u8>, i32), HglibError> {
let mut args = self.files.to_vec();
args.insert(0, self.pattern);
runcommand!(
client,
"grep",
args,
"--all",
self.all,
"-a",
self.text,
"-f",
self.follow,
"-i",
self.ignorecase,
"-l",
self.fileswithmatches,
"-n",
self.line,
"-u",
self.user,
"-d",
self.date,
"-I",
self.include,
"-X",
self.exclude,
"--print0",
true
)
}
}
#[derive(Debug, PartialEq)]
#[cfg_attr(test, derive(Clone))]
pub struct GrepRes {
pub filename: String,
pub rev: Option<u64>,
pub line: Option<u32>,
pub match_status: Option<String>,
pub user: Option<String>,
pub date: Option<String>,
pub matched: Option<String>,
}
#[derive(Debug, PartialEq)]
enum FieldType {
Filename,
Rev,
Line,
MatchStatus,
User,
Date,
Matched,
}
impl Client {
fn get_field_types(&mut self, x: &Arg) -> Result<Vec<FieldType>, HglibError> {
let mut field_types = vec![FieldType::Filename];
if x.all || self.version(version::Arg {})? < (5, 2, None) {
field_types.push(FieldType::Rev);
}
if x.line {
field_types.push(FieldType::Line);
}
if x.all {
field_types.push(FieldType::MatchStatus);
}
if x.user {
field_types.push(FieldType::User);
}
if x.date {
field_types.push(FieldType::Date);
}
if !x.fileswithmatches {
field_types.push(FieldType::Matched);
}
Ok(field_types)
}
pub fn grep(&mut self, x: Arg) -> Result<Vec<GrepRes>, HglibError> {
let mut res = Vec::new();
let data = match x.run(self) {
Ok(ret) => ret.0,
Err(e) => {
if e.code == 1 {
return Ok(res);
} else {
return Err(e);
}
}
};
let field_types = self.get_field_types(&x)?;
for (n, element) in data.split(|x| *x == b'\0').enumerate() {
match field_types[n % field_types.len()] {
FieldType::Filename => {
let filename = String::from_utf8(element.to_vec())?;
res.push(GrepRes {
filename,
rev: None,
line: None,
match_status: None,
user: None,
date: None,
matched: None,
});
}
FieldType::Rev => {
res.last_mut().unwrap().rev =
Some(element.iter().fold(0, |r, x| r * 10 + (*x - b'0') as u64));
}
FieldType::Line => {
res.last_mut().unwrap().line =
Some(element.iter().fold(0, |r, x| r * 10 + (*x - b'0') as u32));
}
FieldType::MatchStatus => {
res.last_mut().unwrap().match_status =
Some(String::from_utf8(element.to_vec())?);
}
FieldType::User => {
res.last_mut().unwrap().user = Some(String::from_utf8(element.to_vec())?);
}
FieldType::Date => {
let sdate = std::str::from_utf8(element)?;
res.last_mut().unwrap().date = Some(sdate.to_string());
}
FieldType::Matched => {
res.last_mut().unwrap().matched = Some(String::from_utf8(element.to_vec())?);
}
}
}
res.pop();
Ok(res)
}
}