From 0bf278e72f81de9576c63fac43f3d281b6bdbfe4 Mon Sep 17 00:00:00 2001 From: Andrew Gallant Date: Sat, 3 Sep 2016 21:48:23 -0400 Subject: [PATCH] making search work (finally) --- grep/src/search.rs | 4 +- src/main.rs | 65 ++++++- src/printer.rs | 10 +- src/search.rs | 476 +++++++++++++++++++++++++-------------------- 4 files changed, 331 insertions(+), 224 deletions(-) diff --git a/grep/src/search.rs b/grep/src/search.rs index 6e35ec9e..6451cc55 100644 --- a/grep/src/search.rs +++ b/grep/src/search.rs @@ -230,7 +230,7 @@ impl Grep { fn find_line_end(&self, buf: &[u8], pos: usize) -> usize { memchr(self.opts.line_terminator, &buf[pos..]) - .map_or(buf.len(), |i| pos + i) + .map_or(buf.len(), |i| pos + i + 1) } } @@ -281,7 +281,7 @@ mod tests { let start = memrchr(b'\n', &haystack[..s]) .map_or(0, |i| i + 1); let end = memchr(b'\n', &haystack[e..]) - .map_or(haystack.len(), |i| e + i); + .map_or(haystack.len(), |i| e + i + 1); lines.push(Match { start: start, end: end, diff --git a/src/main.rs b/src/main.rs index 43fb2e30..80bb2d5d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,9 @@ extern crate crossbeam; extern crate docopt; extern crate env_logger; extern crate grep; +#[cfg(test)] +#[macro_use] +extern crate lazy_static; #[macro_use] extern crate log; extern crate memchr; @@ -136,7 +139,7 @@ fn run(mut args: Args) -> Result<()> { } let args = Arc::new(args); let mut workers = vec![]; - let stdout = Arc::new(Mutex::new(io::BufWriter::new(io::stdout()))); + let out = Arc::new(Mutex::new(Out::new(args.clone(), io::stdout()))); let mut chan_work_send = { let (worker, stealer) = chase_lev::deque(); @@ -146,7 +149,7 @@ fn run(mut args: Args) -> Result<()> { .case_insensitive(args.flag_ignore_case); let worker = Worker { args: args.clone(), - stdout: stdout.clone(), + out: out.clone(), chan_work: stealer.clone(), inpbuf: InputBuffer::new(), outbuf: Some(vec![]), @@ -200,6 +203,26 @@ impl Args { ig.ignore_hidden(!self.flag_hidden); walk::Iter::new(ig, wd) } + + fn before_context(&self) -> usize { + if self.flag_context > 0 { + self.flag_context + } else { + self.flag_before_context + } + } + + fn after_context(&self) -> usize { + if self.flag_context > 0 { + self.flag_context + } else { + self.flag_after_context + } + } + + fn has_context(&self) -> bool { + self.before_context() > 0 || self.after_context() > 0 + } } enum Message { @@ -209,7 +232,7 @@ enum Message { struct Worker { args: Arc, - stdout: Arc>>, + out: Arc>>, chan_work: Stealer>, inpbuf: InputBuffer, outbuf: Option>, @@ -245,21 +268,43 @@ impl Worker { searcher = searcher.count(self.args.flag_count); searcher = searcher.line_number(self.args.flag_line_number); searcher = searcher.invert_match(self.args.flag_invert_match); - searcher = searcher.after_context( - self.args.flag_after_context); - searcher = searcher.before_context( - self.args.flag_before_context); + searcher = searcher.after_context(self.args.after_context()); + searcher = searcher.before_context(self.args.before_context()); if let Err(err) = searcher.run() { eprintln!("{}", err); } } let outbuf = printer.into_inner(); if !outbuf.is_empty() { - let mut stdout = self.stdout.lock(); - let _ = stdout.write_all(&outbuf); - let _ = stdout.flush(); + let mut out = self.out.lock(); + out.write_file_matches(&outbuf); } self.outbuf = Some(outbuf); } } } + +struct Out { + args: Arc, + wtr: io::BufWriter, + printed: bool, +} + +impl Out { + fn new(args: Arc, wtr: W) -> Out { + Out { + args: args, + wtr: io::BufWriter::new(wtr), + printed: false, + } + } + + fn write_file_matches(&mut self, buf: &[u8]) { + if self.printed && self.args.has_context() { + let _ = self.wtr.write_all(b"--\n"); + } + let _ = self.wtr.write_all(buf); + let _ = self.wtr.flush(); + self.printed = true; + } +} diff --git a/src/printer.rs b/src/printer.rs index 74bb8c9e..57867f88 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -65,7 +65,9 @@ impl Printer { self.write(b":"); } self.write(&buf[start..end]); - self.write(b"\n"); + if buf[start..end].last() != Some(&b'\n') { + self.write(b"\n"); + } } pub fn context>( @@ -83,11 +85,13 @@ impl Printer { self.write(b"-"); } self.write(&buf[start..end]); - self.write(b"\n"); + if buf[start..end].last() != Some(&b'\n') { + self.write(b"\n"); + } } pub fn binary_matched>(&mut self, path: P) { - wln!(&mut self.wtr, "binary file {} matches", path.as_ref().display()); + wln!(&mut self.wtr, "Binary file {} matches", path.as_ref().display()); } fn write(&mut self, buf: &[u8]) { diff --git a/src/search.rs b/src/search.rs index eaa3b6f5..7e178ff5 100644 --- a/src/search.rs +++ b/src/search.rs @@ -164,6 +164,9 @@ impl<'a, R: io::Read, W: io::Write> Searcher<'a, R, W> { let upto = self.inp.lastnl; self.print_after_context(upto); if !try!(self.fill()) { + if self.inp.is_binary { + self.printer.binary_matched(self.path); + } break; } while self.inp.pos < self.inp.lastnl { @@ -195,7 +198,7 @@ impl<'a, R: io::Read, W: io::Write> Searcher<'a, R, W> { } } if matched { - self.inp.pos = self.last_match.end() + 1; + self.inp.pos = self.last_match.end(); } else { self.inp.pos = self.inp.lastnl; } @@ -220,7 +223,7 @@ impl<'a, R: io::Read, W: io::Write> Searcher<'a, R, W> { } else { self.last_printed = 0; } - if keep_from < self.last_line { + if keep_from <= self.last_line { self.last_line = self.last_line - keep_from; } else { self.count_lines(keep_from); @@ -240,7 +243,7 @@ impl<'a, R: io::Read, W: io::Write> Searcher<'a, R, W> { if !self.count { self.print_match(start, end); } - self.inp.pos = end + 1; + self.inp.pos = end; self.match_count += 1; } } @@ -251,14 +254,14 @@ impl<'a, R: io::Read, W: io::Write> Searcher<'a, R, W> { return; } let start = self.last_printed; - let end = upto.saturating_sub(1); + let end = upto; if start >= end { return; } let before_context_start = start + start_of_previous_lines( &self.inp.buf[start..], - end - start, + end - start - 1, self.before_context); let mut it = IterLines::new(before_context_start); while let Some((s, e)) = it.next(&self.inp.buf[..end]) { @@ -286,24 +289,22 @@ impl<'a, R: io::Read, W: io::Write> Searcher<'a, R, W> { #[inline(always)] fn print_match(&mut self, start: usize, end: usize) { - let last_printed = cmp::min(end + 1, self.inp.lastnl); self.print_separator(start); self.count_lines(start); - self.add_line(last_printed); + self.add_line(end); self.printer.matched( &self.path, &self.inp.buf, start, end, self.line_count); - self.last_printed = last_printed; + self.last_printed = end; self.after_context_remaining = self.after_context; } #[inline(always)] fn print_context(&mut self, start: usize, end: usize) { - let last_printed = cmp::min(end + 1, self.inp.lastnl); self.count_lines(start); - self.add_line(last_printed); + self.add_line(end); self.printer.context( &self.path, &self.inp.buf, start, end, self.line_count); - self.last_printed = last_printed; + self.last_printed = end; } #[inline(always)] @@ -424,6 +425,7 @@ impl InputBuffer { &mut self.buf[self.end..self.end + self.read_size])); if self.first { if is_binary(&self.buf[self.end..self.end + n]) { + self.is_binary = true; return Ok(false); } } @@ -435,10 +437,6 @@ impl InputBuffer { self.lastnl = self.end; break; } - // We know there is no nl between self.start..self.end since: - // 1) If this is the first iteration, then any bytes preceding - // self.end do not contain nl by construction. - // 2) Subsequent iterations only occur if no nl could be found. self.lastnl = memrchr(b'\n', &self.buf[self.end..self.end + n]) .map(|i| self.end + i + 1) @@ -495,6 +493,8 @@ impl IterLines { /// Return the start and end position of the next line in the buffer. The /// buffer given should be the same on every call. + /// + /// The range returned includes the new line. #[inline(always)] fn next(&mut self, buf: &[u8]) -> Option<(usize, usize)> { match memchr(b'\n', &buf[self.pos..]) { @@ -509,8 +509,8 @@ impl IterLines { } Some(end) => { let start = self.pos; - let end = self.pos + end; - self.pos = end + 1; + let end = self.pos + end + 1; + self.pos = end; Some((start, end)) } } @@ -532,7 +532,7 @@ fn start_of_previous_lines( mut end: usize, mut count: usize, ) -> usize { - if buf.is_empty() { + if buf[..end].is_empty() { return 0; } if count == 0 { @@ -567,6 +567,9 @@ fn start_of_previous_lines( count -= 1; end = i; if end == 0 { + if buf[end] == b'\n' && count == 0 { + end += 1; + } return end; } end -= 1; @@ -591,6 +594,32 @@ mod tests { use super::{InputBuffer, Searcher, start_of_previous_lines}; + lazy_static! { + static ref SHERLOCK: &'static str = "\ +For the Doctor Watsons of this world, as opposed to the Sherlock +Holmeses, success in the province of detective work must always +be, to a very large extent, the result of luck. Sherlock Holmes +can extract a clew from a wisp of straw or a flake of cigar ash; +but Doctor Watson has to have it taken out for him and dusted, +and exhibited clearly, with a label attached.\ +"; + static ref CODE: &'static str = "\ +extern crate snap; + +use std::io; + +fn main() { + let stdin = io::stdin(); + let stdout = io::stdout(); + + // Wrap the stdin reader in a Snappy reader. + let mut rdr = snap::Reader::new(stdin.lock()); + let mut wtr = stdout.lock(); + io::copy(&mut rdr, &mut wtr).expect(\"I/O operation failed\"); +} +"; + } + fn hay(s: &str) -> io::Cursor> { io::Cursor::new(s.to_string().into_bytes()) } @@ -603,16 +632,43 @@ mod tests { &Path::new("/baz.rs") } + type TestSearcher<'a> = Searcher<'a, io::Cursor>, Vec>; + + fn search_smallcap TestSearcher>( + pat: &str, + haystack: &str, + mut map: F, + ) -> (u64, String) { + let mut inp = InputBuffer::with_capacity(1); + let mut pp = Printer::new(vec![]); + let grep = GrepBuilder::new(pat).build().unwrap(); + let count = { + let searcher = Searcher::new( + &mut inp, &mut pp, &grep, test_path(), hay(haystack)); + map(searcher).run().unwrap() + }; + (count, String::from_utf8(pp.into_inner()).unwrap()) + } + + fn search TestSearcher>( + pat: &str, + haystack: &str, + mut map: F, + ) -> (u64, String) { + let mut inp = InputBuffer::with_capacity(4096); + let mut pp = Printer::new(vec![]); + let grep = GrepBuilder::new(pat).build().unwrap(); + let count = { + let searcher = Searcher::new( + &mut inp, &mut pp, &grep, test_path(), hay(haystack)); + map(searcher).run().unwrap() + }; + (count, String::from_utf8(pp.into_inner()).unwrap()) + } + #[test] fn previous_lines() { - let text = &b"\ -For the Doctor Watsons of this world, as opposed to the Sherlock -Holmeses, success in the province of detective work must always -be, to a very large extent, the result of luck. Sherlock Holmes -can extract a clew from a wisp of straw or a flake of cigar ash; -but Doctor Watson has to have it taken out for him and dusted, -and exhibited clearly, with a label attached.\ -"[..]; + let text = SHERLOCK.as_bytes(); assert_eq!(366, text.len()); assert_eq!(0, start_of_previous_lines(text, 366, 100)); @@ -712,24 +768,8 @@ and exhibited clearly, with a label attached.\ #[test] fn basic_search() { - let text = hay("\ -For the Doctor Watsons of this world, as opposed to the Sherlock -Holmeses, success in the province of detective work must always -be, to a very large extent, the result of luck. Sherlock Holmes -can extract a clew from a wisp of straw or a flake of cigar ash; -but Doctor Watson has to have it taken out for him and dusted, -and exhibited clearly, with a label attached.\ -"); - let mut inp = InputBuffer::with_capacity(1); - let mut pp = Printer::new(vec![]); - let grep = matcher("Sherlock"); - let count = { - let searcher = Searcher::new( - &mut inp, &mut pp, &grep, test_path(), text); - searcher.run().unwrap() - }; + let (count, out) = search_smallcap("Sherlock", &*SHERLOCK, |s|s); assert_eq!(2, count); - let out = String::from_utf8(pp.into_inner()).unwrap(); assert_eq!(out, "\ /baz.rs:For the Doctor Watsons of this world, as opposed to the Sherlock /baz.rs:be, to a very large extent, the result of luck. Sherlock Holmes @@ -738,24 +778,9 @@ and exhibited clearly, with a label attached.\ #[test] fn line_numbers() { - let text = hay("\ -For the Doctor Watsons of this world, as opposed to the Sherlock -Holmeses, success in the province of detective work must always -be, to a very large extent, the result of luck. Sherlock Holmes -can extract a clew from a wisp of straw or a flake of cigar ash; -but Doctor Watson has to have it taken out for him and dusted, -and exhibited clearly, with a label attached.\ -"); - let mut inp = InputBuffer::with_capacity(1); - let mut pp = Printer::new(vec![]); - let grep = matcher("Sherlock"); - let count = { - let searcher = Searcher::new( - &mut inp, &mut pp, &grep, test_path(), text); - searcher.line_number(true).run().unwrap() - }; + let (count, out) = search_smallcap( + "Sherlock", &*SHERLOCK, |s| s.line_number(true)); assert_eq!(2, count); - let out = String::from_utf8(pp.into_inner()).unwrap(); assert_eq!(out, "\ /baz.rs:1:For the Doctor Watsons of this world, as opposed to the Sherlock /baz.rs:3:be, to a very large extent, the result of luck. Sherlock Holmes @@ -764,47 +789,17 @@ and exhibited clearly, with a label attached.\ #[test] fn count() { - let text = hay("\ -For the Doctor Watsons of this world, as opposed to the Sherlock -Holmeses, success in the province of detective work must always -be, to a very large extent, the result of luck. Sherlock Holmes -can extract a clew from a wisp of straw or a flake of cigar ash; -but Doctor Watson has to have it taken out for him and dusted, -and exhibited clearly, with a label attached.\ -"); - let mut inp = InputBuffer::with_capacity(1); - let mut pp = Printer::new(vec![]); - let grep = matcher("Sherlock"); - let count = { - let searcher = Searcher::new( - &mut inp, &mut pp, &grep, test_path(), text); - searcher.count(true).run().unwrap() - }; + let (count, out) = search_smallcap( + "Sherlock", &*SHERLOCK, |s| s.count(true)); assert_eq!(2, count); - let out = String::from_utf8(pp.into_inner()).unwrap(); assert_eq!(out, "/baz.rs:2\n"); } #[test] fn invert_match() { - let text = hay("\ -For the Doctor Watsons of this world, as opposed to the Sherlock -Holmeses, success in the province of detective work must always -be, to a very large extent, the result of luck. Sherlock Holmes -can extract a clew from a wisp of straw or a flake of cigar ash; -but Doctor Watson has to have it taken out for him and dusted, -and exhibited clearly, with a label attached.\ -"); - let mut inp = InputBuffer::with_capacity(1); - let mut pp = Printer::new(vec![]); - let grep = matcher("Sherlock"); - let count = { - let searcher = Searcher::new( - &mut inp, &mut pp, &grep, test_path(), text); - searcher.invert_match(true).run().unwrap() - }; + let (count, out) = search_smallcap( + "Sherlock", &*SHERLOCK, |s| s.invert_match(true)); assert_eq!(4, count); - let out = String::from_utf8(pp.into_inner()).unwrap(); assert_eq!(out, "\ /baz.rs:Holmeses, success in the province of detective work must always /baz.rs:can extract a clew from a wisp of straw or a flake of cigar ash; @@ -815,24 +810,10 @@ and exhibited clearly, with a label attached.\ #[test] fn invert_match_line_numbers() { - let text = hay("\ -For the Doctor Watsons of this world, as opposed to the Sherlock -Holmeses, success in the province of detective work must always -be, to a very large extent, the result of luck. Sherlock Holmes -can extract a clew from a wisp of straw or a flake of cigar ash; -but Doctor Watson has to have it taken out for him and dusted, -and exhibited clearly, with a label attached.\ -"); - let mut inp = InputBuffer::with_capacity(1); - let mut pp = Printer::new(vec![]); - let grep = matcher("Sherlock"); - let count = { - let searcher = Searcher::new( - &mut inp, &mut pp, &grep, test_path(), text); - searcher.invert_match(true).line_number(true).run().unwrap() - }; + let (count, out) = search_smallcap("Sherlock", &*SHERLOCK, |s| { + s.invert_match(true).line_number(true) + }); assert_eq!(4, count); - let out = String::from_utf8(pp.into_inner()).unwrap(); assert_eq!(out, "\ /baz.rs:2:Holmeses, success in the province of detective work must always /baz.rs:4:can extract a clew from a wisp of straw or a flake of cigar ash; @@ -843,95 +824,91 @@ and exhibited clearly, with a label attached.\ #[test] fn invert_match_count() { - let text = hay("\ -For the Doctor Watsons of this world, as opposed to the Sherlock -Holmeses, success in the province of detective work must always -be, to a very large extent, the result of luck. Sherlock Holmes -can extract a clew from a wisp of straw or a flake of cigar ash; -but Doctor Watson has to have it taken out for him and dusted, -and exhibited clearly, with a label attached.\ -"); - let mut inp = InputBuffer::with_capacity(1); - let mut pp = Printer::new(vec![]); - let grep = matcher("Sherlock"); - let count = { - let searcher = Searcher::new( - &mut inp, &mut pp, &grep, test_path(), text); - searcher.invert_match(true).count(true).run().unwrap() - }; + let (count, out) = search_smallcap("Sherlock", &*SHERLOCK, |s| { + s.invert_match(true).count(true) + }); assert_eq!(4, count); - let out = String::from_utf8(pp.into_inner()).unwrap(); assert_eq!(out, "/baz.rs:4\n"); } - macro_rules! before_context { - ($name:ident, $query:expr, $num:expr, $expect:expr) => { - before_context!($name, $query, $num, $expect, false); - }; - ($name:ident, $query:expr, $num:expr, $expect:expr, $invert:expr) => { - #[test] - fn $name() { - let text = hay("\ -For the Doctor Watsons of this world, as opposed to the Sherlock -Holmeses, success in the province of detective work must always -be, to a very large extent, the result of luck. Sherlock Holmes -can extract a clew from a wisp of straw or a flake of cigar ash; -but Doctor Watson has to have it taken out for him and dusted, -and exhibited clearly, with a label attached.\ -"); - let mut inp = InputBuffer::with_capacity(4096); - let mut pp = Printer::new(vec![]); - let grep = matcher($query); - let count = { - let mut searcher = Searcher::new( - &mut inp, &mut pp, &grep, test_path(), text); - searcher = searcher.line_number(true); - searcher = searcher.before_context($num); - searcher = searcher.invert_match($invert); - searcher.run().unwrap() - }; - let out = String::from_utf8(pp.into_inner()).unwrap(); - assert_eq!(out, $expect); - } - }; - } - - before_context!(before_context_one, "Sherlock", 1, "\ + #[test] + fn before_context_one1() { + let (count, out) = search_smallcap("Sherlock", &*SHERLOCK, |s| { + s.line_number(true).before_context(1) + }); + assert_eq!(2, count); + assert_eq!(out, "\ /baz.rs:1:For the Doctor Watsons of this world, as opposed to the Sherlock /baz.rs-2-Holmeses, success in the province of detective work must always /baz.rs:3:be, to a very large extent, the result of luck. Sherlock Holmes "); + } - before_context!(before_context_invert_one1, "Sherlock", 1, "\ + #[test] + fn before_context_invert_one1() { + let (count, out) = search_smallcap("Sherlock", &*SHERLOCK, |s| { + s.line_number(true).before_context(1).invert_match(true) + }); + assert_eq!(4, count); + assert_eq!(out, "\ /baz.rs-1-For the Doctor Watsons of this world, as opposed to the Sherlock /baz.rs:2:Holmeses, success in the province of detective work must always /baz.rs-3-be, to a very large extent, the result of luck. Sherlock Holmes /baz.rs:4:can extract a clew from a wisp of straw or a flake of cigar ash; /baz.rs:5:but Doctor Watson has to have it taken out for him and dusted, /baz.rs:6:and exhibited clearly, with a label attached. -", true); +"); + } - before_context!(before_context_invert_one2, " a ", 1, "\ + #[test] + fn before_context_invert_one2() { + let (count, out) = search_smallcap(" a ", &*SHERLOCK, |s| { + s.line_number(true).before_context(1).invert_match(true) + }); + assert_eq!(3, count); + assert_eq!(out, "\ /baz.rs:1:For the Doctor Watsons of this world, as opposed to the Sherlock /baz.rs:2:Holmeses, success in the province of detective work must always -- /baz.rs-4-can extract a clew from a wisp of straw or a flake of cigar ash; /baz.rs:5:but Doctor Watson has to have it taken out for him and dusted, -", true); +"); + } - before_context!(before_context_two1, "Sherlock", 2, "\ + #[test] + fn before_context_two1() { + let (count, out) = search_smallcap("Sherlock", &*SHERLOCK, |s| { + s.line_number(true).before_context(2) + }); + assert_eq!(2, count); + assert_eq!(out, "\ /baz.rs:1:For the Doctor Watsons of this world, as opposed to the Sherlock /baz.rs-2-Holmeses, success in the province of detective work must always /baz.rs:3:be, to a very large extent, the result of luck. Sherlock Holmes "); + } - before_context!(before_context_two2, "dusted", 2, "\ + #[test] + fn before_context_two2() { + let (count, out) = search_smallcap("dusted", &*SHERLOCK, |s| { + s.line_number(true).before_context(2) + }); + assert_eq!(1, count); + assert_eq!(out, "\ /baz.rs-3-be, to a very large extent, the result of luck. Sherlock Holmes /baz.rs-4-can extract a clew from a wisp of straw or a flake of cigar ash; /baz.rs:5:but Doctor Watson has to have it taken out for him and dusted, "); + } - before_context!(before_context_two3, "success|attached", 2, "\ + #[test] + fn before_context_two3() { + let (count, out) = search_smallcap( + "success|attached", &*SHERLOCK, |s| { + s.line_number(true).before_context(2) + }); + assert_eq!(2, count); + assert_eq!(out, "\ /baz.rs-1-For the Doctor Watsons of this world, as opposed to the Sherlock /baz.rs:2:Holmeses, success in the province of detective work must always -- @@ -939,91 +916,150 @@ and exhibited clearly, with a label attached.\ /baz.rs-5-but Doctor Watson has to have it taken out for him and dusted, /baz.rs:6:and exhibited clearly, with a label attached. "); + } - before_context!(before_context_three, "Sherlock", 3, "\ + #[test] + fn before_context_two4() { + let (count, out) = search("stdin", &*CODE, |s| { + s.line_number(true).before_context(2) + }); + assert_eq!(3, count); + assert_eq!(out, "\ +/baz.rs-4- +/baz.rs-5-fn main() { +/baz.rs:6: let stdin = io::stdin(); +/baz.rs-7- let stdout = io::stdout(); +/baz.rs-8- +/baz.rs:9: // Wrap the stdin reader in a Snappy reader. +/baz.rs:10: let mut rdr = snap::Reader::new(stdin.lock()); +"); + } + + #[test] + fn before_context_two5() { + let (count, out) = search("stdout", &*CODE, |s| { + s.line_number(true).before_context(2) + }); + assert_eq!(2, count); + assert_eq!(out, "\ +/baz.rs-5-fn main() { +/baz.rs-6- let stdin = io::stdin(); +/baz.rs:7: let stdout = io::stdout(); +-- +/baz.rs-9- // Wrap the stdin reader in a Snappy reader. +/baz.rs-10- let mut rdr = snap::Reader::new(stdin.lock()); +/baz.rs:11: let mut wtr = stdout.lock(); +"); + } + + #[test] + fn before_context_three1() { + let (count, out) = search_smallcap("Sherlock", &*SHERLOCK, |s| { + s.line_number(true).before_context(3) + }); + assert_eq!(2, count); + assert_eq!(out, "\ /baz.rs:1:For the Doctor Watsons of this world, as opposed to the Sherlock /baz.rs-2-Holmeses, success in the province of detective work must always /baz.rs:3:be, to a very large extent, the result of luck. Sherlock Holmes "); - - macro_rules! after_context { - ($name:ident, $query:expr, $num:expr, $expect:expr) => { - after_context!($name, $query, $num, $expect, false); - }; - ($name:ident, $query:expr, $num:expr, $expect:expr, $invert:expr) => { - #[test] - fn $name() { - let text = hay("\ -For the Doctor Watsons of this world, as opposed to the Sherlock -Holmeses, success in the province of detective work must always -be, to a very large extent, the result of luck. Sherlock Holmes -can extract a clew from a wisp of straw or a flake of cigar ash; -but Doctor Watson has to have it taken out for him and dusted, -and exhibited clearly, with a label attached.\ -"); - let mut inp = InputBuffer::with_capacity(4096); - let mut pp = Printer::new(vec![]); - let grep = matcher($query); - let count = { - let mut searcher = Searcher::new( - &mut inp, &mut pp, &grep, test_path(), text); - searcher = searcher.line_number(true); - searcher = searcher.after_context($num); - searcher = searcher.invert_match($invert); - searcher.run().unwrap() - }; - let out = String::from_utf8(pp.into_inner()).unwrap(); - assert_eq!(out, $expect); - } - }; } - after_context!(after_context_one, "Sherlock", 1, "\ + #[test] + fn after_context_one1() { + let (count, out) = search_smallcap("Sherlock", &*SHERLOCK, |s| { + s.line_number(true).after_context(1) + }); + assert_eq!(2, count); + assert_eq!(out, "\ /baz.rs:1:For the Doctor Watsons of this world, as opposed to the Sherlock /baz.rs-2-Holmeses, success in the province of detective work must always /baz.rs:3:be, to a very large extent, the result of luck. Sherlock Holmes /baz.rs-4-can extract a clew from a wisp of straw or a flake of cigar ash; "); + } - after_context!(after_context_invert_one1, "Sherlock", 1, "\ + #[test] + fn after_context_invert_one1() { + let (count, out) = search_smallcap("Sherlock", &*SHERLOCK, |s| { + s.line_number(true).after_context(1).invert_match(true) + }); + assert_eq!(4, count); + assert_eq!(out, "\ /baz.rs:2:Holmeses, success in the province of detective work must always /baz.rs-3-be, to a very large extent, the result of luck. Sherlock Holmes /baz.rs:4:can extract a clew from a wisp of straw or a flake of cigar ash; /baz.rs:5:but Doctor Watson has to have it taken out for him and dusted, /baz.rs:6:and exhibited clearly, with a label attached. -", true); +"); + } - after_context!(after_context_invert_one2, " a ", 1, "\ + #[test] + fn after_context_invert_one2() { + let (count, out) = search_smallcap(" a ", &*SHERLOCK, |s| { + s.line_number(true).after_context(1).invert_match(true) + }); + assert_eq!(3, count); + assert_eq!(out, "\ /baz.rs:1:For the Doctor Watsons of this world, as opposed to the Sherlock /baz.rs:2:Holmeses, success in the province of detective work must always /baz.rs-3-be, to a very large extent, the result of luck. Sherlock Holmes -- /baz.rs:5:but Doctor Watson has to have it taken out for him and dusted, /baz.rs-6-and exhibited clearly, with a label attached. -", true); +"); + } - after_context!(after_context_two1, "Sherlock", 2, "\ + #[test] + fn after_context_two1() { + let (count, out) = search_smallcap("Sherlock", &*SHERLOCK, |s| { + s.line_number(true).after_context(2) + }); + assert_eq!(2, count); + assert_eq!(out, "\ /baz.rs:1:For the Doctor Watsons of this world, as opposed to the Sherlock /baz.rs-2-Holmeses, success in the province of detective work must always /baz.rs:3:be, to a very large extent, the result of luck. Sherlock Holmes /baz.rs-4-can extract a clew from a wisp of straw or a flake of cigar ash; /baz.rs-5-but Doctor Watson has to have it taken out for him and dusted, "); + } - after_context!(after_context_two2, "dusted", 2, "\ + #[test] + fn after_context_two2() { + let (count, out) = search_smallcap("dusted", &*SHERLOCK, |s| { + s.line_number(true).after_context(2) + }); + assert_eq!(1, count); + assert_eq!(out, "\ /baz.rs:5:but Doctor Watson has to have it taken out for him and dusted, /baz.rs-6-and exhibited clearly, with a label attached. "); + } - after_context!(after_context_two3, "success|attached", 2, "\ + #[test] + fn after_context_two3() { + let (count, out) = search_smallcap( + "success|attached", &*SHERLOCK, |s| { + s.line_number(true).after_context(2) + }); + assert_eq!(2, count); + assert_eq!(out, "\ /baz.rs:2:Holmeses, success in the province of detective work must always /baz.rs-3-be, to a very large extent, the result of luck. Sherlock Holmes /baz.rs-4-can extract a clew from a wisp of straw or a flake of cigar ash; -- /baz.rs:6:and exhibited clearly, with a label attached. "); + } - after_context!(after_context_three, "Sherlock", 3, "\ + #[test] + fn after_context_three1() { + let (count, out) = search_smallcap("Sherlock", &*SHERLOCK, |s| { + s.line_number(true).after_context(3) + }); + assert_eq!(2, count); + assert_eq!(out, "\ /baz.rs:1:For the Doctor Watsons of this world, as opposed to the Sherlock /baz.rs-2-Holmeses, success in the province of detective work must always /baz.rs:3:be, to a very large extent, the result of luck. Sherlock Holmes @@ -1031,4 +1067,26 @@ and exhibited clearly, with a label attached.\ /baz.rs-5-but Doctor Watson has to have it taken out for him and dusted, /baz.rs-6-and exhibited clearly, with a label attached. "); + } + + #[test] + fn before_after_context_two1() { + let (count, out) = search( + r"fn main|let mut rdr", &*CODE, |s| { + s.line_number(true).after_context(2).before_context(2) + }); + assert_eq!(2, count); + assert_eq!(out, "\ +/baz.rs-3-use std::io; +/baz.rs-4- +/baz.rs:5:fn main() { +/baz.rs-6- let stdin = io::stdin(); +/baz.rs-7- let stdout = io::stdout(); +/baz.rs-8- +/baz.rs-9- // Wrap the stdin reader in a Snappy reader. +/baz.rs:10: let mut rdr = snap::Reader::new(stdin.lock()); +/baz.rs-11- let mut wtr = stdout.lock(); +/baz.rs-12- io::copy(&mut rdr, &mut wtr).expect(\"I/O operation failed\"); +"); + } }