mirror of
https://github.com/BurntSushi/ripgrep.git
synced 2025-05-18 17:20:21 -07:00
style: rustfmt everything
This is why I was so intent on clearing the PR queue. This will effectively invalidate all existing patches, so I wanted to start from a clean slate. We do make one little tweak: we put the default type definitions in their own file and tell rustfmt to keep its grubby mits off of it. We also sort it lexicographically and hopefully will enforce that from here on.
This commit is contained in:
parent
c95f29e3ba
commit
0bc4f0447b
18
build.rs
18
build.rs
@ -21,7 +21,8 @@ fn main() {
|
|||||||
eprintln!(
|
eprintln!(
|
||||||
"OUT_DIR environment variable not defined. \
|
"OUT_DIR environment variable not defined. \
|
||||||
Please file a bug: \
|
Please file a bug: \
|
||||||
https://github.com/BurntSushi/ripgrep/issues/new");
|
https://github.com/BurntSushi/ripgrep/issues/new"
|
||||||
|
);
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -90,8 +91,10 @@ fn generate_man_page<P: AsRef<Path>>(outdir: P) -> io::Result<()> {
|
|||||||
File::create(&txt_path)?.write_all(tpl.as_bytes())?;
|
File::create(&txt_path)?.write_all(tpl.as_bytes())?;
|
||||||
let result = process::Command::new("a2x")
|
let result = process::Command::new("a2x")
|
||||||
.arg("--no-xmllint")
|
.arg("--no-xmllint")
|
||||||
.arg("--doctype").arg("manpage")
|
.arg("--doctype")
|
||||||
.arg("--format").arg("manpage")
|
.arg("manpage")
|
||||||
|
.arg("--format")
|
||||||
|
.arg("manpage")
|
||||||
.arg(&txt_path)
|
.arg(&txt_path)
|
||||||
.spawn()?
|
.spawn()?
|
||||||
.wait()?;
|
.wait()?;
|
||||||
@ -114,7 +117,7 @@ fn formatted_options() -> io::Result<String> {
|
|||||||
// ripgrep only has two positional arguments, and probably will only
|
// ripgrep only has two positional arguments, and probably will only
|
||||||
// ever have two positional arguments, so we just hardcode them into
|
// ever have two positional arguments, so we just hardcode them into
|
||||||
// the template.
|
// the template.
|
||||||
if let app::RGArgKind::Positional{..} = arg.kind {
|
if let app::RGArgKind::Positional { .. } = arg.kind {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
formatted.push(formatted_arg(&arg)?);
|
formatted.push(formatted_arg(&arg)?);
|
||||||
@ -124,7 +127,9 @@ fn formatted_options() -> io::Result<String> {
|
|||||||
|
|
||||||
fn formatted_arg(arg: &RGArg) -> io::Result<String> {
|
fn formatted_arg(arg: &RGArg) -> io::Result<String> {
|
||||||
match arg.kind {
|
match arg.kind {
|
||||||
RGArgKind::Positional{..} => panic!("unexpected positional argument"),
|
RGArgKind::Positional { .. } => {
|
||||||
|
panic!("unexpected positional argument")
|
||||||
|
}
|
||||||
RGArgKind::Switch { long, short, multiple } => {
|
RGArgKind::Switch { long, short, multiple } => {
|
||||||
let mut out = vec![];
|
let mut out = vec![];
|
||||||
|
|
||||||
@ -163,7 +168,8 @@ fn formatted_arg(arg: &RGArg) -> io::Result<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn formatted_doc_txt(arg: &RGArg) -> io::Result<String> {
|
fn formatted_doc_txt(arg: &RGArg) -> io::Result<String> {
|
||||||
let paragraphs: Vec<String> = arg.doc_long
|
let paragraphs: Vec<String> = arg
|
||||||
|
.doc_long
|
||||||
.replace("{", "{")
|
.replace("{", "{")
|
||||||
.replace("}", r"}")
|
.replace("}", r"}")
|
||||||
.split("\n\n")
|
.split("\n\n")
|
||||||
|
@ -2,13 +2,13 @@ use std::fmt;
|
|||||||
use std::hash;
|
use std::hash;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::path::{Path, is_separator};
|
use std::path::{is_separator, Path};
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
use regex;
|
use regex;
|
||||||
use regex::bytes::Regex;
|
use regex::bytes::Regex;
|
||||||
|
|
||||||
use {Candidate, Error, ErrorKind, new_regex};
|
use {new_regex, Candidate, Error, ErrorKind};
|
||||||
|
|
||||||
/// Describes a matching strategy for a particular pattern.
|
/// Describes a matching strategy for a particular pattern.
|
||||||
///
|
///
|
||||||
@ -85,16 +85,16 @@ pub struct Glob {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for Glob {
|
impl PartialEq for Glob {
|
||||||
fn eq(&self, other: &Glob) -> bool {
|
fn eq(&self, other: &Glob) -> bool {
|
||||||
self.glob == other.glob && self.opts == other.opts
|
self.glob == other.glob && self.opts == other.opts
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl hash::Hash for Glob {
|
impl hash::Hash for Glob {
|
||||||
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
||||||
self.glob.hash(state);
|
self.glob.hash(state);
|
||||||
self.opts.hash(state);
|
self.opts.hash(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Glob {
|
impl fmt::Display for Glob {
|
||||||
@ -227,11 +227,15 @@ struct Tokens(Vec<Token>);
|
|||||||
|
|
||||||
impl Deref for Tokens {
|
impl Deref for Tokens {
|
||||||
type Target = Vec<Token>;
|
type Target = Vec<Token>;
|
||||||
fn deref(&self) -> &Vec<Token> { &self.0 }
|
fn deref(&self) -> &Vec<Token> {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DerefMut for Tokens {
|
impl DerefMut for Tokens {
|
||||||
fn deref_mut(&mut self) -> &mut Vec<Token> { &mut self.0 }
|
fn deref_mut(&mut self) -> &mut Vec<Token> {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
@ -242,10 +246,7 @@ enum Token {
|
|||||||
RecursivePrefix,
|
RecursivePrefix,
|
||||||
RecursiveSuffix,
|
RecursiveSuffix,
|
||||||
RecursiveZeroOrMore,
|
RecursiveZeroOrMore,
|
||||||
Class {
|
Class { negated: bool, ranges: Vec<(char, char)> },
|
||||||
negated: bool,
|
|
||||||
ranges: Vec<(char, char)>,
|
|
||||||
},
|
|
||||||
Alternates(Vec<Tokens>),
|
Alternates(Vec<Tokens>),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,12 +258,9 @@ impl Glob {
|
|||||||
|
|
||||||
/// Returns a matcher for this pattern.
|
/// Returns a matcher for this pattern.
|
||||||
pub fn compile_matcher(&self) -> GlobMatcher {
|
pub fn compile_matcher(&self) -> GlobMatcher {
|
||||||
let re = new_regex(&self.re)
|
let re =
|
||||||
.expect("regex compilation shouldn't fail");
|
new_regex(&self.re).expect("regex compilation shouldn't fail");
|
||||||
GlobMatcher {
|
GlobMatcher { pat: self.clone(), re: re }
|
||||||
pat: self.clone(),
|
|
||||||
re: re,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a strategic matcher.
|
/// Returns a strategic matcher.
|
||||||
@ -273,13 +271,9 @@ impl Glob {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn compile_strategic_matcher(&self) -> GlobStrategic {
|
fn compile_strategic_matcher(&self) -> GlobStrategic {
|
||||||
let strategy = MatchStrategy::new(self);
|
let strategy = MatchStrategy::new(self);
|
||||||
let re = new_regex(&self.re)
|
let re =
|
||||||
.expect("regex compilation shouldn't fail");
|
new_regex(&self.re).expect("regex compilation shouldn't fail");
|
||||||
GlobStrategic {
|
GlobStrategic { strategy: strategy, pat: self.clone(), re: re }
|
||||||
strategy: strategy,
|
|
||||||
pat: self.clone(),
|
|
||||||
re: re,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the original glob pattern used to build this pattern.
|
/// Returns the original glob pattern used to build this pattern.
|
||||||
@ -537,7 +531,7 @@ impl Glob {
|
|||||||
| Token::RecursiveZeroOrMore => {
|
| Token::RecursiveZeroOrMore => {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
Token::Class{..} | Token::Alternates(..) => {
|
Token::Class { .. } | Token::Alternates(..) => {
|
||||||
// We *could* be a little smarter here, but either one
|
// We *could* be a little smarter here, but either one
|
||||||
// of these is going to prevent our literal optimizations
|
// of these is going to prevent our literal optimizations
|
||||||
// anyway, so give up.
|
// anyway, so give up.
|
||||||
@ -574,10 +568,7 @@ impl<'a> GlobBuilder<'a> {
|
|||||||
///
|
///
|
||||||
/// The pattern is not compiled until `build` is called.
|
/// The pattern is not compiled until `build` is called.
|
||||||
pub fn new(glob: &'a str) -> GlobBuilder<'a> {
|
pub fn new(glob: &'a str) -> GlobBuilder<'a> {
|
||||||
GlobBuilder {
|
GlobBuilder { glob: glob, opts: GlobOptions::default() }
|
||||||
glob: glob,
|
|
||||||
opts: GlobOptions::default(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses and builds the pattern.
|
/// Parses and builds the pattern.
|
||||||
@ -875,25 +866,22 @@ impl<'a> Parser<'a> {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let is_suffix =
|
let is_suffix = match self.peek() {
|
||||||
match self.peek() {
|
None => {
|
||||||
None => {
|
assert!(self.bump().is_none());
|
||||||
assert!(self.bump().is_none());
|
true
|
||||||
true
|
}
|
||||||
}
|
Some(',') | Some('}') if self.stack.len() >= 2 => true,
|
||||||
Some(',') | Some('}') if self.stack.len() >= 2 => {
|
Some(c) if is_separator(c) => {
|
||||||
true
|
assert!(self.bump().map(is_separator).unwrap_or(false));
|
||||||
}
|
false
|
||||||
Some(c) if is_separator(c) => {
|
}
|
||||||
assert!(self.bump().map(is_separator).unwrap_or(false));
|
_ => {
|
||||||
false
|
self.push_token(Token::ZeroOrMore)?;
|
||||||
}
|
self.push_token(Token::ZeroOrMore)?;
|
||||||
_ => {
|
return Ok(());
|
||||||
self.push_token(Token::ZeroOrMore)?;
|
}
|
||||||
self.push_token(Token::ZeroOrMore)?;
|
};
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
match self.pop_token()? {
|
match self.pop_token()? {
|
||||||
Token::RecursivePrefix => {
|
Token::RecursivePrefix => {
|
||||||
self.push_token(Token::RecursivePrefix)?;
|
self.push_token(Token::RecursivePrefix)?;
|
||||||
@ -973,7 +961,10 @@ impl<'a> Parser<'a> {
|
|||||||
// invariant: in_range is only set when there is
|
// invariant: in_range is only set when there is
|
||||||
// already at least one character seen.
|
// already at least one character seen.
|
||||||
add_to_last_range(
|
add_to_last_range(
|
||||||
&self.glob, ranges.last_mut().unwrap(), c)?;
|
&self.glob,
|
||||||
|
ranges.last_mut().unwrap(),
|
||||||
|
c,
|
||||||
|
)?;
|
||||||
} else {
|
} else {
|
||||||
ranges.push((c, c));
|
ranges.push((c, c));
|
||||||
}
|
}
|
||||||
@ -987,10 +978,7 @@ impl<'a> Parser<'a> {
|
|||||||
// it as a literal.
|
// it as a literal.
|
||||||
ranges.push(('-', '-'));
|
ranges.push(('-', '-'));
|
||||||
}
|
}
|
||||||
self.push_token(Token::Class {
|
self.push_token(Token::Class { negated: negated, ranges: ranges })
|
||||||
negated: negated,
|
|
||||||
ranges: ranges,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bump(&mut self) -> Option<char> {
|
fn bump(&mut self) -> Option<char> {
|
||||||
@ -1019,9 +1007,9 @@ fn ends_with(needle: &[u8], haystack: &[u8]) -> bool {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use {GlobSetBuilder, ErrorKind};
|
|
||||||
use super::{Glob, GlobBuilder, Token};
|
|
||||||
use super::Token::*;
|
use super::Token::*;
|
||||||
|
use super::{Glob, GlobBuilder, Token};
|
||||||
|
use {ErrorKind, GlobSetBuilder};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
struct Options {
|
struct Options {
|
||||||
@ -1037,7 +1025,7 @@ mod tests {
|
|||||||
let pat = Glob::new($pat).unwrap();
|
let pat = Glob::new($pat).unwrap();
|
||||||
assert_eq!($tokens, pat.tokens.0);
|
assert_eq!($tokens, pat.tokens.0);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! syntaxerr {
|
macro_rules! syntaxerr {
|
||||||
@ -1047,7 +1035,7 @@ mod tests {
|
|||||||
let err = Glob::new($pat).unwrap_err();
|
let err = Glob::new($pat).unwrap_err();
|
||||||
assert_eq!(&$err, err.kind());
|
assert_eq!(&$err, err.kind());
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! toregex {
|
macro_rules! toregex {
|
||||||
@ -1129,7 +1117,9 @@ mod tests {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn s(string: &str) -> String { string.to_string() }
|
fn s(string: &str) -> String {
|
||||||
|
string.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
fn class(s: char, e: char) -> Token {
|
fn class(s: char, e: char) -> Token {
|
||||||
Class { negated: false, ranges: vec![(s, e)] }
|
Class { negated: false, ranges: vec![(s, e)] }
|
||||||
@ -1153,16 +1143,20 @@ mod tests {
|
|||||||
syntax!(any2, "a?b", vec![Literal('a'), Any, Literal('b')]);
|
syntax!(any2, "a?b", vec![Literal('a'), Any, Literal('b')]);
|
||||||
syntax!(seq1, "*", vec![ZeroOrMore]);
|
syntax!(seq1, "*", vec![ZeroOrMore]);
|
||||||
syntax!(seq2, "a*b", vec![Literal('a'), ZeroOrMore, Literal('b')]);
|
syntax!(seq2, "a*b", vec![Literal('a'), ZeroOrMore, Literal('b')]);
|
||||||
syntax!(seq3, "*a*b*", vec![
|
syntax!(
|
||||||
ZeroOrMore, Literal('a'), ZeroOrMore, Literal('b'), ZeroOrMore,
|
seq3,
|
||||||
]);
|
"*a*b*",
|
||||||
|
vec![ZeroOrMore, Literal('a'), ZeroOrMore, Literal('b'), ZeroOrMore,]
|
||||||
|
);
|
||||||
syntax!(rseq1, "**", vec![RecursivePrefix]);
|
syntax!(rseq1, "**", vec![RecursivePrefix]);
|
||||||
syntax!(rseq2, "**/", vec![RecursivePrefix]);
|
syntax!(rseq2, "**/", vec![RecursivePrefix]);
|
||||||
syntax!(rseq3, "/**", vec![RecursiveSuffix]);
|
syntax!(rseq3, "/**", vec![RecursiveSuffix]);
|
||||||
syntax!(rseq4, "/**/", vec![RecursiveZeroOrMore]);
|
syntax!(rseq4, "/**/", vec![RecursiveZeroOrMore]);
|
||||||
syntax!(rseq5, "a/**/b", vec![
|
syntax!(
|
||||||
Literal('a'), RecursiveZeroOrMore, Literal('b'),
|
rseq5,
|
||||||
]);
|
"a/**/b",
|
||||||
|
vec![Literal('a'), RecursiveZeroOrMore, Literal('b'),]
|
||||||
|
);
|
||||||
syntax!(cls1, "[a]", vec![class('a', 'a')]);
|
syntax!(cls1, "[a]", vec![class('a', 'a')]);
|
||||||
syntax!(cls2, "[!a]", vec![classn('a', 'a')]);
|
syntax!(cls2, "[!a]", vec![classn('a', 'a')]);
|
||||||
syntax!(cls3, "[a-z]", vec![class('a', 'z')]);
|
syntax!(cls3, "[a-z]", vec![class('a', 'z')]);
|
||||||
@ -1174,9 +1168,11 @@ mod tests {
|
|||||||
syntax!(cls9, "[a-]", vec![rclass(&[('a', 'a'), ('-', '-')])]);
|
syntax!(cls9, "[a-]", vec![rclass(&[('a', 'a'), ('-', '-')])]);
|
||||||
syntax!(cls10, "[-a-z]", vec![rclass(&[('-', '-'), ('a', 'z')])]);
|
syntax!(cls10, "[-a-z]", vec![rclass(&[('-', '-'), ('a', 'z')])]);
|
||||||
syntax!(cls11, "[a-z-]", vec![rclass(&[('a', 'z'), ('-', '-')])]);
|
syntax!(cls11, "[a-z-]", vec![rclass(&[('a', 'z'), ('-', '-')])]);
|
||||||
syntax!(cls12, "[-a-z-]", vec![
|
syntax!(
|
||||||
rclass(&[('-', '-'), ('a', 'z'), ('-', '-')]),
|
cls12,
|
||||||
]);
|
"[-a-z-]",
|
||||||
|
vec![rclass(&[('-', '-'), ('a', 'z'), ('-', '-')]),]
|
||||||
|
);
|
||||||
syntax!(cls13, "[]-z]", vec![class(']', 'z')]);
|
syntax!(cls13, "[]-z]", vec![class(']', 'z')]);
|
||||||
syntax!(cls14, "[--z]", vec![class('-', 'z')]);
|
syntax!(cls14, "[--z]", vec![class('-', 'z')]);
|
||||||
syntax!(cls15, "[ --]", vec![class(' ', '-')]);
|
syntax!(cls15, "[ --]", vec![class(' ', '-')]);
|
||||||
@ -1194,26 +1190,14 @@ mod tests {
|
|||||||
syntaxerr!(err_range1, "[z-a]", ErrorKind::InvalidRange('z', 'a'));
|
syntaxerr!(err_range1, "[z-a]", ErrorKind::InvalidRange('z', 'a'));
|
||||||
syntaxerr!(err_range2, "[z--]", ErrorKind::InvalidRange('z', '-'));
|
syntaxerr!(err_range2, "[z--]", ErrorKind::InvalidRange('z', '-'));
|
||||||
|
|
||||||
const CASEI: Options = Options {
|
const CASEI: Options =
|
||||||
casei: Some(true),
|
Options { casei: Some(true), litsep: None, bsesc: None };
|
||||||
litsep: None,
|
const SLASHLIT: Options =
|
||||||
bsesc: None,
|
Options { casei: None, litsep: Some(true), bsesc: None };
|
||||||
};
|
const NOBSESC: Options =
|
||||||
const SLASHLIT: Options = Options {
|
Options { casei: None, litsep: None, bsesc: Some(false) };
|
||||||
casei: None,
|
const BSESC: Options =
|
||||||
litsep: Some(true),
|
Options { casei: None, litsep: None, bsesc: Some(true) };
|
||||||
bsesc: None,
|
|
||||||
};
|
|
||||||
const NOBSESC: Options = Options {
|
|
||||||
casei: None,
|
|
||||||
litsep: None,
|
|
||||||
bsesc: Some(false),
|
|
||||||
};
|
|
||||||
const BSESC: Options = Options {
|
|
||||||
casei: None,
|
|
||||||
litsep: None,
|
|
||||||
bsesc: Some(true),
|
|
||||||
};
|
|
||||||
|
|
||||||
toregex!(re_casei, "a", "(?i)^a$", &CASEI);
|
toregex!(re_casei, "a", "(?i)^a$", &CASEI);
|
||||||
|
|
||||||
@ -1311,8 +1295,11 @@ mod tests {
|
|||||||
matches!(matchpat4, "*hello.txt", "some\\path\\to\\hello.txt");
|
matches!(matchpat4, "*hello.txt", "some\\path\\to\\hello.txt");
|
||||||
matches!(matchpat5, "*hello.txt", "/an/absolute/path/to/hello.txt");
|
matches!(matchpat5, "*hello.txt", "/an/absolute/path/to/hello.txt");
|
||||||
matches!(matchpat6, "*some/path/to/hello.txt", "some/path/to/hello.txt");
|
matches!(matchpat6, "*some/path/to/hello.txt", "some/path/to/hello.txt");
|
||||||
matches!(matchpat7, "*some/path/to/hello.txt",
|
matches!(
|
||||||
"a/bigger/some/path/to/hello.txt");
|
matchpat7,
|
||||||
|
"*some/path/to/hello.txt",
|
||||||
|
"a/bigger/some/path/to/hello.txt"
|
||||||
|
);
|
||||||
|
|
||||||
matches!(matchescape, "_[[]_[]]_[?]_[*]_!_", "_[_]_?_*_!_");
|
matches!(matchescape, "_[[]_[]]_[?]_[*]_!_", "_[_]_?_*_!_");
|
||||||
|
|
||||||
@ -1375,28 +1362,44 @@ mod tests {
|
|||||||
nmatches!(matchnot15, "[!-]", "-");
|
nmatches!(matchnot15, "[!-]", "-");
|
||||||
nmatches!(matchnot16, "*hello.txt", "hello.txt-and-then-some");
|
nmatches!(matchnot16, "*hello.txt", "hello.txt-and-then-some");
|
||||||
nmatches!(matchnot17, "*hello.txt", "goodbye.txt");
|
nmatches!(matchnot17, "*hello.txt", "goodbye.txt");
|
||||||
nmatches!(matchnot18, "*some/path/to/hello.txt",
|
nmatches!(
|
||||||
"some/path/to/hello.txt-and-then-some");
|
matchnot18,
|
||||||
nmatches!(matchnot19, "*some/path/to/hello.txt",
|
"*some/path/to/hello.txt",
|
||||||
"some/other/path/to/hello.txt");
|
"some/path/to/hello.txt-and-then-some"
|
||||||
|
);
|
||||||
|
nmatches!(
|
||||||
|
matchnot19,
|
||||||
|
"*some/path/to/hello.txt",
|
||||||
|
"some/other/path/to/hello.txt"
|
||||||
|
);
|
||||||
nmatches!(matchnot20, "a", "foo/a");
|
nmatches!(matchnot20, "a", "foo/a");
|
||||||
nmatches!(matchnot21, "./foo", "foo");
|
nmatches!(matchnot21, "./foo", "foo");
|
||||||
nmatches!(matchnot22, "**/foo", "foofoo");
|
nmatches!(matchnot22, "**/foo", "foofoo");
|
||||||
nmatches!(matchnot23, "**/foo/bar", "foofoo/bar");
|
nmatches!(matchnot23, "**/foo/bar", "foofoo/bar");
|
||||||
nmatches!(matchnot24, "/*.c", "mozilla-sha1/sha1.c");
|
nmatches!(matchnot24, "/*.c", "mozilla-sha1/sha1.c");
|
||||||
nmatches!(matchnot25, "*.c", "mozilla-sha1/sha1.c", SLASHLIT);
|
nmatches!(matchnot25, "*.c", "mozilla-sha1/sha1.c", SLASHLIT);
|
||||||
nmatches!(matchnot26, "**/m4/ltoptions.m4",
|
nmatches!(
|
||||||
"csharp/src/packages/repositories.config", SLASHLIT);
|
matchnot26,
|
||||||
|
"**/m4/ltoptions.m4",
|
||||||
|
"csharp/src/packages/repositories.config",
|
||||||
|
SLASHLIT
|
||||||
|
);
|
||||||
nmatches!(matchnot27, "a[^0-9]b", "a0b");
|
nmatches!(matchnot27, "a[^0-9]b", "a0b");
|
||||||
nmatches!(matchnot28, "a[^0-9]b", "a9b");
|
nmatches!(matchnot28, "a[^0-9]b", "a9b");
|
||||||
nmatches!(matchnot29, "[^-]", "-");
|
nmatches!(matchnot29, "[^-]", "-");
|
||||||
nmatches!(matchnot30, "some/*/needle.txt", "some/needle.txt");
|
nmatches!(matchnot30, "some/*/needle.txt", "some/needle.txt");
|
||||||
nmatches!(
|
nmatches!(
|
||||||
matchrec31,
|
matchrec31,
|
||||||
"some/*/needle.txt", "some/one/two/needle.txt", SLASHLIT);
|
"some/*/needle.txt",
|
||||||
|
"some/one/two/needle.txt",
|
||||||
|
SLASHLIT
|
||||||
|
);
|
||||||
nmatches!(
|
nmatches!(
|
||||||
matchrec32,
|
matchrec32,
|
||||||
"some/*/needle.txt", "some/one/two/three/needle.txt", SLASHLIT);
|
"some/*/needle.txt",
|
||||||
|
"some/one/two/three/needle.txt",
|
||||||
|
SLASHLIT
|
||||||
|
);
|
||||||
|
|
||||||
macro_rules! extract {
|
macro_rules! extract {
|
||||||
($which:ident, $name:ident, $pat:expr, $expect:expr) => {
|
($which:ident, $name:ident, $pat:expr, $expect:expr) => {
|
||||||
@ -1458,19 +1461,27 @@ mod tests {
|
|||||||
literal!(extract_lit7, "foo/bar", Some(s("foo/bar")));
|
literal!(extract_lit7, "foo/bar", Some(s("foo/bar")));
|
||||||
literal!(extract_lit8, "**/foo/bar", None);
|
literal!(extract_lit8, "**/foo/bar", None);
|
||||||
|
|
||||||
basetokens!(extract_basetoks1, "**/foo", Some(&*vec![
|
basetokens!(
|
||||||
Literal('f'), Literal('o'), Literal('o'),
|
extract_basetoks1,
|
||||||
]));
|
"**/foo",
|
||||||
|
Some(&*vec![Literal('f'), Literal('o'), Literal('o'),])
|
||||||
|
);
|
||||||
basetokens!(extract_basetoks2, "**/foo", None, CASEI);
|
basetokens!(extract_basetoks2, "**/foo", None, CASEI);
|
||||||
basetokens!(extract_basetoks3, "**/foo", Some(&*vec![
|
basetokens!(
|
||||||
Literal('f'), Literal('o'), Literal('o'),
|
extract_basetoks3,
|
||||||
]), SLASHLIT);
|
"**/foo",
|
||||||
|
Some(&*vec![Literal('f'), Literal('o'), Literal('o'),]),
|
||||||
|
SLASHLIT
|
||||||
|
);
|
||||||
basetokens!(extract_basetoks4, "*foo", None, SLASHLIT);
|
basetokens!(extract_basetoks4, "*foo", None, SLASHLIT);
|
||||||
basetokens!(extract_basetoks5, "*foo", None);
|
basetokens!(extract_basetoks5, "*foo", None);
|
||||||
basetokens!(extract_basetoks6, "**/fo*o", None);
|
basetokens!(extract_basetoks6, "**/fo*o", None);
|
||||||
basetokens!(extract_basetoks7, "**/fo*o", Some(&*vec![
|
basetokens!(
|
||||||
Literal('f'), Literal('o'), ZeroOrMore, Literal('o'),
|
extract_basetoks7,
|
||||||
]), SLASHLIT);
|
"**/fo*o",
|
||||||
|
Some(&*vec![Literal('f'), Literal('o'), ZeroOrMore, Literal('o'),]),
|
||||||
|
SLASHLIT
|
||||||
|
);
|
||||||
|
|
||||||
ext!(extract_ext1, "**/*.rs", Some(s(".rs")));
|
ext!(extract_ext1, "**/*.rs", Some(s(".rs")));
|
||||||
ext!(extract_ext2, "**/*.rs.bak", None);
|
ext!(extract_ext2, "**/*.rs.bak", None);
|
||||||
|
@ -119,12 +119,12 @@ use std::path::Path;
|
|||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
use aho_corasick::AhoCorasick;
|
use aho_corasick::AhoCorasick;
|
||||||
use bstr::{B, ByteSlice, ByteVec};
|
use bstr::{ByteSlice, ByteVec, B};
|
||||||
use regex::bytes::{Regex, RegexBuilder, RegexSet};
|
use regex::bytes::{Regex, RegexBuilder, RegexSet};
|
||||||
|
|
||||||
use pathutil::{file_name, file_name_ext, normalize_path};
|
|
||||||
use glob::MatchStrategy;
|
use glob::MatchStrategy;
|
||||||
pub use glob::{Glob, GlobBuilder, GlobMatcher};
|
pub use glob::{Glob, GlobBuilder, GlobMatcher};
|
||||||
|
use pathutil::{file_name, file_name_ext, normalize_path};
|
||||||
|
|
||||||
mod glob;
|
mod glob;
|
||||||
mod pathutil;
|
mod pathutil;
|
||||||
@ -202,9 +202,7 @@ impl ErrorKind {
|
|||||||
ErrorKind::UnclosedClass => {
|
ErrorKind::UnclosedClass => {
|
||||||
"unclosed character class; missing ']'"
|
"unclosed character class; missing ']'"
|
||||||
}
|
}
|
||||||
ErrorKind::InvalidRange(_, _) => {
|
ErrorKind::InvalidRange(_, _) => "invalid character range",
|
||||||
"invalid character range"
|
|
||||||
}
|
|
||||||
ErrorKind::UnopenedAlternates => {
|
ErrorKind::UnopenedAlternates => {
|
||||||
"unopened alternate group; missing '{' \
|
"unopened alternate group; missing '{' \
|
||||||
(maybe escape '}' with '[}]'?)"
|
(maybe escape '}' with '[}]'?)"
|
||||||
@ -216,9 +214,7 @@ impl ErrorKind {
|
|||||||
ErrorKind::NestedAlternates => {
|
ErrorKind::NestedAlternates => {
|
||||||
"nested alternate groups are not allowed"
|
"nested alternate groups are not allowed"
|
||||||
}
|
}
|
||||||
ErrorKind::DanglingEscape => {
|
ErrorKind::DanglingEscape => "dangling '\\'",
|
||||||
"dangling '\\'"
|
|
||||||
}
|
|
||||||
ErrorKind::Regex(ref err) => err,
|
ErrorKind::Regex(ref err) => err,
|
||||||
ErrorKind::__Nonexhaustive => unreachable!(),
|
ErrorKind::__Nonexhaustive => unreachable!(),
|
||||||
}
|
}
|
||||||
@ -245,9 +241,7 @@ impl fmt::Display for ErrorKind {
|
|||||||
| ErrorKind::UnclosedAlternates
|
| ErrorKind::UnclosedAlternates
|
||||||
| ErrorKind::NestedAlternates
|
| ErrorKind::NestedAlternates
|
||||||
| ErrorKind::DanglingEscape
|
| ErrorKind::DanglingEscape
|
||||||
| ErrorKind::Regex(_) => {
|
| ErrorKind::Regex(_) => write!(f, "{}", self.description()),
|
||||||
write!(f, "{}", self.description())
|
|
||||||
}
|
|
||||||
ErrorKind::InvalidRange(s, e) => {
|
ErrorKind::InvalidRange(s, e) => {
|
||||||
write!(f, "invalid range; '{}' > '{}'", s, e)
|
write!(f, "invalid range; '{}' > '{}'", s, e)
|
||||||
}
|
}
|
||||||
@ -262,21 +256,20 @@ fn new_regex(pat: &str) -> Result<Regex, Error> {
|
|||||||
.size_limit(10 * (1 << 20))
|
.size_limit(10 * (1 << 20))
|
||||||
.dfa_size_limit(10 * (1 << 20))
|
.dfa_size_limit(10 * (1 << 20))
|
||||||
.build()
|
.build()
|
||||||
.map_err(|err| {
|
.map_err(|err| Error {
|
||||||
Error {
|
glob: Some(pat.to_string()),
|
||||||
glob: Some(pat.to_string()),
|
kind: ErrorKind::Regex(err.to_string()),
|
||||||
kind: ErrorKind::Regex(err.to_string()),
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_regex_set<I, S>(pats: I) -> Result<RegexSet, Error>
|
fn new_regex_set<I, S>(pats: I) -> Result<RegexSet, Error>
|
||||||
where S: AsRef<str>, I: IntoIterator<Item=S> {
|
where
|
||||||
RegexSet::new(pats).map_err(|err| {
|
S: AsRef<str>,
|
||||||
Error {
|
I: IntoIterator<Item = S>,
|
||||||
glob: None,
|
{
|
||||||
kind: ErrorKind::Regex(err.to_string()),
|
RegexSet::new(pats).map_err(|err| Error {
|
||||||
}
|
glob: None,
|
||||||
|
kind: ErrorKind::Regex(err.to_string()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,10 +287,7 @@ impl GlobSet {
|
|||||||
/// Create an empty `GlobSet`. An empty set matches nothing.
|
/// Create an empty `GlobSet`. An empty set matches nothing.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn empty() -> GlobSet {
|
pub fn empty() -> GlobSet {
|
||||||
GlobSet {
|
GlobSet { len: 0, strats: vec![] }
|
||||||
len: 0,
|
|
||||||
strats: vec![],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if this set is empty, and therefore matches nothing.
|
/// Returns true if this set is empty, and therefore matches nothing.
|
||||||
@ -432,11 +422,17 @@ impl GlobSet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
debug!("built glob set; {} literals, {} basenames, {} extensions, \
|
debug!(
|
||||||
|
"built glob set; {} literals, {} basenames, {} extensions, \
|
||||||
{} prefixes, {} suffixes, {} required extensions, {} regexes",
|
{} prefixes, {} suffixes, {} required extensions, {} regexes",
|
||||||
lits.0.len(), base_lits.0.len(), exts.0.len(),
|
lits.0.len(),
|
||||||
prefixes.literals.len(), suffixes.literals.len(),
|
base_lits.0.len(),
|
||||||
required_exts.0.len(), regexes.literals.len());
|
exts.0.len(),
|
||||||
|
prefixes.literals.len(),
|
||||||
|
suffixes.literals.len(),
|
||||||
|
required_exts.0.len(),
|
||||||
|
regexes.literals.len()
|
||||||
|
);
|
||||||
Ok(GlobSet {
|
Ok(GlobSet {
|
||||||
len: pats.len(),
|
len: pats.len(),
|
||||||
strats: vec![
|
strats: vec![
|
||||||
@ -446,7 +442,8 @@ impl GlobSet {
|
|||||||
GlobSetMatchStrategy::Suffix(suffixes.suffix()),
|
GlobSetMatchStrategy::Suffix(suffixes.suffix()),
|
||||||
GlobSetMatchStrategy::Prefix(prefixes.prefix()),
|
GlobSetMatchStrategy::Prefix(prefixes.prefix()),
|
||||||
GlobSetMatchStrategy::RequiredExtension(
|
GlobSetMatchStrategy::RequiredExtension(
|
||||||
required_exts.build()?),
|
required_exts.build()?,
|
||||||
|
),
|
||||||
GlobSetMatchStrategy::Regex(regexes.regex_set()?),
|
GlobSetMatchStrategy::Regex(regexes.regex_set()?),
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
@ -501,11 +498,7 @@ impl<'a> Candidate<'a> {
|
|||||||
let path = normalize_path(Vec::from_path_lossy(path.as_ref()));
|
let path = normalize_path(Vec::from_path_lossy(path.as_ref()));
|
||||||
let basename = file_name(&path).unwrap_or(Cow::Borrowed(B("")));
|
let basename = file_name(&path).unwrap_or(Cow::Borrowed(B("")));
|
||||||
let ext = file_name_ext(&basename).unwrap_or(Cow::Borrowed(B("")));
|
let ext = file_name_ext(&basename).unwrap_or(Cow::Borrowed(B("")));
|
||||||
Candidate {
|
Candidate { path: path, basename: basename, ext: ext }
|
||||||
path: path,
|
|
||||||
basename: basename,
|
|
||||||
ext: ext,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_prefix(&self, max: usize) -> &[u8] {
|
fn path_prefix(&self, max: usize) -> &[u8] {
|
||||||
@ -767,11 +760,7 @@ struct MultiStrategyBuilder {
|
|||||||
|
|
||||||
impl MultiStrategyBuilder {
|
impl MultiStrategyBuilder {
|
||||||
fn new() -> MultiStrategyBuilder {
|
fn new() -> MultiStrategyBuilder {
|
||||||
MultiStrategyBuilder {
|
MultiStrategyBuilder { literals: vec![], map: vec![], longest: 0 }
|
||||||
literals: vec![],
|
|
||||||
map: vec![],
|
|
||||||
longest: 0,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add(&mut self, global_index: usize, literal: String) {
|
fn add(&mut self, global_index: usize, literal: String) {
|
||||||
|
@ -84,7 +84,7 @@ pub fn normalize_path(mut path: Cow<[u8]>) -> Cow<[u8]> {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use bstr::{B, ByteVec};
|
use bstr::{ByteVec, B};
|
||||||
|
|
||||||
use super::{file_name_ext, normalize_path};
|
use super::{file_name_ext, normalize_path};
|
||||||
|
|
||||||
|
@ -38,10 +38,7 @@ impl Default for DecompressionMatcherBuilder {
|
|||||||
impl DecompressionMatcherBuilder {
|
impl DecompressionMatcherBuilder {
|
||||||
/// Create a new builder for configuring a decompression matcher.
|
/// Create a new builder for configuring a decompression matcher.
|
||||||
pub fn new() -> DecompressionMatcherBuilder {
|
pub fn new() -> DecompressionMatcherBuilder {
|
||||||
DecompressionMatcherBuilder {
|
DecompressionMatcherBuilder { commands: vec![], defaults: true }
|
||||||
commands: vec![],
|
|
||||||
defaults: true,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build a matcher for determining how to decompress files.
|
/// Build a matcher for determining how to decompress files.
|
||||||
@ -49,12 +46,11 @@ impl DecompressionMatcherBuilder {
|
|||||||
/// If there was a problem compiling the matcher, then an error is
|
/// If there was a problem compiling the matcher, then an error is
|
||||||
/// returned.
|
/// returned.
|
||||||
pub fn build(&self) -> Result<DecompressionMatcher, CommandError> {
|
pub fn build(&self) -> Result<DecompressionMatcher, CommandError> {
|
||||||
let defaults =
|
let defaults = if !self.defaults {
|
||||||
if !self.defaults {
|
vec![]
|
||||||
vec![]
|
} else {
|
||||||
} else {
|
default_decompression_commands()
|
||||||
default_decompression_commands()
|
};
|
||||||
};
|
|
||||||
let mut glob_builder = GlobSetBuilder::new();
|
let mut glob_builder = GlobSetBuilder::new();
|
||||||
let mut commands = vec![];
|
let mut commands = vec![];
|
||||||
for decomp_cmd in defaults.iter().chain(&self.commands) {
|
for decomp_cmd in defaults.iter().chain(&self.commands) {
|
||||||
@ -93,17 +89,15 @@ impl DecompressionMatcherBuilder {
|
|||||||
program: P,
|
program: P,
|
||||||
args: I,
|
args: I,
|
||||||
) -> &mut DecompressionMatcherBuilder
|
) -> &mut DecompressionMatcherBuilder
|
||||||
where P: AsRef<OsStr>,
|
where
|
||||||
I: IntoIterator<Item=A>,
|
P: AsRef<OsStr>,
|
||||||
A: AsRef<OsStr>,
|
I: IntoIterator<Item = A>,
|
||||||
|
A: AsRef<OsStr>,
|
||||||
{
|
{
|
||||||
|
|
||||||
let glob = glob.to_string();
|
let glob = glob.to_string();
|
||||||
let bin = program.as_ref().to_os_string();
|
let bin = program.as_ref().to_os_string();
|
||||||
let args = args
|
let args =
|
||||||
.into_iter()
|
args.into_iter().map(|a| a.as_ref().to_os_string()).collect();
|
||||||
.map(|a| a.as_ref().to_os_string())
|
|
||||||
.collect();
|
|
||||||
self.commands.push(DecompressionCommand { glob, bin, args });
|
self.commands.push(DecompressionCommand { glob, bin, args });
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -95,51 +95,61 @@ pub fn unescape(s: &str) -> Vec<u8> {
|
|||||||
let mut state = Literal;
|
let mut state = Literal;
|
||||||
for c in s.chars() {
|
for c in s.chars() {
|
||||||
match state {
|
match state {
|
||||||
Escape => {
|
Escape => match c {
|
||||||
match c {
|
'\\' => {
|
||||||
'\\' => { bytes.push(b'\\'); state = Literal; }
|
bytes.push(b'\\');
|
||||||
'n' => { bytes.push(b'\n'); state = Literal; }
|
state = Literal;
|
||||||
'r' => { bytes.push(b'\r'); state = Literal; }
|
|
||||||
't' => { bytes.push(b'\t'); state = Literal; }
|
|
||||||
'x' => { state = HexFirst; }
|
|
||||||
c => {
|
|
||||||
bytes.extend(format!(r"\{}", c).into_bytes());
|
|
||||||
state = Literal;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
'n' => {
|
||||||
HexFirst => {
|
bytes.push(b'\n');
|
||||||
match c {
|
state = Literal;
|
||||||
'0'..='9' | 'A'..='F' | 'a'..='f' => {
|
|
||||||
state = HexSecond(c);
|
|
||||||
}
|
|
||||||
c => {
|
|
||||||
bytes.extend(format!(r"\x{}", c).into_bytes());
|
|
||||||
state = Literal;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
'r' => {
|
||||||
HexSecond(first) => {
|
bytes.push(b'\r');
|
||||||
match c {
|
state = Literal;
|
||||||
'0'..='9' | 'A'..='F' | 'a'..='f' => {
|
|
||||||
let ordinal = format!("{}{}", first, c);
|
|
||||||
let byte = u8::from_str_radix(&ordinal, 16).unwrap();
|
|
||||||
bytes.push(byte);
|
|
||||||
state = Literal;
|
|
||||||
}
|
|
||||||
c => {
|
|
||||||
let original = format!(r"\x{}{}", first, c);
|
|
||||||
bytes.extend(original.into_bytes());
|
|
||||||
state = Literal;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
't' => {
|
||||||
Literal => {
|
bytes.push(b'\t');
|
||||||
match c {
|
state = Literal;
|
||||||
'\\' => { state = Escape; }
|
|
||||||
c => { bytes.extend(c.to_string().as_bytes()); }
|
|
||||||
}
|
}
|
||||||
}
|
'x' => {
|
||||||
|
state = HexFirst;
|
||||||
|
}
|
||||||
|
c => {
|
||||||
|
bytes.extend(format!(r"\{}", c).into_bytes());
|
||||||
|
state = Literal;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
HexFirst => match c {
|
||||||
|
'0'..='9' | 'A'..='F' | 'a'..='f' => {
|
||||||
|
state = HexSecond(c);
|
||||||
|
}
|
||||||
|
c => {
|
||||||
|
bytes.extend(format!(r"\x{}", c).into_bytes());
|
||||||
|
state = Literal;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
HexSecond(first) => match c {
|
||||||
|
'0'..='9' | 'A'..='F' | 'a'..='f' => {
|
||||||
|
let ordinal = format!("{}{}", first, c);
|
||||||
|
let byte = u8::from_str_radix(&ordinal, 16).unwrap();
|
||||||
|
bytes.push(byte);
|
||||||
|
state = Literal;
|
||||||
|
}
|
||||||
|
c => {
|
||||||
|
let original = format!(r"\x{}{}", first, c);
|
||||||
|
bytes.extend(original.into_bytes());
|
||||||
|
state = Literal;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Literal => match c {
|
||||||
|
'\\' => {
|
||||||
|
state = Escape;
|
||||||
|
}
|
||||||
|
c => {
|
||||||
|
bytes.extend(c.to_string().as_bytes());
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
match state {
|
match state {
|
||||||
|
@ -46,7 +46,9 @@ impl ParseSizeError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl error::Error for ParseSizeError {
|
impl error::Error for ParseSizeError {
|
||||||
fn description(&self) -> &str { "invalid size" }
|
fn description(&self) -> &str {
|
||||||
|
"invalid size"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ParseSizeError {
|
impl fmt::Display for ParseSizeError {
|
||||||
@ -54,26 +56,19 @@ impl fmt::Display for ParseSizeError {
|
|||||||
use self::ParseSizeErrorKind::*;
|
use self::ParseSizeErrorKind::*;
|
||||||
|
|
||||||
match self.kind {
|
match self.kind {
|
||||||
InvalidFormat => {
|
InvalidFormat => write!(
|
||||||
write!(
|
f,
|
||||||
f,
|
"invalid format for size '{}', which should be a sequence \
|
||||||
"invalid format for size '{}', which should be a sequence \
|
|
||||||
of digits followed by an optional 'K', 'M' or 'G' \
|
of digits followed by an optional 'K', 'M' or 'G' \
|
||||||
suffix",
|
suffix",
|
||||||
self.original
|
self.original
|
||||||
)
|
),
|
||||||
}
|
InvalidInt(ref err) => write!(
|
||||||
InvalidInt(ref err) => {
|
f,
|
||||||
write!(
|
"invalid integer found in size '{}': {}",
|
||||||
f,
|
self.original, err
|
||||||
"invalid integer found in size '{}': {}",
|
),
|
||||||
self.original,
|
Overflow => write!(f, "size too big in '{}'", self.original),
|
||||||
err
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Overflow => {
|
|
||||||
write!(f, "size too big in '{}'", self.original)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,17 +99,16 @@ pub fn parse_human_readable_size(size: &str) -> Result<u64, ParseSizeError> {
|
|||||||
Some(caps) => caps,
|
Some(caps) => caps,
|
||||||
None => return Err(ParseSizeError::format(size)),
|
None => return Err(ParseSizeError::format(size)),
|
||||||
};
|
};
|
||||||
let value: u64 = caps[1].parse().map_err(|err| {
|
let value: u64 =
|
||||||
ParseSizeError::int(size, err)
|
caps[1].parse().map_err(|err| ParseSizeError::int(size, err))?;
|
||||||
})?;
|
|
||||||
let suffix = match caps.get(2) {
|
let suffix = match caps.get(2) {
|
||||||
None => return Ok(value),
|
None => return Ok(value),
|
||||||
Some(cap) => cap.as_str(),
|
Some(cap) => cap.as_str(),
|
||||||
};
|
};
|
||||||
let bytes = match suffix {
|
let bytes = match suffix {
|
||||||
"K" => value.checked_mul(1<<10),
|
"K" => value.checked_mul(1 << 10),
|
||||||
"M" => value.checked_mul(1<<20),
|
"M" => value.checked_mul(1 << 20),
|
||||||
"G" => value.checked_mul(1<<30),
|
"G" => value.checked_mul(1 << 30),
|
||||||
// Because if the regex matches this group, it must be [KMG].
|
// Because if the regex matches this group, it must be [KMG].
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
@ -134,19 +128,19 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn suffix_k() {
|
fn suffix_k() {
|
||||||
let x = parse_human_readable_size("123K").unwrap();
|
let x = parse_human_readable_size("123K").unwrap();
|
||||||
assert_eq!(123 * (1<<10), x);
|
assert_eq!(123 * (1 << 10), x);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn suffix_m() {
|
fn suffix_m() {
|
||||||
let x = parse_human_readable_size("123M").unwrap();
|
let x = parse_human_readable_size("123M").unwrap();
|
||||||
assert_eq!(123 * (1<<20), x);
|
assert_eq!(123 * (1 << 20), x);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn suffix_g() {
|
fn suffix_g() {
|
||||||
let x = parse_human_readable_size("123G").unwrap();
|
let x = parse_human_readable_size("123G").unwrap();
|
||||||
assert_eq!(123 * (1<<30), x);
|
assert_eq!(123 * (1 << 30), x);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -179,20 +179,18 @@ mod process;
|
|||||||
mod wtr;
|
mod wtr;
|
||||||
|
|
||||||
pub use decompress::{
|
pub use decompress::{
|
||||||
DecompressionMatcher, DecompressionMatcherBuilder,
|
DecompressionMatcher, DecompressionMatcherBuilder, DecompressionReader,
|
||||||
DecompressionReader, DecompressionReaderBuilder,
|
DecompressionReaderBuilder,
|
||||||
};
|
};
|
||||||
pub use escape::{escape, escape_os, unescape, unescape_os};
|
pub use escape::{escape, escape_os, unescape, unescape_os};
|
||||||
pub use human::{ParseSizeError, parse_human_readable_size};
|
pub use human::{parse_human_readable_size, ParseSizeError};
|
||||||
pub use pattern::{
|
pub use pattern::{
|
||||||
InvalidPatternError,
|
pattern_from_bytes, pattern_from_os, patterns_from_path,
|
||||||
pattern_from_os, pattern_from_bytes,
|
patterns_from_reader, patterns_from_stdin, InvalidPatternError,
|
||||||
patterns_from_path, patterns_from_reader, patterns_from_stdin,
|
|
||||||
};
|
};
|
||||||
pub use process::{CommandError, CommandReader, CommandReaderBuilder};
|
pub use process::{CommandError, CommandReader, CommandReaderBuilder};
|
||||||
pub use wtr::{
|
pub use wtr::{
|
||||||
StandardStream,
|
stdout, stdout_buffered_block, stdout_buffered_line, StandardStream,
|
||||||
stdout, stdout_buffered_line, stdout_buffered_block,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Returns true if and only if stdin is believed to be readable.
|
/// Returns true if and only if stdin is believed to be readable.
|
||||||
@ -205,8 +203,8 @@ pub use wtr::{
|
|||||||
pub fn is_readable_stdin() -> bool {
|
pub fn is_readable_stdin() -> bool {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
fn imp() -> bool {
|
fn imp() -> bool {
|
||||||
use std::os::unix::fs::FileTypeExt;
|
|
||||||
use same_file::Handle;
|
use same_file::Handle;
|
||||||
|
use std::os::unix::fs::FileTypeExt;
|
||||||
|
|
||||||
let ft = match Handle::stdin().and_then(|h| h.as_file().metadata()) {
|
let ft = match Handle::stdin().and_then(|h| h.as_file().metadata()) {
|
||||||
Err(_) => return false,
|
Err(_) => return false,
|
||||||
|
@ -29,7 +29,9 @@ impl InvalidPatternError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl error::Error for InvalidPatternError {
|
impl error::Error for InvalidPatternError {
|
||||||
fn description(&self) -> &str { "invalid pattern" }
|
fn description(&self) -> &str {
|
||||||
|
"invalid pattern"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for InvalidPatternError {
|
impl fmt::Display for InvalidPatternError {
|
||||||
@ -39,8 +41,7 @@ impl fmt::Display for InvalidPatternError {
|
|||||||
"found invalid UTF-8 in pattern at byte offset {} \
|
"found invalid UTF-8 in pattern at byte offset {} \
|
||||||
(use hex escape sequences to match arbitrary bytes \
|
(use hex escape sequences to match arbitrary bytes \
|
||||||
in a pattern, e.g., \\xFF): '{}'",
|
in a pattern, e.g., \\xFF): '{}'",
|
||||||
self.valid_up_to,
|
self.valid_up_to, self.original,
|
||||||
self.original,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -79,11 +80,9 @@ pub fn pattern_from_os(pattern: &OsStr) -> Result<&str, InvalidPatternError> {
|
|||||||
pub fn pattern_from_bytes(
|
pub fn pattern_from_bytes(
|
||||||
pattern: &[u8],
|
pattern: &[u8],
|
||||||
) -> Result<&str, InvalidPatternError> {
|
) -> Result<&str, InvalidPatternError> {
|
||||||
str::from_utf8(pattern).map_err(|err| {
|
str::from_utf8(pattern).map_err(|err| InvalidPatternError {
|
||||||
InvalidPatternError {
|
original: escape(pattern),
|
||||||
original: escape(pattern),
|
valid_up_to: err.valid_up_to(),
|
||||||
valid_up_to: err.valid_up_to(),
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,10 +118,7 @@ pub fn patterns_from_stdin() -> io::Result<Vec<String>> {
|
|||||||
let stdin = io::stdin();
|
let stdin = io::stdin();
|
||||||
let locked = stdin.lock();
|
let locked = stdin.lock();
|
||||||
patterns_from_reader(locked).map_err(|err| {
|
patterns_from_reader(locked).map_err(|err| {
|
||||||
io::Error::new(
|
io::Error::new(io::ErrorKind::Other, format!("<stdin>:{}", err))
|
||||||
io::ErrorKind::Other,
|
|
||||||
format!("<stdin>:{}", err),
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,12 +162,10 @@ pub fn patterns_from_reader<R: io::Read>(rdr: R) -> io::Result<Vec<String>> {
|
|||||||
patterns.push(pattern.to_string());
|
patterns.push(pattern.to_string());
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => Err(io::Error::new(
|
||||||
Err(io::Error::new(
|
io::ErrorKind::Other,
|
||||||
io::ErrorKind::Other,
|
format!("{}: {}", line_number, err),
|
||||||
format!("{}: {}", line_number, err),
|
)),
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
Ok(patterns)
|
Ok(patterns)
|
||||||
@ -191,8 +185,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
fn os() {
|
fn os() {
|
||||||
use std::os::unix::ffi::OsStrExt;
|
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
|
use std::os::unix::ffi::OsStrExt;
|
||||||
|
|
||||||
let pat = OsStr::from_bytes(b"abc\xFFxyz");
|
let pat = OsStr::from_bytes(b"abc\xFFxyz");
|
||||||
let err = pattern_from_os(pat).unwrap_err();
|
let err = pattern_from_os(pat).unwrap_err();
|
||||||
|
@ -33,7 +33,9 @@ impl CommandError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl error::Error for CommandError {
|
impl error::Error for CommandError {
|
||||||
fn description(&self) -> &str { "command error" }
|
fn description(&self) -> &str {
|
||||||
|
"command error"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for CommandError {
|
impl fmt::Display for CommandError {
|
||||||
@ -46,7 +48,12 @@ impl fmt::Display for CommandError {
|
|||||||
write!(f, "<stderr is empty>")
|
write!(f, "<stderr is empty>")
|
||||||
} else {
|
} else {
|
||||||
let div = iter::repeat('-').take(79).collect::<String>();
|
let div = iter::repeat('-').take(79).collect::<String>();
|
||||||
write!(f, "\n{div}\n{msg}\n{div}", div=div, msg=msg.trim())
|
write!(
|
||||||
|
f,
|
||||||
|
"\n{div}\n{msg}\n{div}",
|
||||||
|
div = div,
|
||||||
|
msg = msg.trim()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -101,12 +108,11 @@ impl CommandReaderBuilder {
|
|||||||
.stderr(process::Stdio::piped())
|
.stderr(process::Stdio::piped())
|
||||||
.spawn()?;
|
.spawn()?;
|
||||||
let stdout = child.stdout.take().unwrap();
|
let stdout = child.stdout.take().unwrap();
|
||||||
let stderr =
|
let stderr = if self.async_stderr {
|
||||||
if self.async_stderr {
|
StderrReader::async(child.stderr.take().unwrap())
|
||||||
StderrReader::async(child.stderr.take().unwrap())
|
} else {
|
||||||
} else {
|
StderrReader::sync(child.stderr.take().unwrap())
|
||||||
StderrReader::sync(child.stderr.take().unwrap())
|
};
|
||||||
};
|
|
||||||
Ok(CommandReader {
|
Ok(CommandReader {
|
||||||
child: child,
|
child: child,
|
||||||
stdout: stdout,
|
stdout: stdout,
|
||||||
@ -226,9 +232,8 @@ enum StderrReader {
|
|||||||
impl StderrReader {
|
impl StderrReader {
|
||||||
/// Create a reader for stderr that reads contents asynchronously.
|
/// Create a reader for stderr that reads contents asynchronously.
|
||||||
fn async(mut stderr: process::ChildStderr) -> StderrReader {
|
fn async(mut stderr: process::ChildStderr) -> StderrReader {
|
||||||
let handle = thread::spawn(move || {
|
let handle =
|
||||||
stderr_to_command_error(&mut stderr)
|
thread::spawn(move || stderr_to_command_error(&mut stderr));
|
||||||
});
|
|
||||||
StderrReader::Async(Some(handle))
|
StderrReader::Async(Some(handle))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,9 +252,7 @@ impl StderrReader {
|
|||||||
let handle = handle
|
let handle = handle
|
||||||
.take()
|
.take()
|
||||||
.expect("read_to_end cannot be called more than once");
|
.expect("read_to_end cannot be called more than once");
|
||||||
handle
|
handle.join().expect("stderr reading thread does not panic")
|
||||||
.join()
|
|
||||||
.expect("stderr reading thread does not panic")
|
|
||||||
}
|
}
|
||||||
StderrReader::Sync(ref mut stderr) => {
|
StderrReader::Sync(ref mut stderr) => {
|
||||||
stderr_to_command_error(stderr)
|
stderr_to_command_error(stderr)
|
||||||
|
@ -19,7 +19,7 @@ pub fn interpolate<A, N>(
|
|||||||
dst: &mut Vec<u8>,
|
dst: &mut Vec<u8>,
|
||||||
) where
|
) where
|
||||||
A: FnMut(usize, &mut Vec<u8>),
|
A: FnMut(usize, &mut Vec<u8>),
|
||||||
N: FnMut(&str) -> Option<usize>
|
N: FnMut(&str) -> Option<usize>,
|
||||||
{
|
{
|
||||||
while !replacement.is_empty() {
|
while !replacement.is_empty() {
|
||||||
match memchr(b'$', replacement) {
|
match memchr(b'$', replacement) {
|
||||||
@ -134,14 +134,14 @@ fn find_cap_ref(replacement: &[u8]) -> Option<CaptureRef> {
|
|||||||
/// Returns true if and only if the given byte is allowed in a capture name.
|
/// Returns true if and only if the given byte is allowed in a capture name.
|
||||||
fn is_valid_cap_letter(b: &u8) -> bool {
|
fn is_valid_cap_letter(b: &u8) -> bool {
|
||||||
match *b {
|
match *b {
|
||||||
b'0' ..= b'9' | b'a' ..= b'z' | b'A' ..= b'Z' | b'_' => true,
|
b'0'..=b'9' | b'a'..=b'z' | b'A'..=b'Z' | b'_' => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{CaptureRef, find_cap_ref, interpolate};
|
use super::{find_cap_ref, interpolate, CaptureRef};
|
||||||
|
|
||||||
macro_rules! find {
|
macro_rules! find {
|
||||||
($name:ident, $text:expr) => {
|
($name:ident, $text:expr) => {
|
||||||
@ -211,7 +211,7 @@ mod tests {
|
|||||||
fn $name() {
|
fn $name() {
|
||||||
assert_eq!($expected, interpolate_string($map, $caps, $hay));
|
assert_eq!($expected, interpolate_string($map, $caps, $hay));
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interp!(
|
interp!(
|
||||||
|
@ -278,7 +278,7 @@ impl LineTerminator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for LineTerminator {
|
impl Default for LineTerminator {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn default() -> LineTerminator {
|
fn default() -> LineTerminator {
|
||||||
LineTerminator::byte(b'\n')
|
LineTerminator::byte(b'\n')
|
||||||
@ -439,7 +439,8 @@ pub trait Captures {
|
|||||||
haystack: &[u8],
|
haystack: &[u8],
|
||||||
replacement: &[u8],
|
replacement: &[u8],
|
||||||
dst: &mut Vec<u8>,
|
dst: &mut Vec<u8>,
|
||||||
) where F: FnMut(&str) -> Option<usize>
|
) where
|
||||||
|
F: FnMut(&str) -> Option<usize>,
|
||||||
{
|
{
|
||||||
interpolate(
|
interpolate(
|
||||||
replacement,
|
replacement,
|
||||||
@ -463,12 +464,18 @@ pub struct NoCaptures(());
|
|||||||
|
|
||||||
impl NoCaptures {
|
impl NoCaptures {
|
||||||
/// Create an empty set of capturing groups.
|
/// Create an empty set of capturing groups.
|
||||||
pub fn new() -> NoCaptures { NoCaptures(()) }
|
pub fn new() -> NoCaptures {
|
||||||
|
NoCaptures(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Captures for NoCaptures {
|
impl Captures for NoCaptures {
|
||||||
fn len(&self) -> usize { 0 }
|
fn len(&self) -> usize {
|
||||||
fn get(&self, _: usize) -> Option<Match> { None }
|
0
|
||||||
|
}
|
||||||
|
fn get(&self, _: usize) -> Option<Match> {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// NoError provides an error type for matchers that never produce errors.
|
/// NoError provides an error type for matchers that never produce errors.
|
||||||
@ -481,7 +488,9 @@ impl Captures for NoCaptures {
|
|||||||
pub struct NoError(());
|
pub struct NoError(());
|
||||||
|
|
||||||
impl ::std::error::Error for NoError {
|
impl ::std::error::Error for NoError {
|
||||||
fn description(&self) -> &str { "no error" }
|
fn description(&self) -> &str {
|
||||||
|
"no error"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for NoError {
|
impl fmt::Display for NoError {
|
||||||
@ -599,10 +608,7 @@ pub trait Matcher {
|
|||||||
///
|
///
|
||||||
/// The text encoding of `haystack` is not strictly specified. Matchers are
|
/// The text encoding of `haystack` is not strictly specified. Matchers are
|
||||||
/// advised to assume UTF-8, or at worst, some ASCII compatible encoding.
|
/// advised to assume UTF-8, or at worst, some ASCII compatible encoding.
|
||||||
fn find(
|
fn find(&self, haystack: &[u8]) -> Result<Option<Match>, Self::Error> {
|
||||||
&self,
|
|
||||||
haystack: &[u8],
|
|
||||||
) -> Result<Option<Match>, Self::Error> {
|
|
||||||
self.find_at(haystack, 0)
|
self.find_at(haystack, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -614,7 +620,8 @@ pub trait Matcher {
|
|||||||
haystack: &[u8],
|
haystack: &[u8],
|
||||||
mut matched: F,
|
mut matched: F,
|
||||||
) -> Result<(), Self::Error>
|
) -> Result<(), Self::Error>
|
||||||
where F: FnMut(Match) -> bool
|
where
|
||||||
|
F: FnMut(Match) -> bool,
|
||||||
{
|
{
|
||||||
self.try_find_iter(haystack, |m| Ok(matched(m)))
|
self.try_find_iter(haystack, |m| Ok(matched(m)))
|
||||||
.map(|r: Result<(), ()>| r.unwrap())
|
.map(|r: Result<(), ()>| r.unwrap())
|
||||||
@ -632,7 +639,8 @@ pub trait Matcher {
|
|||||||
haystack: &[u8],
|
haystack: &[u8],
|
||||||
mut matched: F,
|
mut matched: F,
|
||||||
) -> Result<Result<(), E>, Self::Error>
|
) -> Result<Result<(), E>, Self::Error>
|
||||||
where F: FnMut(Match) -> Result<bool, E>
|
where
|
||||||
|
F: FnMut(Match) -> Result<bool, E>,
|
||||||
{
|
{
|
||||||
let mut last_end = 0;
|
let mut last_end = 0;
|
||||||
let mut last_match = None;
|
let mut last_match = None;
|
||||||
@ -690,7 +698,8 @@ pub trait Matcher {
|
|||||||
caps: &mut Self::Captures,
|
caps: &mut Self::Captures,
|
||||||
mut matched: F,
|
mut matched: F,
|
||||||
) -> Result<(), Self::Error>
|
) -> Result<(), Self::Error>
|
||||||
where F: FnMut(&Self::Captures) -> bool
|
where
|
||||||
|
F: FnMut(&Self::Captures) -> bool,
|
||||||
{
|
{
|
||||||
self.try_captures_iter(haystack, caps, |caps| Ok(matched(caps)))
|
self.try_captures_iter(haystack, caps, |caps| Ok(matched(caps)))
|
||||||
.map(|r: Result<(), ()>| r.unwrap())
|
.map(|r: Result<(), ()>| r.unwrap())
|
||||||
@ -709,7 +718,8 @@ pub trait Matcher {
|
|||||||
caps: &mut Self::Captures,
|
caps: &mut Self::Captures,
|
||||||
mut matched: F,
|
mut matched: F,
|
||||||
) -> Result<Result<(), E>, Self::Error>
|
) -> Result<Result<(), E>, Self::Error>
|
||||||
where F: FnMut(&Self::Captures) -> Result<bool, E>
|
where
|
||||||
|
F: FnMut(&Self::Captures) -> Result<bool, E>,
|
||||||
{
|
{
|
||||||
let mut last_end = 0;
|
let mut last_end = 0;
|
||||||
let mut last_match = None;
|
let mut last_match = None;
|
||||||
@ -787,7 +797,8 @@ pub trait Matcher {
|
|||||||
dst: &mut Vec<u8>,
|
dst: &mut Vec<u8>,
|
||||||
mut append: F,
|
mut append: F,
|
||||||
) -> Result<(), Self::Error>
|
) -> Result<(), Self::Error>
|
||||||
where F: FnMut(Match, &mut Vec<u8>) -> bool
|
where
|
||||||
|
F: FnMut(Match, &mut Vec<u8>) -> bool,
|
||||||
{
|
{
|
||||||
let mut last_match = 0;
|
let mut last_match = 0;
|
||||||
self.find_iter(haystack, |m| {
|
self.find_iter(haystack, |m| {
|
||||||
@ -810,7 +821,8 @@ pub trait Matcher {
|
|||||||
dst: &mut Vec<u8>,
|
dst: &mut Vec<u8>,
|
||||||
mut append: F,
|
mut append: F,
|
||||||
) -> Result<(), Self::Error>
|
) -> Result<(), Self::Error>
|
||||||
where F: FnMut(&Self::Captures, &mut Vec<u8>) -> bool
|
where
|
||||||
|
F: FnMut(&Self::Captures, &mut Vec<u8>) -> bool,
|
||||||
{
|
{
|
||||||
let mut last_match = 0;
|
let mut last_match = 0;
|
||||||
self.captures_iter(haystack, caps, |caps| {
|
self.captures_iter(haystack, caps, |caps| {
|
||||||
@ -1012,10 +1024,7 @@ impl<'a, M: Matcher> Matcher for &'a M {
|
|||||||
(*self).capture_count()
|
(*self).capture_count()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find(
|
fn find(&self, haystack: &[u8]) -> Result<Option<Match>, Self::Error> {
|
||||||
&self,
|
|
||||||
haystack: &[u8]
|
|
||||||
) -> Result<Option<Match>, Self::Error> {
|
|
||||||
(*self).find(haystack)
|
(*self).find(haystack)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1024,7 +1033,8 @@ impl<'a, M: Matcher> Matcher for &'a M {
|
|||||||
haystack: &[u8],
|
haystack: &[u8],
|
||||||
matched: F,
|
matched: F,
|
||||||
) -> Result<(), Self::Error>
|
) -> Result<(), Self::Error>
|
||||||
where F: FnMut(Match) -> bool
|
where
|
||||||
|
F: FnMut(Match) -> bool,
|
||||||
{
|
{
|
||||||
(*self).find_iter(haystack, matched)
|
(*self).find_iter(haystack, matched)
|
||||||
}
|
}
|
||||||
@ -1034,7 +1044,8 @@ impl<'a, M: Matcher> Matcher for &'a M {
|
|||||||
haystack: &[u8],
|
haystack: &[u8],
|
||||||
matched: F,
|
matched: F,
|
||||||
) -> Result<Result<(), E>, Self::Error>
|
) -> Result<Result<(), E>, Self::Error>
|
||||||
where F: FnMut(Match) -> Result<bool, E>
|
where
|
||||||
|
F: FnMut(Match) -> Result<bool, E>,
|
||||||
{
|
{
|
||||||
(*self).try_find_iter(haystack, matched)
|
(*self).try_find_iter(haystack, matched)
|
||||||
}
|
}
|
||||||
@ -1053,7 +1064,8 @@ impl<'a, M: Matcher> Matcher for &'a M {
|
|||||||
caps: &mut Self::Captures,
|
caps: &mut Self::Captures,
|
||||||
matched: F,
|
matched: F,
|
||||||
) -> Result<(), Self::Error>
|
) -> Result<(), Self::Error>
|
||||||
where F: FnMut(&Self::Captures) -> bool
|
where
|
||||||
|
F: FnMut(&Self::Captures) -> bool,
|
||||||
{
|
{
|
||||||
(*self).captures_iter(haystack, caps, matched)
|
(*self).captures_iter(haystack, caps, matched)
|
||||||
}
|
}
|
||||||
@ -1064,7 +1076,8 @@ impl<'a, M: Matcher> Matcher for &'a M {
|
|||||||
caps: &mut Self::Captures,
|
caps: &mut Self::Captures,
|
||||||
matched: F,
|
matched: F,
|
||||||
) -> Result<Result<(), E>, Self::Error>
|
) -> Result<Result<(), E>, Self::Error>
|
||||||
where F: FnMut(&Self::Captures) -> Result<bool, E>
|
where
|
||||||
|
F: FnMut(&Self::Captures) -> Result<bool, E>,
|
||||||
{
|
{
|
||||||
(*self).try_captures_iter(haystack, caps, matched)
|
(*self).try_captures_iter(haystack, caps, matched)
|
||||||
}
|
}
|
||||||
@ -1075,7 +1088,8 @@ impl<'a, M: Matcher> Matcher for &'a M {
|
|||||||
dst: &mut Vec<u8>,
|
dst: &mut Vec<u8>,
|
||||||
append: F,
|
append: F,
|
||||||
) -> Result<(), Self::Error>
|
) -> Result<(), Self::Error>
|
||||||
where F: FnMut(Match, &mut Vec<u8>) -> bool
|
where
|
||||||
|
F: FnMut(Match, &mut Vec<u8>) -> bool,
|
||||||
{
|
{
|
||||||
(*self).replace(haystack, dst, append)
|
(*self).replace(haystack, dst, append)
|
||||||
}
|
}
|
||||||
@ -1087,7 +1101,8 @@ impl<'a, M: Matcher> Matcher for &'a M {
|
|||||||
dst: &mut Vec<u8>,
|
dst: &mut Vec<u8>,
|
||||||
append: F,
|
append: F,
|
||||||
) -> Result<(), Self::Error>
|
) -> Result<(), Self::Error>
|
||||||
where F: FnMut(&Self::Captures, &mut Vec<u8>) -> bool
|
where
|
||||||
|
F: FnMut(&Self::Captures, &mut Vec<u8>) -> bool,
|
||||||
{
|
{
|
||||||
(*self).replace_with_captures(haystack, caps, dst, append)
|
(*self).replace_with_captures(haystack, caps, dst, append)
|
||||||
}
|
}
|
||||||
@ -1099,7 +1114,7 @@ impl<'a, M: Matcher> Matcher for &'a M {
|
|||||||
fn is_match_at(
|
fn is_match_at(
|
||||||
&self,
|
&self,
|
||||||
haystack: &[u8],
|
haystack: &[u8],
|
||||||
at: usize
|
at: usize,
|
||||||
) -> Result<bool, Self::Error> {
|
) -> Result<bool, Self::Error> {
|
||||||
(*self).is_match_at(haystack, at)
|
(*self).is_match_at(haystack, at)
|
||||||
}
|
}
|
||||||
|
@ -25,18 +25,22 @@ fn find() {
|
|||||||
fn find_iter() {
|
fn find_iter() {
|
||||||
let matcher = matcher(r"(\w+)\s+(\w+)");
|
let matcher = matcher(r"(\w+)\s+(\w+)");
|
||||||
let mut matches = vec![];
|
let mut matches = vec![];
|
||||||
matcher.find_iter(b"aa bb cc dd", |m| {
|
matcher
|
||||||
matches.push(m);
|
.find_iter(b"aa bb cc dd", |m| {
|
||||||
true
|
matches.push(m);
|
||||||
}).unwrap();
|
true
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
assert_eq!(matches, vec![m(0, 5), m(6, 11)]);
|
assert_eq!(matches, vec![m(0, 5), m(6, 11)]);
|
||||||
|
|
||||||
// Test that find_iter respects short circuiting.
|
// Test that find_iter respects short circuiting.
|
||||||
matches.clear();
|
matches.clear();
|
||||||
matcher.find_iter(b"aa bb cc dd", |m| {
|
matcher
|
||||||
matches.push(m);
|
.find_iter(b"aa bb cc dd", |m| {
|
||||||
false
|
matches.push(m);
|
||||||
}).unwrap();
|
false
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
assert_eq!(matches, vec![m(0, 5)]);
|
assert_eq!(matches, vec![m(0, 5)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,14 +51,17 @@ fn try_find_iter() {
|
|||||||
|
|
||||||
let matcher = matcher(r"(\w+)\s+(\w+)");
|
let matcher = matcher(r"(\w+)\s+(\w+)");
|
||||||
let mut matches = vec![];
|
let mut matches = vec![];
|
||||||
let err = matcher.try_find_iter(b"aa bb cc dd", |m| {
|
let err = matcher
|
||||||
if matches.is_empty() {
|
.try_find_iter(b"aa bb cc dd", |m| {
|
||||||
matches.push(m);
|
if matches.is_empty() {
|
||||||
Ok(true)
|
matches.push(m);
|
||||||
} else {
|
Ok(true)
|
||||||
Err(MyError)
|
} else {
|
||||||
}
|
Err(MyError)
|
||||||
}).unwrap().unwrap_err();
|
}
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
|
.unwrap_err();
|
||||||
assert_eq!(matches, vec![m(0, 5)]);
|
assert_eq!(matches, vec![m(0, 5)]);
|
||||||
assert_eq!(err, MyError);
|
assert_eq!(err, MyError);
|
||||||
}
|
}
|
||||||
@ -89,28 +96,30 @@ fn captures_iter() {
|
|||||||
let matcher = matcher(r"(?P<a>\w+)\s+(?P<b>\w+)");
|
let matcher = matcher(r"(?P<a>\w+)\s+(?P<b>\w+)");
|
||||||
let mut caps = matcher.new_captures().unwrap();
|
let mut caps = matcher.new_captures().unwrap();
|
||||||
let mut matches = vec![];
|
let mut matches = vec![];
|
||||||
matcher.captures_iter(b"aa bb cc dd", &mut caps, |caps| {
|
matcher
|
||||||
matches.push(caps.get(0).unwrap());
|
.captures_iter(b"aa bb cc dd", &mut caps, |caps| {
|
||||||
matches.push(caps.get(1).unwrap());
|
matches.push(caps.get(0).unwrap());
|
||||||
matches.push(caps.get(2).unwrap());
|
matches.push(caps.get(1).unwrap());
|
||||||
true
|
matches.push(caps.get(2).unwrap());
|
||||||
}).unwrap();
|
true
|
||||||
assert_eq!(matches, vec![
|
})
|
||||||
m(0, 5), m(0, 2), m(3, 5),
|
.unwrap();
|
||||||
m(6, 11), m(6, 8), m(9, 11),
|
assert_eq!(
|
||||||
]);
|
matches,
|
||||||
|
vec![m(0, 5), m(0, 2), m(3, 5), m(6, 11), m(6, 8), m(9, 11),]
|
||||||
|
);
|
||||||
|
|
||||||
// Test that captures_iter respects short circuiting.
|
// Test that captures_iter respects short circuiting.
|
||||||
matches.clear();
|
matches.clear();
|
||||||
matcher.captures_iter(b"aa bb cc dd", &mut caps, |caps| {
|
matcher
|
||||||
matches.push(caps.get(0).unwrap());
|
.captures_iter(b"aa bb cc dd", &mut caps, |caps| {
|
||||||
matches.push(caps.get(1).unwrap());
|
matches.push(caps.get(0).unwrap());
|
||||||
matches.push(caps.get(2).unwrap());
|
matches.push(caps.get(1).unwrap());
|
||||||
false
|
matches.push(caps.get(2).unwrap());
|
||||||
}).unwrap();
|
false
|
||||||
assert_eq!(matches, vec![
|
})
|
||||||
m(0, 5), m(0, 2), m(3, 5),
|
.unwrap();
|
||||||
]);
|
assert_eq!(matches, vec![m(0, 5), m(0, 2), m(3, 5),]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -121,16 +130,19 @@ fn try_captures_iter() {
|
|||||||
let matcher = matcher(r"(?P<a>\w+)\s+(?P<b>\w+)");
|
let matcher = matcher(r"(?P<a>\w+)\s+(?P<b>\w+)");
|
||||||
let mut caps = matcher.new_captures().unwrap();
|
let mut caps = matcher.new_captures().unwrap();
|
||||||
let mut matches = vec![];
|
let mut matches = vec![];
|
||||||
let err = matcher.try_captures_iter(b"aa bb cc dd", &mut caps, |caps| {
|
let err = matcher
|
||||||
if matches.is_empty() {
|
.try_captures_iter(b"aa bb cc dd", &mut caps, |caps| {
|
||||||
matches.push(caps.get(0).unwrap());
|
if matches.is_empty() {
|
||||||
matches.push(caps.get(1).unwrap());
|
matches.push(caps.get(0).unwrap());
|
||||||
matches.push(caps.get(2).unwrap());
|
matches.push(caps.get(1).unwrap());
|
||||||
Ok(true)
|
matches.push(caps.get(2).unwrap());
|
||||||
} else {
|
Ok(true)
|
||||||
Err(MyError)
|
} else {
|
||||||
}
|
Err(MyError)
|
||||||
}).unwrap().unwrap_err();
|
}
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
|
.unwrap_err();
|
||||||
assert_eq!(matches, vec![m(0, 5), m(0, 2), m(3, 5)]);
|
assert_eq!(matches, vec![m(0, 5), m(0, 2), m(3, 5)]);
|
||||||
assert_eq!(err, MyError);
|
assert_eq!(err, MyError);
|
||||||
}
|
}
|
||||||
@ -150,10 +162,12 @@ fn no_captures() {
|
|||||||
assert!(!matcher.captures(b"homer simpson", &mut caps).unwrap());
|
assert!(!matcher.captures(b"homer simpson", &mut caps).unwrap());
|
||||||
|
|
||||||
let mut called = false;
|
let mut called = false;
|
||||||
matcher.captures_iter(b"homer simpson", &mut caps, |_| {
|
matcher
|
||||||
called = true;
|
.captures_iter(b"homer simpson", &mut caps, |_| {
|
||||||
true
|
called = true;
|
||||||
}).unwrap();
|
true
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
assert!(!called);
|
assert!(!called);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,18 +175,22 @@ fn no_captures() {
|
|||||||
fn replace() {
|
fn replace() {
|
||||||
let matcher = matcher(r"(\w+)\s+(\w+)");
|
let matcher = matcher(r"(\w+)\s+(\w+)");
|
||||||
let mut dst = vec![];
|
let mut dst = vec![];
|
||||||
matcher.replace(b"aa bb cc dd", &mut dst, |_, dst| {
|
matcher
|
||||||
dst.push(b'z');
|
.replace(b"aa bb cc dd", &mut dst, |_, dst| {
|
||||||
true
|
dst.push(b'z');
|
||||||
}).unwrap();
|
true
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
assert_eq!(dst, b"z z");
|
assert_eq!(dst, b"z z");
|
||||||
|
|
||||||
// Test that replacements respect short circuiting.
|
// Test that replacements respect short circuiting.
|
||||||
dst.clear();
|
dst.clear();
|
||||||
matcher.replace(b"aa bb cc dd", &mut dst, |_, dst| {
|
matcher
|
||||||
dst.push(b'z');
|
.replace(b"aa bb cc dd", &mut dst, |_, dst| {
|
||||||
false
|
dst.push(b'z');
|
||||||
}).unwrap();
|
false
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
assert_eq!(dst, b"z cc dd");
|
assert_eq!(dst, b"z cc dd");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,27 +200,31 @@ fn replace_with_captures() {
|
|||||||
let haystack = b"aa bb cc dd";
|
let haystack = b"aa bb cc dd";
|
||||||
let mut caps = matcher.new_captures().unwrap();
|
let mut caps = matcher.new_captures().unwrap();
|
||||||
let mut dst = vec![];
|
let mut dst = vec![];
|
||||||
matcher.replace_with_captures(haystack, &mut caps, &mut dst, |caps, dst| {
|
matcher
|
||||||
caps.interpolate(
|
.replace_with_captures(haystack, &mut caps, &mut dst, |caps, dst| {
|
||||||
|name| matcher.capture_index(name),
|
caps.interpolate(
|
||||||
haystack,
|
|name| matcher.capture_index(name),
|
||||||
b"$2 $1",
|
haystack,
|
||||||
dst,
|
b"$2 $1",
|
||||||
);
|
dst,
|
||||||
true
|
);
|
||||||
}).unwrap();
|
true
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
assert_eq!(dst, b"bb aa dd cc");
|
assert_eq!(dst, b"bb aa dd cc");
|
||||||
|
|
||||||
// Test that replacements respect short circuiting.
|
// Test that replacements respect short circuiting.
|
||||||
dst.clear();
|
dst.clear();
|
||||||
matcher.replace_with_captures(haystack, &mut caps, &mut dst, |caps, dst| {
|
matcher
|
||||||
caps.interpolate(
|
.replace_with_captures(haystack, &mut caps, &mut dst, |caps, dst| {
|
||||||
|name| matcher.capture_index(name),
|
caps.interpolate(
|
||||||
haystack,
|
|name| matcher.capture_index(name),
|
||||||
b"$2 $1",
|
haystack,
|
||||||
dst,
|
b"$2 $1",
|
||||||
);
|
dst,
|
||||||
false
|
);
|
||||||
}).unwrap();
|
false
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
assert_eq!(dst, b"bb aa cc dd");
|
assert_eq!(dst, b"bb aa cc dd");
|
||||||
}
|
}
|
||||||
|
@ -18,10 +18,7 @@ impl RegexMatcher {
|
|||||||
names.insert(name.to_string(), i);
|
names.insert(name.to_string(), i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RegexMatcher {
|
RegexMatcher { re: re, names: names }
|
||||||
re: re,
|
|
||||||
names: names,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,12 +28,9 @@ impl Matcher for RegexMatcher {
|
|||||||
type Captures = RegexCaptures;
|
type Captures = RegexCaptures;
|
||||||
type Error = NoError;
|
type Error = NoError;
|
||||||
|
|
||||||
fn find_at(
|
fn find_at(&self, haystack: &[u8], at: usize) -> Result<Option<Match>> {
|
||||||
&self,
|
Ok(self
|
||||||
haystack: &[u8],
|
.re
|
||||||
at: usize,
|
|
||||||
) -> Result<Option<Match>> {
|
|
||||||
Ok(self.re
|
|
||||||
.find_at(haystack, at)
|
.find_at(haystack, at)
|
||||||
.map(|m| Match::new(m.start(), m.end())))
|
.map(|m| Match::new(m.start(), m.end())))
|
||||||
}
|
}
|
||||||
@ -75,12 +69,9 @@ impl Matcher for RegexMatcherNoCaps {
|
|||||||
type Captures = NoCaptures;
|
type Captures = NoCaptures;
|
||||||
type Error = NoError;
|
type Error = NoError;
|
||||||
|
|
||||||
fn find_at(
|
fn find_at(&self, haystack: &[u8], at: usize) -> Result<Option<Match>> {
|
||||||
&self,
|
Ok(self
|
||||||
haystack: &[u8],
|
.0
|
||||||
at: usize,
|
|
||||||
) -> Result<Option<Match>> {
|
|
||||||
Ok(self.0
|
|
||||||
.find_at(haystack, at)
|
.find_at(haystack, at)
|
||||||
.map(|m| Match::new(m.start(), m.end())))
|
.map(|m| Match::new(m.start(), m.end())))
|
||||||
}
|
}
|
||||||
|
@ -33,13 +33,12 @@ impl RegexMatcherBuilder {
|
|||||||
if self.case_smart && !has_uppercase_literal(pattern) {
|
if self.case_smart && !has_uppercase_literal(pattern) {
|
||||||
builder.caseless(true);
|
builder.caseless(true);
|
||||||
}
|
}
|
||||||
let res =
|
let res = if self.word {
|
||||||
if self.word {
|
let pattern = format!(r"(?<!\w)(?:{})(?!\w)", pattern);
|
||||||
let pattern = format!(r"(?<!\w)(?:{})(?!\w)", pattern);
|
builder.build(&pattern)
|
||||||
builder.build(&pattern)
|
} else {
|
||||||
} else {
|
builder.build(pattern)
|
||||||
builder.build(pattern)
|
};
|
||||||
};
|
|
||||||
res.map_err(Error::regex).map(|regex| {
|
res.map_err(Error::regex).map(|regex| {
|
||||||
let mut names = HashMap::new();
|
let mut names = HashMap::new();
|
||||||
for (i, name) in regex.capture_names().iter().enumerate() {
|
for (i, name) in regex.capture_names().iter().enumerate() {
|
||||||
@ -274,7 +273,8 @@ impl Matcher for RegexMatcher {
|
|||||||
haystack: &[u8],
|
haystack: &[u8],
|
||||||
at: usize,
|
at: usize,
|
||||||
) -> Result<Option<Match>, Error> {
|
) -> Result<Option<Match>, Error> {
|
||||||
Ok(self.regex
|
Ok(self
|
||||||
|
.regex
|
||||||
.find_at(haystack, at)
|
.find_at(haystack, at)
|
||||||
.map_err(Error::regex)?
|
.map_err(Error::regex)?
|
||||||
.map(|m| Match::new(m.start(), m.end())))
|
.map(|m| Match::new(m.start(), m.end())))
|
||||||
@ -297,7 +297,8 @@ impl Matcher for RegexMatcher {
|
|||||||
haystack: &[u8],
|
haystack: &[u8],
|
||||||
mut matched: F,
|
mut matched: F,
|
||||||
) -> Result<Result<(), E>, Error>
|
) -> Result<Result<(), E>, Error>
|
||||||
where F: FnMut(Match) -> Result<bool, E>
|
where
|
||||||
|
F: FnMut(Match) -> Result<bool, E>,
|
||||||
{
|
{
|
||||||
for result in self.regex.find_iter(haystack) {
|
for result in self.regex.find_iter(haystack) {
|
||||||
let m = result.map_err(Error::regex)?;
|
let m = result.map_err(Error::regex)?;
|
||||||
@ -316,10 +317,11 @@ impl Matcher for RegexMatcher {
|
|||||||
at: usize,
|
at: usize,
|
||||||
caps: &mut RegexCaptures,
|
caps: &mut RegexCaptures,
|
||||||
) -> Result<bool, Error> {
|
) -> Result<bool, Error> {
|
||||||
Ok(self.regex
|
Ok(self
|
||||||
.captures_read_at(&mut caps.locs, haystack, at)
|
.regex
|
||||||
.map_err(Error::regex)?
|
.captures_read_at(&mut caps.locs, haystack, at)
|
||||||
.is_some())
|
.map_err(Error::regex)?
|
||||||
|
.is_some())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -383,23 +385,19 @@ fn has_uppercase_literal(pattern: &str) -> bool {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use grep_matcher::{LineMatchKind, Matcher};
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use grep_matcher::{LineMatchKind, Matcher};
|
||||||
|
|
||||||
// Test that enabling word matches does the right thing and demonstrate
|
// Test that enabling word matches does the right thing and demonstrate
|
||||||
// the difference between it and surrounding the regex in `\b`.
|
// the difference between it and surrounding the regex in `\b`.
|
||||||
#[test]
|
#[test]
|
||||||
fn word() {
|
fn word() {
|
||||||
let matcher = RegexMatcherBuilder::new()
|
let matcher =
|
||||||
.word(true)
|
RegexMatcherBuilder::new().word(true).build(r"-2").unwrap();
|
||||||
.build(r"-2")
|
|
||||||
.unwrap();
|
|
||||||
assert!(matcher.is_match(b"abc -2 foo").unwrap());
|
assert!(matcher.is_match(b"abc -2 foo").unwrap());
|
||||||
|
|
||||||
let matcher = RegexMatcherBuilder::new()
|
let matcher =
|
||||||
.word(false)
|
RegexMatcherBuilder::new().word(false).build(r"\b-2\b").unwrap();
|
||||||
.build(r"\b-2\b")
|
|
||||||
.unwrap();
|
|
||||||
assert!(!matcher.is_match(b"abc -2 foo").unwrap());
|
assert!(!matcher.is_match(b"abc -2 foo").unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -432,16 +430,12 @@ mod tests {
|
|||||||
// Test that smart case works.
|
// Test that smart case works.
|
||||||
#[test]
|
#[test]
|
||||||
fn case_smart() {
|
fn case_smart() {
|
||||||
let matcher = RegexMatcherBuilder::new()
|
let matcher =
|
||||||
.case_smart(true)
|
RegexMatcherBuilder::new().case_smart(true).build(r"abc").unwrap();
|
||||||
.build(r"abc")
|
|
||||||
.unwrap();
|
|
||||||
assert!(matcher.is_match(b"ABC").unwrap());
|
assert!(matcher.is_match(b"ABC").unwrap());
|
||||||
|
|
||||||
let matcher = RegexMatcherBuilder::new()
|
let matcher =
|
||||||
.case_smart(true)
|
RegexMatcherBuilder::new().case_smart(true).build(r"aBc").unwrap();
|
||||||
.build(r"aBc")
|
|
||||||
.unwrap();
|
|
||||||
assert!(!matcher.is_match(b"ABC").unwrap());
|
assert!(!matcher.is_match(b"ABC").unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -455,9 +449,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let matcher = RegexMatcherBuilder::new()
|
let matcher = RegexMatcherBuilder::new().build(r"\wfoo\s").unwrap();
|
||||||
.build(r"\wfoo\s")
|
|
||||||
.unwrap();
|
|
||||||
let m = matcher.find_candidate_line(b"afoo ").unwrap().unwrap();
|
let m = matcher.find_candidate_line(b"afoo ").unwrap().unwrap();
|
||||||
assert!(is_confirmed(m));
|
assert!(is_confirmed(m));
|
||||||
}
|
}
|
||||||
|
@ -62,42 +62,32 @@ impl ColorError {
|
|||||||
impl fmt::Display for ColorError {
|
impl fmt::Display for ColorError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
ColorError::UnrecognizedOutType(ref name) => {
|
ColorError::UnrecognizedOutType(ref name) => write!(
|
||||||
write!(
|
f,
|
||||||
f,
|
"unrecognized output type '{}'. Choose from: \
|
||||||
"unrecognized output type '{}'. Choose from: \
|
|
||||||
path, line, column, match.",
|
path, line, column, match.",
|
||||||
name,
|
name,
|
||||||
)
|
),
|
||||||
}
|
ColorError::UnrecognizedSpecType(ref name) => write!(
|
||||||
ColorError::UnrecognizedSpecType(ref name) => {
|
f,
|
||||||
write!(
|
"unrecognized spec type '{}'. Choose from: \
|
||||||
f,
|
|
||||||
"unrecognized spec type '{}'. Choose from: \
|
|
||||||
fg, bg, style, none.",
|
fg, bg, style, none.",
|
||||||
name,
|
name,
|
||||||
)
|
),
|
||||||
}
|
ColorError::UnrecognizedColor(_, ref msg) => write!(f, "{}", msg),
|
||||||
ColorError::UnrecognizedColor(_, ref msg) => {
|
ColorError::UnrecognizedStyle(ref name) => write!(
|
||||||
write!(f, "{}", msg)
|
f,
|
||||||
}
|
"unrecognized style attribute '{}'. Choose from: \
|
||||||
ColorError::UnrecognizedStyle(ref name) => {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"unrecognized style attribute '{}'. Choose from: \
|
|
||||||
nobold, bold, nointense, intense, nounderline, \
|
nobold, bold, nointense, intense, nounderline, \
|
||||||
underline.",
|
underline.",
|
||||||
name,
|
name,
|
||||||
)
|
),
|
||||||
}
|
ColorError::InvalidFormat(ref original) => write!(
|
||||||
ColorError::InvalidFormat(ref original) => {
|
f,
|
||||||
write!(
|
"invalid color spec format: '{}'. Valid format \
|
||||||
f,
|
|
||||||
"invalid color spec format: '{}'. Valid format \
|
|
||||||
is '(path|line|column|match):(fg|bg|style):(value)'.",
|
is '(path|line|column|match):(fg|bg|style):(value)'.",
|
||||||
original,
|
original,
|
||||||
)
|
),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -227,7 +217,7 @@ enum Style {
|
|||||||
Intense,
|
Intense,
|
||||||
NoIntense,
|
NoIntense,
|
||||||
Underline,
|
Underline,
|
||||||
NoUnderline
|
NoUnderline,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ColorSpecs {
|
impl ColorSpecs {
|
||||||
@ -288,18 +278,32 @@ impl SpecValue {
|
|||||||
fn merge_into(&self, cspec: &mut ColorSpec) {
|
fn merge_into(&self, cspec: &mut ColorSpec) {
|
||||||
match *self {
|
match *self {
|
||||||
SpecValue::None => cspec.clear(),
|
SpecValue::None => cspec.clear(),
|
||||||
SpecValue::Fg(ref color) => { cspec.set_fg(Some(color.clone())); }
|
SpecValue::Fg(ref color) => {
|
||||||
SpecValue::Bg(ref color) => { cspec.set_bg(Some(color.clone())); }
|
cspec.set_fg(Some(color.clone()));
|
||||||
SpecValue::Style(ref style) => {
|
|
||||||
match *style {
|
|
||||||
Style::Bold => { cspec.set_bold(true); }
|
|
||||||
Style::NoBold => { cspec.set_bold(false); }
|
|
||||||
Style::Intense => { cspec.set_intense(true); }
|
|
||||||
Style::NoIntense => { cspec.set_intense(false); }
|
|
||||||
Style::Underline => { cspec.set_underline(true); }
|
|
||||||
Style::NoUnderline => { cspec.set_underline(false); }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
SpecValue::Bg(ref color) => {
|
||||||
|
cspec.set_bg(Some(color.clone()));
|
||||||
|
}
|
||||||
|
SpecValue::Style(ref style) => match *style {
|
||||||
|
Style::Bold => {
|
||||||
|
cspec.set_bold(true);
|
||||||
|
}
|
||||||
|
Style::NoBold => {
|
||||||
|
cspec.set_bold(false);
|
||||||
|
}
|
||||||
|
Style::Intense => {
|
||||||
|
cspec.set_intense(true);
|
||||||
|
}
|
||||||
|
Style::NoIntense => {
|
||||||
|
cspec.set_intense(false);
|
||||||
|
}
|
||||||
|
Style::Underline => {
|
||||||
|
cspec.set_underline(true);
|
||||||
|
}
|
||||||
|
Style::NoUnderline => {
|
||||||
|
cspec.set_underline(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -315,10 +319,7 @@ impl FromStr for UserColorSpec {
|
|||||||
let otype: OutType = pieces[0].parse()?;
|
let otype: OutType = pieces[0].parse()?;
|
||||||
match pieces[1].parse()? {
|
match pieces[1].parse()? {
|
||||||
SpecType::None => {
|
SpecType::None => {
|
||||||
Ok(UserColorSpec {
|
Ok(UserColorSpec { ty: otype, value: SpecValue::None })
|
||||||
ty: otype,
|
|
||||||
value: SpecValue::None,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
SpecType::Style => {
|
SpecType::Style => {
|
||||||
if pieces.len() < 3 {
|
if pieces.len() < 3 {
|
||||||
@ -331,18 +332,16 @@ impl FromStr for UserColorSpec {
|
|||||||
if pieces.len() < 3 {
|
if pieces.len() < 3 {
|
||||||
return Err(ColorError::InvalidFormat(s.to_string()));
|
return Err(ColorError::InvalidFormat(s.to_string()));
|
||||||
}
|
}
|
||||||
let color: Color = pieces[2]
|
let color: Color =
|
||||||
.parse()
|
pieces[2].parse().map_err(ColorError::from_parse_error)?;
|
||||||
.map_err(ColorError::from_parse_error)?;
|
|
||||||
Ok(UserColorSpec { ty: otype, value: SpecValue::Fg(color) })
|
Ok(UserColorSpec { ty: otype, value: SpecValue::Fg(color) })
|
||||||
}
|
}
|
||||||
SpecType::Bg => {
|
SpecType::Bg => {
|
||||||
if pieces.len() < 3 {
|
if pieces.len() < 3 {
|
||||||
return Err(ColorError::InvalidFormat(s.to_string()));
|
return Err(ColorError::InvalidFormat(s.to_string()));
|
||||||
}
|
}
|
||||||
let color: Color = pieces[2]
|
let color: Color =
|
||||||
.parse()
|
pieces[2].parse().map_err(ColorError::from_parse_error)?;
|
||||||
.map_err(ColorError::from_parse_error)?;
|
|
||||||
Ok(UserColorSpec { ty: otype, value: SpecValue::Bg(color) })
|
Ok(UserColorSpec { ty: otype, value: SpecValue::Bg(color) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,8 @@ use std::time::Instant;
|
|||||||
|
|
||||||
use grep_matcher::{Match, Matcher};
|
use grep_matcher::{Match, Matcher};
|
||||||
use grep_searcher::{
|
use grep_searcher::{
|
||||||
Searcher,
|
Searcher, Sink, SinkContext, SinkContextKind, SinkError, SinkFinish,
|
||||||
Sink, SinkError, SinkContext, SinkContextKind, SinkFinish, SinkMatch,
|
SinkMatch,
|
||||||
};
|
};
|
||||||
use serde_json as json;
|
use serde_json as json;
|
||||||
|
|
||||||
@ -27,11 +27,7 @@ struct Config {
|
|||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
fn default() -> Config {
|
fn default() -> Config {
|
||||||
Config {
|
Config { pretty: false, max_matches: None, always_begin_end: false }
|
||||||
pretty: false,
|
|
||||||
max_matches: None,
|
|
||||||
always_begin_end: false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -492,8 +488,9 @@ impl<W: io::Write> JSON<W> {
|
|||||||
matcher: M,
|
matcher: M,
|
||||||
path: &'p P,
|
path: &'p P,
|
||||||
) -> JSONSink<'p, 's, M, W>
|
) -> JSONSink<'p, 's, M, W>
|
||||||
where M: Matcher,
|
where
|
||||||
P: ?Sized + AsRef<Path>,
|
M: Matcher,
|
||||||
|
P: ?Sized + AsRef<Path>,
|
||||||
{
|
{
|
||||||
JSONSink {
|
JSONSink {
|
||||||
matcher: matcher,
|
matcher: matcher,
|
||||||
@ -615,10 +612,12 @@ impl<'p, 's, M: Matcher, W: io::Write> JSONSink<'p, 's, M, W> {
|
|||||||
// the extent that it's easy to ensure that we never do more than
|
// the extent that it's easy to ensure that we never do more than
|
||||||
// one search to find the matches.
|
// one search to find the matches.
|
||||||
let matches = &mut self.json.matches;
|
let matches = &mut self.json.matches;
|
||||||
self.matcher.find_iter(bytes, |m| {
|
self.matcher
|
||||||
matches.push(m);
|
.find_iter(bytes, |m| {
|
||||||
true
|
matches.push(m);
|
||||||
}).map_err(io::Error::error_message)?;
|
true
|
||||||
|
})
|
||||||
|
.map_err(io::Error::error_message)?;
|
||||||
// Don't report empty matches appearing at the end of the bytes.
|
// Don't report empty matches appearing at the end of the bytes.
|
||||||
if !matches.is_empty()
|
if !matches.is_empty()
|
||||||
&& matches.last().unwrap().is_empty()
|
&& matches.last().unwrap().is_empty()
|
||||||
@ -650,9 +649,7 @@ impl<'p, 's, M: Matcher, W: io::Write> JSONSink<'p, 's, M, W> {
|
|||||||
if self.begin_printed {
|
if self.begin_printed {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let msg = jsont::Message::Begin(jsont::Begin {
|
let msg = jsont::Message::Begin(jsont::Begin { path: self.path });
|
||||||
path: self.path,
|
|
||||||
});
|
|
||||||
self.json.write_message(&msg)?;
|
self.json.write_message(&msg)?;
|
||||||
self.begin_printed = true;
|
self.begin_printed = true;
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -699,13 +696,12 @@ impl<'p, 's, M: Matcher, W: io::Write> Sink for JSONSink<'p, 's, M, W> {
|
|||||||
self.after_context_remaining =
|
self.after_context_remaining =
|
||||||
self.after_context_remaining.saturating_sub(1);
|
self.after_context_remaining.saturating_sub(1);
|
||||||
}
|
}
|
||||||
let submatches =
|
let submatches = if searcher.invert_match() {
|
||||||
if searcher.invert_match() {
|
self.record_matches(ctx.bytes())?;
|
||||||
self.record_matches(ctx.bytes())?;
|
SubMatches::new(ctx.bytes(), &self.json.matches)
|
||||||
SubMatches::new(ctx.bytes(), &self.json.matches)
|
} else {
|
||||||
} else {
|
SubMatches::empty()
|
||||||
SubMatches::empty()
|
};
|
||||||
};
|
|
||||||
let msg = jsont::Message::Context(jsont::Context {
|
let msg = jsont::Message::Context(jsont::Context {
|
||||||
path: self.path,
|
path: self.path,
|
||||||
lines: ctx.bytes(),
|
lines: ctx.bytes(),
|
||||||
@ -717,10 +713,7 @@ impl<'p, 's, M: Matcher, W: io::Write> Sink for JSONSink<'p, 's, M, W> {
|
|||||||
Ok(!self.should_quit())
|
Ok(!self.should_quit())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn begin(
|
fn begin(&mut self, _searcher: &Searcher) -> Result<bool, io::Error> {
|
||||||
&mut self,
|
|
||||||
_searcher: &Searcher,
|
|
||||||
) -> Result<bool, io::Error> {
|
|
||||||
self.json.wtr.reset_count();
|
self.json.wtr.reset_count();
|
||||||
self.start_time = Instant::now();
|
self.start_time = Instant::now();
|
||||||
self.match_count = 0;
|
self.match_count = 0;
|
||||||
@ -779,7 +772,7 @@ enum SubMatches<'a> {
|
|||||||
impl<'a> SubMatches<'a> {
|
impl<'a> SubMatches<'a> {
|
||||||
/// Create a new set of match ranges from a set of matches and the
|
/// Create a new set of match ranges from a set of matches and the
|
||||||
/// corresponding bytes that those matches apply to.
|
/// corresponding bytes that those matches apply to.
|
||||||
fn new(bytes: &'a[u8], matches: &[Match]) -> SubMatches<'a> {
|
fn new(bytes: &'a [u8], matches: &[Match]) -> SubMatches<'a> {
|
||||||
if matches.len() == 1 {
|
if matches.len() == 1 {
|
||||||
let mat = matches[0];
|
let mat = matches[0];
|
||||||
SubMatches::Small([jsont::SubMatch {
|
SubMatches::Small([jsont::SubMatch {
|
||||||
@ -817,11 +810,11 @@ impl<'a> SubMatches<'a> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use grep_regex::{RegexMatcher, RegexMatcherBuilder};
|
|
||||||
use grep_matcher::LineTerminator;
|
use grep_matcher::LineTerminator;
|
||||||
|
use grep_regex::{RegexMatcher, RegexMatcherBuilder};
|
||||||
use grep_searcher::SearcherBuilder;
|
use grep_searcher::SearcherBuilder;
|
||||||
|
|
||||||
use super::{JSON, JSONBuilder};
|
use super::{JSONBuilder, JSON};
|
||||||
|
|
||||||
const SHERLOCK: &'static [u8] = b"\
|
const SHERLOCK: &'static [u8] = b"\
|
||||||
For the Doctor Watsons of this world, as opposed to the Sherlock
|
For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
@ -832,9 +825,7 @@ but Doctor Watson has to have it taken out for him and dusted,
|
|||||||
and exhibited clearly, with a label attached.
|
and exhibited clearly, with a label attached.
|
||||||
";
|
";
|
||||||
|
|
||||||
fn printer_contents(
|
fn printer_contents(printer: &mut JSON<Vec<u8>>) -> String {
|
||||||
printer: &mut JSON<Vec<u8>>,
|
|
||||||
) -> String {
|
|
||||||
String::from_utf8(printer.get_mut().to_owned()).unwrap()
|
String::from_utf8(printer.get_mut().to_owned()).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -851,11 +842,8 @@ but Doctor Watson has to have it taken out for him and dusted,
|
|||||||
and exhibited clearly, with a label attached.\
|
and exhibited clearly, with a label attached.\
|
||||||
";
|
";
|
||||||
|
|
||||||
let matcher = RegexMatcher::new(
|
let matcher = RegexMatcher::new(r"Watson").unwrap();
|
||||||
r"Watson"
|
let mut printer = JSONBuilder::new().build(vec![]);
|
||||||
).unwrap();
|
|
||||||
let mut printer = JSONBuilder::new()
|
|
||||||
.build(vec![]);
|
|
||||||
SearcherBuilder::new()
|
SearcherBuilder::new()
|
||||||
.binary_detection(BinaryDetection::quit(b'\x00'))
|
.binary_detection(BinaryDetection::quit(b'\x00'))
|
||||||
.heap_limit(Some(80))
|
.heap_limit(Some(80))
|
||||||
@ -871,12 +859,9 @@ and exhibited clearly, with a label attached.\
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn max_matches() {
|
fn max_matches() {
|
||||||
let matcher = RegexMatcher::new(
|
let matcher = RegexMatcher::new(r"Watson").unwrap();
|
||||||
r"Watson"
|
let mut printer =
|
||||||
).unwrap();
|
JSONBuilder::new().max_matches(Some(1)).build(vec![]);
|
||||||
let mut printer = JSONBuilder::new()
|
|
||||||
.max_matches(Some(1))
|
|
||||||
.build(vec![]);
|
|
||||||
SearcherBuilder::new()
|
SearcherBuilder::new()
|
||||||
.build()
|
.build()
|
||||||
.search_reader(&matcher, SHERLOCK, printer.sink(&matcher))
|
.search_reader(&matcher, SHERLOCK, printer.sink(&matcher))
|
||||||
@ -888,11 +873,8 @@ and exhibited clearly, with a label attached.\
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn no_match() {
|
fn no_match() {
|
||||||
let matcher = RegexMatcher::new(
|
let matcher = RegexMatcher::new(r"DOES NOT MATCH").unwrap();
|
||||||
r"DOES NOT MATCH"
|
let mut printer = JSONBuilder::new().build(vec![]);
|
||||||
).unwrap();
|
|
||||||
let mut printer = JSONBuilder::new()
|
|
||||||
.build(vec![]);
|
|
||||||
SearcherBuilder::new()
|
SearcherBuilder::new()
|
||||||
.build()
|
.build()
|
||||||
.search_reader(&matcher, SHERLOCK, printer.sink(&matcher))
|
.search_reader(&matcher, SHERLOCK, printer.sink(&matcher))
|
||||||
@ -904,12 +886,9 @@ and exhibited clearly, with a label attached.\
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn always_begin_end_no_match() {
|
fn always_begin_end_no_match() {
|
||||||
let matcher = RegexMatcher::new(
|
let matcher = RegexMatcher::new(r"DOES NOT MATCH").unwrap();
|
||||||
r"DOES NOT MATCH"
|
let mut printer =
|
||||||
).unwrap();
|
JSONBuilder::new().always_begin_end(true).build(vec![]);
|
||||||
let mut printer = JSONBuilder::new()
|
|
||||||
.always_begin_end(true)
|
|
||||||
.build(vec![]);
|
|
||||||
SearcherBuilder::new()
|
SearcherBuilder::new()
|
||||||
.build()
|
.build()
|
||||||
.search_reader(&matcher, SHERLOCK, printer.sink(&matcher))
|
.search_reader(&matcher, SHERLOCK, printer.sink(&matcher))
|
||||||
@ -924,11 +903,8 @@ and exhibited clearly, with a label attached.\
|
|||||||
fn missing_crlf() {
|
fn missing_crlf() {
|
||||||
let haystack = "test\r\n".as_bytes();
|
let haystack = "test\r\n".as_bytes();
|
||||||
|
|
||||||
let matcher = RegexMatcherBuilder::new()
|
let matcher = RegexMatcherBuilder::new().build("test").unwrap();
|
||||||
.build("test")
|
let mut printer = JSONBuilder::new().build(vec![]);
|
||||||
.unwrap();
|
|
||||||
let mut printer = JSONBuilder::new()
|
|
||||||
.build(vec![]);
|
|
||||||
SearcherBuilder::new()
|
SearcherBuilder::new()
|
||||||
.build()
|
.build()
|
||||||
.search_reader(&matcher, haystack, printer.sink(&matcher))
|
.search_reader(&matcher, haystack, printer.sink(&matcher))
|
||||||
@ -941,12 +917,9 @@ and exhibited clearly, with a label attached.\
|
|||||||
got.lines().nth(1).unwrap(),
|
got.lines().nth(1).unwrap(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let matcher = RegexMatcherBuilder::new()
|
let matcher =
|
||||||
.crlf(true)
|
RegexMatcherBuilder::new().crlf(true).build("test").unwrap();
|
||||||
.build("test")
|
let mut printer = JSONBuilder::new().build(vec![]);
|
||||||
.unwrap();
|
|
||||||
let mut printer = JSONBuilder::new()
|
|
||||||
.build(vec![]);
|
|
||||||
SearcherBuilder::new()
|
SearcherBuilder::new()
|
||||||
.line_terminator(LineTerminator::crlf())
|
.line_terminator(LineTerminator::crlf())
|
||||||
.build()
|
.build()
|
||||||
|
@ -80,7 +80,9 @@ pub struct SubMatch<'a> {
|
|||||||
#[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize)]
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
enum Data<'a> {
|
enum Data<'a> {
|
||||||
Text { text: Cow<'a, str> },
|
Text {
|
||||||
|
text: Cow<'a, str>,
|
||||||
|
},
|
||||||
Bytes {
|
Bytes {
|
||||||
#[serde(serialize_with = "to_base64")]
|
#[serde(serialize_with = "to_base64")]
|
||||||
bytes: &'a [u8],
|
bytes: &'a [u8],
|
||||||
@ -116,32 +118,26 @@ impl<'a> Data<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_base64<T, S>(
|
fn to_base64<T, S>(bytes: T, ser: S) -> Result<S::Ok, S::Error>
|
||||||
bytes: T,
|
where
|
||||||
ser: S,
|
T: AsRef<[u8]>,
|
||||||
) -> Result<S::Ok, S::Error>
|
S: Serializer,
|
||||||
where T: AsRef<[u8]>,
|
|
||||||
S: Serializer
|
|
||||||
{
|
{
|
||||||
ser.serialize_str(&base64::encode(&bytes))
|
ser.serialize_str(&base64::encode(&bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ser_bytes<T, S>(
|
fn ser_bytes<T, S>(bytes: T, ser: S) -> Result<S::Ok, S::Error>
|
||||||
bytes: T,
|
where
|
||||||
ser: S,
|
T: AsRef<[u8]>,
|
||||||
) -> Result<S::Ok, S::Error>
|
S: Serializer,
|
||||||
where T: AsRef<[u8]>,
|
|
||||||
S: Serializer
|
|
||||||
{
|
{
|
||||||
Data::from_bytes(bytes.as_ref()).serialize(ser)
|
Data::from_bytes(bytes.as_ref()).serialize(ser)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ser_path<P, S>(
|
fn ser_path<P, S>(path: &Option<P>, ser: S) -> Result<S::Ok, S::Error>
|
||||||
path: &Option<P>,
|
where
|
||||||
ser: S,
|
P: AsRef<Path>,
|
||||||
) -> Result<S::Ok, S::Error>
|
S: Serializer,
|
||||||
where P: AsRef<Path>,
|
|
||||||
S: Serializer
|
|
||||||
{
|
{
|
||||||
path.as_ref().map(|p| Data::from_path(p.as_ref())).serialize(ser)
|
path.as_ref().map(|p| Data::from_path(p.as_ref())).serialize(ser)
|
||||||
}
|
}
|
||||||
|
@ -84,9 +84,9 @@ extern crate serde_derive;
|
|||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
extern crate termcolor;
|
extern crate termcolor;
|
||||||
|
|
||||||
pub use color::{ColorError, ColorSpecs, UserColorSpec, default_color_specs};
|
pub use color::{default_color_specs, ColorError, ColorSpecs, UserColorSpec};
|
||||||
#[cfg(feature = "serde1")]
|
#[cfg(feature = "serde1")]
|
||||||
pub use json::{JSON, JSONBuilder, JSONSink};
|
pub use json::{JSONBuilder, JSONSink, JSON};
|
||||||
pub use standard::{Standard, StandardBuilder, StandardSink};
|
pub use standard::{Standard, StandardBuilder, StandardSink};
|
||||||
pub use stats::Stats;
|
pub use stats::Stats;
|
||||||
pub use summary::{Summary, SummaryBuilder, SummaryKind, SummarySink};
|
pub use summary::{Summary, SummaryBuilder, SummaryKind, SummarySink};
|
||||||
|
@ -8,16 +8,15 @@ use std::time::Instant;
|
|||||||
use bstr::ByteSlice;
|
use bstr::ByteSlice;
|
||||||
use grep_matcher::{Match, Matcher};
|
use grep_matcher::{Match, Matcher};
|
||||||
use grep_searcher::{
|
use grep_searcher::{
|
||||||
LineStep, Searcher,
|
LineStep, Searcher, Sink, SinkContext, SinkContextKind, SinkError,
|
||||||
Sink, SinkError,
|
SinkFinish, SinkMatch,
|
||||||
SinkContext, SinkContextKind, SinkFinish, SinkMatch,
|
|
||||||
};
|
};
|
||||||
use termcolor::{ColorSpec, NoColor, WriteColor};
|
use termcolor::{ColorSpec, NoColor, WriteColor};
|
||||||
|
|
||||||
use color::ColorSpecs;
|
use color::ColorSpecs;
|
||||||
use counter::CounterWriter;
|
use counter::CounterWriter;
|
||||||
use stats::Stats;
|
use stats::Stats;
|
||||||
use util::{PrinterPath, Replacer, Sunk, trim_ascii_prefix};
|
use util::{trim_ascii_prefix, PrinterPath, Replacer, Sunk};
|
||||||
|
|
||||||
/// The configuration for the standard printer.
|
/// The configuration for the standard printer.
|
||||||
///
|
///
|
||||||
@ -151,10 +150,7 @@ impl StandardBuilder {
|
|||||||
/// This completely overrides any previous color specifications. This does
|
/// This completely overrides any previous color specifications. This does
|
||||||
/// not add to any previously provided color specifications on this
|
/// not add to any previously provided color specifications on this
|
||||||
/// builder.
|
/// builder.
|
||||||
pub fn color_specs(
|
pub fn color_specs(&mut self, specs: ColorSpecs) -> &mut StandardBuilder {
|
||||||
&mut self,
|
|
||||||
specs: ColorSpecs,
|
|
||||||
) -> &mut StandardBuilder {
|
|
||||||
self.config.colors = specs;
|
self.config.colors = specs;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -409,10 +405,7 @@ impl StandardBuilder {
|
|||||||
/// A typical use for this option is to permit cygwin users on Windows to
|
/// A typical use for this option is to permit cygwin users on Windows to
|
||||||
/// set the path separator to `/` instead of using the system default of
|
/// set the path separator to `/` instead of using the system default of
|
||||||
/// `\`.
|
/// `\`.
|
||||||
pub fn separator_path(
|
pub fn separator_path(&mut self, sep: Option<u8>) -> &mut StandardBuilder {
|
||||||
&mut self,
|
|
||||||
sep: Option<u8>,
|
|
||||||
) -> &mut StandardBuilder {
|
|
||||||
self.config.separator_path = sep;
|
self.config.separator_path = sep;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -487,12 +480,7 @@ impl<W: WriteColor> Standard<W> {
|
|||||||
&'s mut self,
|
&'s mut self,
|
||||||
matcher: M,
|
matcher: M,
|
||||||
) -> StandardSink<'static, 's, M, W> {
|
) -> StandardSink<'static, 's, M, W> {
|
||||||
let stats =
|
let stats = if self.config.stats { Some(Stats::new()) } else { None };
|
||||||
if self.config.stats {
|
|
||||||
Some(Stats::new())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
let needs_match_granularity = self.needs_match_granularity();
|
let needs_match_granularity = self.needs_match_granularity();
|
||||||
StandardSink {
|
StandardSink {
|
||||||
matcher: matcher,
|
matcher: matcher,
|
||||||
@ -517,20 +505,18 @@ impl<W: WriteColor> Standard<W> {
|
|||||||
matcher: M,
|
matcher: M,
|
||||||
path: &'p P,
|
path: &'p P,
|
||||||
) -> StandardSink<'p, 's, M, W>
|
) -> StandardSink<'p, 's, M, W>
|
||||||
where M: Matcher,
|
where
|
||||||
P: ?Sized + AsRef<Path>,
|
M: Matcher,
|
||||||
|
P: ?Sized + AsRef<Path>,
|
||||||
{
|
{
|
||||||
if !self.config.path {
|
if !self.config.path {
|
||||||
return self.sink(matcher);
|
return self.sink(matcher);
|
||||||
}
|
}
|
||||||
let stats =
|
let stats = if self.config.stats { Some(Stats::new()) } else { None };
|
||||||
if self.config.stats {
|
|
||||||
Some(Stats::new())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
let ppath = PrinterPath::with_separator(
|
let ppath = PrinterPath::with_separator(
|
||||||
path.as_ref(), self.config.separator_path);
|
path.as_ref(),
|
||||||
|
self.config.separator_path,
|
||||||
|
);
|
||||||
let needs_match_granularity = self.needs_match_granularity();
|
let needs_match_granularity = self.needs_match_granularity();
|
||||||
StandardSink {
|
StandardSink {
|
||||||
matcher: matcher,
|
matcher: matcher,
|
||||||
@ -689,10 +675,12 @@ impl<'p, 's, M: Matcher, W: WriteColor> StandardSink<'p, 's, M, W> {
|
|||||||
// one search to find the matches (well, for replacements, we do one
|
// one search to find the matches (well, for replacements, we do one
|
||||||
// additional search to perform the actual replacement).
|
// additional search to perform the actual replacement).
|
||||||
let matches = &mut self.standard.matches;
|
let matches = &mut self.standard.matches;
|
||||||
self.matcher.find_iter(bytes, |m| {
|
self.matcher
|
||||||
matches.push(m);
|
.find_iter(bytes, |m| {
|
||||||
true
|
matches.push(m);
|
||||||
}).map_err(io::Error::error_message)?;
|
true
|
||||||
|
})
|
||||||
|
.map_err(io::Error::error_message)?;
|
||||||
// Don't report empty matches appearing at the end of the bytes.
|
// Don't report empty matches appearing at the end of the bytes.
|
||||||
if !matches.is_empty()
|
if !matches.is_empty()
|
||||||
&& matches.last().unwrap().is_empty()
|
&& matches.last().unwrap().is_empty()
|
||||||
@ -714,11 +702,7 @@ impl<'p, 's, M: Matcher, W: WriteColor> StandardSink<'p, 's, M, W> {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|r| &*r)
|
.map(|r| &*r)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
self.replacer.replace_all(
|
self.replacer.replace_all(&self.matcher, bytes, replacement)?;
|
||||||
&self.matcher,
|
|
||||||
bytes,
|
|
||||||
replacement,
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -811,10 +795,7 @@ impl<'p, 's, M: Matcher, W: WriteColor> Sink for StandardSink<'p, 's, M, W> {
|
|||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn begin(
|
fn begin(&mut self, _searcher: &Searcher) -> Result<bool, io::Error> {
|
||||||
&mut self,
|
|
||||||
_searcher: &Searcher,
|
|
||||||
) -> Result<bool, io::Error> {
|
|
||||||
self.standard.wtr.borrow_mut().reset_count();
|
self.standard.wtr.borrow_mut().reset_count();
|
||||||
self.start_time = Instant::now();
|
self.start_time = Instant::now();
|
||||||
self.match_count = 0;
|
self.match_count = 0;
|
||||||
@ -887,10 +868,7 @@ impl<'a, M: Matcher, W: WriteColor> StandardImpl<'a, M, W> {
|
|||||||
&sink.standard.matches,
|
&sink.standard.matches,
|
||||||
sink.replacer.replacement(),
|
sink.replacer.replacement(),
|
||||||
);
|
);
|
||||||
StandardImpl {
|
StandardImpl { sunk: sunk, ..StandardImpl::new(searcher, sink) }
|
||||||
sunk: sunk,
|
|
||||||
..StandardImpl::new(searcher, sink)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Bundle self with a searcher and return the core implementation of Sink
|
/// Bundle self with a searcher and return the core implementation of Sink
|
||||||
@ -905,10 +883,7 @@ impl<'a, M: Matcher, W: WriteColor> StandardImpl<'a, M, W> {
|
|||||||
&sink.standard.matches,
|
&sink.standard.matches,
|
||||||
sink.replacer.replacement(),
|
sink.replacer.replacement(),
|
||||||
);
|
);
|
||||||
StandardImpl {
|
StandardImpl { sunk: sunk, ..StandardImpl::new(searcher, sink) }
|
||||||
sunk: sunk,
|
|
||||||
..StandardImpl::new(searcher, sink)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sink(&self) -> io::Result<()> {
|
fn sink(&self) -> io::Result<()> {
|
||||||
@ -1084,10 +1059,7 @@ impl<'a, M: Matcher, W: WriteColor> StandardImpl<'a, M, W> {
|
|||||||
line = line.with_start(upto);
|
line = line.with_start(upto);
|
||||||
if self.exceeds_max_columns(&bytes[this_line]) {
|
if self.exceeds_max_columns(&bytes[this_line]) {
|
||||||
self.write_exceeded_line(
|
self.write_exceeded_line(
|
||||||
bytes,
|
bytes, this_line, matches, &mut midx,
|
||||||
this_line,
|
|
||||||
matches,
|
|
||||||
&mut midx,
|
|
||||||
)?;
|
)?;
|
||||||
} else {
|
} else {
|
||||||
self.write_spec(spec, &bytes[this_line])?;
|
self.write_spec(spec, &bytes[this_line])?;
|
||||||
@ -1178,14 +1150,14 @@ impl<'a, M: Matcher, W: WriteColor> StandardImpl<'a, M, W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn write_line(
|
fn write_line(&self, line: &[u8]) -> io::Result<()> {
|
||||||
&self,
|
|
||||||
line: &[u8],
|
|
||||||
) -> io::Result<()> {
|
|
||||||
if self.exceeds_max_columns(line) {
|
if self.exceeds_max_columns(line) {
|
||||||
let range = Match::new(0, line.len());
|
let range = Match::new(0, line.len());
|
||||||
self.write_exceeded_line(
|
self.write_exceeded_line(
|
||||||
line, range, self.sunk.matches(), &mut 0,
|
line,
|
||||||
|
range,
|
||||||
|
self.sunk.matches(),
|
||||||
|
&mut 0,
|
||||||
)?;
|
)?;
|
||||||
} else {
|
} else {
|
||||||
self.write_trim(line)?;
|
self.write_trim(line)?;
|
||||||
@ -1279,7 +1251,8 @@ impl<'a, M: Matcher, W: WriteColor> StandardImpl<'a, M, W> {
|
|||||||
.map(|(_, end, _)| end)
|
.map(|(_, end, _)| end)
|
||||||
.take(self.config().max_columns.unwrap_or(0) as usize)
|
.take(self.config().max_columns.unwrap_or(0) as usize)
|
||||||
.last()
|
.last()
|
||||||
.unwrap_or(0) + line.start();
|
.unwrap_or(0)
|
||||||
|
+ line.start();
|
||||||
line = line.with_end(end);
|
line = line.with_end(end);
|
||||||
self.write_colored_matches(bytes, line, matches, match_index)?;
|
self.write_colored_matches(bytes, line, matches, match_index)?;
|
||||||
|
|
||||||
@ -1292,16 +1265,12 @@ impl<'a, M: Matcher, W: WriteColor> StandardImpl<'a, M, W> {
|
|||||||
m.start() >= line.end() && m.start() < original.end()
|
m.start() >= line.end() && m.start() < original.end()
|
||||||
})
|
})
|
||||||
.count();
|
.count();
|
||||||
let tense =
|
let tense = if remaining == 1 { "match" } else { "matches" };
|
||||||
if remaining == 1 {
|
|
||||||
"match"
|
|
||||||
} else {
|
|
||||||
"matches"
|
|
||||||
};
|
|
||||||
write!(
|
write!(
|
||||||
self.wtr().borrow_mut(),
|
self.wtr().borrow_mut(),
|
||||||
" [... {} more {}]",
|
" [... {} more {}]",
|
||||||
remaining, tense,
|
remaining,
|
||||||
|
tense,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
self.write_line_term()?;
|
self.write_line_term()?;
|
||||||
@ -1396,7 +1365,8 @@ impl<'a, M: Matcher, W: WriteColor> StandardImpl<'a, M, W> {
|
|||||||
}
|
}
|
||||||
let remainder = format!(
|
let remainder = format!(
|
||||||
"after match (found {:?} byte around offset {})\n",
|
"after match (found {:?} byte around offset {})\n",
|
||||||
[byte].as_bstr(), offset,
|
[byte].as_bstr(),
|
||||||
|
offset,
|
||||||
);
|
);
|
||||||
self.write(remainder.as_bytes())?;
|
self.write(remainder.as_bytes())?;
|
||||||
} else if let Some(byte) = bin.convert_byte() {
|
} else if let Some(byte) = bin.convert_byte() {
|
||||||
@ -1407,7 +1377,8 @@ impl<'a, M: Matcher, W: WriteColor> StandardImpl<'a, M, W> {
|
|||||||
}
|
}
|
||||||
let remainder = format!(
|
let remainder = format!(
|
||||||
"matches (found {:?} byte around offset {})\n",
|
"matches (found {:?} byte around offset {})\n",
|
||||||
[byte].as_bstr(), offset,
|
[byte].as_bstr(),
|
||||||
|
offset,
|
||||||
);
|
);
|
||||||
self.write(remainder.as_bytes())?;
|
self.write(remainder.as_bytes())?;
|
||||||
}
|
}
|
||||||
@ -1600,17 +1571,14 @@ but Doctor Watson has to have it taken out for him and dusted,\r
|
|||||||
and exhibited clearly, with a label attached.\
|
and exhibited clearly, with a label attached.\
|
||||||
";
|
";
|
||||||
|
|
||||||
fn printer_contents(
|
fn printer_contents(printer: &mut Standard<NoColor<Vec<u8>>>) -> String {
|
||||||
printer: &mut Standard<NoColor<Vec<u8>>>,
|
|
||||||
) -> String {
|
|
||||||
String::from_utf8(printer.get_mut().get_ref().to_owned()).unwrap()
|
String::from_utf8(printer.get_mut().get_ref().to_owned()).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn reports_match() {
|
fn reports_match() {
|
||||||
let matcher = RegexMatcher::new("Sherlock").unwrap();
|
let matcher = RegexMatcher::new("Sherlock").unwrap();
|
||||||
let mut printer = StandardBuilder::new()
|
let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
|
||||||
.build(NoColor::new(vec![]));
|
|
||||||
let mut sink = printer.sink(&matcher);
|
let mut sink = printer.sink(&matcher);
|
||||||
SearcherBuilder::new()
|
SearcherBuilder::new()
|
||||||
.line_number(false)
|
.line_number(false)
|
||||||
@ -1620,8 +1588,7 @@ and exhibited clearly, with a label attached.\
|
|||||||
assert!(sink.has_match());
|
assert!(sink.has_match());
|
||||||
|
|
||||||
let matcher = RegexMatcher::new("zzzzz").unwrap();
|
let matcher = RegexMatcher::new("zzzzz").unwrap();
|
||||||
let mut printer = StandardBuilder::new()
|
let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
|
||||||
.build(NoColor::new(vec![]));
|
|
||||||
let mut sink = printer.sink(&matcher);
|
let mut sink = printer.sink(&matcher);
|
||||||
SearcherBuilder::new()
|
SearcherBuilder::new()
|
||||||
.line_number(false)
|
.line_number(false)
|
||||||
@ -1636,8 +1603,7 @@ and exhibited clearly, with a label attached.\
|
|||||||
use grep_searcher::BinaryDetection;
|
use grep_searcher::BinaryDetection;
|
||||||
|
|
||||||
let matcher = RegexMatcher::new("Sherlock").unwrap();
|
let matcher = RegexMatcher::new("Sherlock").unwrap();
|
||||||
let mut printer = StandardBuilder::new()
|
let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
|
||||||
.build(NoColor::new(vec![]));
|
|
||||||
let mut sink = printer.sink(&matcher);
|
let mut sink = printer.sink(&matcher);
|
||||||
SearcherBuilder::new()
|
SearcherBuilder::new()
|
||||||
.line_number(false)
|
.line_number(false)
|
||||||
@ -1647,8 +1613,7 @@ and exhibited clearly, with a label attached.\
|
|||||||
assert!(sink.binary_byte_offset().is_none());
|
assert!(sink.binary_byte_offset().is_none());
|
||||||
|
|
||||||
let matcher = RegexMatcher::new(".+").unwrap();
|
let matcher = RegexMatcher::new(".+").unwrap();
|
||||||
let mut printer = StandardBuilder::new()
|
let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
|
||||||
.build(NoColor::new(vec![]));
|
|
||||||
let mut sink = printer.sink(&matcher);
|
let mut sink = printer.sink(&matcher);
|
||||||
SearcherBuilder::new()
|
SearcherBuilder::new()
|
||||||
.line_number(false)
|
.line_number(false)
|
||||||
@ -1664,9 +1629,8 @@ and exhibited clearly, with a label attached.\
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
let matcher = RegexMatcher::new("Sherlock|opposed").unwrap();
|
let matcher = RegexMatcher::new("Sherlock|opposed").unwrap();
|
||||||
let mut printer = StandardBuilder::new()
|
let mut printer =
|
||||||
.stats(true)
|
StandardBuilder::new().stats(true).build(NoColor::new(vec![]));
|
||||||
.build(NoColor::new(vec![]));
|
|
||||||
let stats = {
|
let stats = {
|
||||||
let mut sink = printer.sink(&matcher);
|
let mut sink = printer.sink(&matcher);
|
||||||
SearcherBuilder::new()
|
SearcherBuilder::new()
|
||||||
@ -1685,7 +1649,6 @@ and exhibited clearly, with a label attached.\
|
|||||||
assert_eq!(stats.bytes_printed(), buf.len() as u64);
|
assert_eq!(stats.bytes_printed(), buf.len() as u64);
|
||||||
assert_eq!(stats.matched_lines(), 2);
|
assert_eq!(stats.matched_lines(), 2);
|
||||||
assert_eq!(stats.matches(), 3);
|
assert_eq!(stats.matches(), 3);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1693,9 +1656,8 @@ and exhibited clearly, with a label attached.\
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
let matcher = RegexMatcher::new("Sherlock|opposed").unwrap();
|
let matcher = RegexMatcher::new("Sherlock|opposed").unwrap();
|
||||||
let mut printer = StandardBuilder::new()
|
let mut printer =
|
||||||
.stats(true)
|
StandardBuilder::new().stats(true).build(NoColor::new(vec![]));
|
||||||
.build(NoColor::new(vec![]));
|
|
||||||
let stats = {
|
let stats = {
|
||||||
let mut sink = printer.sink(&matcher);
|
let mut sink = printer.sink(&matcher);
|
||||||
SearcherBuilder::new()
|
SearcherBuilder::new()
|
||||||
@ -1860,9 +1822,8 @@ and exhibited clearly, with a label attached.
|
|||||||
#[test]
|
#[test]
|
||||||
fn path() {
|
fn path() {
|
||||||
let matcher = RegexMatcher::new("Watson").unwrap();
|
let matcher = RegexMatcher::new("Watson").unwrap();
|
||||||
let mut printer = StandardBuilder::new()
|
let mut printer =
|
||||||
.path(false)
|
StandardBuilder::new().path(false).build(NoColor::new(vec![]));
|
||||||
.build(NoColor::new(vec![]));
|
|
||||||
SearcherBuilder::new()
|
SearcherBuilder::new()
|
||||||
.line_number(true)
|
.line_number(true)
|
||||||
.build()
|
.build()
|
||||||
@ -1963,9 +1924,8 @@ books/sherlockZbut Doctor Watson has to have it taken out for him and dusted,
|
|||||||
#[test]
|
#[test]
|
||||||
fn heading() {
|
fn heading() {
|
||||||
let matcher = RegexMatcher::new("Watson").unwrap();
|
let matcher = RegexMatcher::new("Watson").unwrap();
|
||||||
let mut printer = StandardBuilder::new()
|
let mut printer =
|
||||||
.heading(true)
|
StandardBuilder::new().heading(true).build(NoColor::new(vec![]));
|
||||||
.build(NoColor::new(vec![]));
|
|
||||||
SearcherBuilder::new()
|
SearcherBuilder::new()
|
||||||
.line_number(false)
|
.line_number(false)
|
||||||
.build()
|
.build()
|
||||||
@ -1988,9 +1948,8 @@ but Doctor Watson has to have it taken out for him and dusted,
|
|||||||
#[test]
|
#[test]
|
||||||
fn no_heading() {
|
fn no_heading() {
|
||||||
let matcher = RegexMatcher::new("Watson").unwrap();
|
let matcher = RegexMatcher::new("Watson").unwrap();
|
||||||
let mut printer = StandardBuilder::new()
|
let mut printer =
|
||||||
.heading(false)
|
StandardBuilder::new().heading(false).build(NoColor::new(vec![]));
|
||||||
.build(NoColor::new(vec![]));
|
|
||||||
SearcherBuilder::new()
|
SearcherBuilder::new()
|
||||||
.line_number(false)
|
.line_number(false)
|
||||||
.build()
|
.build()
|
||||||
@ -2012,9 +1971,8 @@ sherlock:but Doctor Watson has to have it taken out for him and dusted,
|
|||||||
#[test]
|
#[test]
|
||||||
fn no_heading_multiple() {
|
fn no_heading_multiple() {
|
||||||
let matcher = RegexMatcher::new("Watson").unwrap();
|
let matcher = RegexMatcher::new("Watson").unwrap();
|
||||||
let mut printer = StandardBuilder::new()
|
let mut printer =
|
||||||
.heading(false)
|
StandardBuilder::new().heading(false).build(NoColor::new(vec![]));
|
||||||
.build(NoColor::new(vec![]));
|
|
||||||
SearcherBuilder::new()
|
SearcherBuilder::new()
|
||||||
.line_number(false)
|
.line_number(false)
|
||||||
.build()
|
.build()
|
||||||
@ -2049,9 +2007,8 @@ sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
|
|||||||
#[test]
|
#[test]
|
||||||
fn heading_multiple() {
|
fn heading_multiple() {
|
||||||
let matcher = RegexMatcher::new("Watson").unwrap();
|
let matcher = RegexMatcher::new("Watson").unwrap();
|
||||||
let mut printer = StandardBuilder::new()
|
let mut printer =
|
||||||
.heading(true)
|
StandardBuilder::new().heading(true).build(NoColor::new(vec![]));
|
||||||
.build(NoColor::new(vec![]));
|
|
||||||
SearcherBuilder::new()
|
SearcherBuilder::new()
|
||||||
.line_number(false)
|
.line_number(false)
|
||||||
.build()
|
.build()
|
||||||
@ -2161,8 +2118,7 @@ Watson
|
|||||||
#[test]
|
#[test]
|
||||||
fn line_number() {
|
fn line_number() {
|
||||||
let matcher = RegexMatcher::new("Watson").unwrap();
|
let matcher = RegexMatcher::new("Watson").unwrap();
|
||||||
let mut printer = StandardBuilder::new()
|
let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
|
||||||
.build(NoColor::new(vec![]));
|
|
||||||
SearcherBuilder::new()
|
SearcherBuilder::new()
|
||||||
.line_number(true)
|
.line_number(true)
|
||||||
.build()
|
.build()
|
||||||
@ -2184,8 +2140,7 @@ Watson
|
|||||||
#[test]
|
#[test]
|
||||||
fn line_number_multi_line() {
|
fn line_number_multi_line() {
|
||||||
let matcher = RegexMatcher::new("(?s)Watson.+Watson").unwrap();
|
let matcher = RegexMatcher::new("(?s)Watson.+Watson").unwrap();
|
||||||
let mut printer = StandardBuilder::new()
|
let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
|
||||||
.build(NoColor::new(vec![]));
|
|
||||||
SearcherBuilder::new()
|
SearcherBuilder::new()
|
||||||
.line_number(true)
|
.line_number(true)
|
||||||
.multi_line(true)
|
.multi_line(true)
|
||||||
@ -2211,9 +2166,8 @@ Watson
|
|||||||
#[test]
|
#[test]
|
||||||
fn column_number() {
|
fn column_number() {
|
||||||
let matcher = RegexMatcher::new("Watson").unwrap();
|
let matcher = RegexMatcher::new("Watson").unwrap();
|
||||||
let mut printer = StandardBuilder::new()
|
let mut printer =
|
||||||
.column(true)
|
StandardBuilder::new().column(true).build(NoColor::new(vec![]));
|
||||||
.build(NoColor::new(vec![]));
|
|
||||||
SearcherBuilder::new()
|
SearcherBuilder::new()
|
||||||
.line_number(false)
|
.line_number(false)
|
||||||
.build()
|
.build()
|
||||||
@ -2235,9 +2189,8 @@ Watson
|
|||||||
#[test]
|
#[test]
|
||||||
fn column_number_multi_line() {
|
fn column_number_multi_line() {
|
||||||
let matcher = RegexMatcher::new("(?s)Watson.+Watson").unwrap();
|
let matcher = RegexMatcher::new("(?s)Watson.+Watson").unwrap();
|
||||||
let mut printer = StandardBuilder::new()
|
let mut printer =
|
||||||
.column(true)
|
StandardBuilder::new().column(true).build(NoColor::new(vec![]));
|
||||||
.build(NoColor::new(vec![]));
|
|
||||||
SearcherBuilder::new()
|
SearcherBuilder::new()
|
||||||
.line_number(false)
|
.line_number(false)
|
||||||
.multi_line(true)
|
.multi_line(true)
|
||||||
@ -2440,9 +2393,8 @@ and exhibited clearly, with a label attached.
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn max_columns_with_count_preview_two_matches() {
|
fn max_columns_with_count_preview_two_matches() {
|
||||||
let matcher = RegexMatcher::new(
|
let matcher =
|
||||||
"exhibited|dusted|has to have it",
|
RegexMatcher::new("exhibited|dusted|has to have it").unwrap();
|
||||||
).unwrap();
|
|
||||||
let mut printer = StandardBuilder::new()
|
let mut printer = StandardBuilder::new()
|
||||||
.stats(true)
|
.stats(true)
|
||||||
.max_columns(Some(46))
|
.max_columns(Some(46))
|
||||||
@ -2493,9 +2445,9 @@ but Doctor Watson has to have it taken out for him and dusted,
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn max_columns_multi_line_preview() {
|
fn max_columns_multi_line_preview() {
|
||||||
let matcher = RegexMatcher::new(
|
let matcher =
|
||||||
"(?s)clew|cigar ash.+have it|exhibited",
|
RegexMatcher::new("(?s)clew|cigar ash.+have it|exhibited")
|
||||||
).unwrap();
|
.unwrap();
|
||||||
let mut printer = StandardBuilder::new()
|
let mut printer = StandardBuilder::new()
|
||||||
.stats(true)
|
.stats(true)
|
||||||
.max_columns(Some(46))
|
.max_columns(Some(46))
|
||||||
@ -2673,9 +2625,8 @@ For the Doctor Watsons of this world, as opposed to the Sherlock
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn max_matches_multi_line2() {
|
fn max_matches_multi_line2() {
|
||||||
let matcher = RegexMatcher::new(
|
let matcher =
|
||||||
r"(?s)Watson.+?(Holmeses|clearly)"
|
RegexMatcher::new(r"(?s)Watson.+?(Holmeses|clearly)").unwrap();
|
||||||
).unwrap();
|
|
||||||
let mut printer = StandardBuilder::new()
|
let mut printer = StandardBuilder::new()
|
||||||
.max_matches(Some(1))
|
.max_matches(Some(1))
|
||||||
.build(NoColor::new(vec![]));
|
.build(NoColor::new(vec![]));
|
||||||
@ -2726,9 +2677,8 @@ Holmeses, success in the province of detective work must always
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn only_matching_multi_line1() {
|
fn only_matching_multi_line1() {
|
||||||
let matcher = RegexMatcher::new(
|
let matcher =
|
||||||
r"(?s:.{0})(Doctor Watsons|Sherlock)"
|
RegexMatcher::new(r"(?s:.{0})(Doctor Watsons|Sherlock)").unwrap();
|
||||||
).unwrap();
|
|
||||||
let mut printer = StandardBuilder::new()
|
let mut printer = StandardBuilder::new()
|
||||||
.only_matching(true)
|
.only_matching(true)
|
||||||
.column(true)
|
.column(true)
|
||||||
@ -2755,9 +2705,8 @@ Holmeses, success in the province of detective work must always
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn only_matching_multi_line2() {
|
fn only_matching_multi_line2() {
|
||||||
let matcher = RegexMatcher::new(
|
let matcher =
|
||||||
r"(?s)Watson.+?(Holmeses|clearly)"
|
RegexMatcher::new(r"(?s)Watson.+?(Holmeses|clearly)").unwrap();
|
||||||
).unwrap();
|
|
||||||
let mut printer = StandardBuilder::new()
|
let mut printer = StandardBuilder::new()
|
||||||
.only_matching(true)
|
.only_matching(true)
|
||||||
.column(true)
|
.column(true)
|
||||||
@ -2844,9 +2793,8 @@ Holmeses, success in the province of detective work must always
|
|||||||
// can match across multiple lines without actually doing so. This is
|
// can match across multiple lines without actually doing so. This is
|
||||||
// so we can test multi-line handling in the case of a match on only
|
// so we can test multi-line handling in the case of a match on only
|
||||||
// one line.
|
// one line.
|
||||||
let matcher = RegexMatcher::new(
|
let matcher =
|
||||||
r"(?s:.{0})(Doctor Watsons|Sherlock)"
|
RegexMatcher::new(r"(?s:.{0})(Doctor Watsons|Sherlock)").unwrap();
|
||||||
).unwrap();
|
|
||||||
let mut printer = StandardBuilder::new()
|
let mut printer = StandardBuilder::new()
|
||||||
.only_matching(true)
|
.only_matching(true)
|
||||||
.max_columns(Some(10))
|
.max_columns(Some(10))
|
||||||
@ -2878,9 +2826,8 @@ Holmeses, success in the province of detective work must always
|
|||||||
// can match across multiple lines without actually doing so. This is
|
// can match across multiple lines without actually doing so. This is
|
||||||
// so we can test multi-line handling in the case of a match on only
|
// so we can test multi-line handling in the case of a match on only
|
||||||
// one line.
|
// one line.
|
||||||
let matcher = RegexMatcher::new(
|
let matcher =
|
||||||
r"(?s:.{0})(Doctor Watsons|Sherlock)"
|
RegexMatcher::new(r"(?s:.{0})(Doctor Watsons|Sherlock)").unwrap();
|
||||||
).unwrap();
|
|
||||||
let mut printer = StandardBuilder::new()
|
let mut printer = StandardBuilder::new()
|
||||||
.only_matching(true)
|
.only_matching(true)
|
||||||
.max_columns(Some(10))
|
.max_columns(Some(10))
|
||||||
@ -2909,9 +2856,8 @@ Holmeses, success in the province of detective work must always
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn only_matching_max_columns_multi_line2() {
|
fn only_matching_max_columns_multi_line2() {
|
||||||
let matcher = RegexMatcher::new(
|
let matcher =
|
||||||
r"(?s)Watson.+?(Holmeses|clearly)"
|
RegexMatcher::new(r"(?s)Watson.+?(Holmeses|clearly)").unwrap();
|
||||||
).unwrap();
|
|
||||||
let mut printer = StandardBuilder::new()
|
let mut printer = StandardBuilder::new()
|
||||||
.only_matching(true)
|
.only_matching(true)
|
||||||
.max_columns(Some(50))
|
.max_columns(Some(50))
|
||||||
@ -2940,9 +2886,8 @@ Holmeses, success in the province of detective work must always
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn only_matching_max_columns_preview_multi_line2() {
|
fn only_matching_max_columns_preview_multi_line2() {
|
||||||
let matcher = RegexMatcher::new(
|
let matcher =
|
||||||
r"(?s)Watson.+?(Holmeses|clearly)"
|
RegexMatcher::new(r"(?s)Watson.+?(Holmeses|clearly)").unwrap();
|
||||||
).unwrap();
|
|
||||||
let mut printer = StandardBuilder::new()
|
let mut printer = StandardBuilder::new()
|
||||||
.only_matching(true)
|
.only_matching(true)
|
||||||
.max_columns(Some(50))
|
.max_columns(Some(50))
|
||||||
@ -2998,9 +2943,8 @@ Holmeses, success in the province of detective work must always
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn per_match_multi_line1() {
|
fn per_match_multi_line1() {
|
||||||
let matcher = RegexMatcher::new(
|
let matcher =
|
||||||
r"(?s:.{0})(Doctor Watsons|Sherlock)"
|
RegexMatcher::new(r"(?s:.{0})(Doctor Watsons|Sherlock)").unwrap();
|
||||||
).unwrap();
|
|
||||||
let mut printer = StandardBuilder::new()
|
let mut printer = StandardBuilder::new()
|
||||||
.per_match(true)
|
.per_match(true)
|
||||||
.column(true)
|
.column(true)
|
||||||
@ -3027,9 +2971,8 @@ Holmeses, success in the province of detective work must always
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn per_match_multi_line2() {
|
fn per_match_multi_line2() {
|
||||||
let matcher = RegexMatcher::new(
|
let matcher =
|
||||||
r"(?s)Watson.+?(Holmeses|clearly)",
|
RegexMatcher::new(r"(?s)Watson.+?(Holmeses|clearly)").unwrap();
|
||||||
).unwrap();
|
|
||||||
let mut printer = StandardBuilder::new()
|
let mut printer = StandardBuilder::new()
|
||||||
.per_match(true)
|
.per_match(true)
|
||||||
.column(true)
|
.column(true)
|
||||||
@ -3057,9 +3000,8 @@ Holmeses, success in the province of detective work must always
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn per_match_multi_line3() {
|
fn per_match_multi_line3() {
|
||||||
let matcher = RegexMatcher::new(
|
let matcher =
|
||||||
r"(?s)Watson.+?Holmeses|always.+?be",
|
RegexMatcher::new(r"(?s)Watson.+?Holmeses|always.+?be").unwrap();
|
||||||
).unwrap();
|
|
||||||
let mut printer = StandardBuilder::new()
|
let mut printer = StandardBuilder::new()
|
||||||
.per_match(true)
|
.per_match(true)
|
||||||
.column(true)
|
.column(true)
|
||||||
@ -3194,9 +3136,8 @@ Holmeses, success in the province of detective work must always
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn replacement_max_columns_preview2() {
|
fn replacement_max_columns_preview2() {
|
||||||
let matcher = RegexMatcher::new(
|
let matcher =
|
||||||
"exhibited|dusted|has to have it",
|
RegexMatcher::new("exhibited|dusted|has to have it").unwrap();
|
||||||
).unwrap();
|
|
||||||
let mut printer = StandardBuilder::new()
|
let mut printer = StandardBuilder::new()
|
||||||
.max_columns(Some(43))
|
.max_columns(Some(43))
|
||||||
.max_columns_preview(true)
|
.max_columns_preview(true)
|
||||||
@ -3277,8 +3218,7 @@ and xxx clearly, with a label attached.
|
|||||||
#[test]
|
#[test]
|
||||||
fn invert() {
|
fn invert() {
|
||||||
let matcher = RegexMatcher::new(r"Sherlock").unwrap();
|
let matcher = RegexMatcher::new(r"Sherlock").unwrap();
|
||||||
let mut printer = StandardBuilder::new()
|
let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
|
||||||
.build(NoColor::new(vec![]));
|
|
||||||
SearcherBuilder::new()
|
SearcherBuilder::new()
|
||||||
.line_number(true)
|
.line_number(true)
|
||||||
.invert_match(true)
|
.invert_match(true)
|
||||||
@ -3303,8 +3243,7 @@ and xxx clearly, with a label attached.
|
|||||||
#[test]
|
#[test]
|
||||||
fn invert_multi_line() {
|
fn invert_multi_line() {
|
||||||
let matcher = RegexMatcher::new(r"(?s:.{0})Sherlock").unwrap();
|
let matcher = RegexMatcher::new(r"(?s:.{0})Sherlock").unwrap();
|
||||||
let mut printer = StandardBuilder::new()
|
let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
|
||||||
.build(NoColor::new(vec![]));
|
|
||||||
SearcherBuilder::new()
|
SearcherBuilder::new()
|
||||||
.multi_line(true)
|
.multi_line(true)
|
||||||
.line_number(true)
|
.line_number(true)
|
||||||
@ -3330,8 +3269,7 @@ and xxx clearly, with a label attached.
|
|||||||
#[test]
|
#[test]
|
||||||
fn invert_context() {
|
fn invert_context() {
|
||||||
let matcher = RegexMatcher::new(r"Sherlock").unwrap();
|
let matcher = RegexMatcher::new(r"Sherlock").unwrap();
|
||||||
let mut printer = StandardBuilder::new()
|
let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
|
||||||
.build(NoColor::new(vec![]));
|
|
||||||
SearcherBuilder::new()
|
SearcherBuilder::new()
|
||||||
.line_number(true)
|
.line_number(true)
|
||||||
.invert_match(true)
|
.invert_match(true)
|
||||||
@ -3360,8 +3298,7 @@ and xxx clearly, with a label attached.
|
|||||||
#[test]
|
#[test]
|
||||||
fn invert_context_multi_line() {
|
fn invert_context_multi_line() {
|
||||||
let matcher = RegexMatcher::new(r"(?s:.{0})Sherlock").unwrap();
|
let matcher = RegexMatcher::new(r"(?s:.{0})Sherlock").unwrap();
|
||||||
let mut printer = StandardBuilder::new()
|
let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
|
||||||
.build(NoColor::new(vec![]));
|
|
||||||
SearcherBuilder::new()
|
SearcherBuilder::new()
|
||||||
.multi_line(true)
|
.multi_line(true)
|
||||||
.line_number(true)
|
.line_number(true)
|
||||||
|
@ -34,8 +34,8 @@ impl<'a> Add<&'a Stats> for Stats {
|
|||||||
Stats {
|
Stats {
|
||||||
elapsed: NiceDuration(self.elapsed.0 + rhs.elapsed.0),
|
elapsed: NiceDuration(self.elapsed.0 + rhs.elapsed.0),
|
||||||
searches: self.searches + rhs.searches,
|
searches: self.searches + rhs.searches,
|
||||||
searches_with_match:
|
searches_with_match: self.searches_with_match
|
||||||
self.searches_with_match + rhs.searches_with_match,
|
+ rhs.searches_with_match,
|
||||||
bytes_searched: self.bytes_searched + rhs.bytes_searched,
|
bytes_searched: self.bytes_searched + rhs.bytes_searched,
|
||||||
bytes_printed: self.bytes_printed + rhs.bytes_printed,
|
bytes_printed: self.bytes_printed + rhs.bytes_printed,
|
||||||
matched_lines: self.matched_lines + rhs.matched_lines,
|
matched_lines: self.matched_lines + rhs.matched_lines,
|
||||||
|
@ -168,10 +168,7 @@ impl SummaryBuilder {
|
|||||||
///
|
///
|
||||||
/// This is a convenience routine for
|
/// This is a convenience routine for
|
||||||
/// `SummaryBuilder::build(termcolor::NoColor::new(wtr))`.
|
/// `SummaryBuilder::build(termcolor::NoColor::new(wtr))`.
|
||||||
pub fn build_no_color<W: io::Write>(
|
pub fn build_no_color<W: io::Write>(&self, wtr: W) -> Summary<NoColor<W>> {
|
||||||
&self,
|
|
||||||
wtr: W,
|
|
||||||
) -> Summary<NoColor<W>> {
|
|
||||||
self.build(NoColor::new(wtr))
|
self.build(NoColor::new(wtr))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,10 +201,7 @@ impl SummaryBuilder {
|
|||||||
/// builder.
|
/// builder.
|
||||||
///
|
///
|
||||||
/// The default color specifications provide no styling.
|
/// The default color specifications provide no styling.
|
||||||
pub fn color_specs(
|
pub fn color_specs(&mut self, specs: ColorSpecs) -> &mut SummaryBuilder {
|
||||||
&mut self,
|
|
||||||
specs: ColorSpecs,
|
|
||||||
) -> &mut SummaryBuilder {
|
|
||||||
self.config.colors = specs;
|
self.config.colors = specs;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -281,10 +275,7 @@ impl SummaryBuilder {
|
|||||||
/// `CountMatches` modes.
|
/// `CountMatches` modes.
|
||||||
///
|
///
|
||||||
/// By default, this is set to `:`.
|
/// By default, this is set to `:`.
|
||||||
pub fn separator_field(
|
pub fn separator_field(&mut self, sep: Vec<u8>) -> &mut SummaryBuilder {
|
||||||
&mut self,
|
|
||||||
sep: Vec<u8>,
|
|
||||||
) -> &mut SummaryBuilder {
|
|
||||||
self.config.separator_field = Arc::new(sep);
|
self.config.separator_field = Arc::new(sep);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -300,10 +291,7 @@ impl SummaryBuilder {
|
|||||||
/// `\`.
|
/// `\`.
|
||||||
///
|
///
|
||||||
/// This is disabled by default.
|
/// This is disabled by default.
|
||||||
pub fn separator_path(
|
pub fn separator_path(&mut self, sep: Option<u8>) -> &mut SummaryBuilder {
|
||||||
&mut self,
|
|
||||||
sep: Option<u8>,
|
|
||||||
) -> &mut SummaryBuilder {
|
|
||||||
self.config.separator_path = sep;
|
self.config.separator_path = sep;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -382,12 +370,11 @@ impl<W: WriteColor> Summary<W> {
|
|||||||
&'s mut self,
|
&'s mut self,
|
||||||
matcher: M,
|
matcher: M,
|
||||||
) -> SummarySink<'static, 's, M, W> {
|
) -> SummarySink<'static, 's, M, W> {
|
||||||
let stats =
|
let stats = if self.config.stats || self.config.kind.requires_stats() {
|
||||||
if self.config.stats || self.config.kind.requires_stats() {
|
Some(Stats::new())
|
||||||
Some(Stats::new())
|
} else {
|
||||||
} else {
|
None
|
||||||
None
|
};
|
||||||
};
|
|
||||||
SummarySink {
|
SummarySink {
|
||||||
matcher: matcher,
|
matcher: matcher,
|
||||||
summary: self,
|
summary: self,
|
||||||
@ -408,20 +395,22 @@ impl<W: WriteColor> Summary<W> {
|
|||||||
matcher: M,
|
matcher: M,
|
||||||
path: &'p P,
|
path: &'p P,
|
||||||
) -> SummarySink<'p, 's, M, W>
|
) -> SummarySink<'p, 's, M, W>
|
||||||
where M: Matcher,
|
where
|
||||||
P: ?Sized + AsRef<Path>,
|
M: Matcher,
|
||||||
|
P: ?Sized + AsRef<Path>,
|
||||||
{
|
{
|
||||||
if !self.config.path && !self.config.kind.requires_path() {
|
if !self.config.path && !self.config.kind.requires_path() {
|
||||||
return self.sink(matcher);
|
return self.sink(matcher);
|
||||||
}
|
}
|
||||||
let stats =
|
let stats = if self.config.stats || self.config.kind.requires_stats() {
|
||||||
if self.config.stats || self.config.kind.requires_stats() {
|
Some(Stats::new())
|
||||||
Some(Stats::new())
|
} else {
|
||||||
} else {
|
None
|
||||||
None
|
};
|
||||||
};
|
|
||||||
let ppath = PrinterPath::with_separator(
|
let ppath = PrinterPath::with_separator(
|
||||||
path.as_ref(), self.config.separator_path);
|
path.as_ref(),
|
||||||
|
self.config.separator_path,
|
||||||
|
);
|
||||||
SummarySink {
|
SummarySink {
|
||||||
matcher: matcher,
|
matcher: matcher,
|
||||||
summary: self,
|
summary: self,
|
||||||
@ -596,10 +585,12 @@ impl<'p, 's, M: Matcher, W: WriteColor> Sink for SummarySink<'p, 's, M, W> {
|
|||||||
self.match_count += 1;
|
self.match_count += 1;
|
||||||
if let Some(ref mut stats) = self.stats {
|
if let Some(ref mut stats) = self.stats {
|
||||||
let mut match_count = 0;
|
let mut match_count = 0;
|
||||||
self.matcher.find_iter(mat.bytes(), |_| {
|
self.matcher
|
||||||
match_count += 1;
|
.find_iter(mat.bytes(), |_| {
|
||||||
true
|
match_count += 1;
|
||||||
}).map_err(io::Error::error_message)?;
|
true
|
||||||
|
})
|
||||||
|
.map_err(io::Error::error_message)?;
|
||||||
stats.add_matches(match_count);
|
stats.add_matches(match_count);
|
||||||
stats.add_matched_lines(mat.lines().count() as u64);
|
stats.add_matched_lines(mat.lines().count() as u64);
|
||||||
} else if self.summary.config.kind.quit_early() {
|
} else if self.summary.config.kind.quit_early() {
|
||||||
@ -608,10 +599,7 @@ impl<'p, 's, M: Matcher, W: WriteColor> Sink for SummarySink<'p, 's, M, W> {
|
|||||||
Ok(!self.should_quit())
|
Ok(!self.should_quit())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn begin(
|
fn begin(&mut self, _searcher: &Searcher) -> Result<bool, io::Error> {
|
||||||
&mut self,
|
|
||||||
_searcher: &Searcher,
|
|
||||||
) -> Result<bool, io::Error> {
|
|
||||||
if self.path.is_none() && self.summary.config.kind.requires_path() {
|
if self.path.is_none() && self.summary.config.kind.requires_path() {
|
||||||
return Err(io::Error::error_message(format!(
|
return Err(io::Error::error_message(format!(
|
||||||
"output kind {:?} requires a file path",
|
"output kind {:?} requires a file path",
|
||||||
@ -674,8 +662,7 @@ impl<'p, 's, M: Matcher, W: WriteColor> Sink for SummarySink<'p, 's, M, W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let show_count =
|
let show_count =
|
||||||
!self.summary.config.exclude_zero
|
!self.summary.config.exclude_zero || self.match_count > 0;
|
||||||
|| self.match_count > 0;
|
|
||||||
match self.summary.config.kind {
|
match self.summary.config.kind {
|
||||||
SummaryKind::Count => {
|
SummaryKind::Count => {
|
||||||
if show_count {
|
if show_count {
|
||||||
@ -686,7 +673,8 @@ impl<'p, 's, M: Matcher, W: WriteColor> Sink for SummarySink<'p, 's, M, W> {
|
|||||||
}
|
}
|
||||||
SummaryKind::CountMatches => {
|
SummaryKind::CountMatches => {
|
||||||
if show_count {
|
if show_count {
|
||||||
let stats = self.stats
|
let stats = self
|
||||||
|
.stats
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.expect("CountMatches should enable stats tracking");
|
.expect("CountMatches should enable stats tracking");
|
||||||
self.write_path_field()?;
|
self.write_path_field()?;
|
||||||
@ -716,7 +704,7 @@ mod tests {
|
|||||||
use grep_searcher::SearcherBuilder;
|
use grep_searcher::SearcherBuilder;
|
||||||
use termcolor::NoColor;
|
use termcolor::NoColor;
|
||||||
|
|
||||||
use super::{Summary, SummaryKind, SummaryBuilder};
|
use super::{Summary, SummaryBuilder, SummaryKind};
|
||||||
|
|
||||||
const SHERLOCK: &'static [u8] = b"\
|
const SHERLOCK: &'static [u8] = b"\
|
||||||
For the Doctor Watsons of this world, as opposed to the Sherlock
|
For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
@ -727,45 +715,41 @@ but Doctor Watson has to have it taken out for him and dusted,
|
|||||||
and exhibited clearly, with a label attached.
|
and exhibited clearly, with a label attached.
|
||||||
";
|
";
|
||||||
|
|
||||||
fn printer_contents(
|
fn printer_contents(printer: &mut Summary<NoColor<Vec<u8>>>) -> String {
|
||||||
printer: &mut Summary<NoColor<Vec<u8>>>,
|
|
||||||
) -> String {
|
|
||||||
String::from_utf8(printer.get_mut().get_ref().to_owned()).unwrap()
|
String::from_utf8(printer.get_mut().get_ref().to_owned()).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn path_with_match_error() {
|
fn path_with_match_error() {
|
||||||
let matcher = RegexMatcher::new(
|
let matcher = RegexMatcher::new(r"Watson").unwrap();
|
||||||
r"Watson"
|
|
||||||
).unwrap();
|
|
||||||
let mut printer = SummaryBuilder::new()
|
let mut printer = SummaryBuilder::new()
|
||||||
.kind(SummaryKind::PathWithMatch)
|
.kind(SummaryKind::PathWithMatch)
|
||||||
.build_no_color(vec![]);
|
.build_no_color(vec![]);
|
||||||
let res = SearcherBuilder::new()
|
let res = SearcherBuilder::new().build().search_reader(
|
||||||
.build()
|
&matcher,
|
||||||
.search_reader(&matcher, SHERLOCK, printer.sink(&matcher));
|
SHERLOCK,
|
||||||
|
printer.sink(&matcher),
|
||||||
|
);
|
||||||
assert!(res.is_err());
|
assert!(res.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn path_without_match_error() {
|
fn path_without_match_error() {
|
||||||
let matcher = RegexMatcher::new(
|
let matcher = RegexMatcher::new(r"Watson").unwrap();
|
||||||
r"Watson"
|
|
||||||
).unwrap();
|
|
||||||
let mut printer = SummaryBuilder::new()
|
let mut printer = SummaryBuilder::new()
|
||||||
.kind(SummaryKind::PathWithoutMatch)
|
.kind(SummaryKind::PathWithoutMatch)
|
||||||
.build_no_color(vec![]);
|
.build_no_color(vec![]);
|
||||||
let res = SearcherBuilder::new()
|
let res = SearcherBuilder::new().build().search_reader(
|
||||||
.build()
|
&matcher,
|
||||||
.search_reader(&matcher, SHERLOCK, printer.sink(&matcher));
|
SHERLOCK,
|
||||||
|
printer.sink(&matcher),
|
||||||
|
);
|
||||||
assert!(res.is_err());
|
assert!(res.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn count_no_path() {
|
fn count_no_path() {
|
||||||
let matcher = RegexMatcher::new(
|
let matcher = RegexMatcher::new(r"Watson").unwrap();
|
||||||
r"Watson"
|
|
||||||
).unwrap();
|
|
||||||
let mut printer = SummaryBuilder::new()
|
let mut printer = SummaryBuilder::new()
|
||||||
.kind(SummaryKind::Count)
|
.kind(SummaryKind::Count)
|
||||||
.build_no_color(vec![]);
|
.build_no_color(vec![]);
|
||||||
@ -780,9 +764,7 @@ and exhibited clearly, with a label attached.
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn count_no_path_even_with_path() {
|
fn count_no_path_even_with_path() {
|
||||||
let matcher = RegexMatcher::new(
|
let matcher = RegexMatcher::new(r"Watson").unwrap();
|
||||||
r"Watson"
|
|
||||||
).unwrap();
|
|
||||||
let mut printer = SummaryBuilder::new()
|
let mut printer = SummaryBuilder::new()
|
||||||
.kind(SummaryKind::Count)
|
.kind(SummaryKind::Count)
|
||||||
.path(false)
|
.path(false)
|
||||||
@ -802,9 +784,7 @@ and exhibited clearly, with a label attached.
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn count_path() {
|
fn count_path() {
|
||||||
let matcher = RegexMatcher::new(
|
let matcher = RegexMatcher::new(r"Watson").unwrap();
|
||||||
r"Watson"
|
|
||||||
).unwrap();
|
|
||||||
let mut printer = SummaryBuilder::new()
|
let mut printer = SummaryBuilder::new()
|
||||||
.kind(SummaryKind::Count)
|
.kind(SummaryKind::Count)
|
||||||
.build_no_color(vec![]);
|
.build_no_color(vec![]);
|
||||||
@ -823,9 +803,7 @@ and exhibited clearly, with a label attached.
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn count_path_with_zero() {
|
fn count_path_with_zero() {
|
||||||
let matcher = RegexMatcher::new(
|
let matcher = RegexMatcher::new(r"NO MATCH").unwrap();
|
||||||
r"NO MATCH"
|
|
||||||
).unwrap();
|
|
||||||
let mut printer = SummaryBuilder::new()
|
let mut printer = SummaryBuilder::new()
|
||||||
.kind(SummaryKind::Count)
|
.kind(SummaryKind::Count)
|
||||||
.exclude_zero(false)
|
.exclude_zero(false)
|
||||||
@ -845,9 +823,7 @@ and exhibited clearly, with a label attached.
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn count_path_without_zero() {
|
fn count_path_without_zero() {
|
||||||
let matcher = RegexMatcher::new(
|
let matcher = RegexMatcher::new(r"NO MATCH").unwrap();
|
||||||
r"NO MATCH"
|
|
||||||
).unwrap();
|
|
||||||
let mut printer = SummaryBuilder::new()
|
let mut printer = SummaryBuilder::new()
|
||||||
.kind(SummaryKind::Count)
|
.kind(SummaryKind::Count)
|
||||||
.exclude_zero(true)
|
.exclude_zero(true)
|
||||||
@ -867,9 +843,7 @@ and exhibited clearly, with a label attached.
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn count_path_field_separator() {
|
fn count_path_field_separator() {
|
||||||
let matcher = RegexMatcher::new(
|
let matcher = RegexMatcher::new(r"Watson").unwrap();
|
||||||
r"Watson"
|
|
||||||
).unwrap();
|
|
||||||
let mut printer = SummaryBuilder::new()
|
let mut printer = SummaryBuilder::new()
|
||||||
.kind(SummaryKind::Count)
|
.kind(SummaryKind::Count)
|
||||||
.separator_field(b"ZZ".to_vec())
|
.separator_field(b"ZZ".to_vec())
|
||||||
@ -889,9 +863,7 @@ and exhibited clearly, with a label attached.
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn count_path_terminator() {
|
fn count_path_terminator() {
|
||||||
let matcher = RegexMatcher::new(
|
let matcher = RegexMatcher::new(r"Watson").unwrap();
|
||||||
r"Watson"
|
|
||||||
).unwrap();
|
|
||||||
let mut printer = SummaryBuilder::new()
|
let mut printer = SummaryBuilder::new()
|
||||||
.kind(SummaryKind::Count)
|
.kind(SummaryKind::Count)
|
||||||
.path_terminator(Some(b'\x00'))
|
.path_terminator(Some(b'\x00'))
|
||||||
@ -911,9 +883,7 @@ and exhibited clearly, with a label attached.
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn count_path_separator() {
|
fn count_path_separator() {
|
||||||
let matcher = RegexMatcher::new(
|
let matcher = RegexMatcher::new(r"Watson").unwrap();
|
||||||
r"Watson"
|
|
||||||
).unwrap();
|
|
||||||
let mut printer = SummaryBuilder::new()
|
let mut printer = SummaryBuilder::new()
|
||||||
.kind(SummaryKind::Count)
|
.kind(SummaryKind::Count)
|
||||||
.separator_path(Some(b'\\'))
|
.separator_path(Some(b'\\'))
|
||||||
@ -933,9 +903,7 @@ and exhibited clearly, with a label attached.
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn count_max_matches() {
|
fn count_max_matches() {
|
||||||
let matcher = RegexMatcher::new(
|
let matcher = RegexMatcher::new(r"Watson").unwrap();
|
||||||
r"Watson"
|
|
||||||
).unwrap();
|
|
||||||
let mut printer = SummaryBuilder::new()
|
let mut printer = SummaryBuilder::new()
|
||||||
.kind(SummaryKind::Count)
|
.kind(SummaryKind::Count)
|
||||||
.max_matches(Some(1))
|
.max_matches(Some(1))
|
||||||
@ -951,9 +919,7 @@ and exhibited clearly, with a label attached.
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn count_matches() {
|
fn count_matches() {
|
||||||
let matcher = RegexMatcher::new(
|
let matcher = RegexMatcher::new(r"Watson|Sherlock").unwrap();
|
||||||
r"Watson|Sherlock"
|
|
||||||
).unwrap();
|
|
||||||
let mut printer = SummaryBuilder::new()
|
let mut printer = SummaryBuilder::new()
|
||||||
.kind(SummaryKind::CountMatches)
|
.kind(SummaryKind::CountMatches)
|
||||||
.build_no_color(vec![]);
|
.build_no_color(vec![]);
|
||||||
@ -972,9 +938,7 @@ and exhibited clearly, with a label attached.
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn path_with_match_found() {
|
fn path_with_match_found() {
|
||||||
let matcher = RegexMatcher::new(
|
let matcher = RegexMatcher::new(r"Watson").unwrap();
|
||||||
r"Watson"
|
|
||||||
).unwrap();
|
|
||||||
let mut printer = SummaryBuilder::new()
|
let mut printer = SummaryBuilder::new()
|
||||||
.kind(SummaryKind::PathWithMatch)
|
.kind(SummaryKind::PathWithMatch)
|
||||||
.build_no_color(vec![]);
|
.build_no_color(vec![]);
|
||||||
@ -993,9 +957,7 @@ and exhibited clearly, with a label attached.
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn path_with_match_not_found() {
|
fn path_with_match_not_found() {
|
||||||
let matcher = RegexMatcher::new(
|
let matcher = RegexMatcher::new(r"ZZZZZZZZ").unwrap();
|
||||||
r"ZZZZZZZZ"
|
|
||||||
).unwrap();
|
|
||||||
let mut printer = SummaryBuilder::new()
|
let mut printer = SummaryBuilder::new()
|
||||||
.kind(SummaryKind::PathWithMatch)
|
.kind(SummaryKind::PathWithMatch)
|
||||||
.build_no_color(vec![]);
|
.build_no_color(vec![]);
|
||||||
@ -1012,12 +974,9 @@ and exhibited clearly, with a label attached.
|
|||||||
assert_eq_printed!("", got);
|
assert_eq_printed!("", got);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn path_without_match_found() {
|
fn path_without_match_found() {
|
||||||
let matcher = RegexMatcher::new(
|
let matcher = RegexMatcher::new(r"ZZZZZZZZZ").unwrap();
|
||||||
r"ZZZZZZZZZ"
|
|
||||||
).unwrap();
|
|
||||||
let mut printer = SummaryBuilder::new()
|
let mut printer = SummaryBuilder::new()
|
||||||
.kind(SummaryKind::PathWithoutMatch)
|
.kind(SummaryKind::PathWithoutMatch)
|
||||||
.build_no_color(vec![]);
|
.build_no_color(vec![]);
|
||||||
@ -1036,9 +995,7 @@ and exhibited clearly, with a label attached.
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn path_without_match_not_found() {
|
fn path_without_match_not_found() {
|
||||||
let matcher = RegexMatcher::new(
|
let matcher = RegexMatcher::new(r"Watson").unwrap();
|
||||||
r"Watson"
|
|
||||||
).unwrap();
|
|
||||||
let mut printer = SummaryBuilder::new()
|
let mut printer = SummaryBuilder::new()
|
||||||
.kind(SummaryKind::PathWithoutMatch)
|
.kind(SummaryKind::PathWithoutMatch)
|
||||||
.build_no_color(vec![]);
|
.build_no_color(vec![]);
|
||||||
@ -1057,9 +1014,7 @@ and exhibited clearly, with a label attached.
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn quiet() {
|
fn quiet() {
|
||||||
let matcher = RegexMatcher::new(
|
let matcher = RegexMatcher::new(r"Watson|Sherlock").unwrap();
|
||||||
r"Watson|Sherlock"
|
|
||||||
).unwrap();
|
|
||||||
let mut printer = SummaryBuilder::new()
|
let mut printer = SummaryBuilder::new()
|
||||||
.kind(SummaryKind::Quiet)
|
.kind(SummaryKind::Quiet)
|
||||||
.build_no_color(vec![]);
|
.build_no_color(vec![]);
|
||||||
@ -1081,9 +1036,7 @@ and exhibited clearly, with a label attached.
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn quiet_with_stats() {
|
fn quiet_with_stats() {
|
||||||
let matcher = RegexMatcher::new(
|
let matcher = RegexMatcher::new(r"Watson|Sherlock").unwrap();
|
||||||
r"Watson|Sherlock"
|
|
||||||
).unwrap();
|
|
||||||
let mut printer = SummaryBuilder::new()
|
let mut printer = SummaryBuilder::new()
|
||||||
.kind(SummaryKind::Quiet)
|
.kind(SummaryKind::Quiet)
|
||||||
.stats(true)
|
.stats(true)
|
||||||
|
@ -7,8 +7,7 @@ use std::time;
|
|||||||
use bstr::{ByteSlice, ByteVec};
|
use bstr::{ByteSlice, ByteVec};
|
||||||
use grep_matcher::{Captures, LineTerminator, Match, Matcher};
|
use grep_matcher::{Captures, LineTerminator, Match, Matcher};
|
||||||
use grep_searcher::{
|
use grep_searcher::{
|
||||||
LineIter,
|
LineIter, SinkContext, SinkContextKind, SinkError, SinkMatch,
|
||||||
SinkError, SinkContext, SinkContextKind, SinkMatch,
|
|
||||||
};
|
};
|
||||||
#[cfg(feature = "serde1")]
|
#[cfg(feature = "serde1")]
|
||||||
use serde::{Serialize, Serializer};
|
use serde::{Serialize, Serializer};
|
||||||
@ -58,19 +57,13 @@ impl<M: Matcher> Replacer<M> {
|
|||||||
replacement: &[u8],
|
replacement: &[u8],
|
||||||
) -> io::Result<()> {
|
) -> io::Result<()> {
|
||||||
{
|
{
|
||||||
let &mut Space {
|
let &mut Space { ref mut dst, ref mut caps, ref mut matches } =
|
||||||
ref mut dst,
|
self.allocate(matcher)?;
|
||||||
ref mut caps,
|
|
||||||
ref mut matches,
|
|
||||||
} = self.allocate(matcher)?;
|
|
||||||
dst.clear();
|
dst.clear();
|
||||||
matches.clear();
|
matches.clear();
|
||||||
|
|
||||||
matcher.replace_with_captures(
|
matcher
|
||||||
subject,
|
.replace_with_captures(subject, caps, dst, |caps, dst| {
|
||||||
caps,
|
|
||||||
dst,
|
|
||||||
|caps, dst| {
|
|
||||||
let start = dst.len();
|
let start = dst.len();
|
||||||
caps.interpolate(
|
caps.interpolate(
|
||||||
|name| matcher.capture_index(name),
|
|name| matcher.capture_index(name),
|
||||||
@ -81,8 +74,8 @@ impl<M: Matcher> Replacer<M> {
|
|||||||
let end = dst.len();
|
let end = dst.len();
|
||||||
matches.push(Match::new(start, end));
|
matches.push(Match::new(start, end));
|
||||||
true
|
true
|
||||||
},
|
})
|
||||||
).map_err(io::Error::error_message)?;
|
.map_err(io::Error::error_message)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -122,14 +115,10 @@ impl<M: Matcher> Replacer<M> {
|
|||||||
/// matcher fails.
|
/// matcher fails.
|
||||||
fn allocate(&mut self, matcher: &M) -> io::Result<&mut Space<M>> {
|
fn allocate(&mut self, matcher: &M) -> io::Result<&mut Space<M>> {
|
||||||
if self.space.is_none() {
|
if self.space.is_none() {
|
||||||
let caps = matcher
|
let caps =
|
||||||
.new_captures()
|
matcher.new_captures().map_err(io::Error::error_message)?;
|
||||||
.map_err(io::Error::error_message)?;
|
self.space =
|
||||||
self.space = Some(Space {
|
Some(Space { caps: caps, dst: vec![], matches: vec![] });
|
||||||
caps: caps,
|
|
||||||
dst: vec![],
|
|
||||||
matches: vec![],
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
Ok(self.space.as_mut().unwrap())
|
Ok(self.space.as_mut().unwrap())
|
||||||
}
|
}
|
||||||
@ -176,9 +165,8 @@ impl<'a> Sunk<'a> {
|
|||||||
original_matches: &'a [Match],
|
original_matches: &'a [Match],
|
||||||
replacement: Option<(&'a [u8], &'a [Match])>,
|
replacement: Option<(&'a [u8], &'a [Match])>,
|
||||||
) -> Sunk<'a> {
|
) -> Sunk<'a> {
|
||||||
let (bytes, matches) = replacement.unwrap_or_else(|| {
|
let (bytes, matches) =
|
||||||
(sunk.bytes(), original_matches)
|
replacement.unwrap_or_else(|| (sunk.bytes(), original_matches));
|
||||||
});
|
|
||||||
Sunk {
|
Sunk {
|
||||||
bytes: bytes,
|
bytes: bytes,
|
||||||
absolute_byte_offset: sunk.absolute_byte_offset(),
|
absolute_byte_offset: sunk.absolute_byte_offset(),
|
||||||
@ -195,9 +183,8 @@ impl<'a> Sunk<'a> {
|
|||||||
original_matches: &'a [Match],
|
original_matches: &'a [Match],
|
||||||
replacement: Option<(&'a [u8], &'a [Match])>,
|
replacement: Option<(&'a [u8], &'a [Match])>,
|
||||||
) -> Sunk<'a> {
|
) -> Sunk<'a> {
|
||||||
let (bytes, matches) = replacement.unwrap_or_else(|| {
|
let (bytes, matches) =
|
||||||
(sunk.bytes(), original_matches)
|
replacement.unwrap_or_else(|| (sunk.bytes(), original_matches));
|
||||||
});
|
|
||||||
Sunk {
|
Sunk {
|
||||||
bytes: bytes,
|
bytes: bytes,
|
||||||
absolute_byte_offset: sunk.absolute_byte_offset(),
|
absolute_byte_offset: sunk.absolute_byte_offset(),
|
||||||
@ -289,13 +276,17 @@ impl<'a> PrinterPath<'a> {
|
|||||||
/// path separators that are both replaced by `new_sep`. In all other
|
/// path separators that are both replaced by `new_sep`. In all other
|
||||||
/// environments, only `/` is treated as a path separator.
|
/// environments, only `/` is treated as a path separator.
|
||||||
fn replace_separator(&mut self, new_sep: u8) {
|
fn replace_separator(&mut self, new_sep: u8) {
|
||||||
let transformed_path: Vec<u8> = self.0.bytes().map(|b| {
|
let transformed_path: Vec<u8> = self
|
||||||
if b == b'/' || (cfg!(windows) && b == b'\\') {
|
.0
|
||||||
new_sep
|
.bytes()
|
||||||
} else {
|
.map(|b| {
|
||||||
b
|
if b == b'/' || (cfg!(windows) && b == b'\\') {
|
||||||
}
|
new_sep
|
||||||
}).collect();
|
} else {
|
||||||
|
b
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
self.0 = Cow::Owned(transformed_path);
|
self.0 = Cow::Owned(transformed_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use regex_syntax::ast::{self, Ast};
|
|
||||||
use regex_syntax::ast::parse::Parser;
|
use regex_syntax::ast::parse::Parser;
|
||||||
|
use regex_syntax::ast::{self, Ast};
|
||||||
|
|
||||||
/// The results of analyzing AST of a regular expression (e.g., for supporting
|
/// The results of analyzing AST of a regular expression (e.g., for supporting
|
||||||
/// smart case).
|
/// smart case).
|
||||||
|
@ -51,8 +51,8 @@ impl Default for Config {
|
|||||||
octal: false,
|
octal: false,
|
||||||
// These size limits are much bigger than what's in the regex
|
// These size limits are much bigger than what's in the regex
|
||||||
// crate.
|
// crate.
|
||||||
size_limit: 100 * (1<<20),
|
size_limit: 100 * (1 << 20),
|
||||||
dfa_size_limit: 1000 * (1<<20),
|
dfa_size_limit: 1000 * (1 << 20),
|
||||||
nest_limit: 250,
|
nest_limit: 250,
|
||||||
line_terminator: None,
|
line_terminator: None,
|
||||||
crlf: false,
|
crlf: false,
|
||||||
@ -95,10 +95,7 @@ impl Config {
|
|||||||
|
|
||||||
/// Accounting for the `smart_case` config knob, return true if and only if
|
/// Accounting for the `smart_case` config knob, return true if and only if
|
||||||
/// this pattern should be matched case insensitively.
|
/// this pattern should be matched case insensitively.
|
||||||
fn is_case_insensitive(
|
fn is_case_insensitive(&self, analysis: &AstAnalysis) -> bool {
|
||||||
&self,
|
|
||||||
analysis: &AstAnalysis,
|
|
||||||
) -> bool {
|
|
||||||
if self.case_insensitive {
|
if self.case_insensitive {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -116,9 +113,7 @@ impl Config {
|
|||||||
/// are enabled, since if multi-line can impact the match semantics of a
|
/// are enabled, since if multi-line can impact the match semantics of a
|
||||||
/// regex, then it is by definition not a simple alternation of literals.
|
/// regex, then it is by definition not a simple alternation of literals.
|
||||||
pub fn can_plain_aho_corasick(&self) -> bool {
|
pub fn can_plain_aho_corasick(&self) -> bool {
|
||||||
!self.word
|
!self.word && !self.case_insensitive && !self.case_smart
|
||||||
&& !self.case_insensitive
|
|
||||||
&& !self.case_smart
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform analysis on the AST of this pattern.
|
/// Perform analysis on the AST of this pattern.
|
||||||
@ -203,8 +198,7 @@ impl ConfiguredHIR {
|
|||||||
pub fn with_pattern<F: FnMut(&str) -> String>(
|
pub fn with_pattern<F: FnMut(&str) -> String>(
|
||||||
&self,
|
&self,
|
||||||
mut f: F,
|
mut f: F,
|
||||||
) -> Result<ConfiguredHIR, Error>
|
) -> Result<ConfiguredHIR, Error> {
|
||||||
{
|
|
||||||
self.pattern_to_hir(&f(&self.expr.to_string()))
|
self.pattern_to_hir(&f(&self.expr.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,9 +76,8 @@ impl Matcher for CRLFMatcher {
|
|||||||
caps: &mut RegexCaptures,
|
caps: &mut RegexCaptures,
|
||||||
) -> Result<bool, NoError> {
|
) -> Result<bool, NoError> {
|
||||||
caps.strip_crlf(false);
|
caps.strip_crlf(false);
|
||||||
let r = self.regex.captures_read_at(
|
let r =
|
||||||
caps.locations_mut(), haystack, at,
|
self.regex.captures_read_at(caps.locations_mut(), haystack, at);
|
||||||
);
|
|
||||||
if !r.is_some() {
|
if !r.is_some() {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
@ -163,8 +162,8 @@ pub fn crlfify(expr: Hir) -> Hir {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use regex_syntax::Parser;
|
|
||||||
use super::crlfify;
|
use super::crlfify;
|
||||||
|
use regex_syntax::Parser;
|
||||||
|
|
||||||
fn roundtrip(pattern: &str) -> String {
|
fn roundtrip(pattern: &str) -> String {
|
||||||
let expr1 = Parser::new().parse(pattern).unwrap();
|
let expr1 = Parser::new().parse(pattern).unwrap();
|
||||||
|
@ -5,8 +5,8 @@ the regex engine doesn't look for inner literals. Since we're doing line based
|
|||||||
searching, we can use them, so we need to do it ourselves.
|
searching, we can use them, so we need to do it ourselves.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use regex_syntax::hir::{self, Hir, HirKind};
|
|
||||||
use regex_syntax::hir::literal::{Literal, Literals};
|
use regex_syntax::hir::literal::{Literal, Literals};
|
||||||
|
use regex_syntax::hir::{self, Hir, HirKind};
|
||||||
|
|
||||||
use util;
|
use util;
|
||||||
|
|
||||||
@ -159,10 +159,8 @@ impl LiteralSets {
|
|||||||
};
|
};
|
||||||
|
|
||||||
debug!("prefix/suffix literals found: {:?}", lits);
|
debug!("prefix/suffix literals found: {:?}", lits);
|
||||||
let alts: Vec<String> = lits
|
let alts: Vec<String> =
|
||||||
.into_iter()
|
lits.into_iter().map(|x| util::bytes_to_regex(x)).collect();
|
||||||
.map(|x| util::bytes_to_regex(x))
|
|
||||||
.collect();
|
|
||||||
// We're matching raw bytes, so disable Unicode mode.
|
// We're matching raw bytes, so disable Unicode mode.
|
||||||
Some(format!("(?-u:{})", alts.join("|")))
|
Some(format!("(?-u:{})", alts.join("|")))
|
||||||
} else {
|
} else {
|
||||||
@ -194,24 +192,28 @@ fn union_required(expr: &Hir, lits: &mut Literals) {
|
|||||||
HirKind::Group(hir::Group { ref hir, .. }) => {
|
HirKind::Group(hir::Group { ref hir, .. }) => {
|
||||||
union_required(&**hir, lits);
|
union_required(&**hir, lits);
|
||||||
}
|
}
|
||||||
HirKind::Repetition(ref x) => {
|
HirKind::Repetition(ref x) => match x.kind {
|
||||||
match x.kind {
|
hir::RepetitionKind::ZeroOrOne => lits.cut(),
|
||||||
hir::RepetitionKind::ZeroOrOne => lits.cut(),
|
hir::RepetitionKind::ZeroOrMore => lits.cut(),
|
||||||
hir::RepetitionKind::ZeroOrMore => lits.cut(),
|
hir::RepetitionKind::OneOrMore => {
|
||||||
hir::RepetitionKind::OneOrMore => {
|
union_required(&x.hir, lits);
|
||||||
union_required(&x.hir, lits);
|
|
||||||
}
|
|
||||||
hir::RepetitionKind::Range(ref rng) => {
|
|
||||||
let (min, max) = match *rng {
|
|
||||||
hir::RepetitionRange::Exactly(m) => (m, Some(m)),
|
|
||||||
hir::RepetitionRange::AtLeast(m) => (m, None),
|
|
||||||
hir::RepetitionRange::Bounded(m, n) => (m, Some(n)),
|
|
||||||
};
|
|
||||||
repeat_range_literals(
|
|
||||||
&x.hir, min, max, x.greedy, lits, union_required);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
hir::RepetitionKind::Range(ref rng) => {
|
||||||
|
let (min, max) = match *rng {
|
||||||
|
hir::RepetitionRange::Exactly(m) => (m, Some(m)),
|
||||||
|
hir::RepetitionRange::AtLeast(m) => (m, None),
|
||||||
|
hir::RepetitionRange::Bounded(m, n) => (m, Some(n)),
|
||||||
|
};
|
||||||
|
repeat_range_literals(
|
||||||
|
&x.hir,
|
||||||
|
min,
|
||||||
|
max,
|
||||||
|
x.greedy,
|
||||||
|
lits,
|
||||||
|
union_required,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
HirKind::Concat(ref es) if es.is_empty() => {}
|
HirKind::Concat(ref es) if es.is_empty() => {}
|
||||||
HirKind::Concat(ref es) if es.len() == 1 => {
|
HirKind::Concat(ref es) if es.len() == 1 => {
|
||||||
union_required(&es[0], lits)
|
union_required(&es[0], lits)
|
||||||
@ -310,9 +312,9 @@ fn is_simple(expr: &Hir) -> bool {
|
|||||||
| HirKind::Repetition(_)
|
| HirKind::Repetition(_)
|
||||||
| HirKind::Concat(_)
|
| HirKind::Concat(_)
|
||||||
| HirKind::Alternation(_) => true,
|
| HirKind::Alternation(_) => true,
|
||||||
HirKind::Anchor(_)
|
HirKind::Anchor(_) | HirKind::WordBoundary(_) | HirKind::Group(_) => {
|
||||||
| HirKind::WordBoundary(_)
|
false
|
||||||
| HirKind::Group(_) => false,
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -328,8 +330,8 @@ fn count_byte_class(cls: &hir::ClassBytes) -> u32 {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use regex_syntax::Parser;
|
|
||||||
use super::LiteralSets;
|
use super::LiteralSets;
|
||||||
|
use regex_syntax::Parser;
|
||||||
|
|
||||||
fn sets(pattern: &str) -> LiteralSets {
|
fn sets(pattern: &str) -> LiteralSets {
|
||||||
let hir = Parser::new().parse(pattern).unwrap();
|
let hir = Parser::new().parse(pattern).unwrap();
|
||||||
@ -380,8 +382,10 @@ mod tests {
|
|||||||
fn regression_1319() {
|
fn regression_1319() {
|
||||||
// Regression from:
|
// Regression from:
|
||||||
// https://github.com/BurntSushi/ripgrep/issues/1319
|
// https://github.com/BurntSushi/ripgrep/issues/1319
|
||||||
assert_eq!(one_regex(r"TTGAGTCCAGGAG[ATCG]{2}C"),
|
assert_eq!(
|
||||||
|
one_regex(r"TTGAGTCCAGGAG[ATCG]{2}C"),
|
||||||
pat("TTGAGTCCAGGAGA|TTGAGTCCAGGAGC|\
|
pat("TTGAGTCCAGGAGA|TTGAGTCCAGGAGC|\
|
||||||
TTGAGTCCAGGAGG|TTGAGTCCAGGAGT"));
|
TTGAGTCCAGGAGG|TTGAGTCCAGGAGT")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use grep_matcher::{
|
use grep_matcher::{
|
||||||
Captures, LineMatchKind, LineTerminator, Match, Matcher, NoError, ByteSet,
|
ByteSet, Captures, LineMatchKind, LineTerminator, Match, Matcher, NoError,
|
||||||
};
|
};
|
||||||
use regex::bytes::{CaptureLocations, Regex};
|
use regex::bytes::{CaptureLocations, Regex};
|
||||||
|
|
||||||
@ -34,9 +34,7 @@ impl Default for RegexMatcherBuilder {
|
|||||||
impl RegexMatcherBuilder {
|
impl RegexMatcherBuilder {
|
||||||
/// Create a new builder for configuring a regex matcher.
|
/// Create a new builder for configuring a regex matcher.
|
||||||
pub fn new() -> RegexMatcherBuilder {
|
pub fn new() -> RegexMatcherBuilder {
|
||||||
RegexMatcherBuilder {
|
RegexMatcherBuilder { config: Config::default() }
|
||||||
config: Config::default(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build a new matcher using the current configuration for the provided
|
/// Build a new matcher using the current configuration for the provided
|
||||||
@ -382,9 +380,7 @@ impl RegexMatcher {
|
|||||||
/// given pattern contains a literal `\n`. Other uses of `\n` (such as in
|
/// given pattern contains a literal `\n`. Other uses of `\n` (such as in
|
||||||
/// `\s`) are removed transparently.
|
/// `\s`) are removed transparently.
|
||||||
pub fn new_line_matcher(pattern: &str) -> Result<RegexMatcher, Error> {
|
pub fn new_line_matcher(pattern: &str) -> Result<RegexMatcher, Error> {
|
||||||
RegexMatcherBuilder::new()
|
RegexMatcherBuilder::new().line_terminator(Some(b'\n')).build(pattern)
|
||||||
.line_terminator(Some(b'\n'))
|
|
||||||
.build(pattern)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -499,12 +495,9 @@ impl Matcher for RegexMatcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_iter<F>(
|
fn find_iter<F>(&self, haystack: &[u8], matched: F) -> Result<(), NoError>
|
||||||
&self,
|
where
|
||||||
haystack: &[u8],
|
F: FnMut(Match) -> bool,
|
||||||
matched: F,
|
|
||||||
) -> Result<(), NoError>
|
|
||||||
where F: FnMut(Match) -> bool
|
|
||||||
{
|
{
|
||||||
use self::RegexMatcherImpl::*;
|
use self::RegexMatcherImpl::*;
|
||||||
match self.matcher {
|
match self.matcher {
|
||||||
@ -520,7 +513,8 @@ impl Matcher for RegexMatcher {
|
|||||||
haystack: &[u8],
|
haystack: &[u8],
|
||||||
matched: F,
|
matched: F,
|
||||||
) -> Result<Result<(), E>, NoError>
|
) -> Result<Result<(), E>, NoError>
|
||||||
where F: FnMut(Match) -> Result<bool, E>
|
where
|
||||||
|
F: FnMut(Match) -> Result<bool, E>,
|
||||||
{
|
{
|
||||||
use self::RegexMatcherImpl::*;
|
use self::RegexMatcherImpl::*;
|
||||||
match self.matcher {
|
match self.matcher {
|
||||||
@ -551,7 +545,8 @@ impl Matcher for RegexMatcher {
|
|||||||
caps: &mut RegexCaptures,
|
caps: &mut RegexCaptures,
|
||||||
matched: F,
|
matched: F,
|
||||||
) -> Result<(), NoError>
|
) -> Result<(), NoError>
|
||||||
where F: FnMut(&RegexCaptures) -> bool
|
where
|
||||||
|
F: FnMut(&RegexCaptures) -> bool,
|
||||||
{
|
{
|
||||||
use self::RegexMatcherImpl::*;
|
use self::RegexMatcherImpl::*;
|
||||||
match self.matcher {
|
match self.matcher {
|
||||||
@ -568,7 +563,8 @@ impl Matcher for RegexMatcher {
|
|||||||
caps: &mut RegexCaptures,
|
caps: &mut RegexCaptures,
|
||||||
matched: F,
|
matched: F,
|
||||||
) -> Result<Result<(), E>, NoError>
|
) -> Result<Result<(), E>, NoError>
|
||||||
where F: FnMut(&RegexCaptures) -> Result<bool, E>
|
where
|
||||||
|
F: FnMut(&RegexCaptures) -> Result<bool, E>,
|
||||||
{
|
{
|
||||||
use self::RegexMatcherImpl::*;
|
use self::RegexMatcherImpl::*;
|
||||||
match self.matcher {
|
match self.matcher {
|
||||||
@ -602,7 +598,8 @@ impl Matcher for RegexMatcher {
|
|||||||
dst: &mut Vec<u8>,
|
dst: &mut Vec<u8>,
|
||||||
append: F,
|
append: F,
|
||||||
) -> Result<(), NoError>
|
) -> Result<(), NoError>
|
||||||
where F: FnMut(Match, &mut Vec<u8>) -> bool
|
where
|
||||||
|
F: FnMut(Match, &mut Vec<u8>) -> bool,
|
||||||
{
|
{
|
||||||
use self::RegexMatcherImpl::*;
|
use self::RegexMatcherImpl::*;
|
||||||
match self.matcher {
|
match self.matcher {
|
||||||
@ -620,7 +617,8 @@ impl Matcher for RegexMatcher {
|
|||||||
dst: &mut Vec<u8>,
|
dst: &mut Vec<u8>,
|
||||||
append: F,
|
append: F,
|
||||||
) -> Result<(), NoError>
|
) -> Result<(), NoError>
|
||||||
where F: FnMut(&Self::Captures, &mut Vec<u8>) -> bool
|
where
|
||||||
|
F: FnMut(&Self::Captures, &mut Vec<u8>) -> bool,
|
||||||
{
|
{
|
||||||
use self::RegexMatcherImpl::*;
|
use self::RegexMatcherImpl::*;
|
||||||
match self.matcher {
|
match self.matcher {
|
||||||
@ -745,7 +743,8 @@ impl Matcher for StandardMatcher {
|
|||||||
haystack: &[u8],
|
haystack: &[u8],
|
||||||
at: usize,
|
at: usize,
|
||||||
) -> Result<Option<Match>, NoError> {
|
) -> Result<Option<Match>, NoError> {
|
||||||
Ok(self.regex
|
Ok(self
|
||||||
|
.regex
|
||||||
.find_at(haystack, at)
|
.find_at(haystack, at)
|
||||||
.map(|m| Match::new(m.start(), m.end())))
|
.map(|m| Match::new(m.start(), m.end())))
|
||||||
}
|
}
|
||||||
@ -767,7 +766,8 @@ impl Matcher for StandardMatcher {
|
|||||||
haystack: &[u8],
|
haystack: &[u8],
|
||||||
mut matched: F,
|
mut matched: F,
|
||||||
) -> Result<Result<(), E>, NoError>
|
) -> Result<Result<(), E>, NoError>
|
||||||
where F: FnMut(Match) -> Result<bool, E>
|
where
|
||||||
|
F: FnMut(Match) -> Result<bool, E>,
|
||||||
{
|
{
|
||||||
for m in self.regex.find_iter(haystack) {
|
for m in self.regex.find_iter(haystack) {
|
||||||
match matched(Match::new(m.start(), m.end())) {
|
match matched(Match::new(m.start(), m.end())) {
|
||||||
@ -785,9 +785,10 @@ impl Matcher for StandardMatcher {
|
|||||||
at: usize,
|
at: usize,
|
||||||
caps: &mut RegexCaptures,
|
caps: &mut RegexCaptures,
|
||||||
) -> Result<bool, NoError> {
|
) -> Result<bool, NoError> {
|
||||||
Ok(self.regex.captures_read_at(
|
Ok(self
|
||||||
&mut caps.locations_mut(), haystack, at,
|
.regex
|
||||||
).is_some())
|
.captures_read_at(&mut caps.locations_mut(), haystack, at)
|
||||||
|
.is_some())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shortest_match_at(
|
fn shortest_match_at(
|
||||||
@ -901,7 +902,9 @@ impl RegexCaptures {
|
|||||||
offset: usize,
|
offset: usize,
|
||||||
) -> RegexCaptures {
|
) -> RegexCaptures {
|
||||||
RegexCaptures(RegexCapturesImp::Regex {
|
RegexCaptures(RegexCapturesImp::Regex {
|
||||||
locs, offset, strip_crlf: false,
|
locs,
|
||||||
|
offset,
|
||||||
|
strip_crlf: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -910,9 +913,7 @@ impl RegexCaptures {
|
|||||||
RegexCapturesImp::AhoCorasick { .. } => {
|
RegexCapturesImp::AhoCorasick { .. } => {
|
||||||
panic!("getting locations for simple captures is invalid")
|
panic!("getting locations for simple captures is invalid")
|
||||||
}
|
}
|
||||||
RegexCapturesImp::Regex { ref locs, .. } => {
|
RegexCapturesImp::Regex { ref locs, .. } => locs,
|
||||||
locs
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -921,9 +922,7 @@ impl RegexCaptures {
|
|||||||
RegexCapturesImp::AhoCorasick { .. } => {
|
RegexCapturesImp::AhoCorasick { .. } => {
|
||||||
panic!("getting locations for simple captures is invalid")
|
panic!("getting locations for simple captures is invalid")
|
||||||
}
|
}
|
||||||
RegexCapturesImp::Regex { ref mut locs, .. } => {
|
RegexCapturesImp::Regex { ref mut locs, .. } => locs,
|
||||||
locs
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -952,23 +951,19 @@ impl RegexCaptures {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use grep_matcher::{LineMatchKind, Matcher};
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use grep_matcher::{LineMatchKind, Matcher};
|
||||||
|
|
||||||
// Test that enabling word matches does the right thing and demonstrate
|
// Test that enabling word matches does the right thing and demonstrate
|
||||||
// the difference between it and surrounding the regex in `\b`.
|
// the difference between it and surrounding the regex in `\b`.
|
||||||
#[test]
|
#[test]
|
||||||
fn word() {
|
fn word() {
|
||||||
let matcher = RegexMatcherBuilder::new()
|
let matcher =
|
||||||
.word(true)
|
RegexMatcherBuilder::new().word(true).build(r"-2").unwrap();
|
||||||
.build(r"-2")
|
|
||||||
.unwrap();
|
|
||||||
assert!(matcher.is_match(b"abc -2 foo").unwrap());
|
assert!(matcher.is_match(b"abc -2 foo").unwrap());
|
||||||
|
|
||||||
let matcher = RegexMatcherBuilder::new()
|
let matcher =
|
||||||
.word(false)
|
RegexMatcherBuilder::new().word(false).build(r"\b-2\b").unwrap();
|
||||||
.build(r"\b-2\b")
|
|
||||||
.unwrap();
|
|
||||||
assert!(!matcher.is_match(b"abc -2 foo").unwrap());
|
assert!(!matcher.is_match(b"abc -2 foo").unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -977,9 +972,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn line_terminator() {
|
fn line_terminator() {
|
||||||
// This works, because there's no line terminator specified.
|
// This works, because there's no line terminator specified.
|
||||||
let matcher = RegexMatcherBuilder::new()
|
let matcher = RegexMatcherBuilder::new().build(r"abc\sxyz").unwrap();
|
||||||
.build(r"abc\sxyz")
|
|
||||||
.unwrap();
|
|
||||||
assert!(matcher.is_match(b"abc\nxyz").unwrap());
|
assert!(matcher.is_match(b"abc\nxyz").unwrap());
|
||||||
|
|
||||||
// This doesn't.
|
// This doesn't.
|
||||||
@ -1029,16 +1022,12 @@ mod tests {
|
|||||||
// Test that smart case works.
|
// Test that smart case works.
|
||||||
#[test]
|
#[test]
|
||||||
fn case_smart() {
|
fn case_smart() {
|
||||||
let matcher = RegexMatcherBuilder::new()
|
let matcher =
|
||||||
.case_smart(true)
|
RegexMatcherBuilder::new().case_smart(true).build(r"abc").unwrap();
|
||||||
.build(r"abc")
|
|
||||||
.unwrap();
|
|
||||||
assert!(matcher.is_match(b"ABC").unwrap());
|
assert!(matcher.is_match(b"ABC").unwrap());
|
||||||
|
|
||||||
let matcher = RegexMatcherBuilder::new()
|
let matcher =
|
||||||
.case_smart(true)
|
RegexMatcherBuilder::new().case_smart(true).build(r"aBc").unwrap();
|
||||||
.build(r"aBc")
|
|
||||||
.unwrap();
|
|
||||||
assert!(!matcher.is_match(b"ABC").unwrap());
|
assert!(!matcher.is_match(b"ABC").unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1060,9 +1049,7 @@ mod tests {
|
|||||||
|
|
||||||
// With no line terminator set, we can't employ any optimizations,
|
// With no line terminator set, we can't employ any optimizations,
|
||||||
// so we get a confirmed match.
|
// so we get a confirmed match.
|
||||||
let matcher = RegexMatcherBuilder::new()
|
let matcher = RegexMatcherBuilder::new().build(r"\wfoo\s").unwrap();
|
||||||
.build(r"\wfoo\s")
|
|
||||||
.unwrap();
|
|
||||||
let m = matcher.find_candidate_line(b"afoo ").unwrap().unwrap();
|
let m = matcher.find_candidate_line(b"afoo ").unwrap().unwrap();
|
||||||
assert!(is_confirmed(m));
|
assert!(is_confirmed(m));
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use aho_corasick::{AhoCorasick, AhoCorasickBuilder, MatchKind};
|
use aho_corasick::{AhoCorasick, AhoCorasickBuilder, MatchKind};
|
||||||
use grep_matcher::{Matcher, Match, NoError};
|
use grep_matcher::{Match, Matcher, NoError};
|
||||||
use regex_syntax::hir::Hir;
|
use regex_syntax::hir::Hir;
|
||||||
|
|
||||||
use error::Error;
|
use error::Error;
|
||||||
@ -93,15 +93,13 @@ pub fn alternation_literals(expr: &Hir) -> Option<Vec<Vec<u8>>> {
|
|||||||
_ => return None, // one literal isn't worth it
|
_ => return None, // one literal isn't worth it
|
||||||
};
|
};
|
||||||
|
|
||||||
let extendlit = |lit: &Literal, dst: &mut Vec<u8>| {
|
let extendlit = |lit: &Literal, dst: &mut Vec<u8>| match *lit {
|
||||||
match *lit {
|
Literal::Unicode(c) => {
|
||||||
Literal::Unicode(c) => {
|
let mut buf = [0; 4];
|
||||||
let mut buf = [0; 4];
|
dst.extend_from_slice(c.encode_utf8(&mut buf).as_bytes());
|
||||||
dst.extend_from_slice(c.encode_utf8(&mut buf).as_bytes());
|
}
|
||||||
}
|
Literal::Byte(b) => {
|
||||||
Literal::Byte(b) => {
|
dst.push(b);
|
||||||
dst.push(b);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -11,14 +11,9 @@ pub fn non_matching_bytes(expr: &Hir) -> ByteSet {
|
|||||||
|
|
||||||
/// Remove any bytes from the given set that can occur in a matched produced by
|
/// Remove any bytes from the given set that can occur in a matched produced by
|
||||||
/// the given expression.
|
/// the given expression.
|
||||||
fn remove_matching_bytes(
|
fn remove_matching_bytes(expr: &Hir, set: &mut ByteSet) {
|
||||||
expr: &Hir,
|
|
||||||
set: &mut ByteSet,
|
|
||||||
) {
|
|
||||||
match *expr.kind() {
|
match *expr.kind() {
|
||||||
HirKind::Empty
|
HirKind::Empty | HirKind::Anchor(_) | HirKind::WordBoundary(_) => {}
|
||||||
| HirKind::Anchor(_)
|
|
||||||
| HirKind::WordBoundary(_) => {}
|
|
||||||
HirKind::Literal(hir::Literal::Unicode(c)) => {
|
HirKind::Literal(hir::Literal::Unicode(c)) => {
|
||||||
for &b in c.encode_utf8(&mut [0; 4]).as_bytes() {
|
for &b in c.encode_utf8(&mut [0; 4]).as_bytes() {
|
||||||
set.remove(b);
|
set.remove(b);
|
||||||
@ -105,15 +100,20 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dot() {
|
fn dot() {
|
||||||
assert_eq!(sparse(&extract(".")), vec![
|
assert_eq!(
|
||||||
b'\n',
|
sparse(&extract(".")),
|
||||||
192, 193, 245, 246, 247, 248, 249,
|
vec![
|
||||||
250, 251, 252, 253, 254, 255,
|
b'\n', 192, 193, 245, 246, 247, 248, 249, 250, 251, 252, 253,
|
||||||
]);
|
254, 255,
|
||||||
assert_eq!(sparse(&extract("(?s).")), vec![
|
]
|
||||||
192, 193, 245, 246, 247, 248, 249,
|
);
|
||||||
250, 251, 252, 253, 254, 255,
|
assert_eq!(
|
||||||
]);
|
sparse(&extract("(?s).")),
|
||||||
|
vec![
|
||||||
|
192, 193, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
|
||||||
|
255,
|
||||||
|
]
|
||||||
|
);
|
||||||
assert_eq!(sparse(&extract("(?-u).")), vec![b'\n']);
|
assert_eq!(sparse(&extract("(?-u).")), vec![b'\n']);
|
||||||
assert_eq!(sparse(&extract("(?s-u).")), vec![]);
|
assert_eq!(sparse(&extract("(?s-u).")), vec![]);
|
||||||
}
|
}
|
||||||
|
@ -33,10 +33,7 @@ pub fn strip_from_match(
|
|||||||
|
|
||||||
/// The implementation of strip_from_match. The given byte must be ASCII. This
|
/// The implementation of strip_from_match. The given byte must be ASCII. This
|
||||||
/// function panics otherwise.
|
/// function panics otherwise.
|
||||||
fn strip_from_match_ascii(
|
fn strip_from_match_ascii(expr: Hir, byte: u8) -> Result<Hir, Error> {
|
||||||
expr: Hir,
|
|
||||||
byte: u8,
|
|
||||||
) -> Result<Hir, Error> {
|
|
||||||
assert!(byte <= 0x7F);
|
assert!(byte <= 0x7F);
|
||||||
let chr = byte as char;
|
let chr = byte as char;
|
||||||
assert_eq!(chr.len_utf8(), 1);
|
assert_eq!(chr.len_utf8(), 1);
|
||||||
@ -88,13 +85,15 @@ fn strip_from_match_ascii(
|
|||||||
Hir::group(x)
|
Hir::group(x)
|
||||||
}
|
}
|
||||||
HirKind::Concat(xs) => {
|
HirKind::Concat(xs) => {
|
||||||
let xs = xs.into_iter()
|
let xs = xs
|
||||||
|
.into_iter()
|
||||||
.map(|e| strip_from_match_ascii(e, byte))
|
.map(|e| strip_from_match_ascii(e, byte))
|
||||||
.collect::<Result<Vec<Hir>, Error>>()?;
|
.collect::<Result<Vec<Hir>, Error>>()?;
|
||||||
Hir::concat(xs)
|
Hir::concat(xs)
|
||||||
}
|
}
|
||||||
HirKind::Alternation(xs) => {
|
HirKind::Alternation(xs) => {
|
||||||
let xs = xs.into_iter()
|
let xs = xs
|
||||||
|
.into_iter()
|
||||||
.map(|e| strip_from_match_ascii(e, byte))
|
.map(|e| strip_from_match_ascii(e, byte))
|
||||||
.collect::<Result<Vec<Hir>, Error>>()?;
|
.collect::<Result<Vec<Hir>, Error>>()?;
|
||||||
Hir::alternation(xs)
|
Hir::alternation(xs)
|
||||||
@ -106,8 +105,8 @@ fn strip_from_match_ascii(
|
|||||||
mod tests {
|
mod tests {
|
||||||
use regex_syntax::Parser;
|
use regex_syntax::Parser;
|
||||||
|
|
||||||
|
use super::{strip_from_match, LineTerminator};
|
||||||
use error::Error;
|
use error::Error;
|
||||||
use super::{LineTerminator, strip_from_match};
|
|
||||||
|
|
||||||
fn roundtrip(pattern: &str, byte: u8) -> String {
|
fn roundtrip(pattern: &str, byte: u8) -> String {
|
||||||
roundtrip_line_term(pattern, LineTerminator::byte(byte)).unwrap()
|
roundtrip_line_term(pattern, LineTerminator::byte(byte)).unwrap()
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
/// Converts an arbitrary sequence of bytes to a literal suitable for building
|
/// Converts an arbitrary sequence of bytes to a literal suitable for building
|
||||||
/// a regular expression.
|
/// a regular expression.
|
||||||
pub fn bytes_to_regex(bs: &[u8]) -> String {
|
pub fn bytes_to_regex(bs: &[u8]) -> String {
|
||||||
use std::fmt::Write;
|
|
||||||
use regex_syntax::is_meta_character;
|
use regex_syntax::is_meta_character;
|
||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
let mut s = String::with_capacity(bs.len());
|
let mut s = String::with_capacity(bs.len());
|
||||||
for &b in bs {
|
for &b in bs {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use grep_matcher::{Match, Matcher, NoError};
|
use grep_matcher::{Match, Matcher, NoError};
|
||||||
@ -45,9 +45,8 @@ impl WordMatcher {
|
|||||||
/// The given options are used to construct the regular expression
|
/// The given options are used to construct the regular expression
|
||||||
/// internally.
|
/// internally.
|
||||||
pub fn new(expr: &ConfiguredHIR) -> Result<WordMatcher, Error> {
|
pub fn new(expr: &ConfiguredHIR) -> Result<WordMatcher, Error> {
|
||||||
let original = expr.with_pattern(|pat| {
|
let original =
|
||||||
format!("^(?:{})$", pat)
|
expr.with_pattern(|pat| format!("^(?:{})$", pat))?.regex()?;
|
||||||
})?.regex()?;
|
|
||||||
let word_expr = expr.with_pattern(|pat| {
|
let word_expr = expr.with_pattern(|pat| {
|
||||||
let pat = format!(r"(?:(?-m:^)|\W)({})(?:(?-m:$)|\W)", pat);
|
let pat = format!(r"(?:(?-m:^)|\W)({})(?:(?-m:$)|\W)", pat);
|
||||||
debug!("word regex: {:?}", pat);
|
debug!("word regex: {:?}", pat);
|
||||||
@ -112,9 +111,8 @@ impl WordMatcher {
|
|||||||
}
|
}
|
||||||
let (_, slen) = bstr::decode_utf8(&haystack[cand]);
|
let (_, slen) = bstr::decode_utf8(&haystack[cand]);
|
||||||
let (_, elen) = bstr::decode_last_utf8(&haystack[cand]);
|
let (_, elen) = bstr::decode_last_utf8(&haystack[cand]);
|
||||||
cand = cand
|
cand =
|
||||||
.with_start(cand.start() + slen)
|
cand.with_start(cand.start() + slen).with_end(cand.end() - elen);
|
||||||
.with_end(cand.end() - elen);
|
|
||||||
if self.original.is_match(&haystack[cand]) {
|
if self.original.is_match(&haystack[cand]) {
|
||||||
Ok(Some(cand))
|
Ok(Some(cand))
|
||||||
} else {
|
} else {
|
||||||
@ -148,9 +146,8 @@ impl Matcher for WordMatcher {
|
|||||||
Err(()) => {}
|
Err(()) => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
let cell = self.locs.get_or(|| {
|
let cell =
|
||||||
RefCell::new(self.regex.capture_locations())
|
self.locs.get_or(|| RefCell::new(self.regex.capture_locations()));
|
||||||
});
|
|
||||||
let mut caps = cell.borrow_mut();
|
let mut caps = cell.borrow_mut();
|
||||||
self.regex.captures_read_at(&mut caps, haystack, at);
|
self.regex.captures_read_at(&mut caps, haystack, at);
|
||||||
Ok(caps.get(1).map(|m| Match::new(m.0, m.1)))
|
Ok(caps.get(1).map(|m| Match::new(m.0, m.1)))
|
||||||
@ -174,9 +171,8 @@ impl Matcher for WordMatcher {
|
|||||||
at: usize,
|
at: usize,
|
||||||
caps: &mut RegexCaptures,
|
caps: &mut RegexCaptures,
|
||||||
) -> Result<bool, NoError> {
|
) -> Result<bool, NoError> {
|
||||||
let r = self.regex.captures_read_at(
|
let r =
|
||||||
caps.locations_mut(), haystack, at,
|
self.regex.captures_read_at(caps.locations_mut(), haystack, at);
|
||||||
);
|
|
||||||
Ok(r.is_some())
|
Ok(r.is_some())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,9 +183,9 @@ impl Matcher for WordMatcher {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use grep_matcher::{Captures, Match, Matcher};
|
|
||||||
use config::Config;
|
|
||||||
use super::WordMatcher;
|
use super::WordMatcher;
|
||||||
|
use config::Config;
|
||||||
|
use grep_matcher::{Captures, Match, Matcher};
|
||||||
|
|
||||||
fn matcher(pattern: &str) -> WordMatcher {
|
fn matcher(pattern: &str) -> WordMatcher {
|
||||||
let chir = Config::default().hir(pattern).unwrap();
|
let chir = Config::default().hir(pattern).unwrap();
|
||||||
|
@ -7,8 +7,8 @@ use std::io;
|
|||||||
use std::process;
|
use std::process;
|
||||||
|
|
||||||
use grep_regex::RegexMatcher;
|
use grep_regex::RegexMatcher;
|
||||||
use grep_searcher::Searcher;
|
|
||||||
use grep_searcher::sinks::UTF8;
|
use grep_searcher::sinks::UTF8;
|
||||||
|
use grep_searcher::Searcher;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
if let Err(err) = example() {
|
if let Err(err) = example() {
|
||||||
@ -20,14 +20,18 @@ fn main() {
|
|||||||
fn example() -> Result<(), Box<dyn Error>> {
|
fn example() -> Result<(), Box<dyn Error>> {
|
||||||
let pattern = match env::args().nth(1) {
|
let pattern = match env::args().nth(1) {
|
||||||
Some(pattern) => pattern,
|
Some(pattern) => pattern,
|
||||||
None => return Err(From::from(format!(
|
None => {
|
||||||
"Usage: search-stdin <pattern>"
|
return Err(From::from(format!("Usage: search-stdin <pattern>")))
|
||||||
))),
|
}
|
||||||
};
|
};
|
||||||
let matcher = RegexMatcher::new(&pattern)?;
|
let matcher = RegexMatcher::new(&pattern)?;
|
||||||
Searcher::new().search_reader(&matcher, io::stdin(), UTF8(|lnum, line| {
|
Searcher::new().search_reader(
|
||||||
print!("{}:{}", lnum, line);
|
&matcher,
|
||||||
Ok(true)
|
io::stdin(),
|
||||||
}))?;
|
UTF8(|lnum, line| {
|
||||||
|
print!("{}:{}", lnum, line);
|
||||||
|
Ok(true)
|
||||||
|
}),
|
||||||
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -112,14 +112,13 @@ extern crate regex;
|
|||||||
|
|
||||||
pub use lines::{LineIter, LineStep};
|
pub use lines::{LineIter, LineStep};
|
||||||
pub use searcher::{
|
pub use searcher::{
|
||||||
BinaryDetection, ConfigError, Encoding, MmapChoice,
|
BinaryDetection, ConfigError, Encoding, MmapChoice, Searcher,
|
||||||
Searcher, SearcherBuilder,
|
SearcherBuilder,
|
||||||
};
|
|
||||||
pub use sink::{
|
|
||||||
Sink, SinkError,
|
|
||||||
SinkContext, SinkContextKind, SinkFinish, SinkMatch,
|
|
||||||
};
|
};
|
||||||
pub use sink::sinks;
|
pub use sink::sinks;
|
||||||
|
pub use sink::{
|
||||||
|
Sink, SinkContext, SinkContextKind, SinkError, SinkFinish, SinkMatch,
|
||||||
|
};
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod macros;
|
mod macros;
|
||||||
|
@ -4,7 +4,7 @@ use std::io;
|
|||||||
use bstr::ByteSlice;
|
use bstr::ByteSlice;
|
||||||
|
|
||||||
/// The default buffer capacity that we use for the line buffer.
|
/// The default buffer capacity that we use for the line buffer.
|
||||||
pub(crate) const DEFAULT_BUFFER_CAPACITY: usize = 8 * (1<<10); // 8 KB
|
pub(crate) const DEFAULT_BUFFER_CAPACITY: usize = 8 * (1 << 10); // 8 KB
|
||||||
|
|
||||||
/// The behavior of a searcher in the face of long lines and big contexts.
|
/// The behavior of a searcher in the face of long lines and big contexts.
|
||||||
///
|
///
|
||||||
@ -442,16 +442,15 @@ impl LineBuffer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
BinaryDetection::Convert(byte) => {
|
BinaryDetection::Convert(byte) => {
|
||||||
if let Some(i) = replace_bytes(
|
if let Some(i) =
|
||||||
newbytes,
|
replace_bytes(newbytes, byte, self.config.lineterm)
|
||||||
byte,
|
{
|
||||||
self.config.lineterm,
|
|
||||||
) {
|
|
||||||
// Record only the first binary offset.
|
// Record only the first binary offset.
|
||||||
if self.binary_byte_offset.is_none() {
|
if self.binary_byte_offset.is_none() {
|
||||||
self.binary_byte_offset =
|
self.binary_byte_offset = Some(
|
||||||
Some(self.absolute_byte_offset
|
self.absolute_byte_offset
|
||||||
+ (oldend + i) as u64);
|
+ (oldend + i) as u64,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -542,9 +541,9 @@ fn replace_bytes(bytes: &mut [u8], src: u8, replacement: u8) -> Option<usize> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::str;
|
|
||||||
use bstr::{ByteSlice, ByteVec};
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use bstr::{ByteSlice, ByteVec};
|
||||||
|
use std::str;
|
||||||
|
|
||||||
const SHERLOCK: &'static str = "\
|
const SHERLOCK: &'static str = "\
|
||||||
For the Doctor Watsons of this world, as opposed to the Sherlock
|
For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
@ -858,10 +857,13 @@ and exhibited clearly, with a label attached.\
|
|||||||
assert!(rdr.buffer().is_empty());
|
assert!(rdr.buffer().is_empty());
|
||||||
|
|
||||||
assert!(rdr.fill().unwrap());
|
assert!(rdr.fill().unwrap());
|
||||||
assert_eq!(rdr.bstr(), "\
|
assert_eq!(
|
||||||
|
rdr.bstr(),
|
||||||
|
"\
|
||||||
For the Doctor Watsons of this world, as opposed to the Sherlock
|
For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
Holmeses, s\
|
Holmeses, s\
|
||||||
");
|
"
|
||||||
|
);
|
||||||
rdr.consume_all();
|
rdr.consume_all();
|
||||||
|
|
||||||
assert!(!rdr.fill().unwrap());
|
assert!(!rdr.fill().unwrap());
|
||||||
|
@ -130,14 +130,9 @@ pub fn without_terminator(bytes: &[u8], line_term: LineTerminator) -> &[u8] {
|
|||||||
///
|
///
|
||||||
/// Line terminators are considered part of the line they terminate.
|
/// Line terminators are considered part of the line they terminate.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn locate(
|
pub fn locate(bytes: &[u8], line_term: u8, range: Match) -> Match {
|
||||||
bytes: &[u8],
|
let line_start =
|
||||||
line_term: u8,
|
bytes[..range.start()].rfind_byte(line_term).map_or(0, |i| i + 1);
|
||||||
range: Match,
|
|
||||||
) -> Match {
|
|
||||||
let line_start = bytes[..range.start()]
|
|
||||||
.rfind_byte(line_term)
|
|
||||||
.map_or(0, |i| i + 1);
|
|
||||||
let line_end =
|
let line_end =
|
||||||
if range.end() > line_start && bytes[range.end() - 1] == line_term {
|
if range.end() > line_start && bytes[range.end() - 1] == line_term {
|
||||||
range.end()
|
range.end()
|
||||||
@ -201,10 +196,10 @@ fn preceding_by_pos(
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use grep_matcher::Match;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use std::str;
|
use std::str;
|
||||||
use grep_matcher::Match;
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
const SHERLOCK: &'static str = "\
|
const SHERLOCK: &'static str = "\
|
||||||
For the Doctor Watsons of this world, as opposed to the Sherlock
|
For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
@ -260,29 +255,37 @@ and exhibited clearly, with a label attached.\
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
loc(t, lines[0].start, lines[0].end),
|
loc(t, lines[0].start, lines[0].end),
|
||||||
m(lines[0].start, lines[0].end));
|
m(lines[0].start, lines[0].end)
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
loc(t, lines[0].start + 1, lines[0].end),
|
loc(t, lines[0].start + 1, lines[0].end),
|
||||||
m(lines[0].start, lines[0].end));
|
m(lines[0].start, lines[0].end)
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
loc(t, lines[0].end - 1, lines[0].end),
|
loc(t, lines[0].end - 1, lines[0].end),
|
||||||
m(lines[0].start, lines[0].end));
|
m(lines[0].start, lines[0].end)
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
loc(t, lines[0].end, lines[0].end),
|
loc(t, lines[0].end, lines[0].end),
|
||||||
m(lines[1].start, lines[1].end));
|
m(lines[1].start, lines[1].end)
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
loc(t, lines[5].start, lines[5].end),
|
loc(t, lines[5].start, lines[5].end),
|
||||||
m(lines[5].start, lines[5].end));
|
m(lines[5].start, lines[5].end)
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
loc(t, lines[5].start + 1, lines[5].end),
|
loc(t, lines[5].start + 1, lines[5].end),
|
||||||
m(lines[5].start, lines[5].end));
|
m(lines[5].start, lines[5].end)
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
loc(t, lines[5].end - 1, lines[5].end),
|
loc(t, lines[5].end - 1, lines[5].end),
|
||||||
m(lines[5].start, lines[5].end));
|
m(lines[5].start, lines[5].end)
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
loc(t, lines[5].end, lines[5].end),
|
loc(t, lines[5].end, lines[5].end),
|
||||||
m(lines[5].start, lines[5].end));
|
m(lines[5].start, lines[5].end)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -3,12 +3,11 @@ use std::cmp;
|
|||||||
use bstr::ByteSlice;
|
use bstr::ByteSlice;
|
||||||
|
|
||||||
use grep_matcher::{LineMatchKind, Matcher};
|
use grep_matcher::{LineMatchKind, Matcher};
|
||||||
use lines::{self, LineStep};
|
|
||||||
use line_buffer::BinaryDetection;
|
use line_buffer::BinaryDetection;
|
||||||
|
use lines::{self, LineStep};
|
||||||
use searcher::{Config, Range, Searcher};
|
use searcher::{Config, Range, Searcher};
|
||||||
use sink::{
|
use sink::{
|
||||||
Sink, SinkError,
|
Sink, SinkContext, SinkContextKind, SinkError, SinkFinish, SinkMatch,
|
||||||
SinkFinish, SinkContext, SinkContextKind, SinkMatch,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -36,11 +35,7 @@ impl<'s, M: Matcher, S: Sink> Core<'s, M, S> {
|
|||||||
binary: bool,
|
binary: bool,
|
||||||
) -> Core<'s, M, S> {
|
) -> Core<'s, M, S> {
|
||||||
let line_number =
|
let line_number =
|
||||||
if searcher.config.line_number {
|
if searcher.config.line_number { Some(1) } else { None };
|
||||||
Some(1)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
let core = Core {
|
let core = Core {
|
||||||
config: &searcher.config,
|
config: &searcher.config,
|
||||||
matcher: matcher,
|
matcher: matcher,
|
||||||
@ -108,10 +103,8 @@ impl<'s, M: Matcher, S: Sink> Core<'s, M, S> {
|
|||||||
) -> Result<(), S::Error> {
|
) -> Result<(), S::Error> {
|
||||||
self.sink.finish(
|
self.sink.finish(
|
||||||
&self.searcher,
|
&self.searcher,
|
||||||
&SinkFinish {
|
&SinkFinish { byte_count, binary_byte_offset },
|
||||||
byte_count,
|
)
|
||||||
binary_byte_offset,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn match_by_line(&mut self, buf: &[u8]) -> Result<bool, S::Error> {
|
pub fn match_by_line(&mut self, buf: &[u8]) -> Result<bool, S::Error> {
|
||||||
@ -123,23 +116,22 @@ impl<'s, M: Matcher, S: Sink> Core<'s, M, S> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn roll(&mut self, buf: &[u8]) -> usize {
|
pub fn roll(&mut self, buf: &[u8]) -> usize {
|
||||||
let consumed =
|
let consumed = if self.config.max_context() == 0 {
|
||||||
if self.config.max_context() == 0 {
|
buf.len()
|
||||||
buf.len()
|
} else {
|
||||||
} else {
|
// It might seem like all we need to care about here is just
|
||||||
// It might seem like all we need to care about here is just
|
// the "before context," but in order to sink the context
|
||||||
// the "before context," but in order to sink the context
|
// separator (when before_context==0 and after_context>0), we
|
||||||
// separator (when before_context==0 and after_context>0), we
|
// need to know something about the position of the previous
|
||||||
// need to know something about the position of the previous
|
// line visited, even if we're at the beginning of the buffer.
|
||||||
// line visited, even if we're at the beginning of the buffer.
|
let context_start = lines::preceding(
|
||||||
let context_start = lines::preceding(
|
buf,
|
||||||
buf,
|
self.config.line_term.as_byte(),
|
||||||
self.config.line_term.as_byte(),
|
self.config.max_context(),
|
||||||
self.config.max_context(),
|
);
|
||||||
);
|
let consumed = cmp::max(context_start, self.last_line_visited);
|
||||||
let consumed = cmp::max(context_start, self.last_line_visited);
|
consumed
|
||||||
consumed
|
};
|
||||||
};
|
|
||||||
self.count_lines(buf, consumed);
|
self.count_lines(buf, consumed);
|
||||||
self.absolute_byte_offset += consumed as u64;
|
self.absolute_byte_offset += consumed as u64;
|
||||||
self.last_line_counted = 0;
|
self.last_line_counted = 0;
|
||||||
@ -185,11 +177,12 @@ impl<'s, M: Matcher, S: Sink> Core<'s, M, S> {
|
|||||||
if range.is_empty() {
|
if range.is_empty() {
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
let before_context_start = range.start() + lines::preceding(
|
let before_context_start = range.start()
|
||||||
&buf[range],
|
+ lines::preceding(
|
||||||
self.config.line_term.as_byte(),
|
&buf[range],
|
||||||
self.config.before_context - 1,
|
self.config.line_term.as_byte(),
|
||||||
);
|
self.config.before_context - 1,
|
||||||
|
);
|
||||||
|
|
||||||
let range = Range::new(before_context_start, range.end());
|
let range = Range::new(before_context_start, range.end());
|
||||||
let mut stepper = LineStep::new(
|
let mut stepper = LineStep::new(
|
||||||
@ -552,8 +545,7 @@ impl<'s, M: Matcher, S: Sink> Core<'s, M, S> {
|
|||||||
) -> Result<bool, S::Error> {
|
) -> Result<bool, S::Error> {
|
||||||
let is_gap = self.last_line_visited < start_of_line;
|
let is_gap = self.last_line_visited < start_of_line;
|
||||||
let any_context =
|
let any_context =
|
||||||
self.config.before_context > 0
|
self.config.before_context > 0 || self.config.after_context > 0;
|
||||||
|| self.config.after_context > 0;
|
|
||||||
|
|
||||||
if !any_context || !self.has_sunk || !is_gap {
|
if !any_context || !self.has_sunk || !is_gap {
|
||||||
Ok(true)
|
Ok(true)
|
||||||
|
@ -2,12 +2,12 @@ use std::cmp;
|
|||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
use grep_matcher::Matcher;
|
use grep_matcher::Matcher;
|
||||||
|
use line_buffer::{LineBufferReader, DEFAULT_BUFFER_CAPACITY};
|
||||||
use lines::{self, LineStep};
|
use lines::{self, LineStep};
|
||||||
use line_buffer::{DEFAULT_BUFFER_CAPACITY, LineBufferReader};
|
|
||||||
use sink::{Sink, SinkError};
|
use sink::{Sink, SinkError};
|
||||||
|
|
||||||
use searcher::{Config, Range, Searcher};
|
|
||||||
use searcher::core::Core;
|
use searcher::core::Core;
|
||||||
|
use searcher::{Config, Range, Searcher};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ReadByLine<'s, M: 's, R, S> {
|
pub struct ReadByLine<'s, M: 's, R, S> {
|
||||||
@ -17,9 +17,10 @@ pub struct ReadByLine<'s, M: 's, R, S> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'s, M, R, S> ReadByLine<'s, M, R, S>
|
impl<'s, M, R, S> ReadByLine<'s, M, R, S>
|
||||||
where M: Matcher,
|
where
|
||||||
R: io::Read,
|
M: Matcher,
|
||||||
S: Sink
|
R: io::Read,
|
||||||
|
S: Sink,
|
||||||
{
|
{
|
||||||
pub fn new(
|
pub fn new(
|
||||||
searcher: &'s Searcher,
|
searcher: &'s Searcher,
|
||||||
@ -38,9 +39,8 @@ where M: Matcher,
|
|||||||
|
|
||||||
pub fn run(mut self) -> Result<(), S::Error> {
|
pub fn run(mut self) -> Result<(), S::Error> {
|
||||||
if self.core.begin()? {
|
if self.core.begin()? {
|
||||||
while
|
while self.fill()? && self.core.match_by_line(self.rdr.buffer())? {
|
||||||
self.fill()? && self.core.match_by_line(self.rdr.buffer())?
|
}
|
||||||
{}
|
|
||||||
}
|
}
|
||||||
self.core.finish(
|
self.core.finish(
|
||||||
self.rdr.absolute_byte_offset(),
|
self.rdr.absolute_byte_offset(),
|
||||||
@ -82,7 +82,7 @@ where M: Matcher,
|
|||||||
|
|
||||||
fn should_binary_quit(&self) -> bool {
|
fn should_binary_quit(&self) -> bool {
|
||||||
self.rdr.binary_byte_offset().is_some()
|
self.rdr.binary_byte_offset().is_some()
|
||||||
&& self.config.binary.quit_byte().is_some()
|
&& self.config.binary.quit_byte().is_some()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,14 +111,11 @@ impl<'s, M: Matcher, S: Sink> SliceByLine<'s, M, S> {
|
|||||||
|
|
||||||
pub fn run(mut self) -> Result<(), S::Error> {
|
pub fn run(mut self) -> Result<(), S::Error> {
|
||||||
if self.core.begin()? {
|
if self.core.begin()? {
|
||||||
let binary_upto = cmp::min(
|
let binary_upto =
|
||||||
self.slice.len(),
|
cmp::min(self.slice.len(), DEFAULT_BUFFER_CAPACITY);
|
||||||
DEFAULT_BUFFER_CAPACITY,
|
|
||||||
);
|
|
||||||
let binary_range = Range::new(0, binary_upto);
|
let binary_range = Range::new(0, binary_upto);
|
||||||
if !self.core.detect_binary(self.slice, &binary_range)? {
|
if !self.core.detect_binary(self.slice, &binary_range)? {
|
||||||
while
|
while !self.slice[self.core.pos()..].is_empty()
|
||||||
!self.slice[self.core.pos()..].is_empty()
|
|
||||||
&& self.core.match_by_line(self.slice)?
|
&& self.core.match_by_line(self.slice)?
|
||||||
{}
|
{}
|
||||||
}
|
}
|
||||||
@ -163,10 +160,8 @@ impl<'s, M: Matcher, S: Sink> MultiLine<'s, M, S> {
|
|||||||
|
|
||||||
pub fn run(mut self) -> Result<(), S::Error> {
|
pub fn run(mut self) -> Result<(), S::Error> {
|
||||||
if self.core.begin()? {
|
if self.core.begin()? {
|
||||||
let binary_upto = cmp::min(
|
let binary_upto =
|
||||||
self.slice.len(),
|
cmp::min(self.slice.len(), DEFAULT_BUFFER_CAPACITY);
|
||||||
DEFAULT_BUFFER_CAPACITY,
|
|
||||||
);
|
|
||||||
let binary_range = Range::new(0, binary_upto);
|
let binary_range = Range::new(0, binary_upto);
|
||||||
if !self.core.detect_binary(self.slice, &binary_range)? {
|
if !self.core.detect_binary(self.slice, &binary_range)? {
|
||||||
let mut keepgoing = true;
|
let mut keepgoing = true;
|
||||||
@ -218,11 +213,8 @@ impl<'s, M: Matcher, S: Sink> MultiLine<'s, M, S> {
|
|||||||
};
|
};
|
||||||
self.advance(&mat);
|
self.advance(&mat);
|
||||||
|
|
||||||
let line = lines::locate(
|
let line =
|
||||||
self.slice,
|
lines::locate(self.slice, self.config.line_term.as_byte(), mat);
|
||||||
self.config.line_term.as_byte(),
|
|
||||||
mat,
|
|
||||||
);
|
|
||||||
// We delay sinking the match to make sure we group adjacent matches
|
// We delay sinking the match to make sure we group adjacent matches
|
||||||
// together in a single sink. Adjacent matches are distinct matches
|
// together in a single sink. Adjacent matches are distinct matches
|
||||||
// that start and end on the same line, respectively. This guarantees
|
// that start and end on the same line, respectively. This guarantees
|
||||||
@ -502,7 +494,8 @@ byte count:366
|
|||||||
let byte_count = haystack.len();
|
let byte_count = haystack.len();
|
||||||
let exp = format!(
|
let exp = format!(
|
||||||
"4:abc\n8:defxxxabc\n18:defxxx\n\nbyte count:{}\n",
|
"4:abc\n8:defxxxabc\n18:defxxx\n\nbyte count:{}\n",
|
||||||
byte_count);
|
byte_count
|
||||||
|
);
|
||||||
|
|
||||||
SearcherTester::new(haystack, "abc\ndef")
|
SearcherTester::new(haystack, "abc\ndef")
|
||||||
.by_line(false)
|
.by_line(false)
|
||||||
@ -517,7 +510,8 @@ byte count:366
|
|||||||
let byte_count = haystack.len();
|
let byte_count = haystack.len();
|
||||||
let exp = format!(
|
let exp = format!(
|
||||||
"4:abc\n8:defabc\n15:defxxx\n\nbyte count:{}\n",
|
"4:abc\n8:defabc\n15:defxxx\n\nbyte count:{}\n",
|
||||||
byte_count);
|
byte_count
|
||||||
|
);
|
||||||
|
|
||||||
SearcherTester::new(haystack, "abc\ndef")
|
SearcherTester::new(haystack, "abc\ndef")
|
||||||
.by_line(false)
|
.by_line(false)
|
||||||
@ -571,9 +565,8 @@ d
|
|||||||
";
|
";
|
||||||
let byte_count = haystack.len();
|
let byte_count = haystack.len();
|
||||||
let exp = format!("4:\n7:\n8:\n\nbyte count:{}\n", byte_count);
|
let exp = format!("4:\n7:\n8:\n\nbyte count:{}\n", byte_count);
|
||||||
let exp_line = format!(
|
let exp_line =
|
||||||
"3:4:\n5:7:\n6:8:\n\nbyte count:{}\n",
|
format!("3:4:\n5:7:\n6:8:\n\nbyte count:{}\n", byte_count);
|
||||||
byte_count);
|
|
||||||
|
|
||||||
SearcherTester::new(haystack, r"^$")
|
SearcherTester::new(haystack, r"^$")
|
||||||
.expected_no_line_number(&exp)
|
.expected_no_line_number(&exp)
|
||||||
@ -595,9 +588,8 @@ c
|
|||||||
d";
|
d";
|
||||||
let byte_count = haystack.len();
|
let byte_count = haystack.len();
|
||||||
let exp = format!("4:\n7:\n8:\n\nbyte count:{}\n", byte_count);
|
let exp = format!("4:\n7:\n8:\n\nbyte count:{}\n", byte_count);
|
||||||
let exp_line = format!(
|
let exp_line =
|
||||||
"3:4:\n5:7:\n6:8:\n\nbyte count:{}\n",
|
format!("3:4:\n5:7:\n6:8:\n\nbyte count:{}\n", byte_count);
|
||||||
byte_count);
|
|
||||||
|
|
||||||
SearcherTester::new(haystack, r"^$")
|
SearcherTester::new(haystack, r"^$")
|
||||||
.expected_no_line_number(&exp)
|
.expected_no_line_number(&exp)
|
||||||
@ -620,12 +612,9 @@ d
|
|||||||
|
|
||||||
";
|
";
|
||||||
let byte_count = haystack.len();
|
let byte_count = haystack.len();
|
||||||
let exp = format!(
|
let exp = format!("4:\n7:\n8:\n11:\n\nbyte count:{}\n", byte_count);
|
||||||
"4:\n7:\n8:\n11:\n\nbyte count:{}\n",
|
let exp_line =
|
||||||
byte_count);
|
format!("3:4:\n5:7:\n6:8:\n8:11:\n\nbyte count:{}\n", byte_count);
|
||||||
let exp_line = format!(
|
|
||||||
"3:4:\n5:7:\n6:8:\n8:11:\n\nbyte count:{}\n",
|
|
||||||
byte_count);
|
|
||||||
|
|
||||||
SearcherTester::new(haystack, r"^$")
|
SearcherTester::new(haystack, r"^$")
|
||||||
.expected_no_line_number(&exp)
|
.expected_no_line_number(&exp)
|
||||||
@ -667,11 +656,8 @@ d
|
|||||||
let mut searcher = SearcherBuilder::new()
|
let mut searcher = SearcherBuilder::new()
|
||||||
.heap_limit(Some(3)) // max line length is 4, one byte short
|
.heap_limit(Some(3)) // max line length is 4, one byte short
|
||||||
.build();
|
.build();
|
||||||
let result = searcher.search_reader(
|
let result =
|
||||||
&matcher,
|
searcher.search_reader(&matcher, haystack.as_bytes(), &mut sink);
|
||||||
haystack.as_bytes(),
|
|
||||||
&mut sink,
|
|
||||||
);
|
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -691,11 +677,8 @@ d
|
|||||||
.multi_line(true)
|
.multi_line(true)
|
||||||
.heap_limit(Some(haystack.len())) // actually need one more byte
|
.heap_limit(Some(haystack.len())) // actually need one more byte
|
||||||
.build();
|
.build();
|
||||||
let result = searcher.search_reader(
|
let result =
|
||||||
&matcher,
|
searcher.search_reader(&matcher, haystack.as_bytes(), &mut sink);
|
||||||
haystack.as_bytes(),
|
|
||||||
&mut sink,
|
|
||||||
);
|
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1508,12 +1491,16 @@ and exhibited clearly, with a label attached.\
|
|||||||
|
|
||||||
let haystack = SHERLOCK;
|
let haystack = SHERLOCK;
|
||||||
let matcher = RegexMatcher::new("Sherlock");
|
let matcher = RegexMatcher::new("Sherlock");
|
||||||
let mut searcher = SearcherBuilder::new()
|
let mut searcher = SearcherBuilder::new().line_number(true).build();
|
||||||
.line_number(true)
|
searcher
|
||||||
.build();
|
.search_reader(
|
||||||
searcher.search_reader(&matcher, haystack, sinks::Lossy(|n, line| {
|
&matcher,
|
||||||
print!("{}:{}", n, line);
|
haystack,
|
||||||
Ok(true)
|
sinks::Lossy(|n, line| {
|
||||||
})).unwrap();
|
print!("{}:{}", n, line);
|
||||||
|
Ok(true)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,10 +9,10 @@ use encoding_rs;
|
|||||||
use encoding_rs_io::DecodeReaderBytesBuilder;
|
use encoding_rs_io::DecodeReaderBytesBuilder;
|
||||||
use grep_matcher::{LineTerminator, Match, Matcher};
|
use grep_matcher::{LineTerminator, Match, Matcher};
|
||||||
use line_buffer::{
|
use line_buffer::{
|
||||||
self, BufferAllocation, LineBuffer, LineBufferBuilder, LineBufferReader,
|
self, alloc_error, BufferAllocation, LineBuffer, LineBufferBuilder,
|
||||||
DEFAULT_BUFFER_CAPACITY, alloc_error,
|
LineBufferReader, DEFAULT_BUFFER_CAPACITY,
|
||||||
};
|
};
|
||||||
use searcher::glue::{ReadByLine, SliceByLine, MultiLine};
|
use searcher::glue::{MultiLine, ReadByLine, SliceByLine};
|
||||||
use sink::{Sink, SinkError};
|
use sink::{Sink, SinkError};
|
||||||
|
|
||||||
pub use self::mmap::MmapChoice;
|
pub use self::mmap::MmapChoice;
|
||||||
@ -211,12 +211,11 @@ impl Config {
|
|||||||
.binary_detection(self.binary.0);
|
.binary_detection(self.binary.0);
|
||||||
|
|
||||||
if let Some(limit) = self.heap_limit {
|
if let Some(limit) = self.heap_limit {
|
||||||
let (capacity, additional) =
|
let (capacity, additional) = if limit <= DEFAULT_BUFFER_CAPACITY {
|
||||||
if limit <= DEFAULT_BUFFER_CAPACITY {
|
(limit, 0)
|
||||||
(limit, 0)
|
} else {
|
||||||
} else {
|
(DEFAULT_BUFFER_CAPACITY, limit - DEFAULT_BUFFER_CAPACITY)
|
||||||
(DEFAULT_BUFFER_CAPACITY, limit - DEFAULT_BUFFER_CAPACITY)
|
};
|
||||||
};
|
|
||||||
builder
|
builder
|
||||||
.capacity(capacity)
|
.capacity(capacity)
|
||||||
.buffer_alloc(BufferAllocation::Error(additional));
|
.buffer_alloc(BufferAllocation::Error(additional));
|
||||||
@ -258,7 +257,9 @@ pub enum ConfigError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ::std::error::Error for ConfigError {
|
impl ::std::error::Error for ConfigError {
|
||||||
fn description(&self) -> &str { "grep-searcher configuration error" }
|
fn description(&self) -> &str {
|
||||||
|
"grep-searcher configuration error"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ConfigError {
|
impl fmt::Display for ConfigError {
|
||||||
@ -272,17 +273,14 @@ impl fmt::Display for ConfigError {
|
|||||||
f,
|
f,
|
||||||
"grep config error: mismatched line terminators, \
|
"grep config error: mismatched line terminators, \
|
||||||
matcher has {:?} but searcher has {:?}",
|
matcher has {:?} but searcher has {:?}",
|
||||||
matcher,
|
matcher, searcher
|
||||||
searcher
|
|
||||||
)
|
|
||||||
}
|
|
||||||
ConfigError::UnknownEncoding { ref label } => {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"grep config error: unknown encoding: {}",
|
|
||||||
String::from_utf8_lossy(label),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
ConfigError::UnknownEncoding { ref label } => write!(
|
||||||
|
f,
|
||||||
|
"grep config error: unknown encoding: {}",
|
||||||
|
String::from_utf8_lossy(label),
|
||||||
|
),
|
||||||
_ => panic!("BUG: unexpected variant found"),
|
_ => panic!("BUG: unexpected variant found"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -310,9 +308,7 @@ impl Default for SearcherBuilder {
|
|||||||
impl SearcherBuilder {
|
impl SearcherBuilder {
|
||||||
/// Create a new searcher builder with a default configuration.
|
/// Create a new searcher builder with a default configuration.
|
||||||
pub fn new() -> SearcherBuilder {
|
pub fn new() -> SearcherBuilder {
|
||||||
SearcherBuilder {
|
SearcherBuilder { config: Config::default() }
|
||||||
config: Config::default(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build a searcher with the given matcher.
|
/// Build a searcher with the given matcher.
|
||||||
@ -334,7 +330,7 @@ impl SearcherBuilder {
|
|||||||
Searcher {
|
Searcher {
|
||||||
config: config,
|
config: config,
|
||||||
decode_builder: decode_builder,
|
decode_builder: decode_builder,
|
||||||
decode_buffer: RefCell::new(vec![0; 8 * (1<<10)]),
|
decode_buffer: RefCell::new(vec![0; 8 * (1 << 10)]),
|
||||||
line_buffer: RefCell::new(self.config.line_buffer()),
|
line_buffer: RefCell::new(self.config.line_buffer()),
|
||||||
multi_line_buffer: RefCell::new(vec![]),
|
multi_line_buffer: RefCell::new(vec![]),
|
||||||
}
|
}
|
||||||
@ -622,9 +618,10 @@ impl Searcher {
|
|||||||
path: P,
|
path: P,
|
||||||
write_to: S,
|
write_to: S,
|
||||||
) -> Result<(), S::Error>
|
) -> Result<(), S::Error>
|
||||||
where P: AsRef<Path>,
|
where
|
||||||
M: Matcher,
|
P: AsRef<Path>,
|
||||||
S: Sink,
|
M: Matcher,
|
||||||
|
S: Sink,
|
||||||
{
|
{
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
let file = File::open(path).map_err(S::Error::error_io)?;
|
let file = File::open(path).map_err(S::Error::error_io)?;
|
||||||
@ -643,8 +640,9 @@ impl Searcher {
|
|||||||
file: &File,
|
file: &File,
|
||||||
write_to: S,
|
write_to: S,
|
||||||
) -> Result<(), S::Error>
|
) -> Result<(), S::Error>
|
||||||
where M: Matcher,
|
where
|
||||||
S: Sink,
|
M: Matcher,
|
||||||
|
S: Sink,
|
||||||
{
|
{
|
||||||
self.search_file_maybe_path(matcher, None, file, write_to)
|
self.search_file_maybe_path(matcher, None, file, write_to)
|
||||||
}
|
}
|
||||||
@ -656,8 +654,9 @@ impl Searcher {
|
|||||||
file: &File,
|
file: &File,
|
||||||
write_to: S,
|
write_to: S,
|
||||||
) -> Result<(), S::Error>
|
) -> Result<(), S::Error>
|
||||||
where M: Matcher,
|
where
|
||||||
S: Sink,
|
M: Matcher,
|
||||||
|
S: Sink,
|
||||||
{
|
{
|
||||||
if let Some(mmap) = self.config.mmap.open(file, path) {
|
if let Some(mmap) = self.config.mmap.open(file, path) {
|
||||||
trace!("{:?}: searching via memory map", path);
|
trace!("{:?}: searching via memory map", path);
|
||||||
@ -675,7 +674,8 @@ impl Searcher {
|
|||||||
matcher,
|
matcher,
|
||||||
&*self.multi_line_buffer.borrow(),
|
&*self.multi_line_buffer.borrow(),
|
||||||
write_to,
|
write_to,
|
||||||
).run()
|
)
|
||||||
|
.run()
|
||||||
} else {
|
} else {
|
||||||
trace!("{:?}: searching using generic reader", path);
|
trace!("{:?}: searching using generic reader", path);
|
||||||
self.search_reader(matcher, file, write_to)
|
self.search_reader(matcher, file, write_to)
|
||||||
@ -699,14 +699,16 @@ impl Searcher {
|
|||||||
read_from: R,
|
read_from: R,
|
||||||
write_to: S,
|
write_to: S,
|
||||||
) -> Result<(), S::Error>
|
) -> Result<(), S::Error>
|
||||||
where M: Matcher,
|
where
|
||||||
R: io::Read,
|
M: Matcher,
|
||||||
S: Sink,
|
R: io::Read,
|
||||||
|
S: Sink,
|
||||||
{
|
{
|
||||||
self.check_config(&matcher).map_err(S::Error::error_config)?;
|
self.check_config(&matcher).map_err(S::Error::error_config)?;
|
||||||
|
|
||||||
let mut decode_buffer = self.decode_buffer.borrow_mut();
|
let mut decode_buffer = self.decode_buffer.borrow_mut();
|
||||||
let read_from = self.decode_builder
|
let read_from = self
|
||||||
|
.decode_builder
|
||||||
.build_with_buffer(read_from, &mut *decode_buffer)
|
.build_with_buffer(read_from, &mut *decode_buffer)
|
||||||
.map_err(S::Error::error_io)?;
|
.map_err(S::Error::error_io)?;
|
||||||
|
|
||||||
@ -719,7 +721,8 @@ impl Searcher {
|
|||||||
matcher,
|
matcher,
|
||||||
&*self.multi_line_buffer.borrow(),
|
&*self.multi_line_buffer.borrow(),
|
||||||
write_to,
|
write_to,
|
||||||
).run()
|
)
|
||||||
|
.run()
|
||||||
} else {
|
} else {
|
||||||
let mut line_buffer = self.line_buffer.borrow_mut();
|
let mut line_buffer = self.line_buffer.borrow_mut();
|
||||||
let rdr = LineBufferReader::new(read_from, &mut *line_buffer);
|
let rdr = LineBufferReader::new(read_from, &mut *line_buffer);
|
||||||
@ -736,8 +739,9 @@ impl Searcher {
|
|||||||
slice: &[u8],
|
slice: &[u8],
|
||||||
write_to: S,
|
write_to: S,
|
||||||
) -> Result<(), S::Error>
|
) -> Result<(), S::Error>
|
||||||
where M: Matcher,
|
where
|
||||||
S: Sink,
|
M: Matcher,
|
||||||
|
S: Sink,
|
||||||
{
|
{
|
||||||
self.check_config(&matcher).map_err(S::Error::error_config)?;
|
self.check_config(&matcher).map_err(S::Error::error_config)?;
|
||||||
|
|
||||||
@ -764,8 +768,7 @@ impl Searcher {
|
|||||||
/// Check that the searcher's configuration and the matcher are consistent
|
/// Check that the searcher's configuration and the matcher are consistent
|
||||||
/// with each other.
|
/// with each other.
|
||||||
fn check_config<M: Matcher>(&self, matcher: M) -> Result<(), ConfigError> {
|
fn check_config<M: Matcher>(&self, matcher: M) -> Result<(), ConfigError> {
|
||||||
if self.config.heap_limit == Some(0)
|
if self.config.heap_limit == Some(0) && !self.config.mmap.is_enabled()
|
||||||
&& !self.config.mmap.is_enabled()
|
|
||||||
{
|
{
|
||||||
return Err(ConfigError::SearchUnavailable);
|
return Err(ConfigError::SearchUnavailable);
|
||||||
}
|
}
|
||||||
@ -785,7 +788,7 @@ impl Searcher {
|
|||||||
/// Returns true if and only if the given slice needs to be transcoded.
|
/// Returns true if and only if the given slice needs to be transcoded.
|
||||||
fn slice_needs_transcoding(&self, slice: &[u8]) -> bool {
|
fn slice_needs_transcoding(&self, slice: &[u8]) -> bool {
|
||||||
self.config.encoding.is_some()
|
self.config.encoding.is_some()
|
||||||
|| (self.config.bom_sniffing && slice_has_utf16_bom(slice))
|
|| (self.config.bom_sniffing && slice_has_utf16_bom(slice))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -886,7 +889,8 @@ impl Searcher {
|
|||||||
assert!(self.config.multi_line);
|
assert!(self.config.multi_line);
|
||||||
|
|
||||||
let mut decode_buffer = self.decode_buffer.borrow_mut();
|
let mut decode_buffer = self.decode_buffer.borrow_mut();
|
||||||
let mut read_from = self.decode_builder
|
let mut read_from = self
|
||||||
|
.decode_builder
|
||||||
.build_with_buffer(file, &mut *decode_buffer)
|
.build_with_buffer(file, &mut *decode_buffer)
|
||||||
.map_err(S::Error::error_io)?;
|
.map_err(S::Error::error_io)?;
|
||||||
|
|
||||||
@ -900,10 +904,8 @@ impl Searcher {
|
|||||||
if self.config.heap_limit.is_none() {
|
if self.config.heap_limit.is_none() {
|
||||||
let mut buf = self.multi_line_buffer.borrow_mut();
|
let mut buf = self.multi_line_buffer.borrow_mut();
|
||||||
buf.clear();
|
buf.clear();
|
||||||
let cap = file
|
let cap =
|
||||||
.metadata()
|
file.metadata().map(|m| m.len() as usize + 1).unwrap_or(0);
|
||||||
.map(|m| m.len() as usize + 1)
|
|
||||||
.unwrap_or(0);
|
|
||||||
buf.reserve(cap);
|
buf.reserve(cap);
|
||||||
read_from.read_to_end(&mut *buf).map_err(S::Error::error_io)?;
|
read_from.read_to_end(&mut *buf).map_err(S::Error::error_io)?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@ -929,7 +931,9 @@ impl Searcher {
|
|||||||
let heap_limit = match self.config.heap_limit {
|
let heap_limit = match self.config.heap_limit {
|
||||||
Some(heap_limit) => heap_limit,
|
Some(heap_limit) => heap_limit,
|
||||||
None => {
|
None => {
|
||||||
read_from.read_to_end(&mut *buf).map_err(S::Error::error_io)?;
|
read_from
|
||||||
|
.read_to_end(&mut *buf)
|
||||||
|
.map_err(S::Error::error_io)?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -983,16 +987,14 @@ fn slice_has_utf16_bom(slice: &[u8]) -> bool {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use testutil::{KitchenSink, RegexMatcher};
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use testutil::{KitchenSink, RegexMatcher};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn config_error_heap_limit() {
|
fn config_error_heap_limit() {
|
||||||
let matcher = RegexMatcher::new("");
|
let matcher = RegexMatcher::new("");
|
||||||
let sink = KitchenSink::new();
|
let sink = KitchenSink::new();
|
||||||
let mut searcher = SearcherBuilder::new()
|
let mut searcher = SearcherBuilder::new().heap_limit(Some(0)).build();
|
||||||
.heap_limit(Some(0))
|
|
||||||
.build();
|
|
||||||
let res = searcher.search_slice(matcher, &[], sink);
|
let res = searcher.search_slice(matcher, &[], sink);
|
||||||
assert!(res.is_err());
|
assert!(res.is_err());
|
||||||
}
|
}
|
||||||
|
@ -200,10 +200,7 @@ pub trait Sink {
|
|||||||
/// `finish` is not called and the error is bubbled back up to the caller
|
/// `finish` is not called and the error is bubbled back up to the caller
|
||||||
/// of the searcher.
|
/// of the searcher.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn begin(
|
fn begin(&mut self, _searcher: &Searcher) -> Result<bool, Self::Error> {
|
||||||
&mut self,
|
|
||||||
_searcher: &Searcher,
|
|
||||||
) -> Result<bool, Self::Error> {
|
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,10 +258,7 @@ impl<'a, S: Sink> Sink for &'a mut S {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn begin(
|
fn begin(&mut self, searcher: &Searcher) -> Result<bool, S::Error> {
|
||||||
&mut self,
|
|
||||||
searcher: &Searcher,
|
|
||||||
) -> Result<bool, S::Error> {
|
|
||||||
(**self).begin(searcher)
|
(**self).begin(searcher)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -317,10 +311,7 @@ impl<S: Sink + ?Sized> Sink for Box<S> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn begin(
|
fn begin(&mut self, searcher: &Searcher) -> Result<bool, S::Error> {
|
||||||
&mut self,
|
|
||||||
searcher: &Searcher,
|
|
||||||
) -> Result<bool, S::Error> {
|
|
||||||
(**self).begin(searcher)
|
(**self).begin(searcher)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -508,8 +499,8 @@ pub mod sinks {
|
|||||||
use std::io;
|
use std::io;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
use searcher::Searcher;
|
|
||||||
use super::{Sink, SinkError, SinkMatch};
|
use super::{Sink, SinkError, SinkMatch};
|
||||||
|
use searcher::Searcher;
|
||||||
|
|
||||||
/// A sink that provides line numbers and matches as strings while ignoring
|
/// A sink that provides line numbers and matches as strings while ignoring
|
||||||
/// everything else.
|
/// everything else.
|
||||||
@ -528,10 +519,12 @@ pub mod sinks {
|
|||||||
/// number of the first line in the match.
|
/// number of the first line in the match.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct UTF8<F>(pub F)
|
pub struct UTF8<F>(pub F)
|
||||||
where F: FnMut(u64, &str) -> Result<bool, io::Error>;
|
where
|
||||||
|
F: FnMut(u64, &str) -> Result<bool, io::Error>;
|
||||||
|
|
||||||
impl<F> Sink for UTF8<F>
|
impl<F> Sink for UTF8<F>
|
||||||
where F: FnMut(u64, &str) -> Result<bool, io::Error>
|
where
|
||||||
|
F: FnMut(u64, &str) -> Result<bool, io::Error>,
|
||||||
{
|
{
|
||||||
type Error = io::Error;
|
type Error = io::Error;
|
||||||
|
|
||||||
@ -574,10 +567,12 @@ pub mod sinks {
|
|||||||
/// number of the first line in the match.
|
/// number of the first line in the match.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Lossy<F>(pub F)
|
pub struct Lossy<F>(pub F)
|
||||||
where F: FnMut(u64, &str) -> Result<bool, io::Error>;
|
where
|
||||||
|
F: FnMut(u64, &str) -> Result<bool, io::Error>;
|
||||||
|
|
||||||
impl<F> Sink for Lossy<F>
|
impl<F> Sink for Lossy<F>
|
||||||
where F: FnMut(u64, &str) -> Result<bool, io::Error>
|
where
|
||||||
|
F: FnMut(u64, &str) -> Result<bool, io::Error>,
|
||||||
{
|
{
|
||||||
type Error = io::Error;
|
type Error = io::Error;
|
||||||
|
|
||||||
@ -622,10 +617,12 @@ pub mod sinks {
|
|||||||
/// number of the first line in the match.
|
/// number of the first line in the match.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Bytes<F>(pub F)
|
pub struct Bytes<F>(pub F)
|
||||||
where F: FnMut(u64, &[u8]) -> Result<bool, io::Error>;
|
where
|
||||||
|
F: FnMut(u64, &[u8]) -> Result<bool, io::Error>;
|
||||||
|
|
||||||
impl<F> Sink for Bytes<F>
|
impl<F> Sink for Bytes<F>
|
||||||
where F: FnMut(u64, &[u8]) -> Result<bool, io::Error>
|
where
|
||||||
|
F: FnMut(u64, &[u8]) -> Result<bool, io::Error>,
|
||||||
{
|
{
|
||||||
type Error = io::Error;
|
type Error = io::Error;
|
||||||
|
|
||||||
|
@ -52,10 +52,7 @@ impl RegexMatcher {
|
|||||||
/// Whether to return every line as a candidate or not.
|
/// Whether to return every line as a candidate or not.
|
||||||
///
|
///
|
||||||
/// This forces searchers to handle the case of reporting a false positive.
|
/// This forces searchers to handle the case of reporting a false positive.
|
||||||
pub fn every_line_is_candidate(
|
pub fn every_line_is_candidate(&mut self, yes: bool) -> &mut RegexMatcher {
|
||||||
&mut self,
|
|
||||||
yes: bool,
|
|
||||||
) -> &mut RegexMatcher {
|
|
||||||
self.every_line_is_candidate = yes;
|
self.every_line_is_candidate = yes;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -70,9 +67,10 @@ impl Matcher for RegexMatcher {
|
|||||||
haystack: &[u8],
|
haystack: &[u8],
|
||||||
at: usize,
|
at: usize,
|
||||||
) -> Result<Option<Match>, NoError> {
|
) -> Result<Option<Match>, NoError> {
|
||||||
Ok(self.regex
|
Ok(self
|
||||||
.find_at(haystack, at)
|
.regex
|
||||||
.map(|m| Match::new(m.start(), m.end())))
|
.find_at(haystack, at)
|
||||||
|
.map(|m| Match::new(m.start(), m.end())))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_captures(&self) -> Result<NoCaptures, NoError> {
|
fn new_captures(&self) -> Result<NoCaptures, NoError> {
|
||||||
@ -253,8 +251,10 @@ impl SearcherTester {
|
|||||||
panic!("an 'expected' string with NO line numbers must be given");
|
panic!("an 'expected' string with NO line numbers must be given");
|
||||||
}
|
}
|
||||||
if self.line_number && self.expected_with_line_number.is_none() {
|
if self.line_number && self.expected_with_line_number.is_none() {
|
||||||
panic!("an 'expected' string with line numbers must be given, \
|
panic!(
|
||||||
or disable testing with line numbers");
|
"an 'expected' string with line numbers must be given, \
|
||||||
|
or disable testing with line numbers"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let configs = self.configs();
|
let configs = self.configs();
|
||||||
@ -465,18 +465,17 @@ impl SearcherTester {
|
|||||||
lens.sort();
|
lens.sort();
|
||||||
lens.reverse();
|
lens.reverse();
|
||||||
|
|
||||||
let context_count =
|
let context_count = if self.passthru {
|
||||||
if self.passthru {
|
self.haystack.lines().count()
|
||||||
self.haystack.lines().count()
|
} else {
|
||||||
} else {
|
// Why do we add 2 here? Well, we need to add 1 in order to
|
||||||
// Why do we add 2 here? Well, we need to add 1 in order to
|
// have room to search at least one line. We add another
|
||||||
// have room to search at least one line. We add another
|
// because the implementation will occasionally include
|
||||||
// because the implementation will occasionally include
|
// an additional line when handling the context. There's
|
||||||
// an additional line when handling the context. There's
|
// no particularly good reason, other than keeping the
|
||||||
// no particularly good reason, other than keeping the
|
// implementation simple.
|
||||||
// implementation simple.
|
2 + self.before_context + self.after_context
|
||||||
2 + self.before_context + self.after_context
|
};
|
||||||
};
|
|
||||||
|
|
||||||
// We add 1 to each line since `str::lines` doesn't include the
|
// We add 1 to each line since `str::lines` doesn't include the
|
||||||
// line terminator.
|
// line terminator.
|
||||||
@ -635,10 +634,11 @@ impl SearcherTester {
|
|||||||
if self.multi_line && self.line_number {
|
if self.multi_line && self.line_number {
|
||||||
let mut builder = builder.clone();
|
let mut builder = builder.clone();
|
||||||
let expected_slice = match self.expected_slice_with_line_number {
|
let expected_slice = match self.expected_slice_with_line_number {
|
||||||
None => {
|
None => self
|
||||||
self.expected_with_line_number
|
.expected_with_line_number
|
||||||
.as_ref().unwrap().to_string()
|
.as_ref()
|
||||||
}
|
.unwrap()
|
||||||
|
.to_string(),
|
||||||
Some(ref e) => e.to_string(),
|
Some(ref e) => e.to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -40,13 +40,11 @@ fn search(pattern: &str, paths: &[OsString]) -> Result<(), Box<dyn Error>> {
|
|||||||
.build();
|
.build();
|
||||||
let mut printer = StandardBuilder::new()
|
let mut printer = StandardBuilder::new()
|
||||||
.color_specs(ColorSpecs::default_with_color())
|
.color_specs(ColorSpecs::default_with_color())
|
||||||
.build(cli::stdout(
|
.build(cli::stdout(if cli::is_tty_stdout() {
|
||||||
if cli::is_tty_stdout() {
|
ColorChoice::Auto
|
||||||
ColorChoice::Auto
|
} else {
|
||||||
} else {
|
ColorChoice::Never
|
||||||
ColorChoice::Never
|
}));
|
||||||
}
|
|
||||||
));
|
|
||||||
|
|
||||||
for path in paths {
|
for path in paths {
|
||||||
for result in WalkDir::new(path) {
|
for result in WalkDir::new(path) {
|
||||||
|
244
ignore/src/default_types.rs
Normal file
244
ignore/src/default_types.rs
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
/// This list represents the default file types that ripgrep ships with. In
|
||||||
|
/// general, any file format is fair game, although it should generally be
|
||||||
|
/// limited to reasonably popular open formats. For other cases, you can add
|
||||||
|
/// types to each invocation of ripgrep with the '--type-add' flag.
|
||||||
|
///
|
||||||
|
/// If you would like to add or improve this list, please file a PR:
|
||||||
|
/// https://github.com/BurntSushi/ripgrep
|
||||||
|
///
|
||||||
|
/// Please try to keep this list sorted lexicographically and wrapped to 79
|
||||||
|
/// columns (inclusive).
|
||||||
|
#[rustfmt::skip]
|
||||||
|
pub const DEFAULT_TYPES: &[(&str, &[&str])] = &[
|
||||||
|
("agda", &["*.agda", "*.lagda"]),
|
||||||
|
("aidl", &["*.aidl"]),
|
||||||
|
("amake", &["*.mk", "*.bp"]),
|
||||||
|
("asciidoc", &["*.adoc", "*.asc", "*.asciidoc"]),
|
||||||
|
("asm", &["*.asm", "*.s", "*.S"]),
|
||||||
|
("asp", &[
|
||||||
|
"*.aspx", "*.aspx.cs", "*.aspx.cs", "*.ascx", "*.ascx.cs", "*.ascx.vb",
|
||||||
|
]),
|
||||||
|
("ats", &["*.ats", "*.dats", "*.sats", "*.hats"]),
|
||||||
|
("avro", &["*.avdl", "*.avpr", "*.avsc"]),
|
||||||
|
("awk", &["*.awk"]),
|
||||||
|
("bazel", &["*.bzl", "WORKSPACE", "BUILD", "BUILD.bazel"]),
|
||||||
|
("bitbake", &["*.bb", "*.bbappend", "*.bbclass", "*.conf", "*.inc"]),
|
||||||
|
("brotli", &["*.br"]),
|
||||||
|
("buildstream", &["*.bst"]),
|
||||||
|
("bzip2", &["*.bz2", "*.tbz2"]),
|
||||||
|
("c", &["*.[chH]", "*.[chH].in", "*.cats"]),
|
||||||
|
("cabal", &["*.cabal"]),
|
||||||
|
("cbor", &["*.cbor"]),
|
||||||
|
("ceylon", &["*.ceylon"]),
|
||||||
|
("clojure", &["*.clj", "*.cljc", "*.cljs", "*.cljx"]),
|
||||||
|
("cmake", &["*.cmake", "CMakeLists.txt"]),
|
||||||
|
("coffeescript", &["*.coffee"]),
|
||||||
|
("config", &["*.cfg", "*.conf", "*.config", "*.ini"]),
|
||||||
|
("cpp", &[
|
||||||
|
"*.[ChH]", "*.cc", "*.[ch]pp", "*.[ch]xx", "*.hh", "*.inl",
|
||||||
|
"*.[ChH].in", "*.cc.in", "*.[ch]pp.in", "*.[ch]xx.in", "*.hh.in",
|
||||||
|
]),
|
||||||
|
("creole", &["*.creole"]),
|
||||||
|
("crystal", &["Projectfile", "*.cr"]),
|
||||||
|
("cs", &["*.cs"]),
|
||||||
|
("csharp", &["*.cs"]),
|
||||||
|
("cshtml", &["*.cshtml"]),
|
||||||
|
("css", &["*.css", "*.scss"]),
|
||||||
|
("csv", &["*.csv"]),
|
||||||
|
("cython", &["*.pyx", "*.pxi", "*.pxd"]),
|
||||||
|
("d", &["*.d"]),
|
||||||
|
("dart", &["*.dart"]),
|
||||||
|
("dhall", &["*.dhall"]),
|
||||||
|
("diff", &["*.patch", "*.diff"]),
|
||||||
|
("docker", &["*Dockerfile*"]),
|
||||||
|
("edn", &["*.edn"]),
|
||||||
|
("elisp", &["*.el"]),
|
||||||
|
("elixir", &["*.ex", "*.eex", "*.exs"]),
|
||||||
|
("elm", &["*.elm"]),
|
||||||
|
("erb", &["*.erb"]),
|
||||||
|
("erlang", &["*.erl", "*.hrl"]),
|
||||||
|
("fidl", &["*.fidl"]),
|
||||||
|
("fish", &["*.fish"]),
|
||||||
|
("fortran", &[
|
||||||
|
"*.f", "*.F", "*.f77", "*.F77", "*.pfo",
|
||||||
|
"*.f90", "*.F90", "*.f95", "*.F95",
|
||||||
|
]),
|
||||||
|
("fsharp", &["*.fs", "*.fsx", "*.fsi"]),
|
||||||
|
("gap", &["*.g", "*.gap", "*.gi", "*.gd", "*.tst"]),
|
||||||
|
("gn", &["*.gn", "*.gni"]),
|
||||||
|
("go", &["*.go"]),
|
||||||
|
("gradle", &["*.gradle"]),
|
||||||
|
("groovy", &["*.groovy", "*.gradle"]),
|
||||||
|
("gzip", &["*.gz", "*.tgz"]),
|
||||||
|
("h", &["*.h", "*.hpp"]),
|
||||||
|
("haml", &["*.haml"]),
|
||||||
|
("haskell", &["*.hs", "*.lhs", "*.cpphs", "*.c2hs", "*.hsc"]),
|
||||||
|
("hbs", &["*.hbs"]),
|
||||||
|
("hs", &["*.hs", "*.lhs"]),
|
||||||
|
("html", &["*.htm", "*.html", "*.ejs"]),
|
||||||
|
("idris", &["*.idr", "*.lidr"]),
|
||||||
|
("java", &["*.java", "*.jsp", "*.jspx", "*.properties"]),
|
||||||
|
("jinja", &["*.j2", "*.jinja", "*.jinja2"]),
|
||||||
|
("jl", &["*.jl"]),
|
||||||
|
("js", &["*.js", "*.jsx", "*.vue"]),
|
||||||
|
("json", &["*.json", "composer.lock"]),
|
||||||
|
("jsonl", &["*.jsonl"]),
|
||||||
|
("julia", &["*.jl"]),
|
||||||
|
("jupyter", &["*.ipynb", "*.jpynb"]),
|
||||||
|
("kotlin", &["*.kt", "*.kts"]),
|
||||||
|
("less", &["*.less"]),
|
||||||
|
("license", &[
|
||||||
|
// General
|
||||||
|
"COPYING", "COPYING[.-]*",
|
||||||
|
"COPYRIGHT", "COPYRIGHT[.-]*",
|
||||||
|
"EULA", "EULA[.-]*",
|
||||||
|
"licen[cs]e", "licen[cs]e.*",
|
||||||
|
"LICEN[CS]E", "LICEN[CS]E[.-]*", "*[.-]LICEN[CS]E*",
|
||||||
|
"NOTICE", "NOTICE[.-]*",
|
||||||
|
"PATENTS", "PATENTS[.-]*",
|
||||||
|
"UNLICEN[CS]E", "UNLICEN[CS]E[.-]*",
|
||||||
|
// GPL (gpl.txt, etc.)
|
||||||
|
"agpl[.-]*",
|
||||||
|
"gpl[.-]*",
|
||||||
|
"lgpl[.-]*",
|
||||||
|
// Other license-specific (APACHE-2.0.txt, etc.)
|
||||||
|
"AGPL-*[0-9]*",
|
||||||
|
"APACHE-*[0-9]*",
|
||||||
|
"BSD-*[0-9]*",
|
||||||
|
"CC-BY-*",
|
||||||
|
"GFDL-*[0-9]*",
|
||||||
|
"GNU-*[0-9]*",
|
||||||
|
"GPL-*[0-9]*",
|
||||||
|
"LGPL-*[0-9]*",
|
||||||
|
"MIT-*[0-9]*",
|
||||||
|
"MPL-*[0-9]*",
|
||||||
|
"OFL-*[0-9]*",
|
||||||
|
]),
|
||||||
|
("lisp", &["*.el", "*.jl", "*.lisp", "*.lsp", "*.sc", "*.scm"]),
|
||||||
|
("lock", &["*.lock", "package-lock.json"]),
|
||||||
|
("log", &["*.log"]),
|
||||||
|
("lua", &["*.lua"]),
|
||||||
|
("lz4", &["*.lz4"]),
|
||||||
|
("lzma", &["*.lzma"]),
|
||||||
|
("m4", &["*.ac", "*.m4"]),
|
||||||
|
("make", &[
|
||||||
|
"[Gg][Nn][Uu]makefile", "[Mm]akefile",
|
||||||
|
"[Gg][Nn][Uu]makefile.am", "[Mm]akefile.am",
|
||||||
|
"[Gg][Nn][Uu]makefile.in", "[Mm]akefile.in",
|
||||||
|
"*.mk", "*.mak"
|
||||||
|
]),
|
||||||
|
("mako", &["*.mako", "*.mao"]),
|
||||||
|
("man", &["*.[0-9lnpx]", "*.[0-9][cEFMmpSx]"]),
|
||||||
|
("markdown", &["*.markdown", "*.md", "*.mdown", "*.mkdn"]),
|
||||||
|
("matlab", &["*.m"]),
|
||||||
|
("md", &["*.markdown", "*.md", "*.mdown", "*.mkdn"]),
|
||||||
|
("mk", &["mkfile"]),
|
||||||
|
("ml", &["*.ml"]),
|
||||||
|
("msbuild", &[
|
||||||
|
"*.csproj", "*.fsproj", "*.vcxproj", "*.proj", "*.props", "*.targets",
|
||||||
|
]),
|
||||||
|
("nim", &["*.nim", "*.nimf", "*.nimble", "*.nims"]),
|
||||||
|
("nix", &["*.nix"]),
|
||||||
|
("objc", &["*.h", "*.m"]),
|
||||||
|
("objcpp", &["*.h", "*.mm"]),
|
||||||
|
("ocaml", &["*.ml", "*.mli", "*.mll", "*.mly"]),
|
||||||
|
("org", &["*.org", "*.org_archive"]),
|
||||||
|
("pascal", &["*.pas", "*.dpr", "*.lpr", "*.pp", "*.inc"]),
|
||||||
|
("pdf", &["*.pdf"]),
|
||||||
|
("perl", &["*.perl", "*.pl", "*.PL", "*.plh", "*.plx", "*.pm", "*.t"]),
|
||||||
|
("php", &["*.php", "*.php3", "*.php4", "*.php5", "*.phtml"]),
|
||||||
|
("pod", &["*.pod"]),
|
||||||
|
("postscript", &["*.eps", "*.ps"]),
|
||||||
|
("protobuf", &["*.proto"]),
|
||||||
|
("ps", &["*.cdxml", "*.ps1", "*.ps1xml", "*.psd1", "*.psm1"]),
|
||||||
|
("puppet", &["*.erb", "*.pp", "*.rb"]),
|
||||||
|
("purs", &["*.purs"]),
|
||||||
|
("py", &["*.py"]),
|
||||||
|
("qmake", &["*.pro", "*.pri", "*.prf"]),
|
||||||
|
("qml", &["*.qml"]),
|
||||||
|
("r", &["*.R", "*.r", "*.Rmd", "*.Rnw"]),
|
||||||
|
("rdoc", &["*.rdoc"]),
|
||||||
|
("readme", &["README*", "*README"]),
|
||||||
|
("robot", &["*.robot"]),
|
||||||
|
("rst", &["*.rst"]),
|
||||||
|
("ruby", &["Gemfile", "*.gemspec", ".irbrc", "Rakefile", "*.rb"]),
|
||||||
|
("rust", &["*.rs"]),
|
||||||
|
("sass", &["*.sass", "*.scss"]),
|
||||||
|
("scala", &["*.scala", "*.sbt"]),
|
||||||
|
("sh", &[
|
||||||
|
// Portable/misc. init files
|
||||||
|
".login", ".logout", ".profile", "profile",
|
||||||
|
// bash-specific init files
|
||||||
|
".bash_login", "bash_login",
|
||||||
|
".bash_logout", "bash_logout",
|
||||||
|
".bash_profile", "bash_profile",
|
||||||
|
".bashrc", "bashrc", "*.bashrc",
|
||||||
|
// csh-specific init files
|
||||||
|
".cshrc", "*.cshrc",
|
||||||
|
// ksh-specific init files
|
||||||
|
".kshrc", "*.kshrc",
|
||||||
|
// tcsh-specific init files
|
||||||
|
".tcshrc",
|
||||||
|
// zsh-specific init files
|
||||||
|
".zshenv", "zshenv",
|
||||||
|
".zlogin", "zlogin",
|
||||||
|
".zlogout", "zlogout",
|
||||||
|
".zprofile", "zprofile",
|
||||||
|
".zshrc", "zshrc",
|
||||||
|
// Extensions
|
||||||
|
"*.bash", "*.csh", "*.ksh", "*.sh", "*.tcsh", "*.zsh",
|
||||||
|
]),
|
||||||
|
("slim", &["*.skim", "*.slim", "*.slime"]),
|
||||||
|
("smarty", &["*.tpl"]),
|
||||||
|
("sml", &["*.sml", "*.sig"]),
|
||||||
|
("soy", &["*.soy"]),
|
||||||
|
("spark", &["*.spark"]),
|
||||||
|
("spec", &["*.spec"]),
|
||||||
|
("sql", &["*.sql", "*.psql"]),
|
||||||
|
("stylus", &["*.styl"]),
|
||||||
|
("sv", &["*.v", "*.vg", "*.sv", "*.svh", "*.h"]),
|
||||||
|
("svg", &["*.svg"]),
|
||||||
|
("swift", &["*.swift"]),
|
||||||
|
("swig", &["*.def", "*.i"]),
|
||||||
|
("systemd", &[
|
||||||
|
"*.automount", "*.conf", "*.device", "*.link", "*.mount", "*.path",
|
||||||
|
"*.scope", "*.service", "*.slice", "*.socket", "*.swap", "*.target",
|
||||||
|
"*.timer",
|
||||||
|
]),
|
||||||
|
("taskpaper", &["*.taskpaper"]),
|
||||||
|
("tcl", &["*.tcl"]),
|
||||||
|
("tex", &["*.tex", "*.ltx", "*.cls", "*.sty", "*.bib", "*.dtx", "*.ins"]),
|
||||||
|
("textile", &["*.textile"]),
|
||||||
|
("tf", &["*.tf"]),
|
||||||
|
("thrift", &["*.thrift"]),
|
||||||
|
("toml", &["*.toml", "Cargo.lock"]),
|
||||||
|
("ts", &["*.ts", "*.tsx"]),
|
||||||
|
("twig", &["*.twig"]),
|
||||||
|
("txt", &["*.txt"]),
|
||||||
|
("typoscript", &["*.typoscript", "*.ts"]),
|
||||||
|
("vala", &["*.vala"]),
|
||||||
|
("vb", &["*.vb"]),
|
||||||
|
("verilog", &["*.v", "*.vh", "*.sv", "*.svh"]),
|
||||||
|
("vhdl", &["*.vhd", "*.vhdl"]),
|
||||||
|
("vim", &["*.vim"]),
|
||||||
|
("vimscript", &["*.vim"]),
|
||||||
|
("webidl", &["*.idl", "*.webidl", "*.widl"]),
|
||||||
|
("wiki", &["*.mediawiki", "*.wiki"]),
|
||||||
|
("xml", &[
|
||||||
|
"*.xml", "*.xml.dist", "*.dtd", "*.xsl", "*.xslt", "*.xsd", "*.xjb",
|
||||||
|
"*.rng", "*.sch", "*.xhtml",
|
||||||
|
]),
|
||||||
|
("xz", &["*.xz", "*.txz"]),
|
||||||
|
("yacc", &["*.y"]),
|
||||||
|
("yaml", &["*.yaml", "*.yml"]),
|
||||||
|
("zig", &["*.zig"]),
|
||||||
|
("zsh", &[
|
||||||
|
".zshenv", "zshenv",
|
||||||
|
".zlogin", "zlogin",
|
||||||
|
".zlogout", "zlogout",
|
||||||
|
".zprofile", "zprofile",
|
||||||
|
".zshrc", "zshrc",
|
||||||
|
"*.zsh",
|
||||||
|
]),
|
||||||
|
("zstd", &["*.zst", "*.zstd"]),
|
||||||
|
];
|
@ -157,7 +157,10 @@ impl Ignore {
|
|||||||
///
|
///
|
||||||
/// Note that this can only be called on an `Ignore` matcher with no
|
/// Note that this can only be called on an `Ignore` matcher with no
|
||||||
/// parents (i.e., `is_root` returns `true`). This will panic otherwise.
|
/// parents (i.e., `is_root` returns `true`). This will panic otherwise.
|
||||||
pub fn add_parents<P: AsRef<Path>>(&self, path: P) -> (Ignore, Option<Error>) {
|
pub fn add_parents<P: AsRef<Path>>(
|
||||||
|
&self,
|
||||||
|
path: P,
|
||||||
|
) -> (Ignore, Option<Error>) {
|
||||||
if !self.0.opts.parents
|
if !self.0.opts.parents
|
||||||
&& !self.0.opts.git_ignore
|
&& !self.0.opts.git_ignore
|
||||||
&& !self.0.opts.git_exclude
|
&& !self.0.opts.git_exclude
|
||||||
@ -218,7 +221,10 @@ impl Ignore {
|
|||||||
/// returned if it exists.
|
/// returned if it exists.
|
||||||
///
|
///
|
||||||
/// Note that all I/O errors are completely ignored.
|
/// Note that all I/O errors are completely ignored.
|
||||||
pub fn add_child<P: AsRef<Path>>(&self, dir: P) -> (Ignore, Option<Error>) {
|
pub fn add_child<P: AsRef<Path>>(
|
||||||
|
&self,
|
||||||
|
dir: P,
|
||||||
|
) -> (Ignore, Option<Error>) {
|
||||||
let (ig, err) = self.add_child_path(dir.as_ref());
|
let (ig, err) = self.add_child_path(dir.as_ref());
|
||||||
(Ignore(Arc::new(ig)), err)
|
(Ignore(Arc::new(ig)), err)
|
||||||
}
|
}
|
||||||
@ -313,7 +319,8 @@ impl Ignore {
|
|||||||
/// Returns true if at least one type of ignore rule should be matched.
|
/// Returns true if at least one type of ignore rule should be matched.
|
||||||
fn has_any_ignore_rules(&self) -> bool {
|
fn has_any_ignore_rules(&self) -> bool {
|
||||||
let opts = self.0.opts;
|
let opts = self.0.opts;
|
||||||
let has_custom_ignore_files = !self.0.custom_ignore_filenames.is_empty();
|
let has_custom_ignore_files =
|
||||||
|
!self.0.custom_ignore_filenames.is_empty();
|
||||||
let has_explicit_ignores = !self.0.explicit_ignores.is_empty();
|
let has_explicit_ignores = !self.0.explicit_ignores.is_empty();
|
||||||
|
|
||||||
opts.ignore
|
opts.ignore
|
||||||
@ -325,7 +332,10 @@ impl Ignore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Like `matched`, but works with a directory entry instead.
|
/// Like `matched`, but works with a directory entry instead.
|
||||||
pub fn matched_dir_entry<'a>(&'a self, dent: &DirEntry) -> Match<IgnoreMatch<'a>> {
|
pub fn matched_dir_entry<'a>(
|
||||||
|
&'a self,
|
||||||
|
dent: &DirEntry,
|
||||||
|
) -> Match<IgnoreMatch<'a>> {
|
||||||
let m = self.matched(dent.path(), dent.is_dir());
|
let m = self.matched(dent.path(), dent.is_dir());
|
||||||
if m.is_none() && self.0.opts.hidden && is_hidden(dent) {
|
if m.is_none() && self.0.opts.hidden && is_hidden(dent) {
|
||||||
return Match::Ignore(IgnoreMatch::hidden());
|
return Match::Ignore(IgnoreMatch::hidden());
|
||||||
@ -337,7 +347,11 @@ impl Ignore {
|
|||||||
/// ignored or not.
|
/// ignored or not.
|
||||||
///
|
///
|
||||||
/// The match contains information about its origin.
|
/// The match contains information about its origin.
|
||||||
fn matched<'a, P: AsRef<Path>>(&'a self, path: P, is_dir: bool) -> Match<IgnoreMatch<'a>> {
|
fn matched<'a, P: AsRef<Path>>(
|
||||||
|
&'a self,
|
||||||
|
path: P,
|
||||||
|
is_dir: bool,
|
||||||
|
) -> Match<IgnoreMatch<'a>> {
|
||||||
// We need to be careful with our path. If it has a leading ./, then
|
// We need to be careful with our path. If it has a leading ./, then
|
||||||
// strip it because it causes nothing but trouble.
|
// strip it because it causes nothing but trouble.
|
||||||
let mut path = path.as_ref();
|
let mut path = path.as_ref();
|
||||||
@ -368,7 +382,8 @@ impl Ignore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !self.0.types.is_empty() {
|
if !self.0.types.is_empty() {
|
||||||
let mat = self.0.types.matched(path, is_dir).map(IgnoreMatch::types);
|
let mat =
|
||||||
|
self.0.types.matched(path, is_dir).map(IgnoreMatch::types);
|
||||||
if mat.is_ignore() {
|
if mat.is_ignore() {
|
||||||
return mat;
|
return mat;
|
||||||
} else if mat.is_whitelist() {
|
} else if mat.is_whitelist() {
|
||||||
@ -380,17 +395,20 @@ impl Ignore {
|
|||||||
|
|
||||||
/// Performs matching only on the ignore files for this directory and
|
/// Performs matching only on the ignore files for this directory and
|
||||||
/// all parent directories.
|
/// all parent directories.
|
||||||
fn matched_ignore<'a>(&'a self, path: &Path, is_dir: bool) -> Match<IgnoreMatch<'a>> {
|
fn matched_ignore<'a>(
|
||||||
let (mut m_custom_ignore, mut m_ignore, mut m_gi, mut m_gi_exclude, mut m_explicit) = (
|
&'a self,
|
||||||
Match::None,
|
path: &Path,
|
||||||
Match::None,
|
is_dir: bool,
|
||||||
Match::None,
|
) -> Match<IgnoreMatch<'a>> {
|
||||||
Match::None,
|
let (
|
||||||
Match::None,
|
mut m_custom_ignore,
|
||||||
);
|
mut m_ignore,
|
||||||
|
mut m_gi,
|
||||||
|
mut m_gi_exclude,
|
||||||
|
mut m_explicit,
|
||||||
|
) = (Match::None, Match::None, Match::None, Match::None, Match::None);
|
||||||
let any_git =
|
let any_git =
|
||||||
!self.0.opts.require_git
|
!self.0.opts.require_git || self.parents().any(|ig| ig.0.has_git);
|
||||||
|| self.parents().any(|ig| ig.0.has_git);
|
|
||||||
let mut saw_git = false;
|
let mut saw_git = false;
|
||||||
for ig in self.parents().take_while(|ig| !ig.0.is_absolute_parent) {
|
for ig in self.parents().take_while(|ig| !ig.0.is_absolute_parent) {
|
||||||
if m_custom_ignore.is_none() {
|
if m_custom_ignore.is_none() {
|
||||||
@ -422,7 +440,9 @@ impl Ignore {
|
|||||||
if self.0.opts.parents {
|
if self.0.opts.parents {
|
||||||
if let Some(abs_parent_path) = self.absolute_base() {
|
if let Some(abs_parent_path) = self.absolute_base() {
|
||||||
let path = abs_parent_path.join(path);
|
let path = abs_parent_path.join(path);
|
||||||
for ig in self.parents().skip_while(|ig| !ig.0.is_absolute_parent) {
|
for ig in
|
||||||
|
self.parents().skip_while(|ig| !ig.0.is_absolute_parent)
|
||||||
|
{
|
||||||
if m_custom_ignore.is_none() {
|
if m_custom_ignore.is_none() {
|
||||||
m_custom_ignore =
|
m_custom_ignore =
|
||||||
ig.0.custom_ignore_matcher
|
ig.0.custom_ignore_matcher
|
||||||
@ -575,7 +595,9 @@ impl IgnoreBuilder {
|
|||||||
is_absolute_parent: true,
|
is_absolute_parent: true,
|
||||||
absolute_base: None,
|
absolute_base: None,
|
||||||
explicit_ignores: Arc::new(self.explicit_ignores.clone()),
|
explicit_ignores: Arc::new(self.explicit_ignores.clone()),
|
||||||
custom_ignore_filenames: Arc::new(self.custom_ignore_filenames.clone()),
|
custom_ignore_filenames: Arc::new(
|
||||||
|
self.custom_ignore_filenames.clone(),
|
||||||
|
),
|
||||||
custom_ignore_matcher: Gitignore::empty(),
|
custom_ignore_matcher: Gitignore::empty(),
|
||||||
ignore_matcher: Gitignore::empty(),
|
ignore_matcher: Gitignore::empty(),
|
||||||
git_global_matcher: Arc::new(git_global_matcher),
|
git_global_matcher: Arc::new(git_global_matcher),
|
||||||
@ -622,8 +644,7 @@ impl IgnoreBuilder {
|
|||||||
&mut self,
|
&mut self,
|
||||||
file_name: S,
|
file_name: S,
|
||||||
) -> &mut IgnoreBuilder {
|
) -> &mut IgnoreBuilder {
|
||||||
self.custom_ignore_filenames
|
self.custom_ignore_filenames.push(file_name.as_ref().to_os_string());
|
||||||
.push(file_name.as_ref().to_os_string());
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -705,7 +726,10 @@ impl IgnoreBuilder {
|
|||||||
/// Process ignore files case insensitively
|
/// Process ignore files case insensitively
|
||||||
///
|
///
|
||||||
/// This is disabled by default.
|
/// This is disabled by default.
|
||||||
pub fn ignore_case_insensitive(&mut self, yes: bool) -> &mut IgnoreBuilder {
|
pub fn ignore_case_insensitive(
|
||||||
|
&mut self,
|
||||||
|
yes: bool,
|
||||||
|
) -> &mut IgnoreBuilder {
|
||||||
self.opts.ignore_case_insensitive = yes;
|
self.opts.ignore_case_insensitive = yes;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -850,10 +874,8 @@ mod tests {
|
|||||||
|
|
||||||
let (gi, err) = Gitignore::new(td.path().join("not-an-ignore"));
|
let (gi, err) = Gitignore::new(td.path().join("not-an-ignore"));
|
||||||
assert!(err.is_none());
|
assert!(err.is_none());
|
||||||
let (ig, err) = IgnoreBuilder::new()
|
let (ig, err) =
|
||||||
.add_ignore(gi)
|
IgnoreBuilder::new().add_ignore(gi).build().add_child(td.path());
|
||||||
.build()
|
|
||||||
.add_child(td.path());
|
|
||||||
assert!(err.is_none());
|
assert!(err.is_none());
|
||||||
assert!(ig.matched("foo", false).is_ignore());
|
assert!(ig.matched("foo", false).is_ignore());
|
||||||
assert!(ig.matched("bar", false).is_whitelist());
|
assert!(ig.matched("bar", false).is_whitelist());
|
||||||
@ -1125,9 +1147,8 @@ mod tests {
|
|||||||
mkdirp(git_dir.join("info"));
|
mkdirp(git_dir.join("info"));
|
||||||
wfile(git_dir.join("info/exclude"), "ignore_me");
|
wfile(git_dir.join("info/exclude"), "ignore_me");
|
||||||
mkdirp(git_dir.join("worktrees/linked-worktree"));
|
mkdirp(git_dir.join("worktrees/linked-worktree"));
|
||||||
let commondir_path = || {
|
let commondir_path =
|
||||||
git_dir.join("worktrees/linked-worktree/commondir")
|
|| git_dir.join("worktrees/linked-worktree/commondir");
|
||||||
};
|
|
||||||
mkdirp(td.path().join("linked-worktree"));
|
mkdirp(td.path().join("linked-worktree"));
|
||||||
let worktree_git_dir_abs = format!(
|
let worktree_git_dir_abs = format!(
|
||||||
"gitdir: {}",
|
"gitdir: {}",
|
||||||
|
@ -332,13 +332,10 @@ impl GitignoreBuilder {
|
|||||||
pub fn build(&self) -> Result<Gitignore, Error> {
|
pub fn build(&self) -> Result<Gitignore, Error> {
|
||||||
let nignore = self.globs.iter().filter(|g| !g.is_whitelist()).count();
|
let nignore = self.globs.iter().filter(|g| !g.is_whitelist()).count();
|
||||||
let nwhite = self.globs.iter().filter(|g| g.is_whitelist()).count();
|
let nwhite = self.globs.iter().filter(|g| g.is_whitelist()).count();
|
||||||
let set =
|
let set = self
|
||||||
self.builder.build().map_err(|err| {
|
.builder
|
||||||
Error::Glob {
|
.build()
|
||||||
glob: None,
|
.map_err(|err| Error::Glob { glob: None, err: err.to_string() })?;
|
||||||
err: err.to_string(),
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
Ok(Gitignore {
|
Ok(Gitignore {
|
||||||
set: set,
|
set: set,
|
||||||
root: self.root.clone(),
|
root: self.root.clone(),
|
||||||
@ -499,18 +496,15 @@ impl GitignoreBuilder {
|
|||||||
if glob.actual.ends_with("/**") {
|
if glob.actual.ends_with("/**") {
|
||||||
glob.actual = format!("{}/*", glob.actual);
|
glob.actual = format!("{}/*", glob.actual);
|
||||||
}
|
}
|
||||||
let parsed =
|
let parsed = GlobBuilder::new(&glob.actual)
|
||||||
GlobBuilder::new(&glob.actual)
|
.literal_separator(true)
|
||||||
.literal_separator(true)
|
.case_insensitive(self.case_insensitive)
|
||||||
.case_insensitive(self.case_insensitive)
|
.backslash_escape(true)
|
||||||
.backslash_escape(true)
|
.build()
|
||||||
.build()
|
.map_err(|err| Error::Glob {
|
||||||
.map_err(|err| {
|
glob: Some(glob.original.clone()),
|
||||||
Error::Glob {
|
err: err.kind().to_string(),
|
||||||
glob: Some(glob.original.clone()),
|
})?;
|
||||||
err: err.kind().to_string(),
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
self.builder.add(parsed);
|
self.builder.add(parsed);
|
||||||
self.globs.push(glob);
|
self.globs.push(glob);
|
||||||
Ok(self)
|
Ok(self)
|
||||||
@ -599,9 +593,8 @@ fn parse_excludes_file(data: &[u8]) -> Option<PathBuf> {
|
|||||||
// probably works in more circumstances. I guess we would ideally have
|
// probably works in more circumstances. I guess we would ideally have
|
||||||
// a full INI parser. Yuck.
|
// a full INI parser. Yuck.
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref RE: Regex = Regex::new(
|
static ref RE: Regex =
|
||||||
r"(?im)^\s*excludesfile\s*=\s*(.+)\s*$"
|
Regex::new(r"(?im)^\s*excludesfile\s*=\s*(.+)\s*$").unwrap();
|
||||||
).unwrap();
|
|
||||||
};
|
};
|
||||||
let caps = match RE.captures(data) {
|
let caps = match RE.captures(data) {
|
||||||
None => return None,
|
None => return None,
|
||||||
@ -630,8 +623,8 @@ fn home_dir() -> Option<PathBuf> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::path::Path;
|
|
||||||
use super::{Gitignore, GitignoreBuilder};
|
use super::{Gitignore, GitignoreBuilder};
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
fn gi_from_str<P: AsRef<Path>>(root: P, s: &str) -> Gitignore {
|
fn gi_from_str<P: AsRef<Path>>(root: P, s: &str) -> Gitignore {
|
||||||
let mut builder = GitignoreBuilder::new(root);
|
let mut builder = GitignoreBuilder::new(root);
|
||||||
@ -726,8 +719,11 @@ mod tests {
|
|||||||
not_ignored!(ignot12, ROOT, "\n\n\n", "foo");
|
not_ignored!(ignot12, ROOT, "\n\n\n", "foo");
|
||||||
not_ignored!(ignot13, ROOT, "foo/**", "foo", true);
|
not_ignored!(ignot13, ROOT, "foo/**", "foo", true);
|
||||||
not_ignored!(
|
not_ignored!(
|
||||||
ignot14, "./third_party/protobuf", "m4/ltoptions.m4",
|
ignot14,
|
||||||
"./third_party/protobuf/csharp/src/packages/repositories.config");
|
"./third_party/protobuf",
|
||||||
|
"m4/ltoptions.m4",
|
||||||
|
"./third_party/protobuf/csharp/src/packages/repositories.config"
|
||||||
|
);
|
||||||
not_ignored!(ignot15, ROOT, "!/bar", "foo/bar");
|
not_ignored!(ignot15, ROOT, "!/bar", "foo/bar");
|
||||||
not_ignored!(ignot16, ROOT, "*\n!**/", "foo", true);
|
not_ignored!(ignot16, ROOT, "*\n!**/", "foo", true);
|
||||||
not_ignored!(ignot17, ROOT, "src/*.rs", "src/grep/src/main.rs");
|
not_ignored!(ignot17, ROOT, "src/*.rs", "src/grep/src/main.rs");
|
||||||
@ -771,9 +767,12 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn case_insensitive() {
|
fn case_insensitive() {
|
||||||
let gi = GitignoreBuilder::new(ROOT)
|
let gi = GitignoreBuilder::new(ROOT)
|
||||||
.case_insensitive(true).unwrap()
|
.case_insensitive(true)
|
||||||
.add_str(None, "*.html").unwrap()
|
.unwrap()
|
||||||
.build().unwrap();
|
.add_str(None, "*.html")
|
||||||
|
.unwrap()
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
assert!(gi.matched("foo.html", false).is_ignore());
|
assert!(gi.matched("foo.html", false).is_ignore());
|
||||||
assert!(gi.matched("foo.HTML", false).is_ignore());
|
assert!(gi.matched("foo.HTML", false).is_ignore());
|
||||||
assert!(!gi.matched("foo.htm", false).is_ignore());
|
assert!(!gi.matched("foo.htm", false).is_ignore());
|
||||||
|
@ -66,10 +66,11 @@ use std::io;
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
pub use walk::{
|
pub use walk::{
|
||||||
DirEntry, Walk, WalkBuilder, WalkParallel, WalkState,
|
DirEntry, ParallelVisitor, ParallelVisitorBuilder, Walk, WalkBuilder,
|
||||||
ParallelVisitorBuilder, ParallelVisitor,
|
WalkParallel, WalkState,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mod default_types;
|
||||||
mod dir;
|
mod dir;
|
||||||
pub mod gitignore;
|
pub mod gitignore;
|
||||||
pub mod overrides;
|
pub mod overrides;
|
||||||
@ -137,22 +138,16 @@ impl Clone for Error {
|
|||||||
fn clone(&self) -> Error {
|
fn clone(&self) -> Error {
|
||||||
match *self {
|
match *self {
|
||||||
Error::Partial(ref errs) => Error::Partial(errs.clone()),
|
Error::Partial(ref errs) => Error::Partial(errs.clone()),
|
||||||
Error::WithLineNumber { line, ref err } => Error::WithLineNumber {
|
Error::WithLineNumber { line, ref err } => {
|
||||||
line: line,
|
Error::WithLineNumber { line: line, err: err.clone() }
|
||||||
err: err.clone(),
|
}
|
||||||
},
|
Error::WithPath { ref path, ref err } => {
|
||||||
Error::WithPath { ref path, ref err } => Error::WithPath {
|
Error::WithPath { path: path.clone(), err: err.clone() }
|
||||||
path: path.clone(),
|
}
|
||||||
err: err.clone(),
|
Error::WithDepth { depth, ref err } => {
|
||||||
},
|
Error::WithDepth { depth: depth, err: err.clone() }
|
||||||
Error::WithDepth { depth, ref err } => Error::WithDepth {
|
}
|
||||||
depth: depth,
|
Error::Loop { ref ancestor, ref child } => Error::Loop {
|
||||||
err: err.clone(),
|
|
||||||
},
|
|
||||||
Error::Loop {
|
|
||||||
ref ancestor,
|
|
||||||
ref child,
|
|
||||||
} => Error::Loop {
|
|
||||||
ancestor: ancestor.clone(),
|
ancestor: ancestor.clone(),
|
||||||
child: child.clone(),
|
child: child.clone(),
|
||||||
},
|
},
|
||||||
@ -160,11 +155,12 @@ impl Clone for Error {
|
|||||||
Some(e) => Error::Io(io::Error::from_raw_os_error(e)),
|
Some(e) => Error::Io(io::Error::from_raw_os_error(e)),
|
||||||
None => Error::Io(io::Error::new(err.kind(), err.to_string())),
|
None => Error::Io(io::Error::new(err.kind(), err.to_string())),
|
||||||
},
|
},
|
||||||
Error::Glob { ref glob, ref err } => Error::Glob {
|
Error::Glob { ref glob, ref err } => {
|
||||||
glob: glob.clone(),
|
Error::Glob { glob: glob.clone(), err: err.clone() }
|
||||||
err: err.clone(),
|
}
|
||||||
},
|
Error::UnrecognizedFileType(ref err) => {
|
||||||
Error::UnrecognizedFileType(ref err) => Error::UnrecognizedFileType(err.clone()),
|
Error::UnrecognizedFileType(err.clone())
|
||||||
|
}
|
||||||
Error::InvalidDefinition => Error::InvalidDefinition,
|
Error::InvalidDefinition => Error::InvalidDefinition,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -221,19 +217,14 @@ impl Error {
|
|||||||
|
|
||||||
/// Turn an error into a tagged error with the given depth.
|
/// Turn an error into a tagged error with the given depth.
|
||||||
fn with_depth(self, depth: usize) -> Error {
|
fn with_depth(self, depth: usize) -> Error {
|
||||||
Error::WithDepth {
|
Error::WithDepth { depth: depth, err: Box::new(self) }
|
||||||
depth: depth,
|
|
||||||
err: Box::new(self),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Turn an error into a tagged error with the given file path and line
|
/// Turn an error into a tagged error with the given file path and line
|
||||||
/// number. If path is empty, then it is omitted from the error.
|
/// number. If path is empty, then it is omitted from the error.
|
||||||
fn tagged<P: AsRef<Path>>(self, path: P, lineno: u64) -> Error {
|
fn tagged<P: AsRef<Path>>(self, path: P, lineno: u64) -> Error {
|
||||||
let errline = Error::WithLineNumber {
|
let errline =
|
||||||
line: lineno,
|
Error::WithLineNumber { line: lineno, err: Box::new(self) };
|
||||||
err: Box::new(self),
|
|
||||||
};
|
|
||||||
if path.as_ref().as_os_str().is_empty() {
|
if path.as_ref().as_os_str().is_empty() {
|
||||||
return errline;
|
return errline;
|
||||||
}
|
}
|
||||||
@ -255,10 +246,7 @@ impl Error {
|
|||||||
let path = err.path().map(|p| p.to_path_buf());
|
let path = err.path().map(|p| p.to_path_buf());
|
||||||
let mut ig_err = Error::Io(io::Error::from(err));
|
let mut ig_err = Error::Io(io::Error::from(err));
|
||||||
if let Some(path) = path {
|
if let Some(path) = path {
|
||||||
ig_err = Error::WithPath {
|
ig_err = Error::WithPath { path: path, err: Box::new(ig_err) };
|
||||||
path: path,
|
|
||||||
err: Box::new(ig_err),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
ig_err
|
ig_err
|
||||||
}
|
}
|
||||||
@ -285,16 +273,18 @@ impl fmt::Display for Error {
|
|||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
Error::Partial(ref errs) => {
|
Error::Partial(ref errs) => {
|
||||||
let msgs: Vec<String> = errs.iter().map(|err| err.to_string()).collect();
|
let msgs: Vec<String> =
|
||||||
|
errs.iter().map(|err| err.to_string()).collect();
|
||||||
write!(f, "{}", msgs.join("\n"))
|
write!(f, "{}", msgs.join("\n"))
|
||||||
}
|
}
|
||||||
Error::WithLineNumber { line, ref err } => write!(f, "line {}: {}", line, err),
|
Error::WithLineNumber { line, ref err } => {
|
||||||
Error::WithPath { ref path, ref err } => write!(f, "{}: {}", path.display(), err),
|
write!(f, "line {}: {}", line, err)
|
||||||
|
}
|
||||||
|
Error::WithPath { ref path, ref err } => {
|
||||||
|
write!(f, "{}: {}", path.display(), err)
|
||||||
|
}
|
||||||
Error::WithDepth { ref err, .. } => err.fmt(f),
|
Error::WithDepth { ref err, .. } => err.fmt(f),
|
||||||
Error::Loop {
|
Error::Loop { ref ancestor, ref child } => write!(
|
||||||
ref ancestor,
|
|
||||||
ref child,
|
|
||||||
} => write!(
|
|
||||||
f,
|
f,
|
||||||
"File system loop found: \
|
"File system loop found: \
|
||||||
{} points to an ancestor {}",
|
{} points to an ancestor {}",
|
||||||
@ -302,15 +292,13 @@ impl fmt::Display for Error {
|
|||||||
ancestor.display()
|
ancestor.display()
|
||||||
),
|
),
|
||||||
Error::Io(ref err) => err.fmt(f),
|
Error::Io(ref err) => err.fmt(f),
|
||||||
Error::Glob {
|
Error::Glob { glob: None, ref err } => write!(f, "{}", err),
|
||||||
glob: None,
|
Error::Glob { glob: Some(ref glob), ref err } => {
|
||||||
ref err,
|
write!(f, "error parsing glob '{}': {}", glob, err)
|
||||||
} => write!(f, "{}", err),
|
}
|
||||||
Error::Glob {
|
Error::UnrecognizedFileType(ref ty) => {
|
||||||
glob: Some(ref glob),
|
write!(f, "unrecognized file type: {}", ty)
|
||||||
ref err,
|
}
|
||||||
} => write!(f, "error parsing glob '{}': {}", glob, err),
|
|
||||||
Error::UnrecognizedFileType(ref ty) => write!(f, "unrecognized file type: {}", ty),
|
|
||||||
Error::InvalidDefinition => write!(
|
Error::InvalidDefinition => write!(
|
||||||
f,
|
f,
|
||||||
"invalid definition (format is type:glob, e.g., \
|
"invalid definition (format is type:glob, e.g., \
|
||||||
@ -456,7 +444,8 @@ mod tests {
|
|||||||
use std::result;
|
use std::result;
|
||||||
|
|
||||||
/// A convenient result type alias.
|
/// A convenient result type alias.
|
||||||
pub type Result<T> = result::Result<T, Box<dyn error::Error + Send + Sync>>;
|
pub type Result<T> =
|
||||||
|
result::Result<T, Box<dyn error::Error + Send + Sync>>;
|
||||||
|
|
||||||
macro_rules! err {
|
macro_rules! err {
|
||||||
($($tt:tt)*) => {
|
($($tt:tt)*) => {
|
||||||
@ -494,8 +483,9 @@ mod tests {
|
|||||||
if path.is_dir() {
|
if path.is_dir() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
fs::create_dir_all(&path)
|
fs::create_dir_all(&path).map_err(|e| {
|
||||||
.map_err(|e| err!("failed to create {}: {}", path.display(), e))?;
|
err!("failed to create {}: {}", path.display(), e)
|
||||||
|
})?;
|
||||||
return Ok(TempDir(path));
|
return Ok(TempDir(path));
|
||||||
}
|
}
|
||||||
Err(err!("failed to create temp dir after {} tries", TRIES))
|
Err(err!("failed to create temp dir after {} tries", TRIES))
|
||||||
|
@ -115,9 +115,7 @@ impl OverrideBuilder {
|
|||||||
///
|
///
|
||||||
/// Matching is done relative to the directory path provided.
|
/// Matching is done relative to the directory path provided.
|
||||||
pub fn new<P: AsRef<Path>>(path: P) -> OverrideBuilder {
|
pub fn new<P: AsRef<Path>>(path: P) -> OverrideBuilder {
|
||||||
OverrideBuilder {
|
OverrideBuilder { builder: GitignoreBuilder::new(path) }
|
||||||
builder: GitignoreBuilder::new(path),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds a new override matcher from the globs added so far.
|
/// Builds a new override matcher from the globs added so far.
|
||||||
@ -240,9 +238,12 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn case_insensitive() {
|
fn case_insensitive() {
|
||||||
let ov = OverrideBuilder::new(ROOT)
|
let ov = OverrideBuilder::new(ROOT)
|
||||||
.case_insensitive(true).unwrap()
|
.case_insensitive(true)
|
||||||
.add("*.html").unwrap()
|
.unwrap()
|
||||||
.build().unwrap();
|
.add("*.html")
|
||||||
|
.unwrap()
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
assert!(ov.matched("foo.html", false).is_whitelist());
|
assert!(ov.matched("foo.html", false).is_whitelist());
|
||||||
assert!(ov.matched("foo.HTML", false).is_whitelist());
|
assert!(ov.matched("foo.HTML", false).is_whitelist());
|
||||||
assert!(ov.matched("foo.htm", false).is_ignore());
|
assert!(ov.matched("foo.htm", false).is_ignore());
|
||||||
@ -251,9 +252,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn default_case_sensitive() {
|
fn default_case_sensitive() {
|
||||||
let ov = OverrideBuilder::new(ROOT)
|
let ov =
|
||||||
.add("*.html").unwrap()
|
OverrideBuilder::new(ROOT).add("*.html").unwrap().build().unwrap();
|
||||||
.build().unwrap();
|
|
||||||
assert!(ov.matched("foo.html", false).is_whitelist());
|
assert!(ov.matched("foo.html", false).is_whitelist());
|
||||||
assert!(ov.matched("foo.HTML", false).is_ignore());
|
assert!(ov.matched("foo.HTML", false).is_ignore());
|
||||||
assert!(ov.matched("foo.htm", false).is_ignore());
|
assert!(ov.matched("foo.htm", false).is_ignore());
|
||||||
|
@ -91,8 +91,8 @@ pub fn strip_prefix<'a, P: AsRef<Path> + ?Sized>(
|
|||||||
/// the empty string.
|
/// the empty string.
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
pub fn is_file_name<P: AsRef<Path>>(path: P) -> bool {
|
pub fn is_file_name<P: AsRef<Path>>(path: P) -> bool {
|
||||||
use std::os::unix::ffi::OsStrExt;
|
|
||||||
use memchr::memchr;
|
use memchr::memchr;
|
||||||
|
use std::os::unix::ffi::OsStrExt;
|
||||||
|
|
||||||
let path = path.as_ref().as_os_str().as_bytes();
|
let path = path.as_ref().as_os_str().as_bytes();
|
||||||
memchr(b'/', path).is_none()
|
memchr(b'/', path).is_none()
|
||||||
@ -113,8 +113,8 @@ pub fn is_file_name<P: AsRef<Path>>(path: P) -> bool {
|
|||||||
pub fn file_name<'a, P: AsRef<Path> + ?Sized>(
|
pub fn file_name<'a, P: AsRef<Path> + ?Sized>(
|
||||||
path: &'a P,
|
path: &'a P,
|
||||||
) -> Option<&'a OsStr> {
|
) -> Option<&'a OsStr> {
|
||||||
use std::os::unix::ffi::OsStrExt;
|
|
||||||
use memchr::memrchr;
|
use memchr::memrchr;
|
||||||
|
use std::os::unix::ffi::OsStrExt;
|
||||||
|
|
||||||
let path = path.as_ref().as_os_str().as_bytes();
|
let path = path.as_ref().as_os_str().as_bytes();
|
||||||
if path.is_empty() {
|
if path.is_empty() {
|
||||||
|
@ -93,243 +93,10 @@ use globset::{GlobBuilder, GlobSet, GlobSetBuilder};
|
|||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use thread_local::ThreadLocal;
|
use thread_local::ThreadLocal;
|
||||||
|
|
||||||
|
use default_types::DEFAULT_TYPES;
|
||||||
use pathutil::file_name;
|
use pathutil::file_name;
|
||||||
use {Error, Match};
|
use {Error, Match};
|
||||||
|
|
||||||
const DEFAULT_TYPES: &'static [(&'static str, &'static [&'static str])] = &[
|
|
||||||
("agda", &["*.agda", "*.lagda"]),
|
|
||||||
("ats", &["*.ats", "*.dats", "*.sats", "*.hats"]),
|
|
||||||
("aidl", &["*.aidl"]),
|
|
||||||
("amake", &["*.mk", "*.bp"]),
|
|
||||||
("asciidoc", &["*.adoc", "*.asc", "*.asciidoc"]),
|
|
||||||
("asm", &["*.asm", "*.s", "*.S"]),
|
|
||||||
("asp", &["*.aspx", "*.aspx.cs", "*.aspx.cs", "*.ascx", "*.ascx.cs", "*.ascx.vb"]),
|
|
||||||
("avro", &["*.avdl", "*.avpr", "*.avsc"]),
|
|
||||||
("awk", &["*.awk"]),
|
|
||||||
("bazel", &["*.bzl", "WORKSPACE", "BUILD", "BUILD.bazel"]),
|
|
||||||
("bitbake", &["*.bb", "*.bbappend", "*.bbclass", "*.conf", "*.inc"]),
|
|
||||||
("brotli", &["*.br"]),
|
|
||||||
("buildstream", &["*.bst"]),
|
|
||||||
("bzip2", &["*.bz2", "*.tbz2"]),
|
|
||||||
("c", &["*.[chH]", "*.[chH].in", "*.cats"]),
|
|
||||||
("cabal", &["*.cabal"]),
|
|
||||||
("cbor", &["*.cbor"]),
|
|
||||||
("ceylon", &["*.ceylon"]),
|
|
||||||
("clojure", &["*.clj", "*.cljc", "*.cljs", "*.cljx"]),
|
|
||||||
("cmake", &["*.cmake", "CMakeLists.txt"]),
|
|
||||||
("coffeescript", &["*.coffee"]),
|
|
||||||
("creole", &["*.creole"]),
|
|
||||||
("config", &["*.cfg", "*.conf", "*.config", "*.ini"]),
|
|
||||||
("cpp", &[
|
|
||||||
"*.[ChH]", "*.cc", "*.[ch]pp", "*.[ch]xx", "*.hh", "*.inl",
|
|
||||||
"*.[ChH].in", "*.cc.in", "*.[ch]pp.in", "*.[ch]xx.in", "*.hh.in",
|
|
||||||
]),
|
|
||||||
("crystal", &["Projectfile", "*.cr"]),
|
|
||||||
("cs", &["*.cs"]),
|
|
||||||
("csharp", &["*.cs"]),
|
|
||||||
("cshtml", &["*.cshtml"]),
|
|
||||||
("css", &["*.css", "*.scss"]),
|
|
||||||
("csv", &["*.csv"]),
|
|
||||||
("cython", &["*.pyx", "*.pxi", "*.pxd"]),
|
|
||||||
("dart", &["*.dart"]),
|
|
||||||
("d", &["*.d"]),
|
|
||||||
("dhall", &["*.dhall"]),
|
|
||||||
("diff", &["*.patch", "*.diff"]),
|
|
||||||
("docker", &["*Dockerfile*"]),
|
|
||||||
("edn", &["*.edn"]),
|
|
||||||
("elisp", &["*.el"]),
|
|
||||||
("elixir", &["*.ex", "*.eex", "*.exs"]),
|
|
||||||
("elm", &["*.elm"]),
|
|
||||||
("erb", &["*.erb"]),
|
|
||||||
("erlang", &["*.erl", "*.hrl"]),
|
|
||||||
("fidl", &["*.fidl"]),
|
|
||||||
("fish", &["*.fish"]),
|
|
||||||
("fortran", &[
|
|
||||||
"*.f", "*.F", "*.f77", "*.F77", "*.pfo",
|
|
||||||
"*.f90", "*.F90", "*.f95", "*.F95",
|
|
||||||
]),
|
|
||||||
("fsharp", &["*.fs", "*.fsx", "*.fsi"]),
|
|
||||||
("gap", &["*.g", "*.gap", "*.gi", "*.gd", "*.tst"]),
|
|
||||||
("gn", &["*.gn", "*.gni"]),
|
|
||||||
("go", &["*.go"]),
|
|
||||||
("gzip", &["*.gz", "*.tgz"]),
|
|
||||||
("groovy", &["*.groovy", "*.gradle"]),
|
|
||||||
("gradle", &["*.gradle"]),
|
|
||||||
("h", &["*.h", "*.hpp"]),
|
|
||||||
("hbs", &["*.hbs"]),
|
|
||||||
("haskell", &["*.hs", "*.lhs", "*.cpphs", "*.c2hs", "*.hsc"]),
|
|
||||||
("haml", &["*.haml"]),
|
|
||||||
("hs", &["*.hs", "*.lhs"]),
|
|
||||||
("html", &["*.htm", "*.html", "*.ejs"]),
|
|
||||||
("idris", &["*.idr", "*.lidr"]),
|
|
||||||
("java", &["*.java", "*.jsp", "*.jspx", "*.properties"]),
|
|
||||||
("jinja", &["*.j2", "*.jinja", "*.jinja2"]),
|
|
||||||
("js", &[
|
|
||||||
"*.js", "*.jsx", "*.vue",
|
|
||||||
]),
|
|
||||||
("json", &["*.json", "composer.lock"]),
|
|
||||||
("jsonl", &["*.jsonl"]),
|
|
||||||
("julia", &["*.jl"]),
|
|
||||||
("jupyter", &["*.ipynb", "*.jpynb"]),
|
|
||||||
("jl", &["*.jl"]),
|
|
||||||
("kotlin", &["*.kt", "*.kts"]),
|
|
||||||
("less", &["*.less"]),
|
|
||||||
("license", &[
|
|
||||||
// General
|
|
||||||
"COPYING", "COPYING[.-]*",
|
|
||||||
"COPYRIGHT", "COPYRIGHT[.-]*",
|
|
||||||
"EULA", "EULA[.-]*",
|
|
||||||
"licen[cs]e", "licen[cs]e.*",
|
|
||||||
"LICEN[CS]E", "LICEN[CS]E[.-]*", "*[.-]LICEN[CS]E*",
|
|
||||||
"NOTICE", "NOTICE[.-]*",
|
|
||||||
"PATENTS", "PATENTS[.-]*",
|
|
||||||
"UNLICEN[CS]E", "UNLICEN[CS]E[.-]*",
|
|
||||||
// GPL (gpl.txt, etc.)
|
|
||||||
"agpl[.-]*",
|
|
||||||
"gpl[.-]*",
|
|
||||||
"lgpl[.-]*",
|
|
||||||
// Other license-specific (APACHE-2.0.txt, etc.)
|
|
||||||
"AGPL-*[0-9]*",
|
|
||||||
"APACHE-*[0-9]*",
|
|
||||||
"BSD-*[0-9]*",
|
|
||||||
"CC-BY-*",
|
|
||||||
"GFDL-*[0-9]*",
|
|
||||||
"GNU-*[0-9]*",
|
|
||||||
"GPL-*[0-9]*",
|
|
||||||
"LGPL-*[0-9]*",
|
|
||||||
"MIT-*[0-9]*",
|
|
||||||
"MPL-*[0-9]*",
|
|
||||||
"OFL-*[0-9]*",
|
|
||||||
]),
|
|
||||||
("lisp", &["*.el", "*.jl", "*.lisp", "*.lsp", "*.sc", "*.scm"]),
|
|
||||||
("lock", &["*.lock", "package-lock.json"]),
|
|
||||||
("log", &["*.log"]),
|
|
||||||
("lua", &["*.lua"]),
|
|
||||||
("lzma", &["*.lzma"]),
|
|
||||||
("lz4", &["*.lz4"]),
|
|
||||||
("m4", &["*.ac", "*.m4"]),
|
|
||||||
("make", &[
|
|
||||||
"[Gg][Nn][Uu]makefile", "[Mm]akefile",
|
|
||||||
"[Gg][Nn][Uu]makefile.am", "[Mm]akefile.am",
|
|
||||||
"[Gg][Nn][Uu]makefile.in", "[Mm]akefile.in",
|
|
||||||
"*.mk", "*.mak"
|
|
||||||
]),
|
|
||||||
("mako", &["*.mako", "*.mao"]),
|
|
||||||
("markdown", &["*.markdown", "*.md", "*.mdown", "*.mkdn"]),
|
|
||||||
("md", &["*.markdown", "*.md", "*.mdown", "*.mkdn"]),
|
|
||||||
("man", &["*.[0-9lnpx]", "*.[0-9][cEFMmpSx]"]),
|
|
||||||
("matlab", &["*.m"]),
|
|
||||||
("mk", &["mkfile"]),
|
|
||||||
("ml", &["*.ml"]),
|
|
||||||
("msbuild", &[
|
|
||||||
"*.csproj", "*.fsproj", "*.vcxproj", "*.proj", "*.props", "*.targets"
|
|
||||||
]),
|
|
||||||
("nim", &["*.nim", "*.nimf", "*.nimble", "*.nims"]),
|
|
||||||
("nix", &["*.nix"]),
|
|
||||||
("objc", &["*.h", "*.m"]),
|
|
||||||
("objcpp", &["*.h", "*.mm"]),
|
|
||||||
("ocaml", &["*.ml", "*.mli", "*.mll", "*.mly"]),
|
|
||||||
("org", &["*.org", "*.org_archive"]),
|
|
||||||
("pascal", &["*.pas", "*.dpr", "*.lpr", "*.pp", "*.inc"]),
|
|
||||||
("perl", &["*.perl", "*.pl", "*.PL", "*.plh", "*.plx", "*.pm", "*.t"]),
|
|
||||||
("pdf", &["*.pdf"]),
|
|
||||||
("php", &["*.php", "*.php3", "*.php4", "*.php5", "*.phtml"]),
|
|
||||||
("pod", &["*.pod"]),
|
|
||||||
("postscript", &["*.eps", "*.ps"]),
|
|
||||||
("protobuf", &["*.proto"]),
|
|
||||||
("ps", &["*.cdxml", "*.ps1", "*.ps1xml", "*.psd1", "*.psm1"]),
|
|
||||||
("puppet", &["*.erb", "*.pp", "*.rb"]),
|
|
||||||
("purs", &["*.purs"]),
|
|
||||||
("py", &["*.py"]),
|
|
||||||
("qmake", &["*.pro", "*.pri", "*.prf"]),
|
|
||||||
("qml", &["*.qml"]),
|
|
||||||
("readme", &["README*", "*README"]),
|
|
||||||
("r", &["*.R", "*.r", "*.Rmd", "*.Rnw"]),
|
|
||||||
("rdoc", &["*.rdoc"]),
|
|
||||||
("robot", &["*.robot"]),
|
|
||||||
("rst", &["*.rst"]),
|
|
||||||
("ruby", &["Gemfile", "*.gemspec", ".irbrc", "Rakefile", "*.rb"]),
|
|
||||||
("rust", &["*.rs"]),
|
|
||||||
("sass", &["*.sass", "*.scss"]),
|
|
||||||
("scala", &["*.scala", "*.sbt"]),
|
|
||||||
("sh", &[
|
|
||||||
// Portable/misc. init files
|
|
||||||
".login", ".logout", ".profile", "profile",
|
|
||||||
// bash-specific init files
|
|
||||||
".bash_login", "bash_login",
|
|
||||||
".bash_logout", "bash_logout",
|
|
||||||
".bash_profile", "bash_profile",
|
|
||||||
".bashrc", "bashrc", "*.bashrc",
|
|
||||||
// csh-specific init files
|
|
||||||
".cshrc", "*.cshrc",
|
|
||||||
// ksh-specific init files
|
|
||||||
".kshrc", "*.kshrc",
|
|
||||||
// tcsh-specific init files
|
|
||||||
".tcshrc",
|
|
||||||
// zsh-specific init files
|
|
||||||
".zshenv", "zshenv",
|
|
||||||
".zlogin", "zlogin",
|
|
||||||
".zlogout", "zlogout",
|
|
||||||
".zprofile", "zprofile",
|
|
||||||
".zshrc", "zshrc",
|
|
||||||
// Extensions
|
|
||||||
"*.bash", "*.csh", "*.ksh", "*.sh", "*.tcsh", "*.zsh",
|
|
||||||
]),
|
|
||||||
("slim", &["*.skim", "*.slim", "*.slime"]),
|
|
||||||
("smarty", &["*.tpl"]),
|
|
||||||
("sml", &["*.sml", "*.sig"]),
|
|
||||||
("soy", &["*.soy"]),
|
|
||||||
("spark", &["*.spark"]),
|
|
||||||
("spec", &["*.spec"]),
|
|
||||||
("sql", &["*.sql", "*.psql"]),
|
|
||||||
("stylus", &["*.styl"]),
|
|
||||||
("sv", &["*.v", "*.vg", "*.sv", "*.svh", "*.h"]),
|
|
||||||
("svg", &["*.svg"]),
|
|
||||||
("swift", &["*.swift"]),
|
|
||||||
("swig", &["*.def", "*.i"]),
|
|
||||||
("systemd", &[
|
|
||||||
"*.automount", "*.conf", "*.device", "*.link", "*.mount", "*.path",
|
|
||||||
"*.scope", "*.service", "*.slice", "*.socket", "*.swap", "*.target",
|
|
||||||
"*.timer",
|
|
||||||
]),
|
|
||||||
("taskpaper", &["*.taskpaper"]),
|
|
||||||
("tcl", &["*.tcl"]),
|
|
||||||
("tex", &["*.tex", "*.ltx", "*.cls", "*.sty", "*.bib", "*.dtx", "*.ins"]),
|
|
||||||
("textile", &["*.textile"]),
|
|
||||||
("thrift", &["*.thrift"]),
|
|
||||||
("tf", &["*.tf"]),
|
|
||||||
("ts", &["*.ts", "*.tsx"]),
|
|
||||||
("txt", &["*.txt"]),
|
|
||||||
("toml", &["*.toml", "Cargo.lock"]),
|
|
||||||
("twig", &["*.twig"]),
|
|
||||||
("typoscript", &["*.typoscript", "*.ts"]),
|
|
||||||
("vala", &["*.vala"]),
|
|
||||||
("vb", &["*.vb"]),
|
|
||||||
("verilog", &["*.v", "*.vh", "*.sv", "*.svh"]),
|
|
||||||
("vhdl", &["*.vhd", "*.vhdl"]),
|
|
||||||
("vim", &["*.vim"]),
|
|
||||||
("vimscript", &["*.vim"]),
|
|
||||||
("wiki", &["*.mediawiki", "*.wiki"]),
|
|
||||||
("webidl", &["*.idl", "*.webidl", "*.widl"]),
|
|
||||||
("xml", &[
|
|
||||||
"*.xml", "*.xml.dist", "*.dtd", "*.xsl", "*.xslt", "*.xsd", "*.xjb",
|
|
||||||
"*.rng", "*.sch", "*.xhtml",
|
|
||||||
]),
|
|
||||||
("xz", &["*.xz", "*.txz"]),
|
|
||||||
("yacc", &["*.y"]),
|
|
||||||
("yaml", &["*.yaml", "*.yml"]),
|
|
||||||
("zig", &["*.zig"]),
|
|
||||||
("zsh", &[
|
|
||||||
".zshenv", "zshenv",
|
|
||||||
".zlogin", "zlogin",
|
|
||||||
".zlogout", "zlogout",
|
|
||||||
".zprofile", "zprofile",
|
|
||||||
".zshrc", "zshrc",
|
|
||||||
"*.zsh",
|
|
||||||
]),
|
|
||||||
("zstd", &["*.zst", "*.zstd"]),
|
|
||||||
];
|
|
||||||
|
|
||||||
/// Glob represents a single glob in a set of file type definitions.
|
/// Glob represents a single glob in a set of file type definitions.
|
||||||
///
|
///
|
||||||
/// There may be more than one glob for a particular file type.
|
/// There may be more than one glob for a particular file type.
|
||||||
@ -359,7 +126,7 @@ enum GlobInner<'a> {
|
|||||||
which: usize,
|
which: usize,
|
||||||
/// Whether the selection was negated or not.
|
/// Whether the selection was negated or not.
|
||||||
negated: bool,
|
negated: bool,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Glob<'a> {
|
impl<'a> Glob<'a> {
|
||||||
@ -373,9 +140,7 @@ impl<'a> Glob<'a> {
|
|||||||
pub fn file_type_def(&self) -> Option<&FileTypeDef> {
|
pub fn file_type_def(&self) -> Option<&FileTypeDef> {
|
||||||
match self {
|
match self {
|
||||||
Glob(GlobInner::UnmatchedIgnore) => None,
|
Glob(GlobInner::UnmatchedIgnore) => None,
|
||||||
Glob(GlobInner::Matched { def, .. }) => {
|
Glob(GlobInner::Matched { def, .. }) => Some(def),
|
||||||
Some(def)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -561,10 +326,7 @@ impl TypesBuilder {
|
|||||||
/// of default type definitions can be added with `add_defaults`, and
|
/// of default type definitions can be added with `add_defaults`, and
|
||||||
/// additional type definitions can be added with `select` and `negate`.
|
/// additional type definitions can be added with `select` and `negate`.
|
||||||
pub fn new() -> TypesBuilder {
|
pub fn new() -> TypesBuilder {
|
||||||
TypesBuilder {
|
TypesBuilder { types: HashMap::new(), selections: vec![] }
|
||||||
types: HashMap::new(),
|
|
||||||
selections: vec![],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build the current set of file type definitions *and* selections into
|
/// Build the current set of file type definitions *and* selections into
|
||||||
@ -589,19 +351,18 @@ impl TypesBuilder {
|
|||||||
GlobBuilder::new(glob)
|
GlobBuilder::new(glob)
|
||||||
.literal_separator(true)
|
.literal_separator(true)
|
||||||
.build()
|
.build()
|
||||||
.map_err(|err| {
|
.map_err(|err| Error::Glob {
|
||||||
Error::Glob {
|
glob: Some(glob.to_string()),
|
||||||
glob: Some(glob.to_string()),
|
err: err.kind().to_string(),
|
||||||
err: err.kind().to_string(),
|
})?,
|
||||||
}
|
);
|
||||||
})?);
|
|
||||||
glob_to_selection.push((isel, iglob));
|
glob_to_selection.push((isel, iglob));
|
||||||
}
|
}
|
||||||
selections.push(selection.clone().map(move |_| def));
|
selections.push(selection.clone().map(move |_| def));
|
||||||
}
|
}
|
||||||
let set = build_set.build().map_err(|err| {
|
let set = build_set
|
||||||
Error::Glob { glob: None, err: err.to_string() }
|
.build()
|
||||||
})?;
|
.map_err(|err| Error::Glob { glob: None, err: err.to_string() })?;
|
||||||
Ok(Types {
|
Ok(Types {
|
||||||
defs: defs,
|
defs: defs,
|
||||||
selections: selections,
|
selections: selections,
|
||||||
@ -673,9 +434,14 @@ impl TypesBuilder {
|
|||||||
return Err(Error::InvalidDefinition);
|
return Err(Error::InvalidDefinition);
|
||||||
}
|
}
|
||||||
let (key, glob) = (name.to_string(), glob.to_string());
|
let (key, glob) = (name.to_string(), glob.to_string());
|
||||||
self.types.entry(key).or_insert_with(|| {
|
self.types
|
||||||
FileTypeDef { name: name.to_string(), globs: vec![] }
|
.entry(key)
|
||||||
}).globs.push(glob);
|
.or_insert_with(|| FileTypeDef {
|
||||||
|
name: name.to_string(),
|
||||||
|
globs: vec![],
|
||||||
|
})
|
||||||
|
.globs
|
||||||
|
.push(glob);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -702,7 +468,10 @@ impl TypesBuilder {
|
|||||||
3 => {
|
3 => {
|
||||||
let name = parts[0];
|
let name = parts[0];
|
||||||
let types_string = parts[2];
|
let types_string = parts[2];
|
||||||
if name.is_empty() || parts[1] != "include" || types_string.is_empty() {
|
if name.is_empty()
|
||||||
|
|| parts[1] != "include"
|
||||||
|
|| types_string.is_empty()
|
||||||
|
{
|
||||||
return Err(Error::InvalidDefinition);
|
return Err(Error::InvalidDefinition);
|
||||||
}
|
}
|
||||||
let types = types_string.split(',');
|
let types = types_string.split(',');
|
||||||
@ -712,14 +481,15 @@ impl TypesBuilder {
|
|||||||
return Err(Error::InvalidDefinition);
|
return Err(Error::InvalidDefinition);
|
||||||
}
|
}
|
||||||
for type_name in types {
|
for type_name in types {
|
||||||
let globs = self.types.get(type_name).unwrap().globs.clone();
|
let globs =
|
||||||
|
self.types.get(type_name).unwrap().globs.clone();
|
||||||
for glob in globs {
|
for glob in globs {
|
||||||
self.add(name, &glob)?;
|
self.add(name, &glob)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Err(Error::InvalidDefinition)
|
_ => Err(Error::InvalidDefinition),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -776,7 +546,7 @@ mod tests {
|
|||||||
"rust:*.rs",
|
"rust:*.rs",
|
||||||
"js:*.js",
|
"js:*.js",
|
||||||
"foo:*.{rs,foo}",
|
"foo:*.{rs,foo}",
|
||||||
"combo:include:html,rust"
|
"combo:include:html,rust",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -810,7 +580,7 @@ mod tests {
|
|||||||
"combo:include:html,python",
|
"combo:include:html,python",
|
||||||
// Bad format
|
// Bad format
|
||||||
"combo:foobar:html,rust",
|
"combo:foobar:html,rust",
|
||||||
""
|
"",
|
||||||
];
|
];
|
||||||
for def in bad_defs {
|
for def in bad_defs {
|
||||||
assert!(btypes.add_def(def).is_err());
|
assert!(btypes.add_def(def).is_err());
|
||||||
|
@ -102,24 +102,15 @@ impl DirEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn new_stdin() -> DirEntry {
|
fn new_stdin() -> DirEntry {
|
||||||
DirEntry {
|
DirEntry { dent: DirEntryInner::Stdin, err: None }
|
||||||
dent: DirEntryInner::Stdin,
|
|
||||||
err: None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_walkdir(dent: walkdir::DirEntry, err: Option<Error>) -> DirEntry {
|
fn new_walkdir(dent: walkdir::DirEntry, err: Option<Error>) -> DirEntry {
|
||||||
DirEntry {
|
DirEntry { dent: DirEntryInner::Walkdir(dent), err: err }
|
||||||
dent: DirEntryInner::Walkdir(dent),
|
|
||||||
err: err,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_raw(dent: DirEntryRaw, err: Option<Error>) -> DirEntry {
|
fn new_raw(dent: DirEntryRaw, err: Option<Error>) -> DirEntry {
|
||||||
DirEntry {
|
DirEntry { dent: DirEntryInner::Raw(dent), err: err }
|
||||||
dent: DirEntryInner::Raw(dent),
|
|
||||||
err: err,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,9 +176,9 @@ impl DirEntryInner {
|
|||||||
));
|
));
|
||||||
Err(err.with_path("<stdin>"))
|
Err(err.with_path("<stdin>"))
|
||||||
}
|
}
|
||||||
Walkdir(ref x) => x
|
Walkdir(ref x) => x.metadata().map_err(|err| {
|
||||||
.metadata()
|
Error::Io(io::Error::from(err)).with_path(x.path())
|
||||||
.map_err(|err| Error::Io(io::Error::from(err)).with_path(x.path())),
|
}),
|
||||||
Raw(ref x) => x.metadata(),
|
Raw(ref x) => x.metadata(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -314,9 +305,7 @@ impl DirEntryRaw {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn file_name(&self) -> &OsStr {
|
fn file_name(&self) -> &OsStr {
|
||||||
self.path
|
self.path.file_name().unwrap_or_else(|| self.path.as_os_str())
|
||||||
.file_name()
|
|
||||||
.unwrap_or_else(|| self.path.as_os_str())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn depth(&self) -> usize {
|
fn depth(&self) -> usize {
|
||||||
@ -328,13 +317,13 @@ impl DirEntryRaw {
|
|||||||
self.ino
|
self.ino
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_entry(depth: usize, ent: &fs::DirEntry) -> Result<DirEntryRaw, Error> {
|
fn from_entry(
|
||||||
|
depth: usize,
|
||||||
|
ent: &fs::DirEntry,
|
||||||
|
) -> Result<DirEntryRaw, Error> {
|
||||||
let ty = ent.file_type().map_err(|err| {
|
let ty = ent.file_type().map_err(|err| {
|
||||||
let err = Error::Io(io::Error::from(err)).with_path(ent.path());
|
let err = Error::Io(io::Error::from(err)).with_path(ent.path());
|
||||||
Error::WithDepth {
|
Error::WithDepth { depth: depth, err: Box::new(err) }
|
||||||
depth: depth,
|
|
||||||
err: Box::new(err),
|
|
||||||
}
|
|
||||||
})?;
|
})?;
|
||||||
DirEntryRaw::from_entry_os(depth, ent, ty)
|
DirEntryRaw::from_entry_os(depth, ent, ty)
|
||||||
}
|
}
|
||||||
@ -347,10 +336,7 @@ impl DirEntryRaw {
|
|||||||
) -> Result<DirEntryRaw, Error> {
|
) -> Result<DirEntryRaw, Error> {
|
||||||
let md = ent.metadata().map_err(|err| {
|
let md = ent.metadata().map_err(|err| {
|
||||||
let err = Error::Io(io::Error::from(err)).with_path(ent.path());
|
let err = Error::Io(io::Error::from(err)).with_path(ent.path());
|
||||||
Error::WithDepth {
|
Error::WithDepth { depth: depth, err: Box::new(err) }
|
||||||
depth: depth,
|
|
||||||
err: Box::new(err),
|
|
||||||
}
|
|
||||||
})?;
|
})?;
|
||||||
Ok(DirEntryRaw {
|
Ok(DirEntryRaw {
|
||||||
path: ent.path(),
|
path: ent.path(),
|
||||||
@ -392,8 +378,13 @@ impl DirEntryRaw {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
fn from_path(depth: usize, pb: PathBuf, link: bool) -> Result<DirEntryRaw, Error> {
|
fn from_path(
|
||||||
let md = fs::metadata(&pb).map_err(|err| Error::Io(err).with_path(&pb))?;
|
depth: usize,
|
||||||
|
pb: PathBuf,
|
||||||
|
link: bool,
|
||||||
|
) -> Result<DirEntryRaw, Error> {
|
||||||
|
let md =
|
||||||
|
fs::metadata(&pb).map_err(|err| Error::Io(err).with_path(&pb))?;
|
||||||
Ok(DirEntryRaw {
|
Ok(DirEntryRaw {
|
||||||
path: pb,
|
path: pb,
|
||||||
ty: md.file_type(),
|
ty: md.file_type(),
|
||||||
@ -404,10 +395,15 @@ impl DirEntryRaw {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
fn from_path(depth: usize, pb: PathBuf, link: bool) -> Result<DirEntryRaw, Error> {
|
fn from_path(
|
||||||
|
depth: usize,
|
||||||
|
pb: PathBuf,
|
||||||
|
link: bool,
|
||||||
|
) -> Result<DirEntryRaw, Error> {
|
||||||
use std::os::unix::fs::MetadataExt;
|
use std::os::unix::fs::MetadataExt;
|
||||||
|
|
||||||
let md = fs::metadata(&pb).map_err(|err| Error::Io(err).with_path(&pb))?;
|
let md =
|
||||||
|
fs::metadata(&pb).map_err(|err| Error::Io(err).with_path(&pb))?;
|
||||||
Ok(DirEntryRaw {
|
Ok(DirEntryRaw {
|
||||||
path: pb,
|
path: pb,
|
||||||
ty: md.file_type(),
|
ty: md.file_type(),
|
||||||
@ -419,7 +415,11 @@ impl DirEntryRaw {
|
|||||||
|
|
||||||
// Placeholder implementation to allow compiling on non-standard platforms (e.g. wasm32).
|
// Placeholder implementation to allow compiling on non-standard platforms (e.g. wasm32).
|
||||||
#[cfg(not(any(windows, unix)))]
|
#[cfg(not(any(windows, unix)))]
|
||||||
fn from_path(depth: usize, pb: PathBuf, link: bool) -> Result<DirEntryRaw, Error> {
|
fn from_path(
|
||||||
|
depth: usize,
|
||||||
|
pb: PathBuf,
|
||||||
|
link: bool,
|
||||||
|
) -> Result<DirEntryRaw, Error> {
|
||||||
Err(Error::Io(io::Error::new(
|
Err(Error::Io(io::Error::new(
|
||||||
io::ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
"unsupported platform",
|
"unsupported platform",
|
||||||
@ -490,7 +490,9 @@ pub struct WalkBuilder {
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
enum Sorter {
|
enum Sorter {
|
||||||
ByName(Arc<dyn Fn(&OsStr, &OsStr) -> cmp::Ordering + Send + Sync + 'static>),
|
ByName(
|
||||||
|
Arc<dyn Fn(&OsStr, &OsStr) -> cmp::Ordering + Send + Sync + 'static>,
|
||||||
|
),
|
||||||
ByPath(Arc<dyn Fn(&Path, &Path) -> cmp::Ordering + Send + Sync + 'static>),
|
ByPath(Arc<dyn Fn(&Path, &Path) -> cmp::Ordering + Send + Sync + 'static>),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -550,10 +552,14 @@ impl WalkBuilder {
|
|||||||
if let Some(ref sorter) = sorter {
|
if let Some(ref sorter) = sorter {
|
||||||
match sorter.clone() {
|
match sorter.clone() {
|
||||||
Sorter::ByName(cmp) => {
|
Sorter::ByName(cmp) => {
|
||||||
wd = wd.sort_by(move |a, b| cmp(a.file_name(), b.file_name()));
|
wd = wd.sort_by(move |a, b| {
|
||||||
|
cmp(a.file_name(), b.file_name())
|
||||||
|
});
|
||||||
}
|
}
|
||||||
Sorter::ByPath(cmp) => {
|
Sorter::ByPath(cmp) => {
|
||||||
wd = wd.sort_by(move |a, b| cmp(a.path(), b.path()));
|
wd = wd.sort_by(move |a, b| {
|
||||||
|
cmp(a.path(), b.path())
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1012,11 +1018,7 @@ enum WalkEvent {
|
|||||||
|
|
||||||
impl From<WalkDir> for WalkEventIter {
|
impl From<WalkDir> for WalkEventIter {
|
||||||
fn from(it: WalkDir) -> WalkEventIter {
|
fn from(it: WalkDir) -> WalkEventIter {
|
||||||
WalkEventIter {
|
WalkEventIter { depth: 0, it: it.into_iter(), next: None }
|
||||||
depth: 0,
|
|
||||||
it: it.into_iter(),
|
|
||||||
next: None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1085,8 +1087,8 @@ pub trait ParallelVisitorBuilder<'s> {
|
|||||||
fn build(&mut self) -> Box<dyn ParallelVisitor + 's>;
|
fn build(&mut self) -> Box<dyn ParallelVisitor + 's>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 's, P: ParallelVisitorBuilder<'s>>
|
impl<'a, 's, P: ParallelVisitorBuilder<'s>> ParallelVisitorBuilder<'s>
|
||||||
ParallelVisitorBuilder<'s> for &'a mut P
|
for &'a mut P
|
||||||
{
|
{
|
||||||
fn build(&mut self) -> Box<dyn ParallelVisitor + 's> {
|
fn build(&mut self) -> Box<dyn ParallelVisitor + 's> {
|
||||||
(**self).build()
|
(**self).build()
|
||||||
@ -1109,16 +1111,17 @@ struct FnBuilder<F> {
|
|||||||
builder: F,
|
builder: F,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s, F: FnMut() -> FnVisitor<'s>> ParallelVisitorBuilder<'s> for FnBuilder<F> {
|
impl<'s, F: FnMut() -> FnVisitor<'s>> ParallelVisitorBuilder<'s>
|
||||||
|
for FnBuilder<F>
|
||||||
|
{
|
||||||
fn build(&mut self) -> Box<dyn ParallelVisitor + 's> {
|
fn build(&mut self) -> Box<dyn ParallelVisitor + 's> {
|
||||||
let visitor = (self.builder)();
|
let visitor = (self.builder)();
|
||||||
Box::new(FnVisitorImp { visitor })
|
Box::new(FnVisitorImp { visitor })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type FnVisitor<'s> = Box<
|
type FnVisitor<'s> =
|
||||||
dyn FnMut(Result<DirEntry, Error>) -> WalkState + Send + 's
|
Box<dyn FnMut(Result<DirEntry, Error>) -> WalkState + Send + 's>;
|
||||||
>;
|
|
||||||
|
|
||||||
struct FnVisitorImp<'s> {
|
struct FnVisitorImp<'s> {
|
||||||
visitor: FnVisitor<'s>,
|
visitor: FnVisitor<'s>,
|
||||||
@ -1263,7 +1266,8 @@ impl WalkParallel {
|
|||||||
for handle in handles {
|
for handle in handles {
|
||||||
handle.join().unwrap();
|
handle.join().unwrap();
|
||||||
}
|
}
|
||||||
}).unwrap(); // Pass along panics from threads
|
})
|
||||||
|
.unwrap(); // Pass along panics from threads
|
||||||
}
|
}
|
||||||
|
|
||||||
fn threads(&self) -> usize {
|
fn threads(&self) -> usize {
|
||||||
@ -1488,7 +1492,9 @@ impl<'s> Worker<'s> {
|
|||||||
let fs_dent = match result {
|
let fs_dent = match result {
|
||||||
Ok(fs_dent) => fs_dent,
|
Ok(fs_dent) => fs_dent,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
return self.visitor.visit(Err(Error::from(err).with_depth(depth)));
|
return self
|
||||||
|
.visitor
|
||||||
|
.visit(Err(Error::from(err).with_depth(depth)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let mut dent = match DirEntryRaw::from_entry(depth, &fs_dent) {
|
let mut dent = match DirEntryRaw::from_entry(depth, &fs_dent) {
|
||||||
@ -1522,15 +1528,16 @@ impl<'s> Worker<'s> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let should_skip_path = should_skip_entry(ig, &dent);
|
let should_skip_path = should_skip_entry(ig, &dent);
|
||||||
let should_skip_filesize = if self.max_filesize.is_some() && !dent.is_dir() {
|
let should_skip_filesize =
|
||||||
skip_filesize(
|
if self.max_filesize.is_some() && !dent.is_dir() {
|
||||||
self.max_filesize.unwrap(),
|
skip_filesize(
|
||||||
dent.path(),
|
self.max_filesize.unwrap(),
|
||||||
&dent.metadata().ok(),
|
dent.path(),
|
||||||
)
|
&dent.metadata().ok(),
|
||||||
} else {
|
)
|
||||||
false
|
} else {
|
||||||
};
|
false
|
||||||
|
};
|
||||||
|
|
||||||
if !should_skip_path && !should_skip_filesize {
|
if !should_skip_path && !should_skip_filesize {
|
||||||
self.tx
|
self.tx
|
||||||
@ -1579,11 +1586,12 @@ impl<'s> Worker<'s> {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
// Wait for next `Work` or `Quit` message.
|
// Wait for next `Work` or `Quit` message.
|
||||||
value = Ok(self.rx.recv().expect(
|
value = Ok(self
|
||||||
"channel disconnected while worker is alive",
|
.rx
|
||||||
));
|
.recv()
|
||||||
|
.expect("channel disconnected while worker is alive"));
|
||||||
self.resume();
|
self.resume();
|
||||||
},
|
}
|
||||||
Err(TryRecvError::Disconnected) => {
|
Err(TryRecvError::Disconnected) => {
|
||||||
unreachable!("channel disconnected while worker is alive");
|
unreachable!("channel disconnected while worker is alive");
|
||||||
}
|
}
|
||||||
@ -1619,18 +1627,11 @@ fn check_symlink_loop(
|
|||||||
child_depth: usize,
|
child_depth: usize,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let hchild = Handle::from_path(child_path).map_err(|err| {
|
let hchild = Handle::from_path(child_path).map_err(|err| {
|
||||||
Error::from(err)
|
Error::from(err).with_path(child_path).with_depth(child_depth)
|
||||||
.with_path(child_path)
|
|
||||||
.with_depth(child_depth)
|
|
||||||
})?;
|
})?;
|
||||||
for ig in ig_parent
|
for ig in ig_parent.parents().take_while(|ig| !ig.is_absolute_parent()) {
|
||||||
.parents()
|
|
||||||
.take_while(|ig| !ig.is_absolute_parent())
|
|
||||||
{
|
|
||||||
let h = Handle::from_path(ig.path()).map_err(|err| {
|
let h = Handle::from_path(ig.path()).map_err(|err| {
|
||||||
Error::from(err)
|
Error::from(err).with_path(child_path).with_depth(child_depth)
|
||||||
.with_path(child_path)
|
|
||||||
.with_depth(child_depth)
|
|
||||||
})?;
|
})?;
|
||||||
if hchild == h {
|
if hchild == h {
|
||||||
return Err(Error::Loop {
|
return Err(Error::Loop {
|
||||||
@ -1645,7 +1646,11 @@ fn check_symlink_loop(
|
|||||||
|
|
||||||
// Before calling this function, make sure that you ensure that is really
|
// Before calling this function, make sure that you ensure that is really
|
||||||
// necessary as the arguments imply a file stat.
|
// necessary as the arguments imply a file stat.
|
||||||
fn skip_filesize(max_filesize: u64, path: &Path, ent: &Option<Metadata>) -> bool {
|
fn skip_filesize(
|
||||||
|
max_filesize: u64,
|
||||||
|
path: &Path,
|
||||||
|
ent: &Option<Metadata>,
|
||||||
|
) -> bool {
|
||||||
let filesize = match *ent {
|
let filesize = match *ent {
|
||||||
Some(ref md) => Some(md.len()),
|
Some(ref md) => Some(md.len()),
|
||||||
None => None,
|
None => None,
|
||||||
@ -1743,7 +1748,8 @@ fn walkdir_is_dir(dent: &walkdir::DirEntry) -> bool {
|
|||||||
/// Returns true if and only if the given path is on the same device as the
|
/// Returns true if and only if the given path is on the same device as the
|
||||||
/// given root device.
|
/// given root device.
|
||||||
fn is_same_file_system(root_device: u64, path: &Path) -> Result<bool, Error> {
|
fn is_same_file_system(root_device: u64, path: &Path) -> Result<bool, Error> {
|
||||||
let dent_device = device_num(path).map_err(|err| Error::Io(err).with_path(path))?;
|
let dent_device =
|
||||||
|
device_num(path).map_err(|err| Error::Io(err).with_path(path))?;
|
||||||
Ok(root_device == dent_device)
|
Ok(root_device == dent_device)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1825,7 +1831,10 @@ mod tests {
|
|||||||
paths
|
paths
|
||||||
}
|
}
|
||||||
|
|
||||||
fn walk_collect_parallel(prefix: &Path, builder: &WalkBuilder) -> Vec<String> {
|
fn walk_collect_parallel(
|
||||||
|
prefix: &Path,
|
||||||
|
builder: &WalkBuilder,
|
||||||
|
) -> Vec<String> {
|
||||||
let mut paths = vec![];
|
let mut paths = vec![];
|
||||||
for dent in walk_collect_entries_parallel(builder) {
|
for dent in walk_collect_entries_parallel(builder) {
|
||||||
let path = dent.path().strip_prefix(prefix).unwrap();
|
let path = dent.path().strip_prefix(prefix).unwrap();
|
||||||
@ -2079,7 +2088,9 @@ mod tests {
|
|||||||
assert_eq!(1, dents.len());
|
assert_eq!(1, dents.len());
|
||||||
assert!(!dents[0].path_is_symlink());
|
assert!(!dents[0].path_is_symlink());
|
||||||
|
|
||||||
let dents = walk_collect_entries_parallel(&WalkBuilder::new(td.path().join("foo")));
|
let dents = walk_collect_entries_parallel(&WalkBuilder::new(
|
||||||
|
td.path().join("foo"),
|
||||||
|
));
|
||||||
assert_eq!(1, dents.len());
|
assert_eq!(1, dents.len());
|
||||||
assert!(!dents[0].path_is_symlink());
|
assert!(!dents[0].path_is_symlink());
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,6 @@ fn test_files_in_root() {
|
|||||||
assert!(m("ROOT/file_root_33").is_none());
|
assert!(m("ROOT/file_root_33").is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_files_in_deep() {
|
fn test_files_in_deep() {
|
||||||
let gitignore = get_gitignore();
|
let gitignore = get_gitignore();
|
||||||
@ -88,7 +87,6 @@ fn test_files_in_deep() {
|
|||||||
assert!(m("ROOT/parent_dir/file_deep_33").is_none());
|
assert!(m("ROOT/parent_dir/file_deep_33").is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_dirs_in_root() {
|
fn test_dirs_in_root() {
|
||||||
let gitignore = get_gitignore();
|
let gitignore = get_gitignore();
|
||||||
@ -193,7 +191,6 @@ fn test_dirs_in_root() {
|
|||||||
assert!(m("ROOT/dir_root_33/child_dir/file", false).is_ignore());
|
assert!(m("ROOT/dir_root_33/child_dir/file", false).is_ignore());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_dirs_in_deep() {
|
fn test_dirs_in_deep() {
|
||||||
let gitignore = get_gitignore();
|
let gitignore = get_gitignore();
|
||||||
@ -205,17 +202,13 @@ fn test_dirs_in_deep() {
|
|||||||
assert!(m("ROOT/parent_dir/dir_deep_00", true).is_ignore());
|
assert!(m("ROOT/parent_dir/dir_deep_00", true).is_ignore());
|
||||||
assert!(m("ROOT/parent_dir/dir_deep_00/file", false).is_ignore());
|
assert!(m("ROOT/parent_dir/dir_deep_00/file", false).is_ignore());
|
||||||
assert!(m("ROOT/parent_dir/dir_deep_00/child_dir", true).is_ignore());
|
assert!(m("ROOT/parent_dir/dir_deep_00/child_dir", true).is_ignore());
|
||||||
assert!(
|
assert!(m("ROOT/parent_dir/dir_deep_00/child_dir/file", false).is_ignore());
|
||||||
m("ROOT/parent_dir/dir_deep_00/child_dir/file", false).is_ignore()
|
|
||||||
);
|
|
||||||
|
|
||||||
// 01
|
// 01
|
||||||
assert!(m("ROOT/parent_dir/dir_deep_01", true).is_ignore());
|
assert!(m("ROOT/parent_dir/dir_deep_01", true).is_ignore());
|
||||||
assert!(m("ROOT/parent_dir/dir_deep_01/file", false).is_ignore());
|
assert!(m("ROOT/parent_dir/dir_deep_01/file", false).is_ignore());
|
||||||
assert!(m("ROOT/parent_dir/dir_deep_01/child_dir", true).is_ignore());
|
assert!(m("ROOT/parent_dir/dir_deep_01/child_dir", true).is_ignore());
|
||||||
assert!(
|
assert!(m("ROOT/parent_dir/dir_deep_01/child_dir/file", false).is_ignore());
|
||||||
m("ROOT/parent_dir/dir_deep_01/child_dir/file", false).is_ignore()
|
|
||||||
);
|
|
||||||
|
|
||||||
// 02
|
// 02
|
||||||
assert!(m("ROOT/parent_dir/dir_deep_02", true).is_none());
|
assert!(m("ROOT/parent_dir/dir_deep_02", true).is_none());
|
||||||
@ -257,67 +250,51 @@ fn test_dirs_in_deep() {
|
|||||||
assert!(m("ROOT/parent_dir/dir_deep_20", true).is_ignore());
|
assert!(m("ROOT/parent_dir/dir_deep_20", true).is_ignore());
|
||||||
assert!(m("ROOT/parent_dir/dir_deep_20/file", false).is_ignore());
|
assert!(m("ROOT/parent_dir/dir_deep_20/file", false).is_ignore());
|
||||||
assert!(m("ROOT/parent_dir/dir_deep_20/child_dir", true).is_ignore());
|
assert!(m("ROOT/parent_dir/dir_deep_20/child_dir", true).is_ignore());
|
||||||
assert!(
|
assert!(m("ROOT/parent_dir/dir_deep_20/child_dir/file", false).is_ignore());
|
||||||
m("ROOT/parent_dir/dir_deep_20/child_dir/file", false).is_ignore()
|
|
||||||
);
|
|
||||||
|
|
||||||
// 21
|
// 21
|
||||||
assert!(m("ROOT/parent_dir/dir_deep_21", true).is_ignore());
|
assert!(m("ROOT/parent_dir/dir_deep_21", true).is_ignore());
|
||||||
assert!(m("ROOT/parent_dir/dir_deep_21/file", false).is_ignore());
|
assert!(m("ROOT/parent_dir/dir_deep_21/file", false).is_ignore());
|
||||||
assert!(m("ROOT/parent_dir/dir_deep_21/child_dir", true).is_ignore());
|
assert!(m("ROOT/parent_dir/dir_deep_21/child_dir", true).is_ignore());
|
||||||
assert!(
|
assert!(m("ROOT/parent_dir/dir_deep_21/child_dir/file", false).is_ignore());
|
||||||
m("ROOT/parent_dir/dir_deep_21/child_dir/file", false).is_ignore()
|
|
||||||
);
|
|
||||||
|
|
||||||
// 22
|
// 22
|
||||||
// dir itself doesn't match
|
// dir itself doesn't match
|
||||||
assert!(m("ROOT/parent_dir/dir_deep_22", true).is_none());
|
assert!(m("ROOT/parent_dir/dir_deep_22", true).is_none());
|
||||||
assert!(m("ROOT/parent_dir/dir_deep_22/file", false).is_ignore());
|
assert!(m("ROOT/parent_dir/dir_deep_22/file", false).is_ignore());
|
||||||
assert!(m("ROOT/parent_dir/dir_deep_22/child_dir", true).is_ignore());
|
assert!(m("ROOT/parent_dir/dir_deep_22/child_dir", true).is_ignore());
|
||||||
assert!(
|
assert!(m("ROOT/parent_dir/dir_deep_22/child_dir/file", false).is_ignore());
|
||||||
m("ROOT/parent_dir/dir_deep_22/child_dir/file", false).is_ignore()
|
|
||||||
);
|
|
||||||
|
|
||||||
// 23
|
// 23
|
||||||
// dir itself doesn't match
|
// dir itself doesn't match
|
||||||
assert!(m("ROOT/parent_dir/dir_deep_23", true).is_none());
|
assert!(m("ROOT/parent_dir/dir_deep_23", true).is_none());
|
||||||
assert!(m("ROOT/parent_dir/dir_deep_23/file", false).is_ignore());
|
assert!(m("ROOT/parent_dir/dir_deep_23/file", false).is_ignore());
|
||||||
assert!(m("ROOT/parent_dir/dir_deep_23/child_dir", true).is_ignore());
|
assert!(m("ROOT/parent_dir/dir_deep_23/child_dir", true).is_ignore());
|
||||||
assert!(
|
assert!(m("ROOT/parent_dir/dir_deep_23/child_dir/file", false).is_ignore());
|
||||||
m("ROOT/parent_dir/dir_deep_23/child_dir/file", false).is_ignore()
|
|
||||||
);
|
|
||||||
|
|
||||||
// 30
|
// 30
|
||||||
assert!(m("ROOT/parent_dir/dir_deep_30", true).is_ignore());
|
assert!(m("ROOT/parent_dir/dir_deep_30", true).is_ignore());
|
||||||
assert!(m("ROOT/parent_dir/dir_deep_30/file", false).is_ignore());
|
assert!(m("ROOT/parent_dir/dir_deep_30/file", false).is_ignore());
|
||||||
assert!(m("ROOT/parent_dir/dir_deep_30/child_dir", true).is_ignore());
|
assert!(m("ROOT/parent_dir/dir_deep_30/child_dir", true).is_ignore());
|
||||||
assert!(
|
assert!(m("ROOT/parent_dir/dir_deep_30/child_dir/file", false).is_ignore());
|
||||||
m("ROOT/parent_dir/dir_deep_30/child_dir/file", false).is_ignore()
|
|
||||||
);
|
|
||||||
|
|
||||||
// 31
|
// 31
|
||||||
assert!(m("ROOT/parent_dir/dir_deep_31", true).is_ignore());
|
assert!(m("ROOT/parent_dir/dir_deep_31", true).is_ignore());
|
||||||
assert!(m("ROOT/parent_dir/dir_deep_31/file", false).is_ignore());
|
assert!(m("ROOT/parent_dir/dir_deep_31/file", false).is_ignore());
|
||||||
assert!(m("ROOT/parent_dir/dir_deep_31/child_dir", true).is_ignore());
|
assert!(m("ROOT/parent_dir/dir_deep_31/child_dir", true).is_ignore());
|
||||||
assert!(
|
assert!(m("ROOT/parent_dir/dir_deep_31/child_dir/file", false).is_ignore());
|
||||||
m("ROOT/parent_dir/dir_deep_31/child_dir/file", false).is_ignore()
|
|
||||||
);
|
|
||||||
|
|
||||||
// 32
|
// 32
|
||||||
// dir itself doesn't match
|
// dir itself doesn't match
|
||||||
assert!(m("ROOT/parent_dir/dir_deep_32", true).is_none());
|
assert!(m("ROOT/parent_dir/dir_deep_32", true).is_none());
|
||||||
assert!(m("ROOT/parent_dir/dir_deep_32/file", false).is_ignore());
|
assert!(m("ROOT/parent_dir/dir_deep_32/file", false).is_ignore());
|
||||||
assert!(m("ROOT/parent_dir/dir_deep_32/child_dir", true).is_ignore());
|
assert!(m("ROOT/parent_dir/dir_deep_32/child_dir", true).is_ignore());
|
||||||
assert!(
|
assert!(m("ROOT/parent_dir/dir_deep_32/child_dir/file", false).is_ignore());
|
||||||
m("ROOT/parent_dir/dir_deep_32/child_dir/file", false).is_ignore()
|
|
||||||
);
|
|
||||||
|
|
||||||
// 33
|
// 33
|
||||||
// dir itself doesn't match
|
// dir itself doesn't match
|
||||||
assert!(m("ROOT/parent_dir/dir_deep_33", true).is_none());
|
assert!(m("ROOT/parent_dir/dir_deep_33", true).is_none());
|
||||||
assert!(m("ROOT/parent_dir/dir_deep_33/file", false).is_ignore());
|
assert!(m("ROOT/parent_dir/dir_deep_33/file", false).is_ignore());
|
||||||
assert!(m("ROOT/parent_dir/dir_deep_33/child_dir", true).is_ignore());
|
assert!(m("ROOT/parent_dir/dir_deep_33/child_dir", true).is_ignore());
|
||||||
assert!(
|
assert!(m("ROOT/parent_dir/dir_deep_33/child_dir/file", false).is_ignore());
|
||||||
m("ROOT/parent_dir/dir_deep_33/child_dir/file", false).is_ignore()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
@ -1 +1,2 @@
|
|||||||
disable_all_formatting = true
|
max_width = 79
|
||||||
|
use_small_heuristics = "max"
|
||||||
|
1092
src/app.rs
1092
src/app.rs
File diff suppressed because it is too large
Load Diff
234
src/args.rs
234
src/args.rs
@ -17,11 +17,8 @@ use grep::pcre2::{
|
|||||||
RegexMatcherBuilder as PCRE2RegexMatcherBuilder,
|
RegexMatcherBuilder as PCRE2RegexMatcherBuilder,
|
||||||
};
|
};
|
||||||
use grep::printer::{
|
use grep::printer::{
|
||||||
ColorSpecs, Stats,
|
default_color_specs, ColorSpecs, JSONBuilder, Standard, StandardBuilder,
|
||||||
JSON, JSONBuilder,
|
Stats, Summary, SummaryBuilder, SummaryKind, JSON,
|
||||||
Standard, StandardBuilder,
|
|
||||||
Summary, SummaryBuilder, SummaryKind,
|
|
||||||
default_color_specs,
|
|
||||||
};
|
};
|
||||||
use grep::regex::{
|
use grep::regex::{
|
||||||
RegexMatcher as RustRegexMatcher,
|
RegexMatcher as RustRegexMatcher,
|
||||||
@ -36,15 +33,12 @@ use ignore::{Walk, WalkBuilder, WalkParallel};
|
|||||||
use log;
|
use log;
|
||||||
use num_cpus;
|
use num_cpus;
|
||||||
use regex;
|
use regex;
|
||||||
use termcolor::{
|
use termcolor::{BufferWriter, ColorChoice, WriteColor};
|
||||||
WriteColor,
|
|
||||||
BufferWriter, ColorChoice,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::app;
|
use crate::app;
|
||||||
use crate::config;
|
use crate::config;
|
||||||
use crate::logger::Logger;
|
use crate::logger::Logger;
|
||||||
use crate::messages::{set_messages, set_ignore_messages};
|
use crate::messages::{set_ignore_messages, set_messages};
|
||||||
use crate::path_printer::{PathPrinter, PathPrinterBuilder};
|
use crate::path_printer::{PathPrinter, PathPrinterBuilder};
|
||||||
use crate::search::{
|
use crate::search::{
|
||||||
PatternMatcher, Printer, SearchWorker, SearchWorkerBuilder,
|
PatternMatcher, Printer, SearchWorker, SearchWorkerBuilder,
|
||||||
@ -84,11 +78,9 @@ impl Command {
|
|||||||
|
|
||||||
match *self {
|
match *self {
|
||||||
Search | SearchParallel => true,
|
Search | SearchParallel => true,
|
||||||
| SearchNever
|
SearchNever | Files | FilesParallel | Types | PCRE2Version => {
|
||||||
| Files
|
false
|
||||||
| FilesParallel
|
}
|
||||||
| Types
|
|
||||||
| PCRE2Version => false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -210,15 +202,12 @@ impl Args {
|
|||||||
.printer_standard(self.paths(), wtr, separator_search)
|
.printer_standard(self.paths(), wtr, separator_search)
|
||||||
.map(Printer::Standard)
|
.map(Printer::Standard)
|
||||||
}
|
}
|
||||||
OutputKind::Summary => {
|
OutputKind::Summary => self
|
||||||
self.matches()
|
.matches()
|
||||||
.printer_summary(self.paths(), wtr)
|
.printer_summary(self.paths(), wtr)
|
||||||
.map(Printer::Summary)
|
.map(Printer::Summary),
|
||||||
}
|
|
||||||
OutputKind::JSON => {
|
OutputKind::JSON => {
|
||||||
self.matches()
|
self.matches().printer_json(wtr).map(Printer::JSON)
|
||||||
.printer_json(wtr)
|
|
||||||
.map(Printer::JSON)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -452,29 +441,23 @@ impl SortBy {
|
|||||||
}
|
}
|
||||||
SortByKind::LastModified => {
|
SortByKind::LastModified => {
|
||||||
builder.sort_by_file_path(move |a, b| {
|
builder.sort_by_file_path(move |a, b| {
|
||||||
sort_by_metadata_time(
|
sort_by_metadata_time(a, b, self.reverse, |md| {
|
||||||
a, b,
|
md.modified()
|
||||||
self.reverse,
|
})
|
||||||
|md| md.modified(),
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
SortByKind::LastAccessed => {
|
SortByKind::LastAccessed => {
|
||||||
builder.sort_by_file_path(move |a, b| {
|
builder.sort_by_file_path(move |a, b| {
|
||||||
sort_by_metadata_time(
|
sort_by_metadata_time(a, b, self.reverse, |md| {
|
||||||
a, b,
|
md.accessed()
|
||||||
self.reverse,
|
})
|
||||||
|md| md.accessed(),
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
SortByKind::Created => {
|
SortByKind::Created => {
|
||||||
builder.sort_by_file_path(move |a, b| {
|
builder.sort_by_file_path(move |a, b| {
|
||||||
sort_by_metadata_time(
|
sort_by_metadata_time(a, b, self.reverse, |md| {
|
||||||
a, b,
|
md.created()
|
||||||
self.reverse,
|
})
|
||||||
|md| md.created(),
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -520,7 +503,7 @@ impl EncodingMode {
|
|||||||
fn has_explicit_encoding(&self) -> bool {
|
fn has_explicit_encoding(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
EncodingMode::Some(_) => true,
|
EncodingMode::Some(_) => true,
|
||||||
_ => false
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -568,13 +551,12 @@ impl ArgMatches {
|
|||||||
let patterns = self.patterns()?;
|
let patterns = self.patterns()?;
|
||||||
let matcher = self.matcher(&patterns)?;
|
let matcher = self.matcher(&patterns)?;
|
||||||
let mut paths = self.paths();
|
let mut paths = self.paths();
|
||||||
let using_default_path =
|
let using_default_path = if paths.is_empty() {
|
||||||
if paths.is_empty() {
|
paths.push(self.path_default());
|
||||||
paths.push(self.path_default());
|
true
|
||||||
true
|
} else {
|
||||||
} else {
|
false
|
||||||
false
|
};
|
||||||
};
|
|
||||||
Ok(Args(Arc::new(ArgsImp {
|
Ok(Args(Arc::new(ArgsImp {
|
||||||
matches: self,
|
matches: self,
|
||||||
patterns: patterns,
|
patterns: patterns,
|
||||||
@ -605,7 +587,8 @@ impl ArgMatches {
|
|||||||
Err(err) => err,
|
Err(err) => err,
|
||||||
};
|
};
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"error building Rust regex in hybrid mode:\n{}", rust_err,
|
"error building Rust regex in hybrid mode:\n{}",
|
||||||
|
rust_err,
|
||||||
);
|
);
|
||||||
let pcre_err = match self.matcher_pcre2(patterns) {
|
let pcre_err = match self.matcher_pcre2(patterns) {
|
||||||
Ok(matcher) => return Ok(PatternMatcher::PCRE2(matcher)),
|
Ok(matcher) => return Ok(PatternMatcher::PCRE2(matcher)),
|
||||||
@ -616,7 +599,10 @@ impl ArgMatches {
|
|||||||
engine or with PCRE2.\n\n\
|
engine or with PCRE2.\n\n\
|
||||||
default regex engine error:\n{}\n{}\n{}\n\n\
|
default regex engine error:\n{}\n{}\n{}\n\n\
|
||||||
PCRE2 regex engine error:\n{}",
|
PCRE2 regex engine error:\n{}",
|
||||||
"~".repeat(79), rust_err, "~".repeat(79), pcre_err,
|
"~".repeat(79),
|
||||||
|
rust_err,
|
||||||
|
"~".repeat(79),
|
||||||
|
pcre_err,
|
||||||
)))
|
)))
|
||||||
} else {
|
} else {
|
||||||
let matcher = match self.matcher_rust(patterns) {
|
let matcher = match self.matcher_rust(patterns) {
|
||||||
@ -660,14 +646,10 @@ impl ArgMatches {
|
|||||||
if self.is_present("multiline") {
|
if self.is_present("multiline") {
|
||||||
builder.dot_matches_new_line(self.is_present("multiline-dotall"));
|
builder.dot_matches_new_line(self.is_present("multiline-dotall"));
|
||||||
if self.is_present("crlf") {
|
if self.is_present("crlf") {
|
||||||
builder
|
builder.crlf(true).line_terminator(None);
|
||||||
.crlf(true)
|
|
||||||
.line_terminator(None);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
builder
|
builder.line_terminator(Some(b'\n')).dot_matches_new_line(false);
|
||||||
.line_terminator(Some(b'\n'))
|
|
||||||
.dot_matches_new_line(false);
|
|
||||||
if self.is_present("crlf") {
|
if self.is_present("crlf") {
|
||||||
builder.crlf(true);
|
builder.crlf(true);
|
||||||
}
|
}
|
||||||
@ -686,12 +668,11 @@ impl ArgMatches {
|
|||||||
if let Some(limit) = self.dfa_size_limit()? {
|
if let Some(limit) = self.dfa_size_limit()? {
|
||||||
builder.dfa_size_limit(limit);
|
builder.dfa_size_limit(limit);
|
||||||
}
|
}
|
||||||
let res =
|
let res = if self.is_present("fixed-strings") {
|
||||||
if self.is_present("fixed-strings") {
|
builder.build_literals(patterns)
|
||||||
builder.build_literals(patterns)
|
} else {
|
||||||
} else {
|
builder.build(&patterns.join("|"))
|
||||||
builder.build(&patterns.join("|"))
|
};
|
||||||
};
|
|
||||||
match res {
|
match res {
|
||||||
Ok(m) => Ok(m),
|
Ok(m) => Ok(m),
|
||||||
Err(err) => Err(From::from(suggest_multiline(err.to_string()))),
|
Err(err) => Err(From::from(suggest_multiline(err.to_string()))),
|
||||||
@ -718,7 +699,7 @@ impl ArgMatches {
|
|||||||
// The PCRE2 docs say that 32KB is the default, and that 1MB
|
// The PCRE2 docs say that 32KB is the default, and that 1MB
|
||||||
// should be big enough for anything. But let's crank it to
|
// should be big enough for anything. But let's crank it to
|
||||||
// 10MB.
|
// 10MB.
|
||||||
.max_jit_stack_size(Some(10 * (1<<20)));
|
.max_jit_stack_size(Some(10 * (1 << 20)));
|
||||||
}
|
}
|
||||||
if self.unicode() {
|
if self.unicode() {
|
||||||
builder.utf(true).ucp(true);
|
builder.utf(true).ucp(true);
|
||||||
@ -822,14 +803,13 @@ impl ArgMatches {
|
|||||||
/// Build a searcher from the command line parameters.
|
/// Build a searcher from the command line parameters.
|
||||||
fn searcher(&self, paths: &[PathBuf]) -> Result<Searcher> {
|
fn searcher(&self, paths: &[PathBuf]) -> Result<Searcher> {
|
||||||
let (ctx_before, ctx_after) = self.contexts()?;
|
let (ctx_before, ctx_after) = self.contexts()?;
|
||||||
let line_term =
|
let line_term = if self.is_present("crlf") {
|
||||||
if self.is_present("crlf") {
|
LineTerminator::crlf()
|
||||||
LineTerminator::crlf()
|
} else if self.is_present("null-data") {
|
||||||
} else if self.is_present("null-data") {
|
LineTerminator::byte(b'\x00')
|
||||||
LineTerminator::byte(b'\x00')
|
} else {
|
||||||
} else {
|
LineTerminator::byte(b'\n')
|
||||||
LineTerminator::byte(b'\n')
|
};
|
||||||
};
|
|
||||||
let mut builder = SearcherBuilder::new();
|
let mut builder = SearcherBuilder::new();
|
||||||
builder
|
builder
|
||||||
.line_terminator(line_term)
|
.line_terminator(line_term)
|
||||||
@ -902,12 +882,9 @@ impl ArgMatches {
|
|||||||
/// Returns the form of binary detection to perform on files that are
|
/// Returns the form of binary detection to perform on files that are
|
||||||
/// implicitly searched via recursive directory traversal.
|
/// implicitly searched via recursive directory traversal.
|
||||||
fn binary_detection_implicit(&self) -> BinaryDetection {
|
fn binary_detection_implicit(&self) -> BinaryDetection {
|
||||||
let none =
|
let none = self.is_present("text") || self.is_present("null-data");
|
||||||
self.is_present("text")
|
|
||||||
|| self.is_present("null-data");
|
|
||||||
let convert =
|
let convert =
|
||||||
self.is_present("binary")
|
self.is_present("binary") || self.unrestricted_count() >= 3;
|
||||||
|| self.unrestricted_count() >= 3;
|
|
||||||
if none {
|
if none {
|
||||||
BinaryDetection::none()
|
BinaryDetection::none()
|
||||||
} else if convert {
|
} else if convert {
|
||||||
@ -925,9 +902,7 @@ impl ArgMatches {
|
|||||||
/// as a filter (but quitting immediately once a NUL byte is seen), and we
|
/// as a filter (but quitting immediately once a NUL byte is seen), and we
|
||||||
/// should never filter out files that the user wants to explicitly search.
|
/// should never filter out files that the user wants to explicitly search.
|
||||||
fn binary_detection_explicit(&self) -> BinaryDetection {
|
fn binary_detection_explicit(&self) -> BinaryDetection {
|
||||||
let none =
|
let none = self.is_present("text") || self.is_present("null-data");
|
||||||
self.is_present("text")
|
|
||||||
|| self.is_present("null-data");
|
|
||||||
if none {
|
if none {
|
||||||
BinaryDetection::none()
|
BinaryDetection::none()
|
||||||
} else {
|
} else {
|
||||||
@ -955,8 +930,8 @@ impl ArgMatches {
|
|||||||
/// case is disabled.
|
/// case is disabled.
|
||||||
fn case_smart(&self) -> bool {
|
fn case_smart(&self) -> bool {
|
||||||
self.is_present("smart-case")
|
self.is_present("smart-case")
|
||||||
&& !self.is_present("ignore-case")
|
&& !self.is_present("ignore-case")
|
||||||
&& !self.is_present("case-sensitive")
|
&& !self.is_present("case-sensitive")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the user's color choice based on command line parameters and
|
/// Returns the user's color choice based on command line parameters and
|
||||||
@ -1012,11 +987,7 @@ impl ArgMatches {
|
|||||||
let after = self.usize_of("after-context")?.unwrap_or(0);
|
let after = self.usize_of("after-context")?.unwrap_or(0);
|
||||||
let before = self.usize_of("before-context")?.unwrap_or(0);
|
let before = self.usize_of("before-context")?.unwrap_or(0);
|
||||||
let both = self.usize_of("context")?.unwrap_or(0);
|
let both = self.usize_of("context")?.unwrap_or(0);
|
||||||
Ok(if both > 0 {
|
Ok(if both > 0 { (both, both) } else { (before, after) })
|
||||||
(both, both)
|
|
||||||
} else {
|
|
||||||
(before, after)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the unescaped context separator in UTF-8 bytes.
|
/// Returns the unescaped context separator in UTF-8 bytes.
|
||||||
@ -1111,8 +1082,8 @@ impl ArgMatches {
|
|||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
cli::is_tty_stdout()
|
cli::is_tty_stdout()
|
||||||
|| self.is_present("heading")
|
|| self.is_present("heading")
|
||||||
|| self.is_present("pretty")
|
|| self.is_present("pretty")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1168,10 +1139,10 @@ impl ArgMatches {
|
|||||||
// tty for human consumption, except for one interesting case: when
|
// tty for human consumption, except for one interesting case: when
|
||||||
// we're only searching stdin. This makes pipelines work as expected.
|
// we're only searching stdin. This makes pipelines work as expected.
|
||||||
(cli::is_tty_stdout() && !self.is_only_stdin(paths))
|
(cli::is_tty_stdout() && !self.is_only_stdin(paths))
|
||||||
|| self.is_present("line-number")
|
|| self.is_present("line-number")
|
||||||
|| self.is_present("column")
|
|| self.is_present("column")
|
||||||
|| self.is_present("pretty")
|
|| self.is_present("pretty")
|
||||||
|| self.is_present("vimgrep")
|
|| self.is_present("vimgrep")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The maximum number of columns allowed on each line.
|
/// The maximum number of columns allowed on each line.
|
||||||
@ -1264,8 +1235,7 @@ impl ArgMatches {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let (count, count_matches) = self.counts();
|
let (count, count_matches) = self.counts();
|
||||||
let summary =
|
let summary = count
|
||||||
count
|
|
||||||
|| count_matches
|
|| count_matches
|
||||||
|| self.is_present("files-with-matches")
|
|| self.is_present("files-with-matches")
|
||||||
|| self.is_present("files-without-match");
|
|| self.is_present("files-without-match");
|
||||||
@ -1325,10 +1295,10 @@ impl ArgMatches {
|
|||||||
/// be used when ripgrep is not otherwise given at least one file path
|
/// be used when ripgrep is not otherwise given at least one file path
|
||||||
/// as a positional argument.
|
/// as a positional argument.
|
||||||
fn path_default(&self) -> PathBuf {
|
fn path_default(&self) -> PathBuf {
|
||||||
let file_is_stdin = self.values_of_os("file")
|
let file_is_stdin = self
|
||||||
|
.values_of_os("file")
|
||||||
.map_or(false, |mut files| files.any(|f| f == "-"));
|
.map_or(false, |mut files| files.any(|f| f == "-"));
|
||||||
let search_cwd =
|
let search_cwd = !cli::is_readable_stdin()
|
||||||
!cli::is_readable_stdin()
|
|
||||||
|| (self.is_present("file") && file_is_stdin)
|
|| (self.is_present("file") && file_is_stdin)
|
||||||
|| self.is_present("files")
|
|| self.is_present("files")
|
||||||
|| self.is_present("type-list")
|
|| self.is_present("type-list")
|
||||||
@ -1357,8 +1327,8 @@ impl ArgMatches {
|
|||||||
the given separator is {} bytes: {}\n\
|
the given separator is {} bytes: {}\n\
|
||||||
In some shells on Windows '/' is automatically \
|
In some shells on Windows '/' is automatically \
|
||||||
expanded. Use '//' instead.",
|
expanded. Use '//' instead.",
|
||||||
sep.len(),
|
sep.len(),
|
||||||
cli::escape(&sep),
|
cli::escape(&sep),
|
||||||
)))
|
)))
|
||||||
} else {
|
} else {
|
||||||
Ok(Some(sep[0]))
|
Ok(Some(sep[0]))
|
||||||
@ -1408,14 +1378,16 @@ impl ArgMatches {
|
|||||||
if let Some(paths) = self.values_of_os("file") {
|
if let Some(paths) = self.values_of_os("file") {
|
||||||
for path in paths {
|
for path in paths {
|
||||||
if path == "-" {
|
if path == "-" {
|
||||||
pats.extend(cli::patterns_from_stdin()?
|
pats.extend(
|
||||||
.into_iter()
|
cli::patterns_from_stdin()?
|
||||||
.map(|p| self.pattern_from_string(p))
|
.into_iter()
|
||||||
|
.map(|p| self.pattern_from_string(p)),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
pats.extend(cli::patterns_from_path(path)?
|
pats.extend(
|
||||||
.into_iter()
|
cli::patterns_from_path(path)?
|
||||||
.map(|p| self.pattern_from_string(p))
|
.into_iter()
|
||||||
|
.map(|p| self.pattern_from_string(p)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1528,7 +1500,7 @@ impl ArgMatches {
|
|||||||
None => match self.value_of_lossy("sortr") {
|
None => match self.value_of_lossy("sortr") {
|
||||||
None => return Ok(SortBy::none()),
|
None => return Ok(SortBy::none()),
|
||||||
Some(choice) => SortBy::desc(SortByKind::new(&choice)),
|
Some(choice) => SortBy::desc(SortByKind::new(&choice)),
|
||||||
}
|
},
|
||||||
Some(choice) => SortBy::asc(SortByKind::new(&choice)),
|
Some(choice) => SortBy::asc(SortByKind::new(&choice)),
|
||||||
};
|
};
|
||||||
Ok(sortby)
|
Ok(sortby)
|
||||||
@ -1571,11 +1543,7 @@ impl ArgMatches {
|
|||||||
return Ok(1);
|
return Ok(1);
|
||||||
}
|
}
|
||||||
let threads = self.usize_of("threads")?.unwrap_or(0);
|
let threads = self.usize_of("threads")?.unwrap_or(0);
|
||||||
Ok(if threads == 0 {
|
Ok(if threads == 0 { cmp::min(12, num_cpus::get()) } else { threads })
|
||||||
cmp::min(12, num_cpus::get())
|
|
||||||
} else {
|
|
||||||
threads
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds a file type matcher from the command line flags.
|
/// Builds a file type matcher from the command line flags.
|
||||||
@ -1623,9 +1591,11 @@ impl ArgMatches {
|
|||||||
} else {
|
} else {
|
||||||
let path_stdin = Path::new("-");
|
let path_stdin = Path::new("-");
|
||||||
self.is_present("with-filename")
|
self.is_present("with-filename")
|
||||||
|| self.is_present("vimgrep")
|
|| self.is_present("vimgrep")
|
||||||
|| paths.len() > 1
|
|| paths.len() > 1
|
||||||
|| paths.get(0).map_or(false, |p| p != path_stdin && p.is_dir())
|
|| paths
|
||||||
|
.get(0)
|
||||||
|
.map_or(false, |p| p != path_stdin && p.is_dir())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1648,11 +1618,7 @@ impl ArgMatches {
|
|||||||
None => return Ok(None),
|
None => return Ok(None),
|
||||||
Some(n) => n,
|
Some(n) => n,
|
||||||
};
|
};
|
||||||
Ok(if n == 0 {
|
Ok(if n == 0 { None } else { Some(n) })
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(n)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Safely reads an arg value with the given name, and if it's present,
|
/// Safely reads an arg value with the given name, and if it's present,
|
||||||
@ -1718,19 +1684,25 @@ fn suggest_pcre2(msg: String) -> String {
|
|||||||
if !msg.contains("backreferences") && !msg.contains("look-around") {
|
if !msg.contains("backreferences") && !msg.contains("look-around") {
|
||||||
msg
|
msg
|
||||||
} else {
|
} else {
|
||||||
format!("{}
|
format!(
|
||||||
|
"{}
|
||||||
|
|
||||||
Consider enabling PCRE2 with the --pcre2 flag, which can handle backreferences
|
Consider enabling PCRE2 with the --pcre2 flag, which can handle backreferences
|
||||||
and look-around.", msg)
|
and look-around.",
|
||||||
|
msg
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn suggest_multiline(msg: String) -> String {
|
fn suggest_multiline(msg: String) -> String {
|
||||||
if msg.contains("the literal") && msg.contains("not allowed") {
|
if msg.contains("the literal") && msg.contains("not allowed") {
|
||||||
format!("{}
|
format!(
|
||||||
|
"{}
|
||||||
|
|
||||||
Consider enabling multiline mode with the --multiline flag (or -U for short).
|
Consider enabling multiline mode with the --multiline flag (or -U for short).
|
||||||
When multiline mode is enabled, new line characters can be matched.", msg)
|
When multiline mode is enabled, new line characters can be matched.",
|
||||||
|
msg
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
msg
|
msg
|
||||||
}
|
}
|
||||||
@ -1738,10 +1710,7 @@ When multiline mode is enabled, new line characters can be matched.", msg)
|
|||||||
|
|
||||||
/// Convert the result of parsing a human readable file size to a `usize`,
|
/// Convert the result of parsing a human readable file size to a `usize`,
|
||||||
/// failing if the type does not fit.
|
/// failing if the type does not fit.
|
||||||
fn u64_to_usize(
|
fn u64_to_usize(arg_name: &str, value: Option<u64>) -> Result<Option<usize>> {
|
||||||
arg_name: &str,
|
|
||||||
value: Option<u64>,
|
|
||||||
) -> Result<Option<usize>> {
|
|
||||||
use std::usize;
|
use std::usize;
|
||||||
|
|
||||||
let value = match value {
|
let value = match value {
|
||||||
@ -1766,7 +1735,8 @@ fn sort_by_metadata_time<G>(
|
|||||||
reverse: bool,
|
reverse: bool,
|
||||||
get_time: G,
|
get_time: G,
|
||||||
) -> cmp::Ordering
|
) -> cmp::Ordering
|
||||||
where G: Fn(&fs::Metadata) -> io::Result<SystemTime>
|
where
|
||||||
|
G: Fn(&fs::Metadata) -> io::Result<SystemTime>,
|
||||||
{
|
{
|
||||||
let t1 = match p1.metadata().and_then(|md| get_time(&md)) {
|
let t1 = match p1.metadata().and_then(|md| get_time(&md)) {
|
||||||
Ok(t) => t,
|
Ok(t) => t,
|
||||||
@ -1789,11 +1759,10 @@ where G: Fn(&fs::Metadata) -> io::Result<SystemTime>
|
|||||||
/// corresponds to a `--help` or `--version` request. In which case, the
|
/// corresponds to a `--help` or `--version` request. In which case, the
|
||||||
/// corresponding output is printed and the current process is exited
|
/// corresponding output is printed and the current process is exited
|
||||||
/// successfully.
|
/// successfully.
|
||||||
fn clap_matches<I, T>(
|
fn clap_matches<I, T>(args: I) -> Result<clap::ArgMatches<'static>>
|
||||||
args: I,
|
where
|
||||||
) -> Result<clap::ArgMatches<'static>>
|
I: IntoIterator<Item = T>,
|
||||||
where I: IntoIterator<Item=T>,
|
T: Into<OsString> + Clone,
|
||||||
T: Into<OsString> + Clone
|
|
||||||
{
|
{
|
||||||
let err = match app::app().get_matches_from_safe(args) {
|
let err = match app::app().get_matches_from_safe(args) {
|
||||||
Ok(matches) => return Ok(matches),
|
Ok(matches) => return Ok(matches),
|
||||||
@ -1831,5 +1800,6 @@ fn current_dir() -> Result<PathBuf> {
|
|||||||
"failed to get current working directory: {} \
|
"failed to get current working directory: {} \
|
||||||
--- did your CWD get deleted?",
|
--- did your CWD get deleted?",
|
||||||
err,
|
err,
|
||||||
).into())
|
)
|
||||||
|
.into())
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
use std::ffi::OsString;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::ffi::OsString;
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use bstr::{io::BufReadExt, ByteSlice};
|
use bstr::{io::BufReadExt, ByteSlice};
|
||||||
@ -102,12 +102,13 @@ fn parse_reader<R: io::Read>(
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::ffi::OsString;
|
|
||||||
use super::parse_reader;
|
use super::parse_reader;
|
||||||
|
use std::ffi::OsString;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn basic() {
|
fn basic() {
|
||||||
let (args, errs) = parse_reader(&b"\
|
let (args, errs) = parse_reader(
|
||||||
|
&b"\
|
||||||
# Test
|
# Test
|
||||||
--context=0
|
--context=0
|
||||||
--smart-case
|
--smart-case
|
||||||
@ -116,13 +117,13 @@ mod tests {
|
|||||||
|
|
||||||
# --bar
|
# --bar
|
||||||
--foo
|
--foo
|
||||||
"[..]).unwrap();
|
"[..],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
assert!(errs.is_empty());
|
assert!(errs.is_empty());
|
||||||
let args: Vec<String> =
|
let args: Vec<String> =
|
||||||
args.into_iter().map(|s| s.into_string().unwrap()).collect();
|
args.into_iter().map(|s| s.into_string().unwrap()).collect();
|
||||||
assert_eq!(args, vec![
|
assert_eq!(args, vec!["--context=0", "--smart-case", "-u", "--foo",]);
|
||||||
"--context=0", "--smart-case", "-u", "--foo",
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We test that we can handle invalid UTF-8 on Unix-like systems.
|
// We test that we can handle invalid UTF-8 on Unix-like systems.
|
||||||
@ -131,32 +132,38 @@ mod tests {
|
|||||||
fn error() {
|
fn error() {
|
||||||
use std::os::unix::ffi::OsStringExt;
|
use std::os::unix::ffi::OsStringExt;
|
||||||
|
|
||||||
let (args, errs) = parse_reader(&b"\
|
let (args, errs) = parse_reader(
|
||||||
|
&b"\
|
||||||
quux
|
quux
|
||||||
foo\xFFbar
|
foo\xFFbar
|
||||||
baz
|
baz
|
||||||
"[..]).unwrap();
|
"[..],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
assert!(errs.is_empty());
|
assert!(errs.is_empty());
|
||||||
assert_eq!(args, vec![
|
assert_eq!(
|
||||||
OsString::from("quux"),
|
args,
|
||||||
OsString::from_vec(b"foo\xFFbar".to_vec()),
|
vec![
|
||||||
OsString::from("baz"),
|
OsString::from("quux"),
|
||||||
]);
|
OsString::from_vec(b"foo\xFFbar".to_vec()),
|
||||||
|
OsString::from("baz"),
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ... but test that invalid UTF-8 fails on Windows.
|
// ... but test that invalid UTF-8 fails on Windows.
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(not(unix))]
|
#[cfg(not(unix))]
|
||||||
fn error() {
|
fn error() {
|
||||||
let (args, errs) = parse_reader(&b"\
|
let (args, errs) = parse_reader(
|
||||||
|
&b"\
|
||||||
quux
|
quux
|
||||||
foo\xFFbar
|
foo\xFFbar
|
||||||
baz
|
baz
|
||||||
"[..]).unwrap();
|
"[..],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
assert_eq!(errs.len(), 1);
|
assert_eq!(errs.len(), 1);
|
||||||
assert_eq!(args, vec![
|
assert_eq!(args, vec![OsString::from("quux"), OsString::from("baz"),]);
|
||||||
OsString::from("quux"),
|
|
||||||
OsString::from("baz"),
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,10 +37,7 @@ impl PathPrinterBuilder {
|
|||||||
/// Create a new path printer with the current configuration that writes
|
/// Create a new path printer with the current configuration that writes
|
||||||
/// paths to the given writer.
|
/// paths to the given writer.
|
||||||
pub fn build<W: WriteColor>(&self, wtr: W) -> PathPrinter<W> {
|
pub fn build<W: WriteColor>(&self, wtr: W) -> PathPrinter<W> {
|
||||||
PathPrinter {
|
PathPrinter { config: self.config.clone(), wtr: wtr }
|
||||||
config: self.config.clone(),
|
|
||||||
wtr: wtr,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the color specification for this printer.
|
/// Set the color specification for this printer.
|
||||||
|
@ -7,9 +7,9 @@ use std::time::Duration;
|
|||||||
use grep::cli;
|
use grep::cli;
|
||||||
use grep::matcher::Matcher;
|
use grep::matcher::Matcher;
|
||||||
#[cfg(feature = "pcre2")]
|
#[cfg(feature = "pcre2")]
|
||||||
use grep::pcre2::{RegexMatcher as PCRE2RegexMatcher};
|
use grep::pcre2::RegexMatcher as PCRE2RegexMatcher;
|
||||||
use grep::printer::{JSON, Standard, Summary, Stats};
|
use grep::printer::{Standard, Stats, Summary, JSON};
|
||||||
use grep::regex::{RegexMatcher as RustRegexMatcher};
|
use grep::regex::RegexMatcher as RustRegexMatcher;
|
||||||
use grep::searcher::{BinaryDetection, Searcher};
|
use grep::searcher::{BinaryDetection, Searcher};
|
||||||
use ignore::overrides::Override;
|
use ignore::overrides::Override;
|
||||||
use serde_json as json;
|
use serde_json as json;
|
||||||
@ -86,8 +86,12 @@ impl SearchWorkerBuilder {
|
|||||||
let command_builder = self.command_builder.clone();
|
let command_builder = self.command_builder.clone();
|
||||||
let decomp_builder = self.decomp_builder.clone();
|
let decomp_builder = self.decomp_builder.clone();
|
||||||
SearchWorker {
|
SearchWorker {
|
||||||
config, command_builder, decomp_builder,
|
config,
|
||||||
matcher, searcher, printer,
|
command_builder,
|
||||||
|
decomp_builder,
|
||||||
|
matcher,
|
||||||
|
searcher,
|
||||||
|
printer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,9 +231,7 @@ impl<W: WriteColor> Printer<W> {
|
|||||||
stats: &Stats,
|
stats: &Stats,
|
||||||
) -> io::Result<()> {
|
) -> io::Result<()> {
|
||||||
match *self {
|
match *self {
|
||||||
Printer::JSON(_) => {
|
Printer::JSON(_) => self.print_stats_json(total_duration, stats),
|
||||||
self.print_stats_json(total_duration, stats)
|
|
||||||
}
|
|
||||||
Printer::Standard(_) | Printer::Summary(_) => {
|
Printer::Standard(_) | Printer::Summary(_) => {
|
||||||
self.print_stats_human(total_duration, stats)
|
self.print_stats_human(total_duration, stats)
|
||||||
}
|
}
|
||||||
@ -273,17 +275,20 @@ impl<W: WriteColor> Printer<W> {
|
|||||||
// the grep-printer crate. We simply "extend" it with the 'summary'
|
// the grep-printer crate. We simply "extend" it with the 'summary'
|
||||||
// message type.
|
// message type.
|
||||||
let fractional = fractional_seconds(total_duration);
|
let fractional = fractional_seconds(total_duration);
|
||||||
json::to_writer(self.get_mut(), &json!({
|
json::to_writer(
|
||||||
"type": "summary",
|
self.get_mut(),
|
||||||
"data": {
|
&json!({
|
||||||
"stats": stats,
|
"type": "summary",
|
||||||
"elapsed_total": {
|
"data": {
|
||||||
"secs": total_duration.as_secs(),
|
"stats": stats,
|
||||||
"nanos": total_duration.subsec_nanos(),
|
"elapsed_total": {
|
||||||
"human": format!("{:0.6}s", fractional),
|
"secs": total_duration.as_secs(),
|
||||||
},
|
"nanos": total_duration.subsec_nanos(),
|
||||||
}
|
"human": format!("{:0.6}s", fractional),
|
||||||
}))?;
|
},
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)?;
|
||||||
write!(self.get_mut(), "\n")
|
write!(self.get_mut(), "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -315,12 +320,11 @@ pub struct SearchWorker<W> {
|
|||||||
impl<W: WriteColor> SearchWorker<W> {
|
impl<W: WriteColor> SearchWorker<W> {
|
||||||
/// Execute a search over the given subject.
|
/// Execute a search over the given subject.
|
||||||
pub fn search(&mut self, subject: &Subject) -> io::Result<SearchResult> {
|
pub fn search(&mut self, subject: &Subject) -> io::Result<SearchResult> {
|
||||||
let bin =
|
let bin = if subject.is_explicit() {
|
||||||
if subject.is_explicit() {
|
self.config.binary_explicit.clone()
|
||||||
self.config.binary_explicit.clone()
|
} else {
|
||||||
} else {
|
self.config.binary_implicit.clone()
|
||||||
self.config.binary_implicit.clone()
|
};
|
||||||
};
|
|
||||||
self.searcher.set_binary_detection(bin);
|
self.searcher.set_binary_detection(bin);
|
||||||
|
|
||||||
let path = subject.path();
|
let path = subject.path();
|
||||||
@ -389,19 +393,15 @@ impl<W: WriteColor> SearchWorker<W> {
|
|||||||
let mut cmd = Command::new(bin);
|
let mut cmd = Command::new(bin);
|
||||||
cmd.arg(path).stdin(Stdio::from(File::open(path)?));
|
cmd.arg(path).stdin(Stdio::from(File::open(path)?));
|
||||||
|
|
||||||
let rdr = self
|
let rdr = self.command_builder.build(&mut cmd).map_err(|err| {
|
||||||
.command_builder
|
io::Error::new(
|
||||||
.build(&mut cmd)
|
io::ErrorKind::Other,
|
||||||
.map_err(|err| {
|
format!(
|
||||||
io::Error::new(
|
"preprocessor command could not start: '{:?}': {}",
|
||||||
io::ErrorKind::Other,
|
cmd, err,
|
||||||
format!(
|
),
|
||||||
"preprocessor command could not start: '{:?}': {}",
|
)
|
||||||
cmd,
|
})?;
|
||||||
err,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
self.search_reader(path, rdr).map_err(|err| {
|
self.search_reader(path, rdr).map_err(|err| {
|
||||||
io::Error::new(
|
io::Error::new(
|
||||||
io::ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
@ -413,10 +413,7 @@ impl<W: WriteColor> SearchWorker<W> {
|
|||||||
/// Attempt to decompress the data at the given file path and search the
|
/// Attempt to decompress the data at the given file path and search the
|
||||||
/// result. If the given file path isn't recognized as a compressed file,
|
/// result. If the given file path isn't recognized as a compressed file,
|
||||||
/// then search it without doing any decompression.
|
/// then search it without doing any decompression.
|
||||||
fn search_decompress(
|
fn search_decompress(&mut self, path: &Path) -> io::Result<SearchResult> {
|
||||||
&mut self,
|
|
||||||
path: &Path,
|
|
||||||
) -> io::Result<SearchResult> {
|
|
||||||
let rdr = self.decomp_builder.build(path)?;
|
let rdr = self.decomp_builder.build(path)?;
|
||||||
self.search_reader(path, rdr)
|
self.search_reader(path, rdr)
|
||||||
}
|
}
|
||||||
|
@ -11,9 +11,7 @@ struct Config {
|
|||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
fn default() -> Config {
|
fn default() -> Config {
|
||||||
Config {
|
Config { strip_dot_prefix: false }
|
||||||
strip_dot_prefix: false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,9 +76,9 @@ impl SubjectBuilder {
|
|||||||
log::debug!(
|
log::debug!(
|
||||||
"ignoring {}: failed to pass subject filter: \
|
"ignoring {}: failed to pass subject filter: \
|
||||||
file type: {:?}, metadata: {:?}",
|
file type: {:?}, metadata: {:?}",
|
||||||
subj.dent.path().display(),
|
subj.dent.path().display(),
|
||||||
subj.dent.file_type(),
|
subj.dent.file_type(),
|
||||||
subj.dent.metadata()
|
subj.dent.metadata()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
|
111
tests/binary.rs
111
tests/binary.rs
@ -36,9 +36,7 @@ const HAY: &'static [u8] = include_bytes!("./data/sherlock-nul.txt");
|
|||||||
// that matches our file.
|
// that matches our file.
|
||||||
rgtest!(after_match1_implicit, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(after_match1_implicit, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create_bytes("hay", HAY);
|
dir.create_bytes("hay", HAY);
|
||||||
cmd.args(&[
|
cmd.args(&["--no-mmap", "-n", "Project Gutenberg EBook", "-g", "hay"]);
|
||||||
"--no-mmap", "-n", "Project Gutenberg EBook", "-g", "hay",
|
|
||||||
]);
|
|
||||||
|
|
||||||
let expected = "\
|
let expected = "\
|
||||||
hay:1:The Project Gutenberg EBook of A Study In Scarlet, by Arthur Conan Doyle
|
hay:1:The Project Gutenberg EBook of A Study In Scarlet, by Arthur Conan Doyle
|
||||||
@ -51,9 +49,7 @@ WARNING: stopped searching binary file hay after match (found \"\\u{0}\" byte ar
|
|||||||
// explicitly. This results in identical behavior, but a different message.
|
// explicitly. This results in identical behavior, but a different message.
|
||||||
rgtest!(after_match1_explicit, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(after_match1_explicit, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create_bytes("hay", HAY);
|
dir.create_bytes("hay", HAY);
|
||||||
cmd.args(&[
|
cmd.args(&["--no-mmap", "-n", "Project Gutenberg EBook", "hay"]);
|
||||||
"--no-mmap", "-n", "Project Gutenberg EBook", "hay",
|
|
||||||
]);
|
|
||||||
|
|
||||||
let expected = "\
|
let expected = "\
|
||||||
1:The Project Gutenberg EBook of A Study In Scarlet, by Arthur Conan Doyle
|
1:The Project Gutenberg EBook of A Study In Scarlet, by Arthur Conan Doyle
|
||||||
@ -64,9 +60,7 @@ Binary file matches (found \"\\u{0}\" byte around offset 9741)
|
|||||||
|
|
||||||
// Like after_match1_explicit, except we feed our content on stdin.
|
// Like after_match1_explicit, except we feed our content on stdin.
|
||||||
rgtest!(after_match1_stdin, |_: Dir, mut cmd: TestCommand| {
|
rgtest!(after_match1_stdin, |_: Dir, mut cmd: TestCommand| {
|
||||||
cmd.args(&[
|
cmd.args(&["--no-mmap", "-n", "Project Gutenberg EBook"]);
|
||||||
"--no-mmap", "-n", "Project Gutenberg EBook",
|
|
||||||
]);
|
|
||||||
|
|
||||||
let expected = "\
|
let expected = "\
|
||||||
1:The Project Gutenberg EBook of A Study In Scarlet, by Arthur Conan Doyle
|
1:The Project Gutenberg EBook of A Study In Scarlet, by Arthur Conan Doyle
|
||||||
@ -81,7 +75,12 @@ Binary file matches (found \"\\u{0}\" byte around offset 9741)
|
|||||||
rgtest!(after_match1_implicit_binary, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(after_match1_implicit_binary, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create_bytes("hay", HAY);
|
dir.create_bytes("hay", HAY);
|
||||||
cmd.args(&[
|
cmd.args(&[
|
||||||
"--no-mmap", "-n", "--binary", "Project Gutenberg EBook", "-g", "hay",
|
"--no-mmap",
|
||||||
|
"-n",
|
||||||
|
"--binary",
|
||||||
|
"Project Gutenberg EBook",
|
||||||
|
"-g",
|
||||||
|
"hay",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let expected = "\
|
let expected = "\
|
||||||
@ -96,7 +95,12 @@ Binary file hay matches (found \"\\u{0}\" byte around offset 9741)
|
|||||||
rgtest!(after_match1_implicit_text, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(after_match1_implicit_text, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create_bytes("hay", HAY);
|
dir.create_bytes("hay", HAY);
|
||||||
cmd.args(&[
|
cmd.args(&[
|
||||||
"--no-mmap", "-n", "--text", "Project Gutenberg EBook", "-g", "hay",
|
"--no-mmap",
|
||||||
|
"-n",
|
||||||
|
"--text",
|
||||||
|
"Project Gutenberg EBook",
|
||||||
|
"-g",
|
||||||
|
"hay",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let expected = "\
|
let expected = "\
|
||||||
@ -109,9 +113,7 @@ hay:1:The Project Gutenberg EBook of A Study In Scarlet, by Arthur Conan Doyle
|
|||||||
// detection should be performed.
|
// detection should be performed.
|
||||||
rgtest!(after_match1_explicit_text, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(after_match1_explicit_text, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create_bytes("hay", HAY);
|
dir.create_bytes("hay", HAY);
|
||||||
cmd.args(&[
|
cmd.args(&["--no-mmap", "-n", "--text", "Project Gutenberg EBook", "hay"]);
|
||||||
"--no-mmap", "-n", "--text", "Project Gutenberg EBook", "hay",
|
|
||||||
]);
|
|
||||||
|
|
||||||
let expected = "\
|
let expected = "\
|
||||||
1:The Project Gutenberg EBook of A Study In Scarlet, by Arthur Conan Doyle
|
1:The Project Gutenberg EBook of A Study In Scarlet, by Arthur Conan Doyle
|
||||||
@ -134,9 +136,7 @@ rgtest!(after_match1_explicit_text, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
// --quiet flag is set. See the next test.)
|
// --quiet flag is set. See the next test.)
|
||||||
rgtest!(after_match1_implicit_path, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(after_match1_implicit_path, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create_bytes("hay", HAY);
|
dir.create_bytes("hay", HAY);
|
||||||
cmd.args(&[
|
cmd.args(&["--no-mmap", "-l", "Project Gutenberg EBook", "-g", "hay"]);
|
||||||
"--no-mmap", "-l", "Project Gutenberg EBook", "-g", "hay",
|
|
||||||
]);
|
|
||||||
eqnice!("hay\n", cmd.stdout());
|
eqnice!("hay\n", cmd.stdout());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -145,9 +145,7 @@ rgtest!(after_match1_implicit_path, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
// manifest as an exit code with no output.)
|
// manifest as an exit code with no output.)
|
||||||
rgtest!(after_match1_implicit_quiet, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(after_match1_implicit_quiet, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create_bytes("hay", HAY);
|
dir.create_bytes("hay", HAY);
|
||||||
cmd.args(&[
|
cmd.args(&["--no-mmap", "-q", "Project Gutenberg EBook", "-g", "hay"]);
|
||||||
"--no-mmap", "-q", "Project Gutenberg EBook", "-g", "hay",
|
|
||||||
]);
|
|
||||||
eqnice!("", cmd.stdout());
|
eqnice!("", cmd.stdout());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -157,32 +155,34 @@ rgtest!(after_match1_implicit_quiet, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
// detects the binary data and suppresses output.
|
// detects the binary data and suppresses output.
|
||||||
rgtest!(after_match1_implicit_count, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(after_match1_implicit_count, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create_bytes("hay", HAY);
|
dir.create_bytes("hay", HAY);
|
||||||
cmd.args(&[
|
cmd.args(&["--no-mmap", "-c", "Project Gutenberg EBook", "-g", "hay"]);
|
||||||
"--no-mmap", "-c", "Project Gutenberg EBook", "-g", "hay",
|
|
||||||
]);
|
|
||||||
cmd.assert_err();
|
cmd.assert_err();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Like after_match1_implicit_count, except the --binary flag is provided,
|
// Like after_match1_implicit_count, except the --binary flag is provided,
|
||||||
// which makes ripgrep disable binary data filtering even for implicit files.
|
// which makes ripgrep disable binary data filtering even for implicit files.
|
||||||
rgtest!(after_match1_implicit_count_binary, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(
|
||||||
dir.create_bytes("hay", HAY);
|
after_match1_implicit_count_binary,
|
||||||
cmd.args(&[
|
|dir: Dir, mut cmd: TestCommand| {
|
||||||
"--no-mmap", "-c", "--binary",
|
dir.create_bytes("hay", HAY);
|
||||||
"Project Gutenberg EBook",
|
cmd.args(&[
|
||||||
"-g", "hay",
|
"--no-mmap",
|
||||||
]);
|
"-c",
|
||||||
eqnice!("hay:1\n", cmd.stdout());
|
"--binary",
|
||||||
});
|
"Project Gutenberg EBook",
|
||||||
|
"-g",
|
||||||
|
"hay",
|
||||||
|
]);
|
||||||
|
eqnice!("hay:1\n", cmd.stdout());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// Like after_match1_implicit_count, except the file path is provided
|
// Like after_match1_implicit_count, except the file path is provided
|
||||||
// explicitly, so binary filtering is disabled and a count is correctly
|
// explicitly, so binary filtering is disabled and a count is correctly
|
||||||
// reported.
|
// reported.
|
||||||
rgtest!(after_match1_explicit_count, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(after_match1_explicit_count, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create_bytes("hay", HAY);
|
dir.create_bytes("hay", HAY);
|
||||||
cmd.args(&[
|
cmd.args(&["--no-mmap", "-c", "Project Gutenberg EBook", "hay"]);
|
||||||
"--no-mmap", "-c", "Project Gutenberg EBook", "hay",
|
|
||||||
]);
|
|
||||||
eqnice!("1\n", cmd.stdout());
|
eqnice!("1\n", cmd.stdout());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -191,9 +191,11 @@ rgtest!(after_match1_explicit_count, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
rgtest!(after_match2_implicit, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(after_match2_implicit, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create_bytes("hay", HAY);
|
dir.create_bytes("hay", HAY);
|
||||||
cmd.args(&[
|
cmd.args(&[
|
||||||
"--no-mmap", "-n",
|
"--no-mmap",
|
||||||
|
"-n",
|
||||||
"Project Gutenberg EBook|a medical student",
|
"Project Gutenberg EBook|a medical student",
|
||||||
"-g", "hay",
|
"-g",
|
||||||
|
"hay",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let expected = "\
|
let expected = "\
|
||||||
@ -208,9 +210,12 @@ WARNING: stopped searching binary file hay after match (found \"\\u{0}\" byte ar
|
|||||||
rgtest!(after_match2_implicit_text, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(after_match2_implicit_text, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create_bytes("hay", HAY);
|
dir.create_bytes("hay", HAY);
|
||||||
cmd.args(&[
|
cmd.args(&[
|
||||||
"--no-mmap", "-n", "--text",
|
"--no-mmap",
|
||||||
|
"-n",
|
||||||
|
"--text",
|
||||||
"Project Gutenberg EBook|a medical student",
|
"Project Gutenberg EBook|a medical student",
|
||||||
"-g", "hay",
|
"-g",
|
||||||
|
"hay",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let expected = "\
|
let expected = "\
|
||||||
@ -224,9 +229,7 @@ hay:236:\"And yet you say he is not a medical student?\"
|
|||||||
// after a NUL byte.
|
// after a NUL byte.
|
||||||
rgtest!(before_match1_implicit, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(before_match1_implicit, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create_bytes("hay", HAY);
|
dir.create_bytes("hay", HAY);
|
||||||
cmd.args(&[
|
cmd.args(&["--no-mmap", "-n", "Heaven", "-g", "hay"]);
|
||||||
"--no-mmap", "-n", "Heaven", "-g", "hay",
|
|
||||||
]);
|
|
||||||
cmd.assert_err();
|
cmd.assert_err();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -234,9 +237,7 @@ rgtest!(before_match1_implicit, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
// occurs after a NUL byte when a file is explicitly searched.
|
// occurs after a NUL byte when a file is explicitly searched.
|
||||||
rgtest!(before_match1_explicit, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(before_match1_explicit, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create_bytes("hay", HAY);
|
dir.create_bytes("hay", HAY);
|
||||||
cmd.args(&[
|
cmd.args(&["--no-mmap", "-n", "Heaven", "hay"]);
|
||||||
"--no-mmap", "-n", "Heaven", "hay",
|
|
||||||
]);
|
|
||||||
|
|
||||||
let expected = "\
|
let expected = "\
|
||||||
Binary file matches (found \"\\u{0}\" byte around offset 9741)
|
Binary file matches (found \"\\u{0}\" byte around offset 9741)
|
||||||
@ -249,9 +250,7 @@ Binary file matches (found \"\\u{0}\" byte around offset 9741)
|
|||||||
// the file were given explicitly.
|
// the file were given explicitly.
|
||||||
rgtest!(before_match1_implicit_binary, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(before_match1_implicit_binary, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create_bytes("hay", HAY);
|
dir.create_bytes("hay", HAY);
|
||||||
cmd.args(&[
|
cmd.args(&["--no-mmap", "-n", "--binary", "Heaven", "-g", "hay"]);
|
||||||
"--no-mmap", "-n", "--binary", "Heaven", "-g", "hay",
|
|
||||||
]);
|
|
||||||
|
|
||||||
let expected = "\
|
let expected = "\
|
||||||
Binary file hay matches (found \"\\u{0}\" byte around offset 9741)
|
Binary file hay matches (found \"\\u{0}\" byte around offset 9741)
|
||||||
@ -263,9 +262,7 @@ Binary file hay matches (found \"\\u{0}\" byte around offset 9741)
|
|||||||
// detection should be performed.
|
// detection should be performed.
|
||||||
rgtest!(before_match1_implicit_text, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(before_match1_implicit_text, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create_bytes("hay", HAY);
|
dir.create_bytes("hay", HAY);
|
||||||
cmd.args(&[
|
cmd.args(&["--no-mmap", "-n", "--text", "Heaven", "-g", "hay"]);
|
||||||
"--no-mmap", "-n", "--text", "Heaven", "-g", "hay",
|
|
||||||
]);
|
|
||||||
|
|
||||||
let expected = "\
|
let expected = "\
|
||||||
hay:238:\"No. Heaven knows what the objects of his studies are. But here we
|
hay:238:\"No. Heaven knows what the objects of his studies are. But here we
|
||||||
@ -277,9 +274,7 @@ hay:238:\"No. Heaven knows what the objects of his studies are. But here we
|
|||||||
// before a NUL byte, but within the same buffer as the NUL byte.
|
// before a NUL byte, but within the same buffer as the NUL byte.
|
||||||
rgtest!(before_match2_implicit, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(before_match2_implicit, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create_bytes("hay", HAY);
|
dir.create_bytes("hay", HAY);
|
||||||
cmd.args(&[
|
cmd.args(&["--no-mmap", "-n", "a medical student", "-g", "hay"]);
|
||||||
"--no-mmap", "-n", "a medical student", "-g", "hay",
|
|
||||||
]);
|
|
||||||
cmd.assert_err();
|
cmd.assert_err();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -290,9 +285,7 @@ rgtest!(before_match2_implicit, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
// the behavior of GNU grep.)
|
// the behavior of GNU grep.)
|
||||||
rgtest!(before_match2_explicit, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(before_match2_explicit, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create_bytes("hay", HAY);
|
dir.create_bytes("hay", HAY);
|
||||||
cmd.args(&[
|
cmd.args(&["--no-mmap", "-n", "a medical student", "hay"]);
|
||||||
"--no-mmap", "-n", "a medical student", "hay",
|
|
||||||
]);
|
|
||||||
|
|
||||||
let expected = "\
|
let expected = "\
|
||||||
Binary file matches (found \"\\u{0}\" byte around offset 9741)
|
Binary file matches (found \"\\u{0}\" byte around offset 9741)
|
||||||
@ -304,9 +297,7 @@ Binary file matches (found \"\\u{0}\" byte around offset 9741)
|
|||||||
// detection should be performed.
|
// detection should be performed.
|
||||||
rgtest!(before_match2_implicit_text, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(before_match2_implicit_text, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create_bytes("hay", HAY);
|
dir.create_bytes("hay", HAY);
|
||||||
cmd.args(&[
|
cmd.args(&["--no-mmap", "-n", "--text", "a medical student", "-g", "hay"]);
|
||||||
"--no-mmap", "-n", "--text", "a medical student", "-g", "hay",
|
|
||||||
]);
|
|
||||||
|
|
||||||
let expected = "\
|
let expected = "\
|
||||||
hay:236:\"And yet you say he is not a medical student?\"
|
hay:236:\"And yet you say he is not a medical student?\"
|
||||||
|
138
tests/feature.rs
138
tests/feature.rs
@ -1,5 +1,5 @@
|
|||||||
use crate::hay::{SHERLOCK, SHERLOCK_CRLF};
|
use crate::hay::{SHERLOCK, SHERLOCK_CRLF};
|
||||||
use crate::util::{Dir, TestCommand, sort_lines};
|
use crate::util::{sort_lines, Dir, TestCommand};
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/1
|
// See: https://github.com/BurntSushi/ripgrep/issues/1
|
||||||
rgtest!(f1_sjis, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(f1_sjis, |dir: Dir, mut cmd: TestCommand| {
|
||||||
@ -181,8 +181,10 @@ rgtest!(f45_precedence_internal, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
dir.create("wat.log", "test");
|
dir.create("wat.log", "test");
|
||||||
|
|
||||||
cmd.args(&[
|
cmd.args(&[
|
||||||
"--ignore-file", ".not-an-ignore1",
|
"--ignore-file",
|
||||||
"--ignore-file", ".not-an-ignore2",
|
".not-an-ignore1",
|
||||||
|
"--ignore-file",
|
||||||
|
".not-an-ignore2",
|
||||||
"test",
|
"test",
|
||||||
]);
|
]);
|
||||||
eqnice!("imp.log:test\n", cmd.stdout());
|
eqnice!("imp.log:test\n", cmd.stdout());
|
||||||
@ -388,28 +390,34 @@ rgtest!(f362_exceeds_regex_size_limit, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/362
|
// See: https://github.com/BurntSushi/ripgrep/issues/362
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[cfg(target_pointer_width = "32")]
|
||||||
rgtest!(f362_u64_to_narrow_usize_overflow, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(
|
||||||
// --dfa-size-limit doesn't apply to PCRE2.
|
f362_u64_to_narrow_usize_overflow,
|
||||||
if dir.is_pcre2() {
|
|dir: Dir, mut cmd: TestCommand| {
|
||||||
return;
|
// --dfa-size-limit doesn't apply to PCRE2.
|
||||||
}
|
if dir.is_pcre2() {
|
||||||
dir.create_size("foo", 1000000);
|
return;
|
||||||
|
}
|
||||||
|
dir.create_size("foo", 1000000);
|
||||||
|
|
||||||
// 2^35 * 2^20 is ok for u64, but not for usize
|
// 2^35 * 2^20 is ok for u64, but not for usize
|
||||||
cmd.arg("--dfa-size-limit").arg("34359738368M").arg("--files");
|
cmd.arg("--dfa-size-limit").arg("34359738368M").arg("--files");
|
||||||
cmd.assert_err();
|
cmd.assert_err();
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/411
|
// See: https://github.com/BurntSushi/ripgrep/issues/411
|
||||||
rgtest!(f411_single_threaded_search_stats, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(
|
||||||
dir.create("sherlock", SHERLOCK);
|
f411_single_threaded_search_stats,
|
||||||
|
|dir: Dir, mut cmd: TestCommand| {
|
||||||
|
dir.create("sherlock", SHERLOCK);
|
||||||
|
|
||||||
let lines = cmd.arg("--stats").arg("Sherlock").stdout();
|
let lines = cmd.arg("--stats").arg("Sherlock").stdout();
|
||||||
assert!(lines.contains("2 matched lines"));
|
assert!(lines.contains("2 matched lines"));
|
||||||
assert!(lines.contains("1 files contained matches"));
|
assert!(lines.contains("1 files contained matches"));
|
||||||
assert!(lines.contains("1 files searched"));
|
assert!(lines.contains("1 files searched"));
|
||||||
assert!(lines.contains("seconds"));
|
assert!(lines.contains("seconds"));
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
rgtest!(f411_parallel_search_stats, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(f411_parallel_search_stats, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create("sherlock_1", SHERLOCK);
|
dir.create("sherlock_1", SHERLOCK);
|
||||||
@ -568,7 +576,7 @@ rgtest!(f948_exit_code_error, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/917
|
// See: https://github.com/BurntSushi/ripgrep/issues/917
|
||||||
rgtest!(f917_trim, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(f917_trim, |dir: Dir, mut cmd: TestCommand| {
|
||||||
const SHERLOCK: &'static str = "\
|
const SHERLOCK: &'static str = "\
|
||||||
zzz
|
zzz
|
||||||
For the Doctor Watsons of this world, as opposed to the Sherlock
|
For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
Holmeses, success in the province of detective work must always
|
Holmeses, success in the province of detective work must always
|
||||||
@ -578,9 +586,7 @@ but Doctor Watson has to have it taken out for him and dusted,
|
|||||||
and exhibited clearly, with a label attached.
|
and exhibited clearly, with a label attached.
|
||||||
";
|
";
|
||||||
dir.create("sherlock", SHERLOCK);
|
dir.create("sherlock", SHERLOCK);
|
||||||
cmd.args(&[
|
cmd.args(&["-n", "-B1", "-A2", "--trim", "Holmeses", "sherlock"]);
|
||||||
"-n", "-B1", "-A2", "--trim", "Holmeses", "sherlock",
|
|
||||||
]);
|
|
||||||
|
|
||||||
let expected = "\
|
let expected = "\
|
||||||
2-For the Doctor Watsons of this world, as opposed to the Sherlock
|
2-For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
@ -596,7 +602,7 @@ but Doctor Watson has to have it taken out for him and dusted,
|
|||||||
// This is like f917_trim, except this tests that trimming occurs even when the
|
// This is like f917_trim, except this tests that trimming occurs even when the
|
||||||
// whitespace is part of a match.
|
// whitespace is part of a match.
|
||||||
rgtest!(f917_trim_match, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(f917_trim_match, |dir: Dir, mut cmd: TestCommand| {
|
||||||
const SHERLOCK: &'static str = "\
|
const SHERLOCK: &'static str = "\
|
||||||
zzz
|
zzz
|
||||||
For the Doctor Watsons of this world, as opposed to the Sherlock
|
For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
Holmeses, success in the province of detective work must always
|
Holmeses, success in the province of detective work must always
|
||||||
@ -606,9 +612,7 @@ but Doctor Watson has to have it taken out for him and dusted,
|
|||||||
and exhibited clearly, with a label attached.
|
and exhibited clearly, with a label attached.
|
||||||
";
|
";
|
||||||
dir.create("sherlock", SHERLOCK);
|
dir.create("sherlock", SHERLOCK);
|
||||||
cmd.args(&[
|
cmd.args(&["-n", "-B1", "-A2", "--trim", r"\s+Holmeses", "sherlock"]);
|
||||||
"-n", "-B1", "-A2", "--trim", r"\s+Holmeses", "sherlock",
|
|
||||||
]);
|
|
||||||
|
|
||||||
let expected = "\
|
let expected = "\
|
||||||
2-For the Doctor Watsons of this world, as opposed to the Sherlock
|
2-For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
@ -636,7 +640,8 @@ rgtest!(f993_null_data, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
rgtest!(f1078_max_columns_preview1, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(f1078_max_columns_preview1, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create("sherlock", SHERLOCK);
|
dir.create("sherlock", SHERLOCK);
|
||||||
cmd.args(&[
|
cmd.args(&[
|
||||||
"-M46", "--max-columns-preview",
|
"-M46",
|
||||||
|
"--max-columns-preview",
|
||||||
"exhibited|dusted|has to have it",
|
"exhibited|dusted|has to have it",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -650,7 +655,8 @@ sherlock:and exhibited clearly, with a label attached.
|
|||||||
rgtest!(f1078_max_columns_preview2, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(f1078_max_columns_preview2, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create("sherlock", SHERLOCK);
|
dir.create("sherlock", SHERLOCK);
|
||||||
cmd.args(&[
|
cmd.args(&[
|
||||||
"-M43", "--max-columns-preview",
|
"-M43",
|
||||||
|
"--max-columns-preview",
|
||||||
// Doing a replacement forces ripgrep to show the number of remaining
|
// Doing a replacement forces ripgrep to show the number of remaining
|
||||||
// matches. Normally, this happens by default when printing a tty with
|
// matches. Normally, this happens by default when printing a tty with
|
||||||
// colors.
|
// colors.
|
||||||
@ -702,10 +708,7 @@ sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
|
|||||||
// Tests if without encoding 'none' flag null bytes are consumed by automatic
|
// Tests if without encoding 'none' flag null bytes are consumed by automatic
|
||||||
// encoding detection.
|
// encoding detection.
|
||||||
rgtest!(f1207_auto_encoding, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(f1207_auto_encoding, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create_bytes(
|
dir.create_bytes("foo", b"\xFF\xFE\x00\x62");
|
||||||
"foo",
|
|
||||||
b"\xFF\xFE\x00\x62"
|
|
||||||
);
|
|
||||||
cmd.arg("-a").arg("\\x00").arg("foo");
|
cmd.arg("-a").arg("\\x00").arg("foo");
|
||||||
cmd.assert_exit_code(1);
|
cmd.assert_exit_code(1);
|
||||||
});
|
});
|
||||||
@ -720,10 +723,7 @@ rgtest!(f1207_ignore_encoding, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dir.create_bytes(
|
dir.create_bytes("foo", b"\xFF\xFE\x00\x62");
|
||||||
"foo",
|
|
||||||
b"\xFF\xFE\x00\x62"
|
|
||||||
);
|
|
||||||
cmd.arg("--encoding").arg("none").arg("-a").arg("\\x00").arg("foo");
|
cmd.arg("--encoding").arg("none").arg("-a").arg("\\x00").arg("foo");
|
||||||
eqnice!("\u{FFFD}\u{FFFD}\x00b\n", cmd.stdout());
|
eqnice!("\u{FFFD}\u{FFFD}\x00b\n", cmd.stdout());
|
||||||
});
|
});
|
||||||
@ -734,25 +734,22 @@ rgtest!(f1414_no_require_git, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
dir.create("foo", "");
|
dir.create("foo", "");
|
||||||
dir.create("bar", "");
|
dir.create("bar", "");
|
||||||
|
|
||||||
let stdout = cmd.args(&[
|
let stdout = cmd.args(&["--sort", "path", "--files"]).stdout();
|
||||||
"--sort", "path",
|
|
||||||
"--files",
|
|
||||||
]).stdout();
|
|
||||||
eqnice!("bar\nfoo\n", stdout);
|
eqnice!("bar\nfoo\n", stdout);
|
||||||
|
|
||||||
let stdout = cmd.args(&[
|
let stdout =
|
||||||
"--sort", "path",
|
cmd.args(&["--sort", "path", "--files", "--no-require-git"]).stdout();
|
||||||
"--files",
|
|
||||||
"--no-require-git",
|
|
||||||
]).stdout();
|
|
||||||
eqnice!("bar\n", stdout);
|
eqnice!("bar\n", stdout);
|
||||||
|
|
||||||
let stdout = cmd.args(&[
|
let stdout = cmd
|
||||||
"--sort", "path",
|
.args(&[
|
||||||
"--files",
|
"--sort",
|
||||||
"--no-require-git",
|
"path",
|
||||||
"--require-git",
|
"--files",
|
||||||
]).stdout();
|
"--no-require-git",
|
||||||
|
"--require-git",
|
||||||
|
])
|
||||||
|
.stdout();
|
||||||
eqnice!("bar\nfoo\n", stdout);
|
eqnice!("bar\nfoo\n", stdout);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -770,12 +767,7 @@ rgtest!(f1420_no_ignore_dot, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
|
|
||||||
rgtest!(no_context_sep, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(no_context_sep, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create("test", "foo\nctx\nbar\nctx\nfoo\nctx");
|
dir.create("test", "foo\nctx\nbar\nctx\nfoo\nctx");
|
||||||
cmd.args(&[
|
cmd.args(&["-A1", "--no-context-separator", "foo", "test"]);
|
||||||
"-A1",
|
|
||||||
"--no-context-separator",
|
|
||||||
"foo",
|
|
||||||
"test",
|
|
||||||
]);
|
|
||||||
eqnice!("foo\nctx\nfoo\nctx\n", cmd.stdout());
|
eqnice!("foo\nctx\nfoo\nctx\n", cmd.stdout());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -783,7 +775,8 @@ rgtest!(no_context_sep_overrides, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
dir.create("test", "foo\nctx\nbar\nctx\nfoo\nctx");
|
dir.create("test", "foo\nctx\nbar\nctx\nfoo\nctx");
|
||||||
cmd.args(&[
|
cmd.args(&[
|
||||||
"-A1",
|
"-A1",
|
||||||
"--context-separator", "AAA",
|
"--context-separator",
|
||||||
|
"AAA",
|
||||||
"--no-context-separator",
|
"--no-context-separator",
|
||||||
"foo",
|
"foo",
|
||||||
"test",
|
"test",
|
||||||
@ -796,7 +789,8 @@ rgtest!(no_context_sep_overridden, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
cmd.args(&[
|
cmd.args(&[
|
||||||
"-A1",
|
"-A1",
|
||||||
"--no-context-separator",
|
"--no-context-separator",
|
||||||
"--context-separator", "AAA",
|
"--context-separator",
|
||||||
|
"AAA",
|
||||||
"foo",
|
"foo",
|
||||||
"test",
|
"test",
|
||||||
]);
|
]);
|
||||||
@ -805,33 +799,19 @@ rgtest!(no_context_sep_overridden, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
|
|
||||||
rgtest!(context_sep, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(context_sep, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create("test", "foo\nctx\nbar\nctx\nfoo\nctx");
|
dir.create("test", "foo\nctx\nbar\nctx\nfoo\nctx");
|
||||||
cmd.args(&[
|
cmd.args(&["-A1", "--context-separator", "AAA", "foo", "test"]);
|
||||||
"-A1",
|
|
||||||
"--context-separator", "AAA",
|
|
||||||
"foo",
|
|
||||||
"test",
|
|
||||||
]);
|
|
||||||
eqnice!("foo\nctx\nAAA\nfoo\nctx\n", cmd.stdout());
|
eqnice!("foo\nctx\nAAA\nfoo\nctx\n", cmd.stdout());
|
||||||
});
|
});
|
||||||
|
|
||||||
rgtest!(context_sep_default, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(context_sep_default, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create("test", "foo\nctx\nbar\nctx\nfoo\nctx");
|
dir.create("test", "foo\nctx\nbar\nctx\nfoo\nctx");
|
||||||
cmd.args(&[
|
cmd.args(&["-A1", "foo", "test"]);
|
||||||
"-A1",
|
|
||||||
"foo",
|
|
||||||
"test",
|
|
||||||
]);
|
|
||||||
eqnice!("foo\nctx\n--\nfoo\nctx\n", cmd.stdout());
|
eqnice!("foo\nctx\n--\nfoo\nctx\n", cmd.stdout());
|
||||||
});
|
});
|
||||||
|
|
||||||
rgtest!(context_sep_empty, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(context_sep_empty, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create("test", "foo\nctx\nbar\nctx\nfoo\nctx");
|
dir.create("test", "foo\nctx\nbar\nctx\nfoo\nctx");
|
||||||
cmd.args(&[
|
cmd.args(&["-A1", "--context-separator", "", "foo", "test"]);
|
||||||
"-A1",
|
|
||||||
"--context-separator", "",
|
|
||||||
"foo",
|
|
||||||
"test",
|
|
||||||
]);
|
|
||||||
eqnice!("foo\nctx\n\nfoo\nctx\n", cmd.stdout());
|
eqnice!("foo\nctx\n\nfoo\nctx\n", cmd.stdout());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -108,8 +108,12 @@ enum Data {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Data {
|
impl Data {
|
||||||
fn text(s: &str) -> Data { Data::Text { text: s.to_string() } }
|
fn text(s: &str) -> Data {
|
||||||
fn bytes(s: &str) -> Data { Data::Bytes { bytes: s.to_string() } }
|
Data::Text { text: s.to_string() }
|
||||||
|
}
|
||||||
|
fn bytes(s: &str) -> Data {
|
||||||
|
Data::Bytes { bytes: s.to_string() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
|
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
|
||||||
@ -172,31 +176,17 @@ rgtest!(basic, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
),
|
),
|
||||||
line_number: Some(3),
|
line_number: Some(3),
|
||||||
absolute_offset: 129,
|
absolute_offset: 129,
|
||||||
submatches: vec![
|
submatches: vec![SubMatch {
|
||||||
SubMatch {
|
m: Data::text("Sherlock Holmes"),
|
||||||
m: Data::text("Sherlock Holmes"),
|
start: 48,
|
||||||
start: 48,
|
end: 63,
|
||||||
end: 63,
|
},],
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(msgs[3].unwrap_end().path, Some(Data::text("sherlock")));
|
||||||
msgs[3].unwrap_end().path,
|
assert_eq!(msgs[3].unwrap_end().binary_offset, None);
|
||||||
Some(Data::text("sherlock"))
|
assert_eq!(msgs[4].unwrap_summary().stats.searches_with_match, 1);
|
||||||
);
|
assert_eq!(msgs[4].unwrap_summary().stats.bytes_printed, 494);
|
||||||
assert_eq!(
|
|
||||||
msgs[3].unwrap_end().binary_offset,
|
|
||||||
None
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
msgs[4].unwrap_summary().stats.searches_with_match,
|
|
||||||
1
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
msgs[4].unwrap_summary().stats.bytes_printed,
|
|
||||||
494
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
@ -239,13 +229,11 @@ rgtest!(notutf8, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
lines: Data::bytes("cXV1eP9iYXo="),
|
lines: Data::bytes("cXV1eP9iYXo="),
|
||||||
line_number: Some(1),
|
line_number: Some(1),
|
||||||
absolute_offset: 0,
|
absolute_offset: 0,
|
||||||
submatches: vec![
|
submatches: vec![SubMatch {
|
||||||
SubMatch {
|
m: Data::bytes("/w=="),
|
||||||
m: Data::bytes("/w=="),
|
start: 4,
|
||||||
start: 4,
|
end: 5,
|
||||||
end: 5,
|
},],
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -282,13 +270,11 @@ rgtest!(notutf8_file, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
lines: Data::bytes("cXV1eP9iYXo="),
|
lines: Data::bytes("cXV1eP9iYXo="),
|
||||||
line_number: Some(1),
|
line_number: Some(1),
|
||||||
absolute_offset: 0,
|
absolute_offset: 0,
|
||||||
submatches: vec![
|
submatches: vec![SubMatch {
|
||||||
SubMatch {
|
m: Data::bytes("/w=="),
|
||||||
m: Data::bytes("/w=="),
|
start: 4,
|
||||||
start: 4,
|
end: 5,
|
||||||
end: 5,
|
},],
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -306,11 +292,7 @@ rgtest!(crlf, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
msgs[1].unwrap_match().submatches[0].clone(),
|
msgs[1].unwrap_match().submatches[0].clone(),
|
||||||
SubMatch {
|
SubMatch { m: Data::text("Sherlock"), start: 56, end: 64 },
|
||||||
m: Data::text("Sherlock"),
|
|
||||||
start: 56,
|
|
||||||
end: 64,
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ macro_rules! rgtest {
|
|||||||
$fun(dir, cmd);
|
$fun(dir, cmd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::hay::SHERLOCK;
|
use crate::hay::SHERLOCK;
|
||||||
use crate::util::{Dir, TestCommand, cmd_exists, sort_lines};
|
use crate::util::{cmd_exists, sort_lines, Dir, TestCommand};
|
||||||
|
|
||||||
// This file contains "miscellaneous" tests that were either written before
|
// This file contains "miscellaneous" tests that were either written before
|
||||||
// features were tracked more explicitly, or were simply written without
|
// features were tracked more explicitly, or were simply written without
|
||||||
@ -65,8 +65,10 @@ rgtest!(with_heading, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
cmd.args(&[
|
cmd.args(&[
|
||||||
// This forces the issue since --with-filename is disabled by default
|
// This forces the issue since --with-filename is disabled by default
|
||||||
// when searching one file.
|
// when searching one file.
|
||||||
"--with-filename", "--heading",
|
"--with-filename",
|
||||||
"Sherlock", "sherlock",
|
"--heading",
|
||||||
|
"Sherlock",
|
||||||
|
"sherlock",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let expected = "\
|
let expected = "\
|
||||||
@ -184,9 +186,7 @@ be, to a very large extent, the result of luck. FooBar Holmes
|
|||||||
|
|
||||||
rgtest!(replace_groups, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(replace_groups, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create("sherlock", SHERLOCK);
|
dir.create("sherlock", SHERLOCK);
|
||||||
cmd.args(&[
|
cmd.args(&["-r", "$2, $1", "([A-Z][a-z]+) ([A-Z][a-z]+)", "sherlock"]);
|
||||||
"-r", "$2, $1", "([A-Z][a-z]+) ([A-Z][a-z]+)", "sherlock",
|
|
||||||
]);
|
|
||||||
|
|
||||||
let expected = "\
|
let expected = "\
|
||||||
For the Watsons, Doctor of this world, as opposed to the Sherlock
|
For the Watsons, Doctor of this world, as opposed to the Sherlock
|
||||||
@ -199,7 +199,8 @@ but Watson, Doctor has to have it taken out for him and dusted,
|
|||||||
rgtest!(replace_named_groups, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(replace_named_groups, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create("sherlock", SHERLOCK);
|
dir.create("sherlock", SHERLOCK);
|
||||||
cmd.args(&[
|
cmd.args(&[
|
||||||
"-r", "$last, $first",
|
"-r",
|
||||||
|
"$last, $first",
|
||||||
"(?P<first>[A-Z][a-z]+) (?P<last>[A-Z][a-z]+)",
|
"(?P<first>[A-Z][a-z]+) (?P<last>[A-Z][a-z]+)",
|
||||||
"sherlock",
|
"sherlock",
|
||||||
]);
|
]);
|
||||||
@ -279,9 +280,7 @@ rgtest!(file_type_add, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
dir.create("file.py", "Sherlock");
|
dir.create("file.py", "Sherlock");
|
||||||
dir.create("file.rs", "Sherlock");
|
dir.create("file.rs", "Sherlock");
|
||||||
dir.create("file.wat", "Sherlock");
|
dir.create("file.wat", "Sherlock");
|
||||||
cmd.args(&[
|
cmd.args(&["--type-add", "wat:*.wat", "-t", "wat", "Sherlock"]);
|
||||||
"--type-add", "wat:*.wat", "-t", "wat", "Sherlock",
|
|
||||||
]);
|
|
||||||
|
|
||||||
eqnice!("file.wat:Sherlock\n", cmd.stdout());
|
eqnice!("file.wat:Sherlock\n", cmd.stdout());
|
||||||
});
|
});
|
||||||
@ -292,9 +291,12 @@ rgtest!(file_type_add_compose, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
dir.create("file.rs", "Sherlock");
|
dir.create("file.rs", "Sherlock");
|
||||||
dir.create("file.wat", "Sherlock");
|
dir.create("file.wat", "Sherlock");
|
||||||
cmd.args(&[
|
cmd.args(&[
|
||||||
"--type-add", "wat:*.wat",
|
"--type-add",
|
||||||
"--type-add", "combo:include:wat,py",
|
"wat:*.wat",
|
||||||
"-t", "combo",
|
"--type-add",
|
||||||
|
"combo:include:wat,py",
|
||||||
|
"-t",
|
||||||
|
"combo",
|
||||||
"Sherlock",
|
"Sherlock",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -394,11 +396,7 @@ rgtest!(count_matches_via_only, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
|
|
||||||
rgtest!(include_zero, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(include_zero, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create("sherlock", SHERLOCK);
|
dir.create("sherlock", SHERLOCK);
|
||||||
cmd.args(&[
|
cmd.args(&["--count", "--include-zero", "nada"]);
|
||||||
"--count",
|
|
||||||
"--include-zero",
|
|
||||||
"nada",
|
|
||||||
]);
|
|
||||||
cmd.assert_err();
|
cmd.assert_err();
|
||||||
|
|
||||||
let output = cmd.cmd().output().unwrap();
|
let output = cmd.cmd().output().unwrap();
|
||||||
@ -410,12 +408,7 @@ rgtest!(include_zero, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
|
|
||||||
rgtest!(include_zero_override, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(include_zero_override, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create("sherlock", SHERLOCK);
|
dir.create("sherlock", SHERLOCK);
|
||||||
cmd.args(&[
|
cmd.args(&["--count", "--include-zero", "--no-include-zero", "nada"]);
|
||||||
"--count",
|
|
||||||
"--include-zero",
|
|
||||||
"--no-include-zero",
|
|
||||||
"nada",
|
|
||||||
]);
|
|
||||||
cmd.assert_err();
|
cmd.assert_err();
|
||||||
|
|
||||||
let output = cmd.cmd().output().unwrap();
|
let output = cmd.cmd().output().unwrap();
|
||||||
|
@ -20,9 +20,7 @@ rgtest!(overlap2, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
// Tests that even in a multiline search, a '.' does not match a newline.
|
// Tests that even in a multiline search, a '.' does not match a newline.
|
||||||
rgtest!(dot_no_newline, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(dot_no_newline, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create("sherlock", SHERLOCK);
|
dir.create("sherlock", SHERLOCK);
|
||||||
cmd.args(&[
|
cmd.args(&["-n", "-U", "of this world.+detective work", "sherlock"]);
|
||||||
"-n", "-U", "of this world.+detective work", "sherlock",
|
|
||||||
]);
|
|
||||||
cmd.assert_err();
|
cmd.assert_err();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -30,8 +28,11 @@ rgtest!(dot_no_newline, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
rgtest!(dot_all, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(dot_all, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create("sherlock", SHERLOCK);
|
dir.create("sherlock", SHERLOCK);
|
||||||
cmd.args(&[
|
cmd.args(&[
|
||||||
"-n", "-U", "--multiline-dotall",
|
"-n",
|
||||||
"of this world.+detective work", "sherlock",
|
"-U",
|
||||||
|
"--multiline-dotall",
|
||||||
|
"of this world.+detective work",
|
||||||
|
"sherlock",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let expected = "\
|
let expected = "\
|
||||||
@ -45,8 +46,11 @@ rgtest!(dot_all, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
rgtest!(only_matching, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(only_matching, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create("sherlock", SHERLOCK);
|
dir.create("sherlock", SHERLOCK);
|
||||||
cmd.args(&[
|
cmd.args(&[
|
||||||
"-n", "-U", "--only-matching",
|
"-n",
|
||||||
r"Watson|Sherlock\p{Any}+?Holmes", "sherlock",
|
"-U",
|
||||||
|
"--only-matching",
|
||||||
|
r"Watson|Sherlock\p{Any}+?Holmes",
|
||||||
|
"sherlock",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let expected = "\
|
let expected = "\
|
||||||
@ -63,8 +67,11 @@ rgtest!(only_matching, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
rgtest!(vimgrep, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(vimgrep, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create("sherlock", SHERLOCK);
|
dir.create("sherlock", SHERLOCK);
|
||||||
cmd.args(&[
|
cmd.args(&[
|
||||||
"-n", "-U", "--vimgrep",
|
"-n",
|
||||||
r"Watson|Sherlock\p{Any}+?Holmes", "sherlock",
|
"-U",
|
||||||
|
"--vimgrep",
|
||||||
|
r"Watson|Sherlock\p{Any}+?Holmes",
|
||||||
|
"sherlock",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let expected = "\
|
let expected = "\
|
||||||
@ -81,9 +88,7 @@ sherlock:5:12:but Doctor Watson has to have it taken out for him and dusted,
|
|||||||
// important test because multiline search must read the entire contents of
|
// important test because multiline search must read the entire contents of
|
||||||
// what it is searching into memory before executing the search.
|
// what it is searching into memory before executing the search.
|
||||||
rgtest!(stdin, |_: Dir, mut cmd: TestCommand| {
|
rgtest!(stdin, |_: Dir, mut cmd: TestCommand| {
|
||||||
cmd.args(&[
|
cmd.args(&["-n", "-U", r"of this world\p{Any}+?detective work"]);
|
||||||
"-n", "-U", r"of this world\p{Any}+?detective work",
|
|
||||||
]);
|
|
||||||
let expected = "\
|
let expected = "\
|
||||||
1:For the Doctor Watsons of this world, as opposed to the Sherlock
|
1:For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
2:Holmeses, success in the province of detective work must always
|
2:Holmeses, success in the province of detective work must always
|
||||||
@ -95,8 +100,11 @@ rgtest!(stdin, |_: Dir, mut cmd: TestCommand| {
|
|||||||
rgtest!(context, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(context, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create("sherlock", SHERLOCK);
|
dir.create("sherlock", SHERLOCK);
|
||||||
cmd.args(&[
|
cmd.args(&[
|
||||||
"-n", "-U", "-C1",
|
"-n",
|
||||||
r"detective work\p{Any}+?result of luck", "sherlock",
|
"-U",
|
||||||
|
"-C1",
|
||||||
|
r"detective work\p{Any}+?result of luck",
|
||||||
|
"sherlock",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let expected = "\
|
let expected = "\
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::hay::SHERLOCK;
|
use crate::hay::SHERLOCK;
|
||||||
use crate::util::{Dir, TestCommand, sort_lines};
|
use crate::util::{sort_lines, Dir, TestCommand};
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/16
|
// See: https://github.com/BurntSushi/ripgrep/issues/16
|
||||||
rgtest!(r16, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(r16, |dir: Dir, mut cmd: TestCommand| {
|
||||||
@ -346,7 +346,10 @@ rgtest!(r391, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
dir.create(".git/description", "");
|
dir.create(".git/description", "");
|
||||||
|
|
||||||
cmd.args(&[
|
cmd.args(&[
|
||||||
"--no-ignore", "--hidden", "--follow", "--files",
|
"--no-ignore",
|
||||||
|
"--hidden",
|
||||||
|
"--follow",
|
||||||
|
"--files",
|
||||||
"--glob",
|
"--glob",
|
||||||
"!{.git,node_modules,plugged}/**",
|
"!{.git,node_modules,plugged}/**",
|
||||||
"--glob",
|
"--glob",
|
||||||
@ -371,14 +374,18 @@ rgtest!(r405, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
rgtest!(r428_color_context_path, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(r428_color_context_path, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create("sherlock", "foo\nbar");
|
dir.create("sherlock", "foo\nbar");
|
||||||
cmd.args(&[
|
cmd.args(&[
|
||||||
"-A1", "-H", "--no-heading", "-N",
|
"-A1",
|
||||||
"--colors=match:none", "--color=always",
|
"-H",
|
||||||
|
"--no-heading",
|
||||||
|
"-N",
|
||||||
|
"--colors=match:none",
|
||||||
|
"--color=always",
|
||||||
"foo",
|
"foo",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let expected = format!(
|
let expected = format!(
|
||||||
"{colored_path}:foo\n{colored_path}-bar\n",
|
"{colored_path}:foo\n{colored_path}-bar\n",
|
||||||
colored_path=
|
colored_path =
|
||||||
"\x1b\x5b\x30\x6d\x1b\x5b\x33\x35\x6dsherlock\x1b\x5b\x30\x6d"
|
"\x1b\x5b\x30\x6d\x1b\x5b\x33\x35\x6dsherlock\x1b\x5b\x30\x6d"
|
||||||
);
|
);
|
||||||
eqnice!(expected, cmd.stdout());
|
eqnice!(expected, cmd.stdout());
|
||||||
@ -414,9 +421,7 @@ rgtest!(r451_only_matching_as_in_issue, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
// See: https://github.com/BurntSushi/ripgrep/issues/451
|
// See: https://github.com/BurntSushi/ripgrep/issues/451
|
||||||
rgtest!(r451_only_matching, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(r451_only_matching, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create("digits.txt", "1 2 3\n123\n");
|
dir.create("digits.txt", "1 2 3\n123\n");
|
||||||
cmd.args(&[
|
cmd.args(&["--only-matching", "--column", r"[0-9]", "digits.txt"]);
|
||||||
"--only-matching", "--column", r"[0-9]", "digits.txt",
|
|
||||||
]);
|
|
||||||
|
|
||||||
let expected = "\
|
let expected = "\
|
||||||
1:1:1
|
1:1:1
|
||||||
@ -517,11 +522,16 @@ rgtest!(r568_leading_hyphen_option_args, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
rgtest!(r599, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(r599, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create("input.txt", "\n\ntest\n");
|
dir.create("input.txt", "\n\ntest\n");
|
||||||
cmd.args(&[
|
cmd.args(&[
|
||||||
"--color", "ansi",
|
"--color",
|
||||||
"--colors", "path:none",
|
"ansi",
|
||||||
"--colors", "line:none",
|
"--colors",
|
||||||
"--colors", "match:fg:red",
|
"path:none",
|
||||||
"--colors", "match:style:nobold",
|
"--colors",
|
||||||
|
"line:none",
|
||||||
|
"--colors",
|
||||||
|
"match:fg:red",
|
||||||
|
"--colors",
|
||||||
|
"match:style:nobold",
|
||||||
"--line-number",
|
"--line-number",
|
||||||
r"^$",
|
r"^$",
|
||||||
"input.txt",
|
"input.txt",
|
||||||
@ -707,16 +717,19 @@ rgtest!(r1203_reverse_suffix_literal, |dir: Dir, _: TestCommand| {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/1223
|
// See: https://github.com/BurntSushi/ripgrep/issues/1223
|
||||||
rgtest!(r1223_no_dir_check_for_default_path, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(
|
||||||
dir.create_dir("-");
|
r1223_no_dir_check_for_default_path,
|
||||||
dir.create("a.json", "{}");
|
|dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create("a.txt", "some text");
|
dir.create_dir("-");
|
||||||
|
dir.create("a.json", "{}");
|
||||||
|
dir.create("a.txt", "some text");
|
||||||
|
|
||||||
eqnice!(
|
eqnice!(
|
||||||
"a.json\na.txt\n",
|
"a.json\na.txt\n",
|
||||||
sort_lines(&cmd.arg("a").pipe(b"a.json\na.txt"))
|
sort_lines(&cmd.arg("a").pipe(b"a.json\na.txt"))
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/1259
|
// See: https://github.com/BurntSushi/ripgrep/issues/1259
|
||||||
rgtest!(r1259_drop_last_byte_nonl, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(r1259_drop_last_byte_nonl, |dir: Dir, mut cmd: TestCommand| {
|
||||||
@ -734,7 +747,8 @@ rgtest!(r1319, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
dir.create("input", "CCAGCTACTCGGGAGGCTGAGGCTGGAGGATCGCTTGAGTCCAGGAGTTC");
|
dir.create("input", "CCAGCTACTCGGGAGGCTGAGGCTGGAGGATCGCTTGAGTCCAGGAGTTC");
|
||||||
eqnice!(
|
eqnice!(
|
||||||
"input:CCAGCTACTCGGGAGGCTGAGGCTGGAGGATCGCTTGAGTCCAGGAGTTC\n",
|
"input:CCAGCTACTCGGGAGGCTGAGGCTGGAGGATCGCTTGAGTCCAGGAGTTC\n",
|
||||||
cmd.arg("TTGAGTCCAGGAG[ATCG]{2}C").stdout());
|
cmd.arg("TTGAGTCCAGGAG[ATCG]{2}C").stdout()
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/1334
|
// See: https://github.com/BurntSushi/ripgrep/issues/1334
|
||||||
@ -753,27 +767,27 @@ rgtest!(r1389_bad_symlinks_no_biscuit, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
dir.create("mydir/file.txt", "test");
|
dir.create("mydir/file.txt", "test");
|
||||||
dir.link_dir("mydir", "mylink");
|
dir.link_dir("mydir", "mylink");
|
||||||
|
|
||||||
let stdout = cmd.args(&[
|
let stdout = cmd
|
||||||
"test",
|
.args(&["test", "--no-ignore", "--sort", "path", "mylink"])
|
||||||
"--no-ignore",
|
.stdout();
|
||||||
"--sort", "path",
|
|
||||||
"mylink",
|
|
||||||
]).stdout();
|
|
||||||
eqnice!("mylink/file.txt:test\n", stdout);
|
eqnice!("mylink/file.txt:test\n", stdout);
|
||||||
});
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/pull/1446
|
// See: https://github.com/BurntSushi/ripgrep/pull/1446
|
||||||
rgtest!(r1446_respect_excludes_in_worktree, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(
|
||||||
dir.create_dir("repo/.git/info");
|
r1446_respect_excludes_in_worktree,
|
||||||
dir.create("repo/.git/info/exclude", "ignored");
|
|dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create_dir("repo/.git/worktrees/repotree");
|
dir.create_dir("repo/.git/info");
|
||||||
dir.create("repo/.git/worktrees/repotree/commondir", "../..");
|
dir.create("repo/.git/info/exclude", "ignored");
|
||||||
|
dir.create_dir("repo/.git/worktrees/repotree");
|
||||||
|
dir.create("repo/.git/worktrees/repotree/commondir", "../..");
|
||||||
|
|
||||||
dir.create_dir("repotree");
|
dir.create_dir("repotree");
|
||||||
dir.create("repotree/.git", "gitdir: repo/.git/worktrees/repotree");
|
dir.create("repotree/.git", "gitdir: repo/.git/worktrees/repotree");
|
||||||
dir.create("repotree/ignored", "");
|
dir.create("repotree/ignored", "");
|
||||||
dir.create("repotree/not-ignored", "");
|
dir.create("repotree/not-ignored", "");
|
||||||
|
|
||||||
cmd.arg("--sort").arg("path").arg("--files").arg("repotree");
|
cmd.arg("--sort").arg("path").arg("--files").arg("repotree");
|
||||||
eqnice!("repotree/not-ignored\n", cmd.stdout());
|
eqnice!("repotree/not-ignored\n", cmd.stdout());
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
@ -72,19 +72,13 @@ impl Dir {
|
|||||||
.parent()
|
.parent()
|
||||||
.expect("executable's directory")
|
.expect("executable's directory")
|
||||||
.to_path_buf();
|
.to_path_buf();
|
||||||
let dir = env::temp_dir()
|
let dir =
|
||||||
.join(TEST_DIR)
|
env::temp_dir().join(TEST_DIR).join(name).join(&format!("{}", id));
|
||||||
.join(name)
|
|
||||||
.join(&format!("{}", id));
|
|
||||||
if dir.exists() {
|
if dir.exists() {
|
||||||
nice_err(&dir, fs::remove_dir_all(&dir));
|
nice_err(&dir, fs::remove_dir_all(&dir));
|
||||||
}
|
}
|
||||||
nice_err(&dir, repeat(|| fs::create_dir_all(&dir)));
|
nice_err(&dir, repeat(|| fs::create_dir_all(&dir)));
|
||||||
Dir {
|
Dir { root: root, dir: dir, pcre2: false }
|
||||||
root: root,
|
|
||||||
dir: dir,
|
|
||||||
pcre2: false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Use PCRE2 for this test.
|
/// Use PCRE2 for this test.
|
||||||
@ -262,12 +256,10 @@ impl TestCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Add any number of arguments to the command.
|
/// Add any number of arguments to the command.
|
||||||
pub fn args<I, A>(
|
pub fn args<I, A>(&mut self, args: I) -> &mut TestCommand
|
||||||
&mut self,
|
where
|
||||||
args: I,
|
I: IntoIterator<Item = A>,
|
||||||
) -> &mut TestCommand
|
A: AsRef<OsStr>,
|
||||||
where I: IntoIterator<Item=A>,
|
|
||||||
A: AsRef<OsStr>
|
|
||||||
{
|
{
|
||||||
self.cmd.args(args);
|
self.cmd.args(args);
|
||||||
self
|
self
|
||||||
@ -292,8 +284,7 @@ impl TestCommand {
|
|||||||
Err(err) => {
|
Err(err) => {
|
||||||
panic!(
|
panic!(
|
||||||
"could not convert from string: {:?}\n\n{}",
|
"could not convert from string: {:?}\n\n{}",
|
||||||
err,
|
err, stdout
|
||||||
stdout
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -311,9 +302,7 @@ impl TestCommand {
|
|||||||
// risk of deadlock between parent and child process.
|
// risk of deadlock between parent and child process.
|
||||||
let mut stdin = child.stdin.take().expect("expected standard input");
|
let mut stdin = child.stdin.take().expect("expected standard input");
|
||||||
let input = input.to_owned();
|
let input = input.to_owned();
|
||||||
let worker = thread::spawn(move || {
|
let worker = thread::spawn(move || stdin.write_all(&input));
|
||||||
stdin.write_all(&input)
|
|
||||||
});
|
|
||||||
|
|
||||||
let output = self.expect_success(child.wait_with_output().unwrap());
|
let output = self.expect_success(child.wait_with_output().unwrap());
|
||||||
worker.join().unwrap().unwrap();
|
worker.join().unwrap().unwrap();
|
||||||
@ -324,8 +313,7 @@ impl TestCommand {
|
|||||||
Err(err) => {
|
Err(err) => {
|
||||||
panic!(
|
panic!(
|
||||||
"could not convert from string: {:?}\n\n{}",
|
"could not convert from string: {:?}\n\n{}",
|
||||||
err,
|
err, stdout
|
||||||
stdout
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -368,9 +356,7 @@ impl TestCommand {
|
|||||||
\n\nexpected: {}\
|
\n\nexpected: {}\
|
||||||
\n\nfound: {}\
|
\n\nfound: {}\
|
||||||
\n\n=====\n",
|
\n\n=====\n",
|
||||||
self.cmd,
|
self.cmd, expected_code, code
|
||||||
expected_code,
|
|
||||||
code
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -396,14 +382,14 @@ impl TestCommand {
|
|||||||
|
|
||||||
fn expect_success(&self, o: process::Output) -> process::Output {
|
fn expect_success(&self, o: process::Output) -> process::Output {
|
||||||
if !o.status.success() {
|
if !o.status.success() {
|
||||||
let suggest =
|
let suggest = if o.stderr.is_empty() {
|
||||||
if o.stderr.is_empty() {
|
"\n\nDid your search end up with no results?".to_string()
|
||||||
"\n\nDid your search end up with no results?".to_string()
|
} else {
|
||||||
} else {
|
"".to_string()
|
||||||
"".to_string()
|
};
|
||||||
};
|
|
||||||
|
|
||||||
panic!("\n\n==========\n\
|
panic!(
|
||||||
|
"\n\n==========\n\
|
||||||
command failed but expected success!\
|
command failed but expected success!\
|
||||||
{}\
|
{}\
|
||||||
\n\ncommand: {:?}\
|
\n\ncommand: {:?}\
|
||||||
@ -412,18 +398,19 @@ impl TestCommand {
|
|||||||
\n\nstdout: {}\
|
\n\nstdout: {}\
|
||||||
\n\nstderr: {}\
|
\n\nstderr: {}\
|
||||||
\n\n==========\n",
|
\n\n==========\n",
|
||||||
suggest, self.cmd, self.dir.dir.display(), o.status,
|
suggest,
|
||||||
String::from_utf8_lossy(&o.stdout),
|
self.cmd,
|
||||||
String::from_utf8_lossy(&o.stderr));
|
self.dir.dir.display(),
|
||||||
|
o.status,
|
||||||
|
String::from_utf8_lossy(&o.stdout),
|
||||||
|
String::from_utf8_lossy(&o.stderr)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
o
|
o
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn nice_err<T, E: error::Error>(
|
fn nice_err<T, E: error::Error>(path: &Path, res: Result<T, E>) -> T {
|
||||||
path: &Path,
|
|
||||||
res: Result<T, E>,
|
|
||||||
) -> T {
|
|
||||||
match res {
|
match res {
|
||||||
Ok(t) => t,
|
Ok(t) => t,
|
||||||
Err(err) => panic!("{}: {:?}", path.display(), err),
|
Err(err) => panic!("{}: {:?}", path.display(), err),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user