1use std::{
2 cell::{Cell, RefCell},
3 cmp,
4 io::{self, Write},
5 path::Path,
6 sync::Arc,
7 time::Instant,
8};
9
10use {
11 bstr::ByteSlice,
12 grep_matcher::{Match, Matcher},
13 grep_searcher::{
14 LineStep, Searcher, Sink, SinkContext, SinkFinish, SinkMatch,
15 },
16 termcolor::{ColorSpec, NoColor, WriteColor},
17};
18
19use crate::{
20 color::ColorSpecs,
21 counter::CounterWriter,
22 hyperlink::{self, HyperlinkConfig},
23 stats::Stats,
24 util::{
25 DecimalFormatter, PrinterPath, Replacer, Sunk,
26 find_iter_at_in_context, trim_ascii_prefix, trim_line_terminator,
27 },
28};
29
30#[derive(Debug, Clone)]
36struct Config {
37 colors: ColorSpecs,
38 hyperlink: HyperlinkConfig,
39 stats: bool,
40 heading: bool,
41 path: bool,
42 only_matching: bool,
43 per_match: bool,
44 per_match_one_line: bool,
45 replacement: Arc<Option<Vec<u8>>>,
46 max_columns: Option<u64>,
47 max_columns_preview: bool,
48 column: bool,
49 byte_offset: bool,
50 trim_ascii: bool,
51 separator_search: Arc<Option<Vec<u8>>>,
52 separator_context: Arc<Option<Vec<u8>>>,
53 separator_field_match: Arc<Vec<u8>>,
54 separator_field_context: Arc<Vec<u8>>,
55 separator_path: Option<u8>,
56 path_terminator: Option<u8>,
57}
58
59impl Default for Config {
60 fn default() -> Config {
61 Config {
62 colors: ColorSpecs::default(),
63 hyperlink: HyperlinkConfig::default(),
64 stats: false,
65 heading: false,
66 path: true,
67 only_matching: false,
68 per_match: false,
69 per_match_one_line: false,
70 replacement: Arc::new(None),
71 max_columns: None,
72 max_columns_preview: false,
73 column: false,
74 byte_offset: false,
75 trim_ascii: false,
76 separator_search: Arc::new(None),
77 separator_context: Arc::new(Some(b"--".to_vec())),
78 separator_field_match: Arc::new(b":".to_vec()),
79 separator_field_context: Arc::new(b"-".to_vec()),
80 separator_path: None,
81 path_terminator: None,
82 }
83 }
84}
85
86#[derive(Clone, Debug)]
99pub struct StandardBuilder {
100 config: Config,
101}
102
103impl StandardBuilder {
104 pub fn new() -> StandardBuilder {
106 StandardBuilder { config: Config::default() }
107 }
108
109 pub fn build<W: WriteColor>(&self, wtr: W) -> Standard<W> {
128 Standard {
129 config: self.config.clone(),
130 wtr: RefCell::new(CounterWriter::new(wtr)),
131 matches: vec![],
132 }
133 }
134
135 pub fn build_no_color<W: io::Write>(
141 &self,
142 wtr: W,
143 ) -> Standard<NoColor<W>> {
144 self.build(NoColor::new(wtr))
145 }
146
147 pub fn color_specs(&mut self, specs: ColorSpecs) -> &mut StandardBuilder {
165 self.config.colors = specs;
166 self
167 }
168
169 pub fn hyperlink(
181 &mut self,
182 config: HyperlinkConfig,
183 ) -> &mut StandardBuilder {
184 self.config.hyperlink = config;
185 self
186 }
187
188 pub fn stats(&mut self, yes: bool) -> &mut StandardBuilder {
204 self.config.stats = yes;
205 self
206 }
207
208 pub fn heading(&mut self, yes: bool) -> &mut StandardBuilder {
219 self.config.heading = yes;
220 self
221 }
222
223 pub fn path(&mut self, yes: bool) -> &mut StandardBuilder {
230 self.config.path = yes;
231 self
232 }
233
234 pub fn only_matching(&mut self, yes: bool) -> &mut StandardBuilder {
239 self.config.only_matching = yes;
240 self
241 }
242
243 pub fn per_match(&mut self, yes: bool) -> &mut StandardBuilder {
261 self.config.per_match = yes;
262 self
263 }
264
265 pub fn per_match_one_line(&mut self, yes: bool) -> &mut StandardBuilder {
276 self.config.per_match_one_line = yes;
277 self
278 }
279
280 pub fn replacement(
291 &mut self,
292 replacement: Option<Vec<u8>>,
293 ) -> &mut StandardBuilder {
294 self.config.replacement = Arc::new(replacement);
295 self
296 }
297
298 pub fn max_columns(&mut self, limit: Option<u64>) -> &mut StandardBuilder {
307 self.config.max_columns = limit;
308 self
309 }
310
311 pub fn max_columns_preview(&mut self, yes: bool) -> &mut StandardBuilder {
322 self.config.max_columns_preview = yes;
323 self
324 }
325
326 pub fn column(&mut self, yes: bool) -> &mut StandardBuilder {
336 self.config.column = yes;
337 self
338 }
339
340 pub fn byte_offset(&mut self, yes: bool) -> &mut StandardBuilder {
348 self.config.byte_offset = yes;
349 self
350 }
351
352 pub fn trim_ascii(&mut self, yes: bool) -> &mut StandardBuilder {
357 self.config.trim_ascii = yes;
358 self
359 }
360
361 pub fn separator_search(
375 &mut self,
376 sep: Option<Vec<u8>>,
377 ) -> &mut StandardBuilder {
378 self.config.separator_search = Arc::new(sep);
379 self
380 }
381
382 pub fn separator_context(
392 &mut self,
393 sep: Option<Vec<u8>>,
394 ) -> &mut StandardBuilder {
395 self.config.separator_context = Arc::new(sep);
396 self
397 }
398
399 pub fn separator_field_match(
408 &mut self,
409 sep: Vec<u8>,
410 ) -> &mut StandardBuilder {
411 self.config.separator_field_match = Arc::new(sep);
412 self
413 }
414
415 pub fn separator_field_context(
424 &mut self,
425 sep: Vec<u8>,
426 ) -> &mut StandardBuilder {
427 self.config.separator_field_context = Arc::new(sep);
428 self
429 }
430
431 pub fn separator_path(&mut self, sep: Option<u8>) -> &mut StandardBuilder {
445 self.config.separator_path = sep;
446 self
447 }
448
449 pub fn path_terminator(
458 &mut self,
459 terminator: Option<u8>,
460 ) -> &mut StandardBuilder {
461 self.config.path_terminator = terminator;
462 self
463 }
464}
465
466#[derive(Clone, Debug)]
480pub struct Standard<W> {
481 config: Config,
482 wtr: RefCell<CounterWriter<W>>,
483 matches: Vec<Match>,
484}
485
486impl<W: WriteColor> Standard<W> {
487 pub fn new(wtr: W) -> Standard<W> {
495 StandardBuilder::new().build(wtr)
496 }
497}
498
499impl<W: io::Write> Standard<NoColor<W>> {
500 pub fn new_no_color(wtr: W) -> Standard<NoColor<W>> {
506 StandardBuilder::new().build_no_color(wtr)
507 }
508}
509
510impl<W: WriteColor> Standard<W> {
511 pub fn sink<'s, M: Matcher>(
516 &'s mut self,
517 matcher: M,
518 ) -> StandardSink<'static, 's, M, W> {
519 let interpolator =
520 hyperlink::Interpolator::new(&self.config.hyperlink);
521 let stats = if self.config.stats { Some(Stats::new()) } else { None };
522 let needs_match_granularity = self.needs_match_granularity();
523 StandardSink {
524 matcher,
525 standard: self,
526 replacer: Replacer::new(),
527 interpolator,
528 path: None,
529 start_time: Instant::now(),
530 match_count: 0,
531 binary_byte_offset: None,
532 stats,
533 needs_match_granularity,
534 }
535 }
536
537 pub fn sink_with_path<'p, 's, M, P>(
542 &'s mut self,
543 matcher: M,
544 path: &'p P,
545 ) -> StandardSink<'p, 's, M, W>
546 where
547 M: Matcher,
548 P: ?Sized + AsRef<Path>,
549 {
550 if !self.config.path {
551 return self.sink(matcher);
552 }
553 let interpolator =
554 hyperlink::Interpolator::new(&self.config.hyperlink);
555 let stats = if self.config.stats { Some(Stats::new()) } else { None };
556 let ppath = PrinterPath::new(path.as_ref())
557 .with_separator(self.config.separator_path);
558 let needs_match_granularity = self.needs_match_granularity();
559 StandardSink {
560 matcher,
561 standard: self,
562 replacer: Replacer::new(),
563 interpolator,
564 path: Some(ppath),
565 start_time: Instant::now(),
566 match_count: 0,
567 binary_byte_offset: None,
568 stats,
569 needs_match_granularity,
570 }
571 }
572
573 fn needs_match_granularity(&self) -> bool {
579 let supports_color = self.wtr.borrow().supports_color();
580 let match_colored = !self.config.colors.matched().is_none();
581
582 (supports_color && match_colored)
584 || self.config.column
586 || self.config.replacement.is_some()
588 || self.config.per_match
590 || self.config.only_matching
592 || self.config.stats
594 }
595}
596
597impl<W> Standard<W> {
598 pub fn has_written(&self) -> bool {
601 self.wtr.borrow().total_count() > 0
602 }
603
604 pub fn get_mut(&mut self) -> &mut W {
606 self.wtr.get_mut().get_mut()
607 }
608
609 pub fn into_inner(self) -> W {
612 self.wtr.into_inner().into_inner()
613 }
614}
615
616#[derive(Debug)]
639pub struct StandardSink<'p, 's, M: Matcher, W> {
640 matcher: M,
641 standard: &'s mut Standard<W>,
642 replacer: Replacer<M>,
643 interpolator: hyperlink::Interpolator,
644 path: Option<PrinterPath<'p>>,
645 start_time: Instant,
646 match_count: u64,
647 binary_byte_offset: Option<u64>,
648 stats: Option<Stats>,
649 needs_match_granularity: bool,
650}
651
652impl<'p, 's, M: Matcher, W: WriteColor> StandardSink<'p, 's, M, W> {
653 pub fn has_match(&self) -> bool {
659 self.match_count > 0
660 }
661
662 pub fn match_count(&self) -> u64 {
670 self.match_count
671 }
672
673 pub fn binary_byte_offset(&self) -> Option<u64> {
684 self.binary_byte_offset
685 }
686
687 pub fn stats(&self) -> Option<&Stats> {
693 self.stats.as_ref()
694 }
695
696 fn record_matches(
699 &mut self,
700 searcher: &Searcher,
701 bytes: &[u8],
702 range: std::ops::Range<usize>,
703 ) -> io::Result<()> {
704 self.standard.matches.clear();
705 if !self.needs_match_granularity {
706 return Ok(());
707 }
708 let matches = &mut self.standard.matches;
716 find_iter_at_in_context(
717 searcher,
718 &self.matcher,
719 bytes,
720 range.clone(),
721 |m| {
722 let (s, e) = (m.start() - range.start, m.end() - range.start);
723 matches.push(Match::new(s, e));
724 true
725 },
726 )?;
727 if !matches.is_empty()
729 && matches.last().unwrap().is_empty()
730 && matches.last().unwrap().start() >= range.end
731 {
732 matches.pop().unwrap();
733 }
734 Ok(())
735 }
736
737 fn replace(
742 &mut self,
743 searcher: &Searcher,
744 bytes: &[u8],
745 range: std::ops::Range<usize>,
746 ) -> io::Result<()> {
747 self.replacer.clear();
748 if self.standard.config.replacement.is_some() {
749 let replacement =
750 (*self.standard.config.replacement).as_ref().unwrap();
751 self.replacer.replace_all(
752 searcher,
753 &self.matcher,
754 bytes,
755 range,
756 replacement,
757 )?;
758 }
759 Ok(())
760 }
761}
762
763impl<'p, 's, M: Matcher, W: WriteColor> Sink for StandardSink<'p, 's, M, W> {
764 type Error = io::Error;
765
766 fn matched(
767 &mut self,
768 searcher: &Searcher,
769 mat: &SinkMatch<'_>,
770 ) -> Result<bool, io::Error> {
771 self.match_count += 1;
772
773 self.record_matches(
774 searcher,
775 mat.buffer(),
776 mat.bytes_range_in_buffer(),
777 )?;
778 self.replace(searcher, mat.buffer(), mat.bytes_range_in_buffer())?;
779
780 if let Some(ref mut stats) = self.stats {
781 stats.add_matches(self.standard.matches.len() as u64);
782 stats.add_matched_lines(mat.lines().count() as u64);
783 }
784 if searcher.binary_detection().convert_byte().is_some() {
785 if self.binary_byte_offset.is_some() {
786 return Ok(false);
787 }
788 }
789 StandardImpl::from_match(searcher, self, mat).sink()?;
790 Ok(true)
791 }
792
793 fn context(
794 &mut self,
795 searcher: &Searcher,
796 ctx: &SinkContext<'_>,
797 ) -> Result<bool, io::Error> {
798 self.standard.matches.clear();
799 self.replacer.clear();
800
801 if searcher.invert_match() {
802 self.record_matches(searcher, ctx.bytes(), 0..ctx.bytes().len())?;
803 self.replace(searcher, ctx.bytes(), 0..ctx.bytes().len())?;
804 }
805 if searcher.binary_detection().convert_byte().is_some() {
806 if self.binary_byte_offset.is_some() {
807 return Ok(false);
808 }
809 }
810
811 StandardImpl::from_context(searcher, self, ctx).sink()?;
812 Ok(true)
813 }
814
815 fn context_break(
816 &mut self,
817 searcher: &Searcher,
818 ) -> Result<bool, io::Error> {
819 StandardImpl::new(searcher, self).write_context_separator()?;
820 Ok(true)
821 }
822
823 fn binary_data(
824 &mut self,
825 searcher: &Searcher,
826 binary_byte_offset: u64,
827 ) -> Result<bool, io::Error> {
828 if searcher.binary_detection().quit_byte().is_some() {
829 if let Some(ref path) = self.path {
830 log::debug!(
831 "ignoring {path}: found binary data at \
832 offset {binary_byte_offset}",
833 path = path.as_path().display(),
834 );
835 }
836 }
837 self.binary_byte_offset = Some(binary_byte_offset);
838 Ok(true)
839 }
840
841 fn begin(&mut self, _searcher: &Searcher) -> Result<bool, io::Error> {
842 self.standard.wtr.borrow_mut().reset_count();
843 self.start_time = Instant::now();
844 self.match_count = 0;
845 self.binary_byte_offset = None;
846 Ok(true)
847 }
848
849 fn finish(
850 &mut self,
851 searcher: &Searcher,
852 finish: &SinkFinish,
853 ) -> Result<(), io::Error> {
854 if let Some(offset) = self.binary_byte_offset {
855 StandardImpl::new(searcher, self).write_binary_message(offset)?;
856 }
857 if let Some(stats) = self.stats.as_mut() {
858 stats.add_elapsed(self.start_time.elapsed());
859 stats.add_searches(1);
860 if self.match_count > 0 {
861 stats.add_searches_with_match(1);
862 }
863 stats.add_bytes_searched(finish.byte_count());
864 stats.add_bytes_printed(self.standard.wtr.borrow().count());
865 }
866 Ok(())
867 }
868}
869
870#[derive(Debug)]
876struct StandardImpl<'a, M: Matcher, W> {
877 searcher: &'a Searcher,
878 sink: &'a StandardSink<'a, 'a, M, W>,
879 sunk: Sunk<'a>,
880 in_color_match: Cell<bool>,
882}
883
884impl<'a, M: Matcher, W: WriteColor> StandardImpl<'a, M, W> {
885 fn new(
887 searcher: &'a Searcher,
888 sink: &'a StandardSink<'_, '_, M, W>,
889 ) -> StandardImpl<'a, M, W> {
890 StandardImpl {
891 searcher,
892 sink,
893 sunk: Sunk::empty(),
894 in_color_match: Cell::new(false),
895 }
896 }
897
898 fn from_match(
901 searcher: &'a Searcher,
902 sink: &'a StandardSink<'_, '_, M, W>,
903 mat: &'a SinkMatch<'a>,
904 ) -> StandardImpl<'a, M, W> {
905 let sunk = Sunk::from_sink_match(
906 mat,
907 &sink.standard.matches,
908 sink.replacer.replacement(),
909 );
910 StandardImpl { sunk, ..StandardImpl::new(searcher, sink) }
911 }
912
913 fn from_context(
916 searcher: &'a Searcher,
917 sink: &'a StandardSink<'_, '_, M, W>,
918 ctx: &'a SinkContext<'a>,
919 ) -> StandardImpl<'a, M, W> {
920 let sunk = Sunk::from_sink_context(
921 ctx,
922 &sink.standard.matches,
923 sink.replacer.replacement(),
924 );
925 StandardImpl { sunk, ..StandardImpl::new(searcher, sink) }
926 }
927
928 fn sink(&self) -> io::Result<()> {
929 self.write_search_prelude()?;
930 if self.sunk.matches().is_empty() {
931 if self.multi_line() && !self.is_context() {
932 self.sink_fast_multi_line()
933 } else {
934 self.sink_fast()
935 }
936 } else {
937 if self.multi_line() && !self.is_context() {
938 self.sink_slow_multi_line()
939 } else {
940 self.sink_slow()
941 }
942 }
943 }
944
945 fn sink_fast(&self) -> io::Result<()> {
952 debug_assert!(self.sunk.matches().is_empty());
953 debug_assert!(!self.multi_line() || self.is_context());
954
955 self.write_prelude(
956 self.sunk.absolute_byte_offset(),
957 self.sunk.line_number(),
958 None,
959 )?;
960 self.write_line(self.sunk.bytes())
961 }
962
963 fn sink_fast_multi_line(&self) -> io::Result<()> {
970 debug_assert!(self.sunk.matches().is_empty());
971 debug_assert!(self.multi_line());
976
977 let line_term = self.searcher.line_terminator().as_byte();
978 let mut absolute_byte_offset = self.sunk.absolute_byte_offset();
979 for (i, line) in self.sunk.lines(line_term).enumerate() {
980 self.write_prelude(
981 absolute_byte_offset,
982 self.sunk.line_number().map(|n| n + i as u64),
983 None,
984 )?;
985 absolute_byte_offset += line.len() as u64;
986
987 self.write_line(line)?;
988 }
989 Ok(())
990 }
991
992 fn sink_slow(&self) -> io::Result<()> {
995 debug_assert!(!self.sunk.matches().is_empty());
996 debug_assert!(!self.multi_line() || self.is_context());
997
998 if self.config().only_matching {
999 for &m in self.sunk.matches() {
1000 self.write_prelude(
1001 self.sunk.absolute_byte_offset() + m.start() as u64,
1002 self.sunk.line_number(),
1003 Some(m.start() as u64 + 1),
1004 )?;
1005
1006 let buf = &self.sunk.bytes()[m];
1007 self.write_colored_line(&[Match::new(0, buf.len())], buf)?;
1008 }
1009 } else if self.config().per_match {
1010 for &m in self.sunk.matches() {
1011 self.write_prelude(
1012 self.sunk.absolute_byte_offset() + m.start() as u64,
1013 self.sunk.line_number(),
1014 Some(m.start() as u64 + 1),
1015 )?;
1016 self.write_colored_line(&[m], self.sunk.bytes())?;
1017 }
1018 } else {
1019 self.write_prelude(
1020 self.sunk.absolute_byte_offset(),
1021 self.sunk.line_number(),
1022 Some(self.sunk.matches()[0].start() as u64 + 1),
1023 )?;
1024 self.write_colored_line(self.sunk.matches(), self.sunk.bytes())?;
1025 }
1026 Ok(())
1027 }
1028
1029 fn sink_slow_multi_line(&self) -> io::Result<()> {
1030 debug_assert!(!self.sunk.matches().is_empty());
1031 debug_assert!(self.multi_line());
1032
1033 if self.config().only_matching {
1034 return self.sink_slow_multi_line_only_matching();
1035 } else if self.config().per_match {
1036 return self.sink_slow_multi_per_match();
1037 }
1038
1039 let line_term = self.searcher.line_terminator().as_byte();
1040 let bytes = self.sunk.bytes();
1041 let matches = self.sunk.matches();
1042 let mut midx = 0;
1043 let mut count = 0;
1044 let mut stepper = LineStep::new(line_term, 0, bytes.len());
1045 while let Some((start, end)) = stepper.next(bytes) {
1046 let mut line = Match::new(start, end);
1047 self.write_prelude(
1048 self.sunk.absolute_byte_offset() + line.start() as u64,
1049 self.sunk.line_number().map(|n| n + count),
1050 Some(matches[0].start() as u64 + 1),
1051 )?;
1052 count += 1;
1053 self.trim_ascii_prefix(bytes, &mut line);
1054 if self.exceeds_max_columns(&bytes[line]) {
1055 self.write_exceeded_line(bytes, line, matches, &mut midx)?;
1056 } else {
1057 self.write_colored_matches(bytes, line, matches, &mut midx)?;
1058 self.write_line_term()?;
1059 }
1060 }
1061 Ok(())
1062 }
1063
1064 fn sink_slow_multi_line_only_matching(&self) -> io::Result<()> {
1065 let line_term = self.searcher.line_terminator().as_byte();
1066 let spec = self.config().colors.matched();
1067 let bytes = self.sunk.bytes();
1068 let matches = self.sunk.matches();
1069 let mut midx = 0;
1070 let mut count = 0;
1071 let mut stepper = LineStep::new(line_term, 0, bytes.len());
1072 while let Some((start, end)) = stepper.next(bytes) {
1073 let mut line = Match::new(start, end);
1074 self.trim_line_terminator(bytes, &mut line);
1075 self.trim_ascii_prefix(bytes, &mut line);
1076 while !line.is_empty() {
1077 if matches[midx].end() <= line.start() {
1078 if midx + 1 < matches.len() {
1079 midx += 1;
1080 continue;
1081 } else {
1082 break;
1083 }
1084 }
1085 let m = matches[midx];
1086
1087 if line.start() < m.start() {
1088 let upto = cmp::min(line.end(), m.start());
1089 line = line.with_start(upto);
1090 } else {
1091 let upto = cmp::min(line.end(), m.end());
1092 self.write_prelude(
1093 self.sunk.absolute_byte_offset() + m.start() as u64,
1094 self.sunk.line_number().map(|n| n + count),
1095 Some(m.start() as u64 + 1),
1096 )?;
1097
1098 let this_line = line.with_end(upto);
1099 line = line.with_start(upto);
1100 if self.exceeds_max_columns(&bytes[this_line]) {
1101 self.write_exceeded_line(
1102 bytes, this_line, matches, &mut midx,
1103 )?;
1104 } else {
1105 self.write_spec(spec, &bytes[this_line])?;
1106 self.write_line_term()?;
1107 }
1108 }
1109 }
1110 count += 1;
1111 }
1112 Ok(())
1113 }
1114
1115 fn sink_slow_multi_per_match(&self) -> io::Result<()> {
1116 let line_term = self.searcher.line_terminator().as_byte();
1117 let spec = self.config().colors.matched();
1118 let bytes = self.sunk.bytes();
1119 for &m in self.sunk.matches() {
1120 let mut count = 0;
1121 let mut stepper = LineStep::new(line_term, 0, bytes.len());
1122 while let Some((start, end)) = stepper.next(bytes) {
1123 let mut line = Match::new(start, end);
1124 if line.start() >= m.end() {
1125 break;
1126 } else if line.end() <= m.start() {
1127 count += 1;
1128 continue;
1129 }
1130 self.write_prelude(
1131 self.sunk.absolute_byte_offset() + line.start() as u64,
1132 self.sunk.line_number().map(|n| n + count),
1133 Some(m.start().saturating_sub(line.start()) as u64 + 1),
1134 )?;
1135 count += 1;
1136 self.trim_line_terminator(bytes, &mut line);
1137 self.trim_ascii_prefix(bytes, &mut line);
1138 if self.exceeds_max_columns(&bytes[line]) {
1139 self.write_exceeded_line(bytes, line, &[m], &mut 0)?;
1140 continue;
1141 }
1142
1143 while !line.is_empty() {
1144 if m.end() <= line.start() {
1145 self.write(&bytes[line])?;
1146 line = line.with_start(line.end());
1147 } else if line.start() < m.start() {
1148 let upto = cmp::min(line.end(), m.start());
1149 self.write(&bytes[line.with_end(upto)])?;
1150 line = line.with_start(upto);
1151 } else {
1152 let upto = cmp::min(line.end(), m.end());
1153 self.write_spec(spec, &bytes[line.with_end(upto)])?;
1154 line = line.with_start(upto);
1155 }
1156 }
1157 self.write_line_term()?;
1158 if self.config().per_match_one_line {
1165 break;
1166 }
1167 }
1168 }
1169 Ok(())
1170 }
1171
1172 #[inline(always)]
1176 fn write_prelude(
1177 &self,
1178 absolute_byte_offset: u64,
1179 line_number: Option<u64>,
1180 column: Option<u64>,
1181 ) -> io::Result<()> {
1182 let mut prelude = PreludeWriter::new(self);
1183 prelude.start(line_number, column)?;
1184 prelude.write_path()?;
1185 prelude.write_line_number(line_number)?;
1186 prelude.write_column_number(column)?;
1187 prelude.write_byte_offset(absolute_byte_offset)?;
1188 prelude.end()
1189 }
1190
1191 #[inline(always)]
1192 fn write_line(&self, line: &[u8]) -> io::Result<()> {
1193 let line = if !self.config().trim_ascii {
1194 line
1195 } else {
1196 let lineterm = self.searcher.line_terminator();
1197 let full_range = Match::new(0, line.len());
1198 let range = trim_ascii_prefix(lineterm, line, full_range);
1199 &line[range]
1200 };
1201 if self.exceeds_max_columns(line) {
1202 let range = Match::new(0, line.len());
1203 self.write_exceeded_line(
1204 line,
1205 range,
1206 self.sunk.matches(),
1207 &mut 0,
1208 )?;
1209 } else {
1210 self.write(line)?;
1212 if !self.has_line_terminator(line) {
1213 self.write_line_term()?;
1214 }
1215 }
1216 Ok(())
1217 }
1218
1219 fn write_colored_line(
1220 &self,
1221 matches: &[Match],
1222 bytes: &[u8],
1223 ) -> io::Result<()> {
1224 let spec = self.config().colors.matched();
1226 if !self.wtr().borrow().supports_color() || spec.is_none() {
1227 return self.write_line(bytes);
1228 }
1229
1230 let mut line = Match::new(0, bytes.len());
1231 self.trim_ascii_prefix(bytes, &mut line);
1232 if self.exceeds_max_columns(bytes) {
1233 self.write_exceeded_line(bytes, line, matches, &mut 0)
1234 } else {
1235 self.write_colored_matches(bytes, line, matches, &mut 0)?;
1236 self.write_line_term()?;
1237 Ok(())
1238 }
1239 }
1240
1241 fn write_colored_matches(
1248 &self,
1249 bytes: &[u8],
1250 mut line: Match,
1251 matches: &[Match],
1252 match_index: &mut usize,
1253 ) -> io::Result<()> {
1254 self.trim_line_terminator(bytes, &mut line);
1255 if matches.is_empty() {
1256 self.write(&bytes[line])?;
1257 return Ok(());
1258 }
1259 self.start_line_highlight()?;
1260 while !line.is_empty() {
1261 if matches[*match_index].end() <= line.start() {
1262 if *match_index + 1 < matches.len() {
1263 *match_index += 1;
1264 continue;
1265 } else {
1266 self.end_color_match()?;
1267 self.write(&bytes[line])?;
1268 break;
1269 }
1270 }
1271
1272 let m = matches[*match_index];
1273 if line.start() < m.start() {
1274 let upto = cmp::min(line.end(), m.start());
1275 self.end_color_match()?;
1276 self.write(&bytes[line.with_end(upto)])?;
1277 line = line.with_start(upto);
1278 } else {
1279 let upto = cmp::min(line.end(), m.end());
1280 self.start_color_match()?;
1281 self.write(&bytes[line.with_end(upto)])?;
1282 line = line.with_start(upto);
1283 }
1284 }
1285 self.end_color_match()?;
1286 self.end_line_highlight()?;
1287 Ok(())
1288 }
1289
1290 fn write_exceeded_line(
1291 &self,
1292 bytes: &[u8],
1293 mut line: Match,
1294 matches: &[Match],
1295 match_index: &mut usize,
1296 ) -> io::Result<()> {
1297 if self.config().max_columns_preview {
1298 let original = line;
1299 let end = bytes[line]
1300 .grapheme_indices()
1301 .map(|(_, end, _)| end)
1302 .take(self.config().max_columns.unwrap_or(0) as usize)
1303 .last()
1304 .unwrap_or(0)
1305 + line.start();
1306 line = line.with_end(end);
1307 self.write_colored_matches(bytes, line, matches, match_index)?;
1308
1309 if matches.is_empty() {
1310 self.write(b" [... omitted end of long line]")?;
1311 } else {
1312 let remaining = matches
1313 .iter()
1314 .filter(|m| {
1315 m.start() >= line.end() && m.start() < original.end()
1316 })
1317 .count();
1318 let tense = if remaining == 1 { "match" } else { "matches" };
1319 write!(
1320 self.wtr().borrow_mut(),
1321 " [... {} more {}]",
1322 remaining,
1323 tense,
1324 )?;
1325 }
1326 self.write_line_term()?;
1327 return Ok(());
1328 }
1329 if self.sunk.original_matches().is_empty() {
1330 if self.is_context() {
1331 self.write(b"[Omitted long context line]")?;
1332 } else {
1333 self.write(b"[Omitted long matching line]")?;
1334 }
1335 } else {
1336 if self.config().only_matching {
1337 if self.is_context() {
1338 self.write(b"[Omitted long context line]")?;
1339 } else {
1340 self.write(b"[Omitted long matching line]")?;
1341 }
1342 } else {
1343 write!(
1344 self.wtr().borrow_mut(),
1345 "[Omitted long line with {} matches]",
1346 self.sunk.original_matches().len(),
1347 )?;
1348 }
1349 }
1350 self.write_line_term()?;
1351 Ok(())
1352 }
1353
1354 fn write_path_line(&self) -> io::Result<()> {
1359 if let Some(path) = self.path() {
1360 self.write_path_hyperlink(path)?;
1361 if let Some(term) = self.config().path_terminator {
1362 self.write(&[term])?;
1363 } else {
1364 self.write_line_term()?;
1365 }
1366 }
1367 Ok(())
1368 }
1369
1370 fn write_search_prelude(&self) -> io::Result<()> {
1371 let this_search_written = self.wtr().borrow().count() > 0;
1372 if this_search_written {
1373 return Ok(());
1374 }
1375 if let Some(ref sep) = *self.config().separator_search {
1376 let ever_written = self.wtr().borrow().total_count() > 0;
1377 if ever_written {
1378 self.write(sep)?;
1379 self.write_line_term()?;
1380 }
1381 }
1382 if self.config().heading {
1383 self.write_path_line()?;
1384 }
1385 Ok(())
1386 }
1387
1388 fn write_binary_message(&self, offset: u64) -> io::Result<()> {
1389 if !self.sink.has_match() {
1390 return Ok(());
1391 }
1392
1393 let bin = self.searcher.binary_detection();
1394 if let Some(byte) = bin.quit_byte() {
1395 if let Some(path) = self.path() {
1396 self.write_path_hyperlink(path)?;
1397 self.write(b": ")?;
1398 }
1399 let remainder = format!(
1400 "WARNING: stopped searching binary file after match \
1401 (found {:?} byte around offset {})\n",
1402 [byte].as_bstr(),
1403 offset,
1404 );
1405 self.write(remainder.as_bytes())?;
1406 } else if let Some(byte) = bin.convert_byte() {
1407 if let Some(path) = self.path() {
1408 self.write_path_hyperlink(path)?;
1409 self.write(b": ")?;
1410 }
1411 let remainder = format!(
1412 "binary file matches (found {:?} byte around offset {})\n",
1413 [byte].as_bstr(),
1414 offset,
1415 );
1416 self.write(remainder.as_bytes())?;
1417 }
1418 Ok(())
1419 }
1420
1421 fn write_context_separator(&self) -> io::Result<()> {
1422 if let Some(ref sep) = *self.config().separator_context {
1423 self.write(sep)?;
1424 self.write_line_term()?;
1425 }
1426 Ok(())
1427 }
1428
1429 fn write_line_term(&self) -> io::Result<()> {
1430 self.write(self.searcher.line_terminator().as_bytes())
1431 }
1432
1433 fn write_spec(&self, spec: &ColorSpec, buf: &[u8]) -> io::Result<()> {
1434 let mut wtr = self.wtr().borrow_mut();
1435 wtr.set_color(spec)?;
1436 wtr.write_all(buf)?;
1437 wtr.reset()?;
1438 Ok(())
1439 }
1440
1441 fn write_path(&self, path: &PrinterPath) -> io::Result<()> {
1442 let mut wtr = self.wtr().borrow_mut();
1443 wtr.set_color(self.config().colors.path())?;
1444 wtr.write_all(path.as_bytes())?;
1445 wtr.reset()
1446 }
1447
1448 fn write_path_hyperlink(&self, path: &PrinterPath) -> io::Result<()> {
1449 let status = self.start_hyperlink(path, None, None)?;
1450 self.write_path(path)?;
1451 self.end_hyperlink(status)
1452 }
1453
1454 fn start_hyperlink(
1455 &self,
1456 path: &PrinterPath,
1457 line_number: Option<u64>,
1458 column: Option<u64>,
1459 ) -> io::Result<hyperlink::InterpolatorStatus> {
1460 let Some(hyperpath) = path.as_hyperlink() else {
1461 return Ok(hyperlink::InterpolatorStatus::inactive());
1462 };
1463 let values =
1464 hyperlink::Values::new(hyperpath).line(line_number).column(column);
1465 self.sink.interpolator.begin(&values, &mut *self.wtr().borrow_mut())
1466 }
1467
1468 fn end_hyperlink(
1469 &self,
1470 status: hyperlink::InterpolatorStatus,
1471 ) -> io::Result<()> {
1472 self.sink.interpolator.finish(status, &mut *self.wtr().borrow_mut())
1473 }
1474
1475 fn start_color_match(&self) -> io::Result<()> {
1476 if self.in_color_match.get() {
1477 return Ok(());
1478 }
1479 self.wtr().borrow_mut().set_color(self.config().colors.matched())?;
1480 self.in_color_match.set(true);
1481 Ok(())
1482 }
1483
1484 fn end_color_match(&self) -> io::Result<()> {
1485 if !self.in_color_match.get() {
1486 return Ok(());
1487 }
1488 if self.highlight_on() {
1489 self.wtr()
1490 .borrow_mut()
1491 .set_color(self.config().colors.highlight())?;
1492 } else {
1493 self.wtr().borrow_mut().reset()?;
1494 }
1495 self.in_color_match.set(false);
1496 Ok(())
1497 }
1498
1499 fn highlight_on(&self) -> bool {
1500 !self.config().colors.highlight().is_none() && !self.is_context()
1501 }
1502
1503 fn start_line_highlight(&self) -> io::Result<()> {
1504 if self.highlight_on() {
1505 self.wtr()
1506 .borrow_mut()
1507 .set_color(self.config().colors.highlight())?;
1508 }
1509 Ok(())
1510 }
1511
1512 fn end_line_highlight(&self) -> io::Result<()> {
1513 if self.highlight_on() {
1514 self.wtr().borrow_mut().reset()?;
1515 }
1516 Ok(())
1517 }
1518
1519 fn write(&self, buf: &[u8]) -> io::Result<()> {
1520 self.wtr().borrow_mut().write_all(buf)
1521 }
1522
1523 fn trim_line_terminator(&self, buf: &[u8], line: &mut Match) {
1524 trim_line_terminator(&self.searcher, buf, line);
1525 }
1526
1527 fn has_line_terminator(&self, buf: &[u8]) -> bool {
1528 self.searcher.line_terminator().is_suffix(buf)
1529 }
1530
1531 fn is_context(&self) -> bool {
1532 self.sunk.context_kind().is_some()
1533 }
1534
1535 fn config(&self) -> &'a Config {
1537 &self.sink.standard.config
1538 }
1539
1540 fn wtr(&self) -> &'a RefCell<CounterWriter<W>> {
1542 &self.sink.standard.wtr
1543 }
1544
1545 fn path(&self) -> Option<&'a PrinterPath<'a>> {
1547 self.sink.path.as_ref()
1548 }
1549
1550 fn separator_field(&self) -> &[u8] {
1553 if self.is_context() {
1554 &self.config().separator_field_context
1555 } else {
1556 &self.config().separator_field_match
1557 }
1558 }
1559
1560 fn exceeds_max_columns(&self, line: &[u8]) -> bool {
1563 self.config().max_columns.map_or(false, |m| line.len() as u64 > m)
1564 }
1565
1566 fn multi_line(&self) -> bool {
1574 self.searcher.multi_line_with_matcher(&self.sink.matcher)
1575 }
1576
1577 fn trim_ascii_prefix(&self, slice: &[u8], range: &mut Match) {
1583 if !self.config().trim_ascii {
1584 return;
1585 }
1586 let lineterm = self.searcher.line_terminator();
1587 *range = trim_ascii_prefix(lineterm, slice, *range)
1588 }
1589}
1590
1591struct PreludeWriter<'a, M: Matcher, W> {
1595 std: &'a StandardImpl<'a, M, W>,
1596 next_separator: PreludeSeparator,
1597 field_separator: &'a [u8],
1598 interp_status: hyperlink::InterpolatorStatus,
1599}
1600
1601enum PreludeSeparator {
1603 None,
1605 FieldSeparator,
1607 PathTerminator,
1609}
1610
1611impl<'a, M: Matcher, W: WriteColor> PreludeWriter<'a, M, W> {
1612 #[inline(always)]
1614 fn new(std: &'a StandardImpl<'a, M, W>) -> PreludeWriter<'a, M, W> {
1615 PreludeWriter {
1616 std,
1617 next_separator: PreludeSeparator::None,
1618 field_separator: std.separator_field(),
1619 interp_status: hyperlink::InterpolatorStatus::inactive(),
1620 }
1621 }
1622
1623 #[inline(always)]
1630 fn start(
1631 &mut self,
1632 line_number: Option<u64>,
1633 column: Option<u64>,
1634 ) -> io::Result<()> {
1635 let Some(path) = self.std.path() else { return Ok(()) };
1636 if self.config().hyperlink.format().is_line_dependent()
1637 || !self.config().heading
1638 {
1639 self.interp_status =
1640 self.std.start_hyperlink(path, line_number, column)?;
1641 }
1642 Ok(())
1643 }
1644
1645 #[inline(always)]
1647 fn end(&mut self) -> io::Result<()> {
1648 self.std.end_hyperlink(std::mem::replace(
1649 &mut self.interp_status,
1650 hyperlink::InterpolatorStatus::inactive(),
1651 ))?;
1652 self.write_separator()
1653 }
1654
1655 #[inline(always)]
1660 fn write_path(&mut self) -> io::Result<()> {
1661 if self.config().heading {
1665 return Ok(());
1666 }
1667 let Some(path) = self.std.path() else { return Ok(()) };
1668 self.write_separator()?;
1669 self.std.write_path(path)?;
1670
1671 self.next_separator = if self.config().path_terminator.is_some() {
1672 PreludeSeparator::PathTerminator
1673 } else {
1674 PreludeSeparator::FieldSeparator
1675 };
1676 Ok(())
1677 }
1678
1679 #[inline(always)]
1681 fn write_line_number(&mut self, line: Option<u64>) -> io::Result<()> {
1682 let Some(line_number) = line else { return Ok(()) };
1683 self.write_separator()?;
1684 let n = DecimalFormatter::new(line_number);
1685 self.std.write_spec(self.config().colors.line(), n.as_bytes())?;
1686 self.next_separator = PreludeSeparator::FieldSeparator;
1687 Ok(())
1688 }
1689
1690 #[inline(always)]
1692 fn write_column_number(&mut self, column: Option<u64>) -> io::Result<()> {
1693 if !self.config().column {
1694 return Ok(());
1695 }
1696 let Some(column_number) = column else { return Ok(()) };
1697 self.write_separator()?;
1698 let n = DecimalFormatter::new(column_number);
1699 self.std.write_spec(self.config().colors.column(), n.as_bytes())?;
1700 self.next_separator = PreludeSeparator::FieldSeparator;
1701 Ok(())
1702 }
1703
1704 #[inline(always)]
1706 fn write_byte_offset(&mut self, offset: u64) -> io::Result<()> {
1707 if !self.config().byte_offset {
1708 return Ok(());
1709 }
1710 self.write_separator()?;
1711 let n = DecimalFormatter::new(offset);
1712 self.std.write_spec(self.config().colors.column(), n.as_bytes())?;
1713 self.next_separator = PreludeSeparator::FieldSeparator;
1714 Ok(())
1715 }
1716
1717 #[inline(always)]
1722 fn write_separator(&mut self) -> io::Result<()> {
1723 match self.next_separator {
1724 PreludeSeparator::None => {}
1725 PreludeSeparator::FieldSeparator => {
1726 self.std.write(self.field_separator)?;
1727 }
1728 PreludeSeparator::PathTerminator => {
1729 if let Some(term) = self.config().path_terminator {
1730 self.std.write(&[term])?;
1731 }
1732 }
1733 }
1734 self.next_separator = PreludeSeparator::None;
1735 Ok(())
1736 }
1737
1738 #[inline(always)]
1739 fn config(&self) -> &Config {
1740 self.std.config()
1741 }
1742}
1743
1744#[cfg(test)]
1745mod tests {
1746 use grep_matcher::LineTerminator;
1747 use grep_regex::{RegexMatcher, RegexMatcherBuilder};
1748 use grep_searcher::SearcherBuilder;
1749 use termcolor::{Ansi, NoColor};
1750
1751 use super::{ColorSpecs, Standard, StandardBuilder};
1752
1753 const SHERLOCK: &'static str = "\
1754For the Doctor Watsons of this world, as opposed to the Sherlock
1755Holmeses, success in the province of detective work must always
1756be, to a very large extent, the result of luck. Sherlock Holmes
1757can extract a clew from a wisp of straw or a flake of cigar ash;
1758but Doctor Watson has to have it taken out for him and dusted,
1759and exhibited clearly, with a label attached.\
1760";
1761
1762 #[allow(dead_code)]
1763 const SHERLOCK_CRLF: &'static str = "\
1764For the Doctor Watsons of this world, as opposed to the Sherlock\r
1765Holmeses, success in the province of detective work must always\r
1766be, to a very large extent, the result of luck. Sherlock Holmes\r
1767can extract a clew from a wisp of straw or a flake of cigar ash;\r
1768but Doctor Watson has to have it taken out for him and dusted,\r
1769and exhibited clearly, with a label attached.\
1770";
1771
1772 fn printer_contents(printer: &mut Standard<NoColor<Vec<u8>>>) -> String {
1773 String::from_utf8(printer.get_mut().get_ref().to_owned()).unwrap()
1774 }
1775
1776 fn printer_contents_ansi(printer: &mut Standard<Ansi<Vec<u8>>>) -> String {
1777 String::from_utf8(printer.get_mut().get_ref().to_owned()).unwrap()
1778 }
1779
1780 #[test]
1781 fn reports_match() {
1782 let matcher = RegexMatcher::new("Sherlock").unwrap();
1783 let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
1784 let mut sink = printer.sink(&matcher);
1785 SearcherBuilder::new()
1786 .line_number(false)
1787 .build()
1788 .search_reader(&matcher, SHERLOCK.as_bytes(), &mut sink)
1789 .unwrap();
1790 assert!(sink.has_match());
1791
1792 let matcher = RegexMatcher::new("zzzzz").unwrap();
1793 let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
1794 let mut sink = printer.sink(&matcher);
1795 SearcherBuilder::new()
1796 .line_number(false)
1797 .build()
1798 .search_reader(&matcher, SHERLOCK.as_bytes(), &mut sink)
1799 .unwrap();
1800 assert!(!sink.has_match());
1801 }
1802
1803 #[test]
1804 fn reports_binary() {
1805 use grep_searcher::BinaryDetection;
1806
1807 let matcher = RegexMatcher::new("Sherlock").unwrap();
1808 let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
1809 let mut sink = printer.sink(&matcher);
1810 SearcherBuilder::new()
1811 .line_number(false)
1812 .build()
1813 .search_reader(&matcher, SHERLOCK.as_bytes(), &mut sink)
1814 .unwrap();
1815 assert!(sink.binary_byte_offset().is_none());
1816
1817 let matcher = RegexMatcher::new(".+").unwrap();
1818 let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
1819 let mut sink = printer.sink(&matcher);
1820 SearcherBuilder::new()
1821 .line_number(false)
1822 .binary_detection(BinaryDetection::quit(b'\x00'))
1823 .build()
1824 .search_reader(&matcher, &b"abc\x00"[..], &mut sink)
1825 .unwrap();
1826 assert_eq!(sink.binary_byte_offset(), Some(3));
1827 }
1828
1829 #[test]
1830 fn reports_stats() {
1831 use std::time::Duration;
1832
1833 let matcher = RegexMatcher::new("Sherlock|opposed").unwrap();
1834 let mut printer =
1835 StandardBuilder::new().stats(true).build(NoColor::new(vec![]));
1836 let stats = {
1837 let mut sink = printer.sink(&matcher);
1838 SearcherBuilder::new()
1839 .line_number(false)
1840 .build()
1841 .search_reader(&matcher, SHERLOCK.as_bytes(), &mut sink)
1842 .unwrap();
1843 sink.stats().unwrap().clone()
1844 };
1845 let buf = printer_contents(&mut printer);
1846
1847 assert!(stats.elapsed() > Duration::default());
1848 assert_eq!(stats.searches(), 1);
1849 assert_eq!(stats.searches_with_match(), 1);
1850 assert_eq!(stats.bytes_searched(), SHERLOCK.len() as u64);
1851 assert_eq!(stats.bytes_printed(), buf.len() as u64);
1852 assert_eq!(stats.matched_lines(), 2);
1853 assert_eq!(stats.matches(), 3);
1854 }
1855
1856 #[test]
1857 fn reports_stats_multiple() {
1858 use std::time::Duration;
1859
1860 let matcher = RegexMatcher::new("Sherlock|opposed").unwrap();
1861 let mut printer =
1862 StandardBuilder::new().stats(true).build(NoColor::new(vec![]));
1863 let stats = {
1864 let mut sink = printer.sink(&matcher);
1865 SearcherBuilder::new()
1866 .line_number(false)
1867 .build()
1868 .search_reader(&matcher, SHERLOCK.as_bytes(), &mut sink)
1869 .unwrap();
1870 SearcherBuilder::new()
1871 .line_number(false)
1872 .build()
1873 .search_reader(&matcher, &b"zzzzzzzzzz"[..], &mut sink)
1874 .unwrap();
1875 SearcherBuilder::new()
1876 .line_number(false)
1877 .build()
1878 .search_reader(&matcher, SHERLOCK.as_bytes(), &mut sink)
1879 .unwrap();
1880 sink.stats().unwrap().clone()
1881 };
1882 let buf = printer_contents(&mut printer);
1883
1884 assert!(stats.elapsed() > Duration::default());
1885 assert_eq!(stats.searches(), 3);
1886 assert_eq!(stats.searches_with_match(), 2);
1887 assert_eq!(stats.bytes_searched(), 10 + 2 * SHERLOCK.len() as u64);
1888 assert_eq!(stats.bytes_printed(), buf.len() as u64);
1889 assert_eq!(stats.matched_lines(), 4);
1890 assert_eq!(stats.matches(), 6);
1891 }
1892
1893 #[test]
1894 fn context_break() {
1895 let matcher = RegexMatcher::new("Watson").unwrap();
1896 let mut printer = StandardBuilder::new()
1897 .separator_context(Some(b"--abc--".to_vec()))
1898 .build(NoColor::new(vec![]));
1899 SearcherBuilder::new()
1900 .line_number(false)
1901 .before_context(1)
1902 .after_context(1)
1903 .build()
1904 .search_reader(
1905 &matcher,
1906 SHERLOCK.as_bytes(),
1907 printer.sink(&matcher),
1908 )
1909 .unwrap();
1910
1911 let got = printer_contents(&mut printer);
1912 let expected = "\
1913For the Doctor Watsons of this world, as opposed to the Sherlock
1914Holmeses, success in the province of detective work must always
1915--abc--
1916can extract a clew from a wisp of straw or a flake of cigar ash;
1917but Doctor Watson has to have it taken out for him and dusted,
1918and exhibited clearly, with a label attached.
1919";
1920 assert_eq_printed!(expected, got);
1921 }
1922
1923 #[test]
1924 fn context_break_multiple_no_heading() {
1925 let matcher = RegexMatcher::new("Watson").unwrap();
1926 let mut printer = StandardBuilder::new()
1927 .separator_search(Some(b"--xyz--".to_vec()))
1928 .separator_context(Some(b"--abc--".to_vec()))
1929 .build(NoColor::new(vec![]));
1930
1931 SearcherBuilder::new()
1932 .line_number(false)
1933 .before_context(1)
1934 .after_context(1)
1935 .build()
1936 .search_reader(
1937 &matcher,
1938 SHERLOCK.as_bytes(),
1939 printer.sink(&matcher),
1940 )
1941 .unwrap();
1942 SearcherBuilder::new()
1943 .line_number(false)
1944 .before_context(1)
1945 .after_context(1)
1946 .build()
1947 .search_reader(
1948 &matcher,
1949 SHERLOCK.as_bytes(),
1950 printer.sink(&matcher),
1951 )
1952 .unwrap();
1953
1954 let got = printer_contents(&mut printer);
1955 let expected = "\
1956For the Doctor Watsons of this world, as opposed to the Sherlock
1957Holmeses, success in the province of detective work must always
1958--abc--
1959can extract a clew from a wisp of straw or a flake of cigar ash;
1960but Doctor Watson has to have it taken out for him and dusted,
1961and exhibited clearly, with a label attached.
1962--xyz--
1963For the Doctor Watsons of this world, as opposed to the Sherlock
1964Holmeses, success in the province of detective work must always
1965--abc--
1966can extract a clew from a wisp of straw or a flake of cigar ash;
1967but Doctor Watson has to have it taken out for him and dusted,
1968and exhibited clearly, with a label attached.
1969";
1970 assert_eq_printed!(expected, got);
1971 }
1972
1973 #[test]
1974 fn context_break_multiple_heading() {
1975 let matcher = RegexMatcher::new("Watson").unwrap();
1976 let mut printer = StandardBuilder::new()
1977 .heading(true)
1978 .separator_search(Some(b"--xyz--".to_vec()))
1979 .separator_context(Some(b"--abc--".to_vec()))
1980 .build(NoColor::new(vec![]));
1981
1982 SearcherBuilder::new()
1983 .line_number(false)
1984 .before_context(1)
1985 .after_context(1)
1986 .build()
1987 .search_reader(
1988 &matcher,
1989 SHERLOCK.as_bytes(),
1990 printer.sink(&matcher),
1991 )
1992 .unwrap();
1993 SearcherBuilder::new()
1994 .line_number(false)
1995 .before_context(1)
1996 .after_context(1)
1997 .build()
1998 .search_reader(
1999 &matcher,
2000 SHERLOCK.as_bytes(),
2001 printer.sink(&matcher),
2002 )
2003 .unwrap();
2004
2005 let got = printer_contents(&mut printer);
2006 let expected = "\
2007For the Doctor Watsons of this world, as opposed to the Sherlock
2008Holmeses, success in the province of detective work must always
2009--abc--
2010can extract a clew from a wisp of straw or a flake of cigar ash;
2011but Doctor Watson has to have it taken out for him and dusted,
2012and exhibited clearly, with a label attached.
2013--xyz--
2014For the Doctor Watsons of this world, as opposed to the Sherlock
2015Holmeses, success in the province of detective work must always
2016--abc--
2017can extract a clew from a wisp of straw or a flake of cigar ash;
2018but Doctor Watson has to have it taken out for him and dusted,
2019and exhibited clearly, with a label attached.
2020";
2021 assert_eq_printed!(expected, got);
2022 }
2023
2024 #[test]
2025 fn path() {
2026 let matcher = RegexMatcher::new("Watson").unwrap();
2027 let mut printer =
2028 StandardBuilder::new().path(false).build(NoColor::new(vec![]));
2029 SearcherBuilder::new()
2030 .line_number(true)
2031 .build()
2032 .search_reader(
2033 &matcher,
2034 SHERLOCK.as_bytes(),
2035 printer.sink_with_path(&matcher, "sherlock"),
2036 )
2037 .unwrap();
2038
2039 let got = printer_contents(&mut printer);
2040 let expected = "\
20411:For the Doctor Watsons of this world, as opposed to the Sherlock
20425:but Doctor Watson has to have it taken out for him and dusted,
2043";
2044 assert_eq_printed!(expected, got);
2045 }
2046
2047 #[test]
2048 fn separator_field() {
2049 let matcher = RegexMatcher::new("Watson").unwrap();
2050 let mut printer = StandardBuilder::new()
2051 .separator_field_match(b"!!".to_vec())
2052 .separator_field_context(b"^^".to_vec())
2053 .build(NoColor::new(vec![]));
2054 SearcherBuilder::new()
2055 .line_number(false)
2056 .before_context(1)
2057 .after_context(1)
2058 .build()
2059 .search_reader(
2060 &matcher,
2061 SHERLOCK.as_bytes(),
2062 printer.sink_with_path(&matcher, "sherlock"),
2063 )
2064 .unwrap();
2065
2066 let got = printer_contents(&mut printer);
2067 let expected = "\
2068sherlock!!For the Doctor Watsons of this world, as opposed to the Sherlock
2069sherlock^^Holmeses, success in the province of detective work must always
2070--
2071sherlock^^can extract a clew from a wisp of straw or a flake of cigar ash;
2072sherlock!!but Doctor Watson has to have it taken out for him and dusted,
2073sherlock^^and exhibited clearly, with a label attached.
2074";
2075 assert_eq_printed!(expected, got);
2076 }
2077
2078 #[test]
2079 fn separator_path() {
2080 let matcher = RegexMatcher::new("Watson").unwrap();
2081 let mut printer = StandardBuilder::new()
2082 .separator_path(Some(b'Z'))
2083 .build(NoColor::new(vec![]));
2084 SearcherBuilder::new()
2085 .line_number(false)
2086 .build()
2087 .search_reader(
2088 &matcher,
2089 SHERLOCK.as_bytes(),
2090 printer.sink_with_path(&matcher, "books/sherlock"),
2091 )
2092 .unwrap();
2093
2094 let got = printer_contents(&mut printer);
2095 let expected = "\
2096booksZsherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
2097booksZsherlock:but Doctor Watson has to have it taken out for him and dusted,
2098";
2099 assert_eq_printed!(expected, got);
2100 }
2101
2102 #[test]
2103 fn path_terminator() {
2104 let matcher = RegexMatcher::new("Watson").unwrap();
2105 let mut printer = StandardBuilder::new()
2106 .path_terminator(Some(b'Z'))
2107 .build(NoColor::new(vec![]));
2108 SearcherBuilder::new()
2109 .line_number(false)
2110 .build()
2111 .search_reader(
2112 &matcher,
2113 SHERLOCK.as_bytes(),
2114 printer.sink_with_path(&matcher, "books/sherlock"),
2115 )
2116 .unwrap();
2117
2118 let got = printer_contents(&mut printer);
2119 let expected = "\
2120books/sherlockZFor the Doctor Watsons of this world, as opposed to the Sherlock
2121books/sherlockZbut Doctor Watson has to have it taken out for him and dusted,
2122";
2123 assert_eq_printed!(expected, got);
2124 }
2125
2126 #[test]
2127 fn heading() {
2128 let matcher = RegexMatcher::new("Watson").unwrap();
2129 let mut printer =
2130 StandardBuilder::new().heading(true).build(NoColor::new(vec![]));
2131 SearcherBuilder::new()
2132 .line_number(false)
2133 .build()
2134 .search_reader(
2135 &matcher,
2136 SHERLOCK.as_bytes(),
2137 printer.sink_with_path(&matcher, "sherlock"),
2138 )
2139 .unwrap();
2140
2141 let got = printer_contents(&mut printer);
2142 let expected = "\
2143sherlock
2144For the Doctor Watsons of this world, as opposed to the Sherlock
2145but Doctor Watson has to have it taken out for him and dusted,
2146";
2147 assert_eq_printed!(expected, got);
2148 }
2149
2150 #[test]
2151 fn no_heading() {
2152 let matcher = RegexMatcher::new("Watson").unwrap();
2153 let mut printer =
2154 StandardBuilder::new().heading(false).build(NoColor::new(vec![]));
2155 SearcherBuilder::new()
2156 .line_number(false)
2157 .build()
2158 .search_reader(
2159 &matcher,
2160 SHERLOCK.as_bytes(),
2161 printer.sink_with_path(&matcher, "sherlock"),
2162 )
2163 .unwrap();
2164
2165 let got = printer_contents(&mut printer);
2166 let expected = "\
2167sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
2168sherlock:but Doctor Watson has to have it taken out for him and dusted,
2169";
2170 assert_eq_printed!(expected, got);
2171 }
2172
2173 #[test]
2174 fn no_heading_multiple() {
2175 let matcher = RegexMatcher::new("Watson").unwrap();
2176 let mut printer =
2177 StandardBuilder::new().heading(false).build(NoColor::new(vec![]));
2178 SearcherBuilder::new()
2179 .line_number(false)
2180 .build()
2181 .search_reader(
2182 &matcher,
2183 SHERLOCK.as_bytes(),
2184 printer.sink_with_path(&matcher, "sherlock"),
2185 )
2186 .unwrap();
2187
2188 let matcher = RegexMatcher::new("Sherlock").unwrap();
2189 SearcherBuilder::new()
2190 .line_number(false)
2191 .build()
2192 .search_reader(
2193 &matcher,
2194 SHERLOCK.as_bytes(),
2195 printer.sink_with_path(&matcher, "sherlock"),
2196 )
2197 .unwrap();
2198
2199 let got = printer_contents(&mut printer);
2200 let expected = "\
2201sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
2202sherlock:but Doctor Watson has to have it taken out for him and dusted,
2203sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
2204sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
2205";
2206 assert_eq_printed!(expected, got);
2207 }
2208
2209 #[test]
2210 fn heading_multiple() {
2211 let matcher = RegexMatcher::new("Watson").unwrap();
2212 let mut printer =
2213 StandardBuilder::new().heading(true).build(NoColor::new(vec![]));
2214 SearcherBuilder::new()
2215 .line_number(false)
2216 .build()
2217 .search_reader(
2218 &matcher,
2219 SHERLOCK.as_bytes(),
2220 printer.sink_with_path(&matcher, "sherlock"),
2221 )
2222 .unwrap();
2223
2224 let matcher = RegexMatcher::new("Sherlock").unwrap();
2225 SearcherBuilder::new()
2226 .line_number(false)
2227 .build()
2228 .search_reader(
2229 &matcher,
2230 SHERLOCK.as_bytes(),
2231 printer.sink_with_path(&matcher, "sherlock"),
2232 )
2233 .unwrap();
2234
2235 let got = printer_contents(&mut printer);
2236 let expected = "\
2237sherlock
2238For the Doctor Watsons of this world, as opposed to the Sherlock
2239but Doctor Watson has to have it taken out for him and dusted,
2240sherlock
2241For the Doctor Watsons of this world, as opposed to the Sherlock
2242be, to a very large extent, the result of luck. Sherlock Holmes
2243";
2244 assert_eq_printed!(expected, got);
2245 }
2246
2247 #[test]
2248 fn trim_ascii() {
2249 let matcher = RegexMatcher::new("Watson").unwrap();
2250 let mut printer = StandardBuilder::new()
2251 .trim_ascii(true)
2252 .build(NoColor::new(vec![]));
2253 SearcherBuilder::new()
2254 .line_number(false)
2255 .build()
2256 .search_reader(
2257 &matcher,
2258 " Watson".as_bytes(),
2259 printer.sink(&matcher),
2260 )
2261 .unwrap();
2262
2263 let got = printer_contents(&mut printer);
2264 let expected = "\
2265Watson
2266";
2267 assert_eq_printed!(expected, got);
2268 }
2269
2270 #[test]
2271 fn trim_ascii_multi_line() {
2272 let matcher = RegexMatcher::new("(?s:.{0})Watson").unwrap();
2273 let mut printer = StandardBuilder::new()
2274 .trim_ascii(true)
2275 .stats(true)
2276 .build(NoColor::new(vec![]));
2277 SearcherBuilder::new()
2278 .line_number(false)
2279 .multi_line(true)
2280 .build()
2281 .search_reader(
2282 &matcher,
2283 " Watson".as_bytes(),
2284 printer.sink(&matcher),
2285 )
2286 .unwrap();
2287
2288 let got = printer_contents(&mut printer);
2289 let expected = "\
2290Watson
2291";
2292 assert_eq_printed!(expected, got);
2293 }
2294
2295 #[test]
2296 fn trim_ascii_with_line_term() {
2297 let matcher = RegexMatcher::new("Watson").unwrap();
2298 let mut printer = StandardBuilder::new()
2299 .trim_ascii(true)
2300 .build(NoColor::new(vec![]));
2301 SearcherBuilder::new()
2302 .line_number(true)
2303 .before_context(1)
2304 .build()
2305 .search_reader(
2306 &matcher,
2307 "\n Watson".as_bytes(),
2308 printer.sink(&matcher),
2309 )
2310 .unwrap();
2311
2312 let got = printer_contents(&mut printer);
2313 let expected = "\
23141-
23152:Watson
2316";
2317 assert_eq_printed!(expected, got);
2318 }
2319
2320 #[test]
2321 fn line_number() {
2322 let matcher = RegexMatcher::new("Watson").unwrap();
2323 let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
2324 SearcherBuilder::new()
2325 .line_number(true)
2326 .build()
2327 .search_reader(
2328 &matcher,
2329 SHERLOCK.as_bytes(),
2330 printer.sink(&matcher),
2331 )
2332 .unwrap();
2333
2334 let got = printer_contents(&mut printer);
2335 let expected = "\
23361:For the Doctor Watsons of this world, as opposed to the Sherlock
23375:but Doctor Watson has to have it taken out for him and dusted,
2338";
2339 assert_eq_printed!(expected, got);
2340 }
2341
2342 #[test]
2343 fn line_number_multi_line() {
2344 let matcher = RegexMatcher::new("(?s)Watson.+Watson").unwrap();
2345 let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
2346 SearcherBuilder::new()
2347 .line_number(true)
2348 .multi_line(true)
2349 .build()
2350 .search_reader(
2351 &matcher,
2352 SHERLOCK.as_bytes(),
2353 printer.sink(&matcher),
2354 )
2355 .unwrap();
2356
2357 let got = printer_contents(&mut printer);
2358 let expected = "\
23591:For the Doctor Watsons of this world, as opposed to the Sherlock
23602:Holmeses, success in the province of detective work must always
23613:be, to a very large extent, the result of luck. Sherlock Holmes
23624:can extract a clew from a wisp of straw or a flake of cigar ash;
23635:but Doctor Watson has to have it taken out for him and dusted,
2364";
2365 assert_eq_printed!(expected, got);
2366 }
2367
2368 #[test]
2369 fn column_number() {
2370 let matcher = RegexMatcher::new("Watson").unwrap();
2371 let mut printer =
2372 StandardBuilder::new().column(true).build(NoColor::new(vec![]));
2373 SearcherBuilder::new()
2374 .line_number(false)
2375 .build()
2376 .search_reader(
2377 &matcher,
2378 SHERLOCK.as_bytes(),
2379 printer.sink(&matcher),
2380 )
2381 .unwrap();
2382
2383 let got = printer_contents(&mut printer);
2384 let expected = "\
238516:For the Doctor Watsons of this world, as opposed to the Sherlock
238612:but Doctor Watson has to have it taken out for him and dusted,
2387";
2388 assert_eq_printed!(expected, got);
2389 }
2390
2391 #[test]
2392 fn column_number_multi_line() {
2393 let matcher = RegexMatcher::new("(?s)Watson.+Watson").unwrap();
2394 let mut printer =
2395 StandardBuilder::new().column(true).build(NoColor::new(vec![]));
2396 SearcherBuilder::new()
2397 .line_number(false)
2398 .multi_line(true)
2399 .build()
2400 .search_reader(
2401 &matcher,
2402 SHERLOCK.as_bytes(),
2403 printer.sink(&matcher),
2404 )
2405 .unwrap();
2406
2407 let got = printer_contents(&mut printer);
2408 let expected = "\
240916:For the Doctor Watsons of this world, as opposed to the Sherlock
241016:Holmeses, success in the province of detective work must always
241116:be, to a very large extent, the result of luck. Sherlock Holmes
241216:can extract a clew from a wisp of straw or a flake of cigar ash;
241316:but Doctor Watson has to have it taken out for him and dusted,
2414";
2415 assert_eq_printed!(expected, got);
2416 }
2417
2418 #[test]
2419 fn byte_offset() {
2420 let matcher = RegexMatcher::new("Watson").unwrap();
2421 let mut printer = StandardBuilder::new()
2422 .byte_offset(true)
2423 .build(NoColor::new(vec![]));
2424 SearcherBuilder::new()
2425 .line_number(false)
2426 .build()
2427 .search_reader(
2428 &matcher,
2429 SHERLOCK.as_bytes(),
2430 printer.sink(&matcher),
2431 )
2432 .unwrap();
2433
2434 let got = printer_contents(&mut printer);
2435 let expected = "\
24360:For the Doctor Watsons of this world, as opposed to the Sherlock
2437258:but Doctor Watson has to have it taken out for him and dusted,
2438";
2439 assert_eq_printed!(expected, got);
2440 }
2441
2442 #[test]
2443 fn byte_offset_multi_line() {
2444 let matcher = RegexMatcher::new("(?s)Watson.+Watson").unwrap();
2445 let mut printer = StandardBuilder::new()
2446 .byte_offset(true)
2447 .build(NoColor::new(vec![]));
2448 SearcherBuilder::new()
2449 .line_number(false)
2450 .multi_line(true)
2451 .build()
2452 .search_reader(
2453 &matcher,
2454 SHERLOCK.as_bytes(),
2455 printer.sink(&matcher),
2456 )
2457 .unwrap();
2458
2459 let got = printer_contents(&mut printer);
2460 let expected = "\
24610:For the Doctor Watsons of this world, as opposed to the Sherlock
246265:Holmeses, success in the province of detective work must always
2463129:be, to a very large extent, the result of luck. Sherlock Holmes
2464193:can extract a clew from a wisp of straw or a flake of cigar ash;
2465258:but Doctor Watson has to have it taken out for him and dusted,
2466";
2467 assert_eq_printed!(expected, got);
2468 }
2469
2470 #[test]
2471 fn max_columns() {
2472 let matcher = RegexMatcher::new("ash|dusted").unwrap();
2473 let mut printer = StandardBuilder::new()
2474 .max_columns(Some(63))
2475 .build(NoColor::new(vec![]));
2476 SearcherBuilder::new()
2477 .line_number(false)
2478 .build()
2479 .search_reader(
2480 &matcher,
2481 SHERLOCK.as_bytes(),
2482 printer.sink(&matcher),
2483 )
2484 .unwrap();
2485
2486 let got = printer_contents(&mut printer);
2487 let expected = "\
2488[Omitted long matching line]
2489but Doctor Watson has to have it taken out for him and dusted,
2490";
2491 assert_eq_printed!(expected, got);
2492 }
2493
2494 #[test]
2495 fn max_columns_preview() {
2496 let matcher = RegexMatcher::new("exhibited|dusted").unwrap();
2497 let mut printer = StandardBuilder::new()
2498 .max_columns(Some(46))
2499 .max_columns_preview(true)
2500 .build(NoColor::new(vec![]));
2501 SearcherBuilder::new()
2502 .line_number(false)
2503 .build()
2504 .search_reader(
2505 &matcher,
2506 SHERLOCK.as_bytes(),
2507 printer.sink(&matcher),
2508 )
2509 .unwrap();
2510
2511 let got = printer_contents(&mut printer);
2512 let expected = "\
2513but Doctor Watson has to have it taken out for [... omitted end of long line]
2514and exhibited clearly, with a label attached.
2515";
2516 assert_eq_printed!(expected, got);
2517 }
2518
2519 #[test]
2520 fn max_columns_with_count() {
2521 let matcher = RegexMatcher::new("cigar|ash|dusted").unwrap();
2522 let mut printer = StandardBuilder::new()
2523 .stats(true)
2524 .max_columns(Some(63))
2525 .build(NoColor::new(vec![]));
2526 SearcherBuilder::new()
2527 .line_number(false)
2528 .build()
2529 .search_reader(
2530 &matcher,
2531 SHERLOCK.as_bytes(),
2532 printer.sink(&matcher),
2533 )
2534 .unwrap();
2535
2536 let got = printer_contents(&mut printer);
2537 let expected = "\
2538[Omitted long line with 2 matches]
2539but Doctor Watson has to have it taken out for him and dusted,
2540";
2541 assert_eq_printed!(expected, got);
2542 }
2543
2544 #[test]
2545 fn max_columns_with_count_preview_no_match() {
2546 let matcher = RegexMatcher::new("exhibited|has to have it").unwrap();
2547 let mut printer = StandardBuilder::new()
2548 .stats(true)
2549 .max_columns(Some(46))
2550 .max_columns_preview(true)
2551 .build(NoColor::new(vec![]));
2552 SearcherBuilder::new()
2553 .line_number(false)
2554 .build()
2555 .search_reader(
2556 &matcher,
2557 SHERLOCK.as_bytes(),
2558 printer.sink(&matcher),
2559 )
2560 .unwrap();
2561
2562 let got = printer_contents(&mut printer);
2563 let expected = "\
2564but Doctor Watson has to have it taken out for [... 0 more matches]
2565and exhibited clearly, with a label attached.
2566";
2567 assert_eq_printed!(expected, got);
2568 }
2569
2570 #[test]
2571 fn max_columns_with_count_preview_one_match() {
2572 let matcher = RegexMatcher::new("exhibited|dusted").unwrap();
2573 let mut printer = StandardBuilder::new()
2574 .stats(true)
2575 .max_columns(Some(46))
2576 .max_columns_preview(true)
2577 .build(NoColor::new(vec![]));
2578 SearcherBuilder::new()
2579 .line_number(false)
2580 .build()
2581 .search_reader(
2582 &matcher,
2583 SHERLOCK.as_bytes(),
2584 printer.sink(&matcher),
2585 )
2586 .unwrap();
2587
2588 let got = printer_contents(&mut printer);
2589 let expected = "\
2590but Doctor Watson has to have it taken out for [... 1 more match]
2591and exhibited clearly, with a label attached.
2592";
2593 assert_eq_printed!(expected, got);
2594 }
2595
2596 #[test]
2597 fn max_columns_with_count_preview_two_matches() {
2598 let matcher =
2599 RegexMatcher::new("exhibited|dusted|has to have it").unwrap();
2600 let mut printer = StandardBuilder::new()
2601 .stats(true)
2602 .max_columns(Some(46))
2603 .max_columns_preview(true)
2604 .build(NoColor::new(vec![]));
2605 SearcherBuilder::new()
2606 .line_number(false)
2607 .build()
2608 .search_reader(
2609 &matcher,
2610 SHERLOCK.as_bytes(),
2611 printer.sink(&matcher),
2612 )
2613 .unwrap();
2614
2615 let got = printer_contents(&mut printer);
2616 let expected = "\
2617but Doctor Watson has to have it taken out for [... 1 more match]
2618and exhibited clearly, with a label attached.
2619";
2620 assert_eq_printed!(expected, got);
2621 }
2622
2623 #[test]
2624 fn max_columns_multi_line() {
2625 let matcher = RegexMatcher::new("(?s)ash.+dusted").unwrap();
2626 let mut printer = StandardBuilder::new()
2627 .max_columns(Some(63))
2628 .build(NoColor::new(vec![]));
2629 SearcherBuilder::new()
2630 .line_number(false)
2631 .multi_line(true)
2632 .build()
2633 .search_reader(
2634 &matcher,
2635 SHERLOCK.as_bytes(),
2636 printer.sink(&matcher),
2637 )
2638 .unwrap();
2639
2640 let got = printer_contents(&mut printer);
2641 let expected = "\
2642[Omitted long matching line]
2643but Doctor Watson has to have it taken out for him and dusted,
2644";
2645 assert_eq_printed!(expected, got);
2646 }
2647
2648 #[test]
2649 fn max_columns_multi_line_preview() {
2650 let matcher =
2651 RegexMatcher::new("(?s)clew|cigar ash.+have it|exhibited")
2652 .unwrap();
2653 let mut printer = StandardBuilder::new()
2654 .stats(true)
2655 .max_columns(Some(46))
2656 .max_columns_preview(true)
2657 .build(NoColor::new(vec![]));
2658 SearcherBuilder::new()
2659 .line_number(false)
2660 .multi_line(true)
2661 .build()
2662 .search_reader(
2663 &matcher,
2664 SHERLOCK.as_bytes(),
2665 printer.sink(&matcher),
2666 )
2667 .unwrap();
2668
2669 let got = printer_contents(&mut printer);
2670 let expected = "\
2671can extract a clew from a wisp of straw or a f [... 1 more match]
2672but Doctor Watson has to have it taken out for [... 0 more matches]
2673and exhibited clearly, with a label attached.
2674";
2675 assert_eq_printed!(expected, got);
2676 }
2677
2678 #[test]
2679 fn max_matches() {
2680 let matcher = RegexMatcher::new("Sherlock").unwrap();
2681 let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
2682 SearcherBuilder::new()
2683 .line_number(false)
2684 .max_matches(Some(1))
2685 .build()
2686 .search_reader(
2687 &matcher,
2688 SHERLOCK.as_bytes(),
2689 printer.sink(&matcher),
2690 )
2691 .unwrap();
2692
2693 let got = printer_contents(&mut printer);
2694 let expected = "\
2695For the Doctor Watsons of this world, as opposed to the Sherlock
2696";
2697 assert_eq_printed!(expected, got);
2698 }
2699
2700 #[test]
2701 fn max_matches_context() {
2702 let matcher = RegexMatcher::new("Doctor Watsons").unwrap();
2704 let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
2705 SearcherBuilder::new()
2706 .max_matches(Some(1))
2707 .line_number(false)
2708 .after_context(1)
2709 .build()
2710 .search_reader(
2711 &matcher,
2712 SHERLOCK.as_bytes(),
2713 printer.sink(&matcher),
2714 )
2715 .unwrap();
2716
2717 let got = printer_contents(&mut printer);
2718 let expected = "\
2719For the Doctor Watsons of this world, as opposed to the Sherlock
2720Holmeses, success in the province of detective work must always
2721";
2722 assert_eq_printed!(expected, got);
2723
2724 let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
2726 SearcherBuilder::new()
2727 .max_matches(Some(1))
2728 .line_number(false)
2729 .after_context(4)
2730 .build()
2731 .search_reader(
2732 &matcher,
2733 SHERLOCK.as_bytes(),
2734 printer.sink(&matcher),
2735 )
2736 .unwrap();
2737
2738 let got = printer_contents(&mut printer);
2739 let expected = "\
2740For the Doctor Watsons of this world, as opposed to the Sherlock
2741Holmeses, success in the province of detective work must always
2742be, to a very large extent, the result of luck. Sherlock Holmes
2743can extract a clew from a wisp of straw or a flake of cigar ash;
2744but Doctor Watson has to have it taken out for him and dusted,
2745";
2746 assert_eq_printed!(expected, got);
2747
2748 let matcher = RegexMatcher::new("Doctor Watsons|but Doctor").unwrap();
2750 let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
2751 SearcherBuilder::new()
2752 .max_matches(Some(2))
2753 .line_number(false)
2754 .after_context(1)
2755 .build()
2756 .search_reader(
2757 &matcher,
2758 SHERLOCK.as_bytes(),
2759 printer.sink(&matcher),
2760 )
2761 .unwrap();
2762
2763 let got = printer_contents(&mut printer);
2764 let expected = "\
2765For the Doctor Watsons of this world, as opposed to the Sherlock
2766Holmeses, success in the province of detective work must always
2767--
2768but Doctor Watson has to have it taken out for him and dusted,
2769and exhibited clearly, with a label attached.
2770";
2771 assert_eq_printed!(expected, got);
2772
2773 let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
2775 SearcherBuilder::new()
2776 .max_matches(Some(2))
2777 .line_number(false)
2778 .after_context(4)
2779 .build()
2780 .search_reader(
2781 &matcher,
2782 SHERLOCK.as_bytes(),
2783 printer.sink(&matcher),
2784 )
2785 .unwrap();
2786
2787 let got = printer_contents(&mut printer);
2788 let expected = "\
2789For the Doctor Watsons of this world, as opposed to the Sherlock
2790Holmeses, success in the province of detective work must always
2791be, to a very large extent, the result of luck. Sherlock Holmes
2792can extract a clew from a wisp of straw or a flake of cigar ash;
2793but Doctor Watson has to have it taken out for him and dusted,
2794and exhibited clearly, with a label attached.
2795";
2796 assert_eq_printed!(expected, got);
2797 }
2798
2799 #[test]
2800 fn max_matches_context_invert() {
2801 let matcher =
2803 RegexMatcher::new("success|extent|clew|dusted|exhibited").unwrap();
2804 let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
2805 SearcherBuilder::new()
2806 .invert_match(true)
2807 .max_matches(Some(1))
2808 .line_number(false)
2809 .after_context(1)
2810 .build()
2811 .search_reader(
2812 &matcher,
2813 SHERLOCK.as_bytes(),
2814 printer.sink(&matcher),
2815 )
2816 .unwrap();
2817
2818 let got = printer_contents(&mut printer);
2819 let expected = "\
2820For the Doctor Watsons of this world, as opposed to the Sherlock
2821Holmeses, success in the province of detective work must always
2822";
2823 assert_eq_printed!(expected, got);
2824
2825 let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
2827 SearcherBuilder::new()
2828 .invert_match(true)
2829 .max_matches(Some(1))
2830 .line_number(false)
2831 .after_context(4)
2832 .build()
2833 .search_reader(
2834 &matcher,
2835 SHERLOCK.as_bytes(),
2836 printer.sink(&matcher),
2837 )
2838 .unwrap();
2839
2840 let got = printer_contents(&mut printer);
2841 let expected = "\
2842For the Doctor Watsons of this world, as opposed to the Sherlock
2843Holmeses, success in the province of detective work must always
2844be, to a very large extent, the result of luck. Sherlock Holmes
2845can extract a clew from a wisp of straw or a flake of cigar ash;
2846but Doctor Watson has to have it taken out for him and dusted,
2847";
2848 assert_eq_printed!(expected, got);
2849
2850 let matcher =
2852 RegexMatcher::new("success|extent|clew|exhibited").unwrap();
2853 let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
2854 SearcherBuilder::new()
2855 .invert_match(true)
2856 .max_matches(Some(2))
2857 .line_number(false)
2858 .after_context(1)
2859 .build()
2860 .search_reader(
2861 &matcher,
2862 SHERLOCK.as_bytes(),
2863 printer.sink(&matcher),
2864 )
2865 .unwrap();
2866
2867 let got = printer_contents(&mut printer);
2868 let expected = "\
2869For the Doctor Watsons of this world, as opposed to the Sherlock
2870Holmeses, success in the province of detective work must always
2871--
2872but Doctor Watson has to have it taken out for him and dusted,
2873and exhibited clearly, with a label attached.
2874";
2875 assert_eq_printed!(expected, got);
2876
2877 let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
2879 SearcherBuilder::new()
2880 .invert_match(true)
2881 .max_matches(Some(2))
2882 .line_number(false)
2883 .after_context(4)
2884 .build()
2885 .search_reader(
2886 &matcher,
2887 SHERLOCK.as_bytes(),
2888 printer.sink(&matcher),
2889 )
2890 .unwrap();
2891
2892 let got = printer_contents(&mut printer);
2893 let expected = "\
2894For the Doctor Watsons of this world, as opposed to the Sherlock
2895Holmeses, success in the province of detective work must always
2896be, to a very large extent, the result of luck. Sherlock Holmes
2897can extract a clew from a wisp of straw or a flake of cigar ash;
2898but Doctor Watson has to have it taken out for him and dusted,
2899and exhibited clearly, with a label attached.
2900";
2901 assert_eq_printed!(expected, got);
2902 }
2903
2904 #[test]
2905 fn max_matches_multi_line1() {
2906 let matcher = RegexMatcher::new("(?s:.{0})Sherlock").unwrap();
2907 let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
2908 SearcherBuilder::new()
2909 .line_number(false)
2910 .multi_line(true)
2911 .max_matches(Some(1))
2912 .build()
2913 .search_reader(
2914 &matcher,
2915 SHERLOCK.as_bytes(),
2916 printer.sink(&matcher),
2917 )
2918 .unwrap();
2919
2920 let got = printer_contents(&mut printer);
2921 let expected = "\
2922For the Doctor Watsons of this world, as opposed to the Sherlock
2923";
2924 assert_eq_printed!(expected, got);
2925 }
2926
2927 #[test]
2928 fn max_matches_multi_line2() {
2929 let matcher =
2930 RegexMatcher::new(r"(?s)Watson.+?(Holmeses|clearly)").unwrap();
2931 let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
2932 SearcherBuilder::new()
2933 .line_number(false)
2934 .multi_line(true)
2935 .max_matches(Some(1))
2936 .build()
2937 .search_reader(
2938 &matcher,
2939 SHERLOCK.as_bytes(),
2940 printer.sink(&matcher),
2941 )
2942 .unwrap();
2943
2944 let got = printer_contents(&mut printer);
2945 let expected = "\
2946For the Doctor Watsons of this world, as opposed to the Sherlock
2947Holmeses, success in the province of detective work must always
2948";
2949 assert_eq_printed!(expected, got);
2950 }
2951
2952 #[test]
2953 fn max_matches_multi_line3() {
2954 let matcher = RegexMatcher::new(r"line 2\nline 3").unwrap();
2955 let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
2956 SearcherBuilder::new()
2957 .line_number(false)
2958 .multi_line(true)
2959 .max_matches(Some(1))
2960 .build()
2961 .search_reader(
2962 &matcher,
2963 "line 2\nline 3 x\nline 2\nline 3\n".as_bytes(),
2964 printer.sink(&matcher),
2965 )
2966 .unwrap();
2967
2968 let got = printer_contents(&mut printer);
2969 let expected = "\
2970line 2
2971line 3 x
2972";
2973 assert_eq_printed!(expected, got);
2974 }
2975
2976 #[test]
2977 fn max_matches_multi_line4() {
2978 let matcher =
2979 RegexMatcher::new(r"line 2\nline 3|x\nline 2\n").unwrap();
2980 let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
2981 SearcherBuilder::new()
2982 .line_number(false)
2983 .multi_line(true)
2984 .max_matches(Some(1))
2985 .build()
2986 .search_reader(
2987 &matcher,
2988 "line 2\nline 3 x\nline 2\nline 3 x\n".as_bytes(),
2989 printer.sink(&matcher),
2990 )
2991 .unwrap();
2992
2993 let got = printer_contents(&mut printer);
2994 let expected = "\
2995line 2
2996line 3 x
2997";
2998 assert_eq_printed!(expected, got);
2999 }
3000
3001 #[test]
3002 fn only_matching() {
3003 let matcher = RegexMatcher::new("Doctor Watsons|Sherlock").unwrap();
3004 let mut printer = StandardBuilder::new()
3005 .only_matching(true)
3006 .column(true)
3007 .build(NoColor::new(vec![]));
3008 SearcherBuilder::new()
3009 .line_number(true)
3010 .build()
3011 .search_reader(
3012 &matcher,
3013 SHERLOCK.as_bytes(),
3014 printer.sink(&matcher),
3015 )
3016 .unwrap();
3017
3018 let got = printer_contents(&mut printer);
3019 let expected = "\
30201:9:Doctor Watsons
30211:57:Sherlock
30223:49:Sherlock
3023";
3024 assert_eq_printed!(expected, got);
3025 }
3026
3027 #[test]
3028 fn only_matching_multi_line1() {
3029 let matcher =
3030 RegexMatcher::new(r"(?s:.{0})(Doctor Watsons|Sherlock)").unwrap();
3031 let mut printer = StandardBuilder::new()
3032 .only_matching(true)
3033 .column(true)
3034 .build(NoColor::new(vec![]));
3035 SearcherBuilder::new()
3036 .multi_line(true)
3037 .line_number(true)
3038 .build()
3039 .search_reader(
3040 &matcher,
3041 SHERLOCK.as_bytes(),
3042 printer.sink(&matcher),
3043 )
3044 .unwrap();
3045
3046 let got = printer_contents(&mut printer);
3047 let expected = "\
30481:9:Doctor Watsons
30491:57:Sherlock
30503:49:Sherlock
3051";
3052 assert_eq_printed!(expected, got);
3053 }
3054
3055 #[test]
3056 fn only_matching_multi_line2() {
3057 let matcher =
3058 RegexMatcher::new(r"(?s)Watson.+?(Holmeses|clearly)").unwrap();
3059 let mut printer = StandardBuilder::new()
3060 .only_matching(true)
3061 .column(true)
3062 .build(NoColor::new(vec![]));
3063 SearcherBuilder::new()
3064 .multi_line(true)
3065 .line_number(true)
3066 .build()
3067 .search_reader(
3068 &matcher,
3069 SHERLOCK.as_bytes(),
3070 printer.sink(&matcher),
3071 )
3072 .unwrap();
3073
3074 let got = printer_contents(&mut printer);
3075 let expected = "\
30761:16:Watsons of this world, as opposed to the Sherlock
30772:16:Holmeses
30785:12:Watson has to have it taken out for him and dusted,
30796:12:and exhibited clearly
3080";
3081 assert_eq_printed!(expected, got);
3082 }
3083
3084 #[test]
3085 fn only_matching_max_columns() {
3086 let matcher = RegexMatcher::new("Doctor Watsons|Sherlock").unwrap();
3087 let mut printer = StandardBuilder::new()
3088 .only_matching(true)
3089 .max_columns(Some(10))
3090 .column(true)
3091 .build(NoColor::new(vec![]));
3092 SearcherBuilder::new()
3093 .line_number(true)
3094 .build()
3095 .search_reader(
3096 &matcher,
3097 SHERLOCK.as_bytes(),
3098 printer.sink(&matcher),
3099 )
3100 .unwrap();
3101
3102 let got = printer_contents(&mut printer);
3103 let expected = "\
31041:9:[Omitted long matching line]
31051:57:Sherlock
31063:49:Sherlock
3107";
3108 assert_eq_printed!(expected, got);
3109 }
3110
3111 #[test]
3112 fn only_matching_max_columns_preview() {
3113 let matcher = RegexMatcher::new("Doctor Watsons|Sherlock").unwrap();
3114 let mut printer = StandardBuilder::new()
3115 .only_matching(true)
3116 .max_columns(Some(10))
3117 .max_columns_preview(true)
3118 .column(true)
3119 .build(NoColor::new(vec![]));
3120 SearcherBuilder::new()
3121 .line_number(true)
3122 .build()
3123 .search_reader(
3124 &matcher,
3125 SHERLOCK.as_bytes(),
3126 printer.sink(&matcher),
3127 )
3128 .unwrap();
3129
3130 let got = printer_contents(&mut printer);
3131 let expected = "\
31321:9:Doctor Wat [... 0 more matches]
31331:57:Sherlock
31343:49:Sherlock
3135";
3136 assert_eq_printed!(expected, got);
3137 }
3138
3139 #[test]
3140 fn only_matching_max_columns_multi_line1() {
3141 let matcher =
3146 RegexMatcher::new(r"(?s:.{0})(Doctor Watsons|Sherlock)").unwrap();
3147 let mut printer = StandardBuilder::new()
3148 .only_matching(true)
3149 .max_columns(Some(10))
3150 .column(true)
3151 .build(NoColor::new(vec![]));
3152 SearcherBuilder::new()
3153 .multi_line(true)
3154 .line_number(true)
3155 .build()
3156 .search_reader(
3157 &matcher,
3158 SHERLOCK.as_bytes(),
3159 printer.sink(&matcher),
3160 )
3161 .unwrap();
3162
3163 let got = printer_contents(&mut printer);
3164 let expected = "\
31651:9:[Omitted long matching line]
31661:57:Sherlock
31673:49:Sherlock
3168";
3169 assert_eq_printed!(expected, got);
3170 }
3171
3172 #[test]
3173 fn only_matching_max_columns_preview_multi_line1() {
3174 let matcher =
3179 RegexMatcher::new(r"(?s:.{0})(Doctor Watsons|Sherlock)").unwrap();
3180 let mut printer = StandardBuilder::new()
3181 .only_matching(true)
3182 .max_columns(Some(10))
3183 .max_columns_preview(true)
3184 .column(true)
3185 .build(NoColor::new(vec![]));
3186 SearcherBuilder::new()
3187 .multi_line(true)
3188 .line_number(true)
3189 .build()
3190 .search_reader(
3191 &matcher,
3192 SHERLOCK.as_bytes(),
3193 printer.sink(&matcher),
3194 )
3195 .unwrap();
3196
3197 let got = printer_contents(&mut printer);
3198 let expected = "\
31991:9:Doctor Wat [... 0 more matches]
32001:57:Sherlock
32013:49:Sherlock
3202";
3203 assert_eq_printed!(expected, got);
3204 }
3205
3206 #[test]
3207 fn only_matching_max_columns_multi_line2() {
3208 let matcher =
3209 RegexMatcher::new(r"(?s)Watson.+?(Holmeses|clearly)").unwrap();
3210 let mut printer = StandardBuilder::new()
3211 .only_matching(true)
3212 .max_columns(Some(50))
3213 .column(true)
3214 .build(NoColor::new(vec![]));
3215 SearcherBuilder::new()
3216 .multi_line(true)
3217 .line_number(true)
3218 .build()
3219 .search_reader(
3220 &matcher,
3221 SHERLOCK.as_bytes(),
3222 printer.sink(&matcher),
3223 )
3224 .unwrap();
3225
3226 let got = printer_contents(&mut printer);
3227 let expected = "\
32281:16:Watsons of this world, as opposed to the Sherlock
32292:16:Holmeses
32305:12:[Omitted long matching line]
32316:12:and exhibited clearly
3232";
3233 assert_eq_printed!(expected, got);
3234 }
3235
3236 #[test]
3237 fn only_matching_max_columns_preview_multi_line2() {
3238 let matcher =
3239 RegexMatcher::new(r"(?s)Watson.+?(Holmeses|clearly)").unwrap();
3240 let mut printer = StandardBuilder::new()
3241 .only_matching(true)
3242 .max_columns(Some(50))
3243 .max_columns_preview(true)
3244 .column(true)
3245 .build(NoColor::new(vec![]));
3246 SearcherBuilder::new()
3247 .multi_line(true)
3248 .line_number(true)
3249 .build()
3250 .search_reader(
3251 &matcher,
3252 SHERLOCK.as_bytes(),
3253 printer.sink(&matcher),
3254 )
3255 .unwrap();
3256
3257 let got = printer_contents(&mut printer);
3258 let expected = "\
32591:16:Watsons of this world, as opposed to the Sherlock
32602:16:Holmeses
32615:12:Watson has to have it taken out for him and dusted [... 0 more matches]
32626:12:and exhibited clearly
3263";
3264 assert_eq_printed!(expected, got);
3265 }
3266
3267 #[test]
3268 fn per_match() {
3269 let matcher = RegexMatcher::new("Doctor Watsons|Sherlock").unwrap();
3270 let mut printer = StandardBuilder::new()
3271 .per_match(true)
3272 .column(true)
3273 .build(NoColor::new(vec![]));
3274 SearcherBuilder::new()
3275 .line_number(true)
3276 .build()
3277 .search_reader(
3278 &matcher,
3279 SHERLOCK.as_bytes(),
3280 printer.sink(&matcher),
3281 )
3282 .unwrap();
3283
3284 let got = printer_contents(&mut printer);
3285 let expected = "\
32861:9:For the Doctor Watsons of this world, as opposed to the Sherlock
32871:57:For the Doctor Watsons of this world, as opposed to the Sherlock
32883:49:be, to a very large extent, the result of luck. Sherlock Holmes
3289";
3290 assert_eq_printed!(expected, got);
3291 }
3292
3293 #[test]
3294 fn per_match_multi_line1() {
3295 let matcher =
3296 RegexMatcher::new(r"(?s:.{0})(Doctor Watsons|Sherlock)").unwrap();
3297 let mut printer = StandardBuilder::new()
3298 .per_match(true)
3299 .column(true)
3300 .build(NoColor::new(vec![]));
3301 SearcherBuilder::new()
3302 .multi_line(true)
3303 .line_number(true)
3304 .build()
3305 .search_reader(
3306 &matcher,
3307 SHERLOCK.as_bytes(),
3308 printer.sink(&matcher),
3309 )
3310 .unwrap();
3311
3312 let got = printer_contents(&mut printer);
3313 let expected = "\
33141:9:For the Doctor Watsons of this world, as opposed to the Sherlock
33151:57:For the Doctor Watsons of this world, as opposed to the Sherlock
33163:49:be, to a very large extent, the result of luck. Sherlock Holmes
3317";
3318 assert_eq_printed!(expected, got);
3319 }
3320
3321 #[test]
3322 fn per_match_multi_line2() {
3323 let matcher =
3324 RegexMatcher::new(r"(?s)Watson.+?(Holmeses|clearly)").unwrap();
3325 let mut printer = StandardBuilder::new()
3326 .per_match(true)
3327 .column(true)
3328 .build(NoColor::new(vec![]));
3329 SearcherBuilder::new()
3330 .multi_line(true)
3331 .line_number(true)
3332 .build()
3333 .search_reader(
3334 &matcher,
3335 SHERLOCK.as_bytes(),
3336 printer.sink(&matcher),
3337 )
3338 .unwrap();
3339
3340 let got = printer_contents(&mut printer);
3341 let expected = "\
33421:16:For the Doctor Watsons of this world, as opposed to the Sherlock
33432:1:Holmeses, success in the province of detective work must always
33445:12:but Doctor Watson has to have it taken out for him and dusted,
33456:1:and exhibited clearly, with a label attached.
3346";
3347 assert_eq_printed!(expected, got);
3348 }
3349
3350 #[test]
3351 fn per_match_multi_line3() {
3352 let matcher =
3353 RegexMatcher::new(r"(?s)Watson.+?Holmeses|always.+?be").unwrap();
3354 let mut printer = StandardBuilder::new()
3355 .per_match(true)
3356 .column(true)
3357 .build(NoColor::new(vec![]));
3358 SearcherBuilder::new()
3359 .multi_line(true)
3360 .line_number(true)
3361 .build()
3362 .search_reader(
3363 &matcher,
3364 SHERLOCK.as_bytes(),
3365 printer.sink(&matcher),
3366 )
3367 .unwrap();
3368
3369 let got = printer_contents(&mut printer);
3370 let expected = "\
33711:16:For the Doctor Watsons of this world, as opposed to the Sherlock
33722:1:Holmeses, success in the province of detective work must always
33732:58:Holmeses, success in the province of detective work must always
33743:1:be, to a very large extent, the result of luck. Sherlock Holmes
3375";
3376 assert_eq_printed!(expected, got);
3377 }
3378
3379 #[test]
3380 fn per_match_multi_line1_only_first_line() {
3381 let matcher =
3382 RegexMatcher::new(r"(?s:.{0})(Doctor Watsons|Sherlock)").unwrap();
3383 let mut printer = StandardBuilder::new()
3384 .per_match(true)
3385 .per_match_one_line(true)
3386 .column(true)
3387 .build(NoColor::new(vec![]));
3388 SearcherBuilder::new()
3389 .multi_line(true)
3390 .line_number(true)
3391 .build()
3392 .search_reader(
3393 &matcher,
3394 SHERLOCK.as_bytes(),
3395 printer.sink(&matcher),
3396 )
3397 .unwrap();
3398
3399 let got = printer_contents(&mut printer);
3400 let expected = "\
34011:9:For the Doctor Watsons of this world, as opposed to the Sherlock
34021:57:For the Doctor Watsons of this world, as opposed to the Sherlock
34033:49:be, to a very large extent, the result of luck. Sherlock Holmes
3404";
3405 assert_eq_printed!(expected, got);
3406 }
3407
3408 #[test]
3409 fn per_match_multi_line2_only_first_line() {
3410 let matcher =
3411 RegexMatcher::new(r"(?s)Watson.+?(Holmeses|clearly)").unwrap();
3412 let mut printer = StandardBuilder::new()
3413 .per_match(true)
3414 .per_match_one_line(true)
3415 .column(true)
3416 .build(NoColor::new(vec![]));
3417 SearcherBuilder::new()
3418 .multi_line(true)
3419 .line_number(true)
3420 .build()
3421 .search_reader(
3422 &matcher,
3423 SHERLOCK.as_bytes(),
3424 printer.sink(&matcher),
3425 )
3426 .unwrap();
3427
3428 let got = printer_contents(&mut printer);
3429 let expected = "\
34301:16:For the Doctor Watsons of this world, as opposed to the Sherlock
34315:12:but Doctor Watson has to have it taken out for him and dusted,
3432";
3433 assert_eq_printed!(expected, got);
3434 }
3435
3436 #[test]
3437 fn per_match_multi_line3_only_first_line() {
3438 let matcher =
3439 RegexMatcher::new(r"(?s)Watson.+?Holmeses|always.+?be").unwrap();
3440 let mut printer = StandardBuilder::new()
3441 .per_match(true)
3442 .per_match_one_line(true)
3443 .column(true)
3444 .build(NoColor::new(vec![]));
3445 SearcherBuilder::new()
3446 .multi_line(true)
3447 .line_number(true)
3448 .build()
3449 .search_reader(
3450 &matcher,
3451 SHERLOCK.as_bytes(),
3452 printer.sink(&matcher),
3453 )
3454 .unwrap();
3455
3456 let got = printer_contents(&mut printer);
3457 let expected = "\
34581:16:For the Doctor Watsons of this world, as opposed to the Sherlock
34592:58:Holmeses, success in the province of detective work must always
3460";
3461 assert_eq_printed!(expected, got);
3462 }
3463
3464 #[test]
3465 fn replacement_passthru() {
3466 let matcher = RegexMatcher::new(r"Sherlock|Doctor (\w+)").unwrap();
3467 let mut printer = StandardBuilder::new()
3468 .replacement(Some(b"doctah $1 MD".to_vec()))
3469 .build(NoColor::new(vec![]));
3470 SearcherBuilder::new()
3471 .line_number(true)
3472 .passthru(true)
3473 .build()
3474 .search_reader(
3475 &matcher,
3476 SHERLOCK.as_bytes(),
3477 printer.sink(&matcher),
3478 )
3479 .unwrap();
3480
3481 let got = printer_contents(&mut printer);
3482 let expected = "\
34831:For the doctah Watsons MD of this world, as opposed to the doctah MD
34842-Holmeses, success in the province of detective work must always
34853:be, to a very large extent, the result of luck. doctah MD Holmes
34864-can extract a clew from a wisp of straw or a flake of cigar ash;
34875:but doctah Watson MD has to have it taken out for him and dusted,
34886-and exhibited clearly, with a label attached.
3489";
3490 assert_eq_printed!(expected, got);
3491 }
3492
3493 #[test]
3494 fn replacement() {
3495 let matcher = RegexMatcher::new(r"Sherlock|Doctor (\w+)").unwrap();
3496 let mut printer = StandardBuilder::new()
3497 .replacement(Some(b"doctah $1 MD".to_vec()))
3498 .build(NoColor::new(vec![]));
3499 SearcherBuilder::new()
3500 .line_number(true)
3501 .build()
3502 .search_reader(
3503 &matcher,
3504 SHERLOCK.as_bytes(),
3505 printer.sink(&matcher),
3506 )
3507 .unwrap();
3508
3509 let got = printer_contents(&mut printer);
3510 let expected = "\
35111:For the doctah Watsons MD of this world, as opposed to the doctah MD
35123:be, to a very large extent, the result of luck. doctah MD Holmes
35135:but doctah Watson MD has to have it taken out for him and dusted,
3514";
3515 assert_eq_printed!(expected, got);
3516 }
3517
3518 #[test]
3523 fn replacement_multi_line() {
3524 let matcher = RegexMatcher::new(r"\n").unwrap();
3525 let mut printer = StandardBuilder::new()
3526 .replacement(Some(b"?".to_vec()))
3527 .build(NoColor::new(vec![]));
3528 SearcherBuilder::new()
3529 .line_number(true)
3530 .multi_line(true)
3531 .build()
3532 .search_reader(
3533 &matcher,
3534 "hello\nworld\n".as_bytes(),
3535 printer.sink(&matcher),
3536 )
3537 .unwrap();
3538
3539 let got = printer_contents(&mut printer);
3540 let expected = "1:hello?world?\n";
3541 assert_eq_printed!(expected, got);
3542 }
3543
3544 #[test]
3545 fn replacement_multi_line_diff_line_term() {
3546 let matcher = RegexMatcherBuilder::new()
3547 .line_terminator(Some(b'\x00'))
3548 .build(r"\n")
3549 .unwrap();
3550 let mut printer = StandardBuilder::new()
3551 .replacement(Some(b"?".to_vec()))
3552 .build(NoColor::new(vec![]));
3553 SearcherBuilder::new()
3554 .line_terminator(LineTerminator::byte(b'\x00'))
3555 .line_number(true)
3556 .multi_line(true)
3557 .build()
3558 .search_reader(
3559 &matcher,
3560 "hello\nworld\n".as_bytes(),
3561 printer.sink(&matcher),
3562 )
3563 .unwrap();
3564
3565 let got = printer_contents(&mut printer);
3566 let expected = "1:hello?world?\x00";
3567 assert_eq_printed!(expected, got);
3568 }
3569
3570 #[test]
3571 fn replacement_multi_line_combine_lines() {
3572 let matcher = RegexMatcher::new(r"\n(.)?").unwrap();
3573 let mut printer = StandardBuilder::new()
3574 .replacement(Some(b"?$1".to_vec()))
3575 .build(NoColor::new(vec![]));
3576 SearcherBuilder::new()
3577 .line_number(true)
3578 .multi_line(true)
3579 .build()
3580 .search_reader(
3581 &matcher,
3582 "hello\nworld\n".as_bytes(),
3583 printer.sink(&matcher),
3584 )
3585 .unwrap();
3586
3587 let got = printer_contents(&mut printer);
3588 let expected = "1:hello?world?\n";
3589 assert_eq_printed!(expected, got);
3590 }
3591
3592 #[test]
3593 fn replacement_max_columns() {
3594 let matcher = RegexMatcher::new(r"Sherlock|Doctor (\w+)").unwrap();
3595 let mut printer = StandardBuilder::new()
3596 .max_columns(Some(67))
3597 .replacement(Some(b"doctah $1 MD".to_vec()))
3598 .build(NoColor::new(vec![]));
3599 SearcherBuilder::new()
3600 .line_number(true)
3601 .build()
3602 .search_reader(
3603 &matcher,
3604 SHERLOCK.as_bytes(),
3605 printer.sink(&matcher),
3606 )
3607 .unwrap();
3608
3609 let got = printer_contents(&mut printer);
3610 let expected = "\
36111:[Omitted long line with 2 matches]
36123:be, to a very large extent, the result of luck. doctah MD Holmes
36135:but doctah Watson MD has to have it taken out for him and dusted,
3614";
3615 assert_eq_printed!(expected, got);
3616 }
3617
3618 #[test]
3619 fn replacement_max_columns_preview1() {
3620 let matcher = RegexMatcher::new(r"Sherlock|Doctor (\w+)").unwrap();
3621 let mut printer = StandardBuilder::new()
3622 .max_columns(Some(67))
3623 .max_columns_preview(true)
3624 .replacement(Some(b"doctah $1 MD".to_vec()))
3625 .build(NoColor::new(vec![]));
3626 SearcherBuilder::new()
3627 .line_number(true)
3628 .build()
3629 .search_reader(
3630 &matcher,
3631 SHERLOCK.as_bytes(),
3632 printer.sink(&matcher),
3633 )
3634 .unwrap();
3635
3636 let got = printer_contents(&mut printer);
3637 let expected = "\
36381:For the doctah Watsons MD of this world, as opposed to the doctah [... 0 more matches]
36393:be, to a very large extent, the result of luck. doctah MD Holmes
36405:but doctah Watson MD has to have it taken out for him and dusted,
3641";
3642 assert_eq_printed!(expected, got);
3643 }
3644
3645 #[test]
3646 fn replacement_max_columns_preview2() {
3647 let matcher =
3648 RegexMatcher::new("exhibited|dusted|has to have it").unwrap();
3649 let mut printer = StandardBuilder::new()
3650 .max_columns(Some(43))
3651 .max_columns_preview(true)
3652 .replacement(Some(b"xxx".to_vec()))
3653 .build(NoColor::new(vec![]));
3654 SearcherBuilder::new()
3655 .line_number(false)
3656 .build()
3657 .search_reader(
3658 &matcher,
3659 SHERLOCK.as_bytes(),
3660 printer.sink(&matcher),
3661 )
3662 .unwrap();
3663
3664 let got = printer_contents(&mut printer);
3665 let expected = "\
3666but Doctor Watson xxx taken out for him and [... 1 more match]
3667and xxx clearly, with a label attached.
3668";
3669 assert_eq_printed!(expected, got);
3670 }
3671
3672 #[test]
3673 fn replacement_only_matching() {
3674 let matcher = RegexMatcher::new(r"Sherlock|Doctor (\w+)").unwrap();
3675 let mut printer = StandardBuilder::new()
3676 .only_matching(true)
3677 .replacement(Some(b"doctah $1 MD".to_vec()))
3678 .build(NoColor::new(vec![]));
3679 SearcherBuilder::new()
3680 .line_number(true)
3681 .build()
3682 .search_reader(
3683 &matcher,
3684 SHERLOCK.as_bytes(),
3685 printer.sink(&matcher),
3686 )
3687 .unwrap();
3688
3689 let got = printer_contents(&mut printer);
3690 let expected = "\
36911:doctah Watsons MD
36921:doctah MD
36933:doctah MD
36945:doctah Watson MD
3695";
3696 assert_eq_printed!(expected, got);
3697 }
3698
3699 #[test]
3700 fn replacement_per_match() {
3701 let matcher = RegexMatcher::new(r"Sherlock|Doctor (\w+)").unwrap();
3702 let mut printer = StandardBuilder::new()
3703 .per_match(true)
3704 .replacement(Some(b"doctah $1 MD".to_vec()))
3705 .build(NoColor::new(vec![]));
3706 SearcherBuilder::new()
3707 .line_number(true)
3708 .build()
3709 .search_reader(
3710 &matcher,
3711 SHERLOCK.as_bytes(),
3712 printer.sink(&matcher),
3713 )
3714 .unwrap();
3715
3716 let got = printer_contents(&mut printer);
3717 let expected = "\
37181:For the doctah Watsons MD of this world, as opposed to the doctah MD
37191:For the doctah Watsons MD of this world, as opposed to the doctah MD
37203:be, to a very large extent, the result of luck. doctah MD Holmes
37215:but doctah Watson MD has to have it taken out for him and dusted,
3722";
3723 assert_eq_printed!(expected, got);
3724 }
3725
3726 #[test]
3727 fn invert() {
3728 let matcher = RegexMatcher::new(r"Sherlock").unwrap();
3729 let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
3730 SearcherBuilder::new()
3731 .line_number(true)
3732 .invert_match(true)
3733 .build()
3734 .search_reader(
3735 &matcher,
3736 SHERLOCK.as_bytes(),
3737 printer.sink(&matcher),
3738 )
3739 .unwrap();
3740
3741 let got = printer_contents(&mut printer);
3742 let expected = "\
37432:Holmeses, success in the province of detective work must always
37444:can extract a clew from a wisp of straw or a flake of cigar ash;
37455:but Doctor Watson has to have it taken out for him and dusted,
37466:and exhibited clearly, with a label attached.
3747";
3748 assert_eq_printed!(expected, got);
3749 }
3750
3751 #[test]
3752 fn invert_multi_line() {
3753 let matcher = RegexMatcher::new(r"(?s:.{0})Sherlock").unwrap();
3754 let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
3755 SearcherBuilder::new()
3756 .multi_line(true)
3757 .line_number(true)
3758 .invert_match(true)
3759 .build()
3760 .search_reader(
3761 &matcher,
3762 SHERLOCK.as_bytes(),
3763 printer.sink(&matcher),
3764 )
3765 .unwrap();
3766
3767 let got = printer_contents(&mut printer);
3768 let expected = "\
37692:Holmeses, success in the province of detective work must always
37704:can extract a clew from a wisp of straw or a flake of cigar ash;
37715:but Doctor Watson has to have it taken out for him and dusted,
37726:and exhibited clearly, with a label attached.
3773";
3774 assert_eq_printed!(expected, got);
3775 }
3776
3777 #[test]
3778 fn invert_context() {
3779 let matcher = RegexMatcher::new(r"Sherlock").unwrap();
3780 let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
3781 SearcherBuilder::new()
3782 .line_number(true)
3783 .invert_match(true)
3784 .before_context(1)
3785 .after_context(1)
3786 .build()
3787 .search_reader(
3788 &matcher,
3789 SHERLOCK.as_bytes(),
3790 printer.sink(&matcher),
3791 )
3792 .unwrap();
3793
3794 let got = printer_contents(&mut printer);
3795 let expected = "\
37961-For the Doctor Watsons of this world, as opposed to the Sherlock
37972:Holmeses, success in the province of detective work must always
37983-be, to a very large extent, the result of luck. Sherlock Holmes
37994:can extract a clew from a wisp of straw or a flake of cigar ash;
38005:but Doctor Watson has to have it taken out for him and dusted,
38016:and exhibited clearly, with a label attached.
3802";
3803 assert_eq_printed!(expected, got);
3804 }
3805
3806 #[test]
3807 fn invert_context_multi_line() {
3808 let matcher = RegexMatcher::new(r"(?s:.{0})Sherlock").unwrap();
3809 let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
3810 SearcherBuilder::new()
3811 .multi_line(true)
3812 .line_number(true)
3813 .invert_match(true)
3814 .before_context(1)
3815 .after_context(1)
3816 .build()
3817 .search_reader(
3818 &matcher,
3819 SHERLOCK.as_bytes(),
3820 printer.sink(&matcher),
3821 )
3822 .unwrap();
3823
3824 let got = printer_contents(&mut printer);
3825 let expected = "\
38261-For the Doctor Watsons of this world, as opposed to the Sherlock
38272:Holmeses, success in the province of detective work must always
38283-be, to a very large extent, the result of luck. Sherlock Holmes
38294:can extract a clew from a wisp of straw or a flake of cigar ash;
38305:but Doctor Watson has to have it taken out for him and dusted,
38316:and exhibited clearly, with a label attached.
3832";
3833 assert_eq_printed!(expected, got);
3834 }
3835
3836 #[test]
3837 fn invert_context_only_matching() {
3838 let matcher = RegexMatcher::new(r"Sherlock").unwrap();
3839 let mut printer = StandardBuilder::new()
3840 .only_matching(true)
3841 .build(NoColor::new(vec![]));
3842 SearcherBuilder::new()
3843 .line_number(true)
3844 .invert_match(true)
3845 .before_context(1)
3846 .after_context(1)
3847 .build()
3848 .search_reader(
3849 &matcher,
3850 SHERLOCK.as_bytes(),
3851 printer.sink(&matcher),
3852 )
3853 .unwrap();
3854
3855 let got = printer_contents(&mut printer);
3856 let expected = "\
38571-Sherlock
38582:Holmeses, success in the province of detective work must always
38593-Sherlock
38604:can extract a clew from a wisp of straw or a flake of cigar ash;
38615:but Doctor Watson has to have it taken out for him and dusted,
38626:and exhibited clearly, with a label attached.
3863";
3864 assert_eq_printed!(expected, got);
3865 }
3866
3867 #[test]
3868 fn invert_context_only_matching_multi_line() {
3869 let matcher = RegexMatcher::new(r"(?s:.{0})Sherlock").unwrap();
3870 let mut printer = StandardBuilder::new()
3871 .only_matching(true)
3872 .build(NoColor::new(vec![]));
3873 SearcherBuilder::new()
3874 .multi_line(true)
3875 .line_number(true)
3876 .invert_match(true)
3877 .before_context(1)
3878 .after_context(1)
3879 .build()
3880 .search_reader(
3881 &matcher,
3882 SHERLOCK.as_bytes(),
3883 printer.sink(&matcher),
3884 )
3885 .unwrap();
3886
3887 let got = printer_contents(&mut printer);
3888 let expected = "\
38891-Sherlock
38902:Holmeses, success in the province of detective work must always
38913-Sherlock
38924:can extract a clew from a wisp of straw or a flake of cigar ash;
38935:but Doctor Watson has to have it taken out for him and dusted,
38946:and exhibited clearly, with a label attached.
3895";
3896 assert_eq_printed!(expected, got);
3897 }
3898
3899 #[test]
3900 fn regression_search_empty_with_crlf() {
3901 let matcher =
3902 RegexMatcherBuilder::new().crlf(true).build(r"x?").unwrap();
3903 let mut printer = StandardBuilder::new()
3904 .color_specs(ColorSpecs::default_with_color())
3905 .build(Ansi::new(vec![]));
3906 SearcherBuilder::new()
3907 .line_terminator(LineTerminator::crlf())
3908 .build()
3909 .search_reader(&matcher, &b"\n"[..], printer.sink(&matcher))
3910 .unwrap();
3911
3912 let got = printer_contents_ansi(&mut printer);
3913 assert!(!got.is_empty());
3914 }
3915
3916 #[test]
3917 fn regression_after_context_with_match() {
3918 let haystack = "\
3919a
3920b
3921c
3922d
3923e
3924d
3925e
3926d
3927e
3928d
3929e
3930";
3931
3932 let matcher = RegexMatcherBuilder::new().build(r"d").unwrap();
3933 let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
3934 SearcherBuilder::new()
3935 .max_matches(Some(1))
3936 .line_number(true)
3937 .after_context(2)
3938 .build()
3939 .search_reader(
3940 &matcher,
3941 haystack.as_bytes(),
3942 printer.sink(&matcher),
3943 )
3944 .unwrap();
3945
3946 let got = printer_contents(&mut printer);
3947 let expected = "4:d\n5-e\n6:d\n";
3948 assert_eq_printed!(expected, got);
3949 }
3950
3951 #[test]
3952 fn regression_crlf_preserve() {
3953 let haystack = "hello\nworld\r\n";
3954 let matcher =
3955 RegexMatcherBuilder::new().crlf(true).build(r".").unwrap();
3956 let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
3957 let mut searcher = SearcherBuilder::new()
3958 .line_number(false)
3959 .line_terminator(LineTerminator::crlf())
3960 .build();
3961
3962 searcher
3963 .search_reader(
3964 &matcher,
3965 haystack.as_bytes(),
3966 printer.sink(&matcher),
3967 )
3968 .unwrap();
3969 let got = printer_contents(&mut printer);
3970 let expected = "hello\nworld\r\n";
3971 assert_eq_printed!(expected, got);
3972
3973 let mut printer = StandardBuilder::new()
3974 .replacement(Some(b"$0".to_vec()))
3975 .build(NoColor::new(vec![]));
3976 searcher
3977 .search_reader(
3978 &matcher,
3979 haystack.as_bytes(),
3980 printer.sink(&matcher),
3981 )
3982 .unwrap();
3983 let got = printer_contents(&mut printer);
3984 let expected = "hello\nworld\r\n";
3985 assert_eq_printed!(expected, got);
3986 }
3987}