extern crate regex;
use std::path::PathBuf;
use std::str;
use clap::{Arg, ArgMatches, SubCommand};
use commands::{default_explain, BasicOptions, StaticSubcommand};
use regex::Regex;
use libpijul::{graph, Branch, Key, PatchId, Transaction, Txn, Value, ROOT_KEY};
use error;
type Result<T> = std::result::Result<T, error::Error>;
pub fn invocation() -> StaticSubcommand {
return SubCommand::with_name("grep")
.about("Search repository files for regex")
.arg(
Arg::with_name("regex")
.help("Regex pattern to search for")
.required(true),
)
.arg(
Arg::with_name("branch")
.long("branch")
.help("The branch to search, defaults to the current branch.")
.takes_value(true)
.required(false),
);
}
struct Grep<'a> {
pattern: &'a Regex,
path: &'a str,
line: usize,
}
impl<'a, 'b, T: 'a + Transaction> graph::LineBuffer<'a, T> for Grep<'b> {
fn output_line(
&mut self,
_: &Key<PatchId>,
contents: Value<'a, T>,
) -> std::result::Result<(), libpijul::Error> {
let c = contents.into_cow();
if let Ok(s) = str::from_utf8(&c) {
if self.pattern.is_match(s) {
println!("{}:{}: {}", self.path, self.line, s)
}
}
self.line += 1;
Ok(())
}
fn output_conflict_marker(&mut self, s: &'a str) -> std::result::Result<(), libpijul::Error> {
let value: Value<'a, T> = Value::from_slice(s.as_bytes());
self.output_line(&ROOT_KEY, value)
}
}
fn grep(
txn: &Txn,
branch: &Branch,
directory: Key<PatchId>,
path: &mut PathBuf,
pattern: &Regex,
) -> Result<()> {
let files = txn.list_files_under_node(branch, directory);
for (node, names) in files {
if names.len() > 1 {
error!("file has several names: {:?}", names);
}
path.push(names[0].1);
if names[0].0.is_dir() {
grep(txn, branch, node, path, pattern)?;
} else {
let mut buffer = Grep {
pattern,
path: path.to_str().unwrap_or("<non-UTF path>"),
line: 1,
};
let mut forward = Vec::new();
let mut graph = txn.retrieve(branch, node);
txn.output_file(branch, &mut buffer, &mut graph, &mut forward)?;
}
path.pop();
}
Ok(())
}
pub fn run(args: &ArgMatches) -> Result<()> {
let opts = BasicOptions::from_args(args)?;
let repo = opts.open_repo()?;
let txn = repo.txn_begin()?;
let branch = txn
.get_branch(&opts.branch())
.ok_or(error::Error::NoSuchBranch)?;
let pattern = Regex::new(args.value_of("regex").unwrap())?;
grep(&txn, &branch, ROOT_KEY, &mut PathBuf::new(), &pattern)
}
pub fn explain(res: Result<()>) {
default_explain(res)
}