pijul_org / pijul

Add grep command (#345)

By Nathan Davis on December 8, 2018
This patch is not signed.
9pjoFiawccUE94wrGQn9jXiCatKtuZHsoUoDxzoXwyjiWSGVYaReS6TiGBDhdXW3RXm6EXP1shpkqcB9mFYsokgX
This patch is in the following branches:
latest
master
testing
18
19
20
* Justin Charette
* Nathan Davis
* pbgc












1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
use libpijul::{graph, Branch, Key, PatchId, Txn, ROOT_KEY};
        }
        else {
            let mut buffer : graph::Writer<Vec<u8>> = graph::Writer::new(Vec::new());
            str::from_utf8(&buffer).map(|contents| {
                for (i, line) in contents.lines().enumerate() {
                    if pattern.is_match(line) {
                        let path = path.to_str().unwrap_or("<non-UTF path>");
                        println!("{}:{}: {}", path, i+1, line)
                    }
                }
            }).unwrap();
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)
}
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
pub mod generate_completions;
pub mod grep;
pub mod hooks;
pub mod info;
pub mod init;
pub mod key;
pub mod log;
pub mod ls;
pub mod mv;
pub mod patch;
pub mod prune;
pub mod pull;
pub mod push;
pub mod record;
pub mod remove;
pub mod revert;
pub mod rollback;
pub mod sign;
pub mod status;
pub mod tag;
pub mod unrecord;

mod fold_until;

use error::Error;
use libpijul::fs_representation::{RepoPath, RepoRoot};
use libpijul::Hash;
use libpijul::{fs_representation, Inode, Repository, Txn, DEFAULT_BRANCH};
use rand;
use std::borrow::Cow;
use std::env::current_dir;
use std::env::var;
use std::fs::{canonicalize, create_dir, metadata};
use std::io::{stderr, Write};
use std::path::{Path, PathBuf};
use std::process::exit;

pub fn all_command_invocations() -> Vec<StaticSubcommand> {
    return vec![
        log::invocation(),
        info::invocation(),
        init::invocation(),
        record::invocation(),
        unrecord::invocation(),
        add::invocation(),
        pull::invocation(),
        push::invocation(),
        apply::invocation(),
        clone::invocation(),
        remove::invocation(),
        mv::invocation(),
        ls::invocation(),
        revert::invocation(),
        patch::invocation(),
        fork::invocation(),
        branches::invocation(),
        prune::invocation(),
        checkout::invocation(),
        diff::invocation(),
        credit::invocation(),
        dist::invocation(),
        key::invocation(),
        rollback::invocation(),
        status::invocation(),
        dependencies::invocation(),
        tag::invocation(),
        sign::invocation(),
        generate_completions::invocation(),
        grep::invocation(),
    ];


                               "challenge" => challenge,
                               "grep" => grep