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