1use grep_matcher::{LineTerminator, Match, Matcher};
2
3use crate::{
4 searcher::glue::{MultiLine, SliceByLine},
5 sink::{Sink, SinkError},
6};
7
8mod core;
9mod glue;
10
11type Range = Match;
15
16#[derive(Clone, Debug, Eq, PartialEq)]
18#[non_exhaustive]
19pub(crate) enum ConfigError {
20 MismatchedLineTerminators {
23 matcher: LineTerminator,
25 searcher: LineTerminator,
27 },
28}
29
30impl std::error::Error for ConfigError {}
31
32impl std::fmt::Display for ConfigError {
33 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34 match *self {
35 ConfigError::MismatchedLineTerminators { matcher, searcher } => {
36 write!(
37 f,
38 "grep config error: mismatched line terminators, \
39 matcher has {:?} but searcher has {:?}",
40 matcher, searcher
41 )
42 }
43 }
44 }
45}
46
47#[derive(Clone, Debug)]
49pub(crate) struct Config {
50 pub(crate) line_term: LineTerminator,
52 pub(crate) line_number: bool,
54 multi_line: bool,
56}
57
58impl Default for Config {
59 fn default() -> Config {
60 Config {
61 line_term: LineTerminator::default(),
62 line_number: true,
63 multi_line: false,
64 }
65 }
66}
67
68#[derive(Clone, Debug)]
70pub struct SearcherBuilder {
71 config: Config,
72}
73
74impl Default for SearcherBuilder {
75 fn default() -> SearcherBuilder {
76 SearcherBuilder::new()
77 }
78}
79
80impl SearcherBuilder {
81 pub fn new() -> SearcherBuilder {
83 SearcherBuilder {
84 config: Config::default(),
85 }
86 }
87
88 pub fn build(&self) -> Searcher {
90 Searcher {
91 config: self.config.clone(),
92 }
93 }
94
95 pub fn line_number(&mut self, yes: bool) -> &mut SearcherBuilder {
97 self.config.line_number = yes;
98 self
99 }
100
101 pub fn multi_line(&mut self, yes: bool) -> &mut SearcherBuilder {
103 self.config.multi_line = yes;
104 self
105 }
106}
107
108#[derive(Clone, Debug)]
111pub struct Searcher {
112 pub(crate) config: Config,
113}
114
115impl Searcher {
116 pub fn new() -> Searcher {
118 SearcherBuilder::new().build()
119 }
120
121 pub fn search_slice<M, S>(&self, matcher: M, slice: &[u8], write_to: S) -> Result<(), S::Error>
124 where
125 M: Matcher,
126 S: Sink,
127 {
128 self.check_config(&matcher)
129 .map_err(S::Error::error_message)?;
130
131 if self.multi_line_with_matcher(&matcher) {
132 MultiLine::new(self, matcher, slice, write_to).run()
133 } else {
134 SliceByLine::new(self, matcher, slice, write_to).run()
135 }
136 }
137
138 fn check_config<M: Matcher>(&self, matcher: M) -> Result<(), ConfigError> {
140 let matcher_line_term = match matcher.line_terminator() {
141 None => return Ok(()),
142 Some(line_term) => line_term,
143 };
144 if matcher_line_term != self.config.line_term {
145 return Err(ConfigError::MismatchedLineTerminators {
146 matcher: matcher_line_term,
147 searcher: self.config.line_term,
148 });
149 }
150 Ok(())
151 }
152}
153
154impl Default for Searcher {
155 fn default() -> Self {
156 Self::new()
157 }
158}
159
160impl Searcher {
162 #[inline]
164 pub fn line_terminator(&self) -> LineTerminator {
165 self.config.line_term
166 }
167
168 #[inline]
171 pub fn line_number(&self) -> bool {
172 self.config.line_number
173 }
174
175 #[inline]
178 pub fn multi_line(&self) -> bool {
179 self.config.multi_line
180 }
181
182 pub fn multi_line_with_matcher<M: Matcher>(&self, matcher: M) -> bool {
185 if !self.multi_line() {
186 return false;
187 }
188 if let Some(line_term) = matcher.line_terminator()
189 && line_term == self.line_terminator()
190 {
191 return false;
192 }
193 if let Some(non_matching) = matcher.non_matching_bytes()
194 && non_matching.contains(self.line_terminator().as_byte())
195 {
196 return false;
197 }
198 true
199 }
200}