use crate::{
matcher::{LineTerminator, Match, Matcher},
searcher::glue::{MultiLine, SliceByLine},
sink::{Sink, SinkError},
};
mod core;
mod glue;
type Range = Match;
#[derive(Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub(crate) enum ConfigError {
MismatchedLineTerminators {
matcher: LineTerminator,
searcher: LineTerminator,
},
}
impl std::error::Error for ConfigError {}
impl std::fmt::Display for ConfigError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
ConfigError::MismatchedLineTerminators { matcher, searcher } => {
write!(
f,
"grep config error: mismatched line terminators, \
matcher has {:?} but searcher has {:?}",
matcher, searcher
)
}
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct Config {
pub(crate) line_term: LineTerminator,
pub(crate) line_number: bool,
multi_line: bool,
}
impl Default for Config {
fn default() -> Config {
Config {
line_term: LineTerminator::default(),
line_number: true,
multi_line: false,
}
}
}
#[derive(Clone, Debug)]
pub struct SearcherBuilder {
config: Config,
}
impl Default for SearcherBuilder {
fn default() -> SearcherBuilder {
SearcherBuilder::new()
}
}
impl SearcherBuilder {
pub fn new() -> SearcherBuilder {
SearcherBuilder {
config: Config::default(),
}
}
pub fn build(&self) -> Searcher {
Searcher {
config: self.config.clone(),
}
}
pub fn line_number(&mut self, yes: bool) -> &mut SearcherBuilder {
self.config.line_number = yes;
self
}
pub fn multi_line(&mut self, yes: bool) -> &mut SearcherBuilder {
self.config.multi_line = yes;
self
}
}
#[derive(Clone, Debug)]
pub struct Searcher {
pub(crate) config: Config,
}
impl Searcher {
pub fn new() -> Searcher {
SearcherBuilder::new().build()
}
pub fn search_slice<M, S>(&self, matcher: M, slice: &[u8], write_to: S) -> Result<(), S::Error>
where
M: Matcher,
S: Sink,
{
self.check_config(&matcher)
.map_err(S::Error::error_message)?;
if self.multi_line_with_matcher(&matcher) {
MultiLine::new(self, matcher, slice, write_to).run()
} else {
SliceByLine::new(self, matcher, slice, write_to).run()
}
}
fn check_config<M: Matcher>(&self, matcher: M) -> Result<(), ConfigError> {
let matcher_line_term = match matcher.line_terminator() {
None => return Ok(()),
Some(line_term) => line_term,
};
if matcher_line_term != self.config.line_term {
return Err(ConfigError::MismatchedLineTerminators {
matcher: matcher_line_term,
searcher: self.config.line_term,
});
}
Ok(())
}
}
impl Default for Searcher {
fn default() -> Self {
Self::new()
}
}
impl Searcher {
#[inline]
pub fn line_terminator(&self) -> LineTerminator {
self.config.line_term
}
#[inline]
pub fn line_number(&self) -> bool {
self.config.line_number
}
#[inline]
pub fn multi_line(&self) -> bool {
self.config.multi_line
}
pub fn multi_line_with_matcher<M: Matcher>(&self, matcher: M) -> bool {
if !self.multi_line() {
return false;
}
if let Some(line_term) = matcher.line_terminator()
&& line_term == self.line_terminator()
{
return false;
}
true
}
}