mirror of
https://github.com/BurntSushi/ripgrep.git
synced 2025-05-29 21:47:42 +02:00
664 lines
23 KiB
Rust
664 lines
23 KiB
Rust
use std::io;
|
|
|
|
use grep_matcher::LineTerminator;
|
|
|
|
use crate::{
|
|
lines::LineIter,
|
|
searcher::{ConfigError, Searcher},
|
|
};
|
|
|
|
/// A trait that describes errors that can be reported by searchers and
|
|
/// implementations of `Sink`.
|
|
///
|
|
/// Unless you have a specialized use case, you probably don't need to
|
|
/// implement this trait explicitly. It's likely that using `std::io::Error`
|
|
/// (which implements this trait) for your error type is good enough,
|
|
/// largely because most errors that occur during search will likely be an
|
|
/// `std::io::Error`.
|
|
pub trait SinkError: Sized {
|
|
/// A constructor for converting any value that satisfies the
|
|
/// `std::fmt::Display` trait into an error.
|
|
fn error_message<T: std::fmt::Display>(message: T) -> Self;
|
|
|
|
/// A constructor for converting I/O errors that occur while searching into
|
|
/// an error of this type.
|
|
///
|
|
/// By default, this is implemented via the `error_message` constructor.
|
|
fn error_io(err: io::Error) -> Self {
|
|
Self::error_message(err)
|
|
}
|
|
|
|
/// A constructor for converting configuration errors that occur while
|
|
/// building a searcher into an error of this type.
|
|
///
|
|
/// By default, this is implemented via the `error_message` constructor.
|
|
fn error_config(err: ConfigError) -> Self {
|
|
Self::error_message(err)
|
|
}
|
|
}
|
|
|
|
/// An `std::io::Error` can be used as an error for `Sink` implementations out
|
|
/// of the box.
|
|
impl SinkError for io::Error {
|
|
fn error_message<T: std::fmt::Display>(message: T) -> io::Error {
|
|
io::Error::new(io::ErrorKind::Other, message.to_string())
|
|
}
|
|
|
|
fn error_io(err: io::Error) -> io::Error {
|
|
err
|
|
}
|
|
}
|
|
|
|
/// A `Box<dyn std::error::Error>` can be used as an error for `Sink`
|
|
/// implementations out of the box.
|
|
impl SinkError for Box<dyn std::error::Error> {
|
|
fn error_message<T: std::fmt::Display>(
|
|
message: T,
|
|
) -> Box<dyn std::error::Error> {
|
|
Box::<dyn std::error::Error>::from(message.to_string())
|
|
}
|
|
}
|
|
|
|
/// A trait that defines how results from searchers are handled.
|
|
///
|
|
/// In this crate, a searcher follows the "push" model. What that means is that
|
|
/// the searcher drives execution, and pushes results back to the caller. This
|
|
/// is in contrast to a "pull" model where the caller drives execution and
|
|
/// takes results as they need them. These are also known as "internal" and
|
|
/// "external" iteration strategies, respectively.
|
|
///
|
|
/// For a variety of reasons, including the complexity of the searcher
|
|
/// implementation, this crate chooses the "push" or "internal" model of
|
|
/// execution. Thus, in order to act on search results, callers must provide
|
|
/// an implementation of this trait to a searcher, and the searcher is then
|
|
/// responsible for calling the methods on this trait.
|
|
///
|
|
/// This trait defines several behaviors:
|
|
///
|
|
/// * What to do when a match is found. Callers must provide this.
|
|
/// * What to do when an error occurs. Callers must provide this via the
|
|
/// [`SinkError`] trait. Generally, callers can just use `std::io::Error` for
|
|
/// this, which already implements `SinkError`.
|
|
/// * What to do when a contextual line is found. By default, these are
|
|
/// ignored.
|
|
/// * What to do when a gap between contextual lines has been found. By
|
|
/// default, this is ignored.
|
|
/// * What to do when a search has started. By default, this does nothing.
|
|
/// * What to do when a search has finished successfully. By default, this does
|
|
/// nothing.
|
|
///
|
|
/// Callers must, at minimum, specify the behavior when an error occurs and
|
|
/// the behavior when a match occurs. The rest is optional. For each behavior,
|
|
/// callers may report an error (say, if writing the result to another
|
|
/// location failed) or simply return `false` if they want the search to stop
|
|
/// (e.g., when implementing a cap on the number of search results to show).
|
|
///
|
|
/// When errors are reported (whether in the searcher or in the implementation
|
|
/// of `Sink`), then searchers quit immediately without calling `finish`.
|
|
///
|
|
/// For simpler uses of `Sink`, callers may elect to use one of
|
|
/// the more convenient but less flexible implementations in the
|
|
/// [`sinks`] module.
|
|
pub trait Sink {
|
|
/// The type of an error that should be reported by a searcher.
|
|
///
|
|
/// Errors of this type are not only returned by the methods on this
|
|
/// trait, but the constructors defined in `SinkError` are also used in
|
|
/// the searcher implementation itself. e.g., When a I/O error occurs when
|
|
/// reading data from a file.
|
|
type Error: SinkError;
|
|
|
|
/// This method is called whenever a match is found.
|
|
///
|
|
/// If multi line is enabled on the searcher, then the match reported here
|
|
/// may span multiple lines and it may include multiple matches. When multi
|
|
/// line is disabled, then the match is guaranteed to span exactly one
|
|
/// non-empty line (where a single line is, at minimum, a line terminator).
|
|
///
|
|
/// If this returns `true`, then searching continues. If this returns
|
|
/// `false`, then searching is stopped immediately and `finish` is called.
|
|
///
|
|
/// If this returns an error, then searching is stopped immediately,
|
|
/// `finish` is not called and the error is bubbled back up to the caller
|
|
/// of the searcher.
|
|
fn matched(
|
|
&mut self,
|
|
_searcher: &Searcher,
|
|
_mat: &SinkMatch<'_>,
|
|
) -> Result<bool, Self::Error>;
|
|
|
|
/// This method is called whenever a context line is found, and is optional
|
|
/// to implement. By default, it does nothing and returns `true`.
|
|
///
|
|
/// In all cases, the context given is guaranteed to span exactly one
|
|
/// non-empty line (where a single line is, at minimum, a line terminator).
|
|
///
|
|
/// If this returns `true`, then searching continues. If this returns
|
|
/// `false`, then searching is stopped immediately and `finish` is called.
|
|
///
|
|
/// If this returns an error, then searching is stopped immediately,
|
|
/// `finish` is not called and the error is bubbled back up to the caller
|
|
/// of the searcher.
|
|
#[inline]
|
|
fn context(
|
|
&mut self,
|
|
_searcher: &Searcher,
|
|
_context: &SinkContext<'_>,
|
|
) -> Result<bool, Self::Error> {
|
|
Ok(true)
|
|
}
|
|
|
|
/// This method is called whenever a break in contextual lines is found,
|
|
/// and is optional to implement. By default, it does nothing and returns
|
|
/// `true`.
|
|
///
|
|
/// A break can only occur when context reporting is enabled (that is,
|
|
/// either or both of `before_context` or `after_context` are greater than
|
|
/// `0`). More precisely, a break occurs between non-contiguous groups of
|
|
/// lines.
|
|
///
|
|
/// If this returns `true`, then searching continues. If this returns
|
|
/// `false`, then searching is stopped immediately and `finish` is called.
|
|
///
|
|
/// If this returns an error, then searching is stopped immediately,
|
|
/// `finish` is not called and the error is bubbled back up to the caller
|
|
/// of the searcher.
|
|
#[inline]
|
|
fn context_break(
|
|
&mut self,
|
|
_searcher: &Searcher,
|
|
) -> Result<bool, Self::Error> {
|
|
Ok(true)
|
|
}
|
|
|
|
/// This method is called whenever binary detection is enabled and binary
|
|
/// data is found. If binary data is found, then this is called at least
|
|
/// once for the first occurrence with the absolute byte offset at which
|
|
/// the binary data begins.
|
|
///
|
|
/// If this returns `true`, then searching continues. If this returns
|
|
/// `false`, then searching is stopped immediately and `finish` is called.
|
|
///
|
|
/// If this returns an error, then searching is stopped immediately,
|
|
/// `finish` is not called and the error is bubbled back up to the caller
|
|
/// of the searcher.
|
|
///
|
|
/// By default, it does nothing and returns `true`.
|
|
#[inline]
|
|
fn binary_data(
|
|
&mut self,
|
|
_searcher: &Searcher,
|
|
_binary_byte_offset: u64,
|
|
) -> Result<bool, Self::Error> {
|
|
Ok(true)
|
|
}
|
|
|
|
/// This method is called when a search has begun, before any search is
|
|
/// executed. By default, this does nothing.
|
|
///
|
|
/// If this returns `true`, then searching continues. If this returns
|
|
/// `false`, then searching is stopped immediately and `finish` is called.
|
|
///
|
|
/// If this returns an error, then searching is stopped immediately,
|
|
/// `finish` is not called and the error is bubbled back up to the caller
|
|
/// of the searcher.
|
|
#[inline]
|
|
fn begin(&mut self, _searcher: &Searcher) -> Result<bool, Self::Error> {
|
|
Ok(true)
|
|
}
|
|
|
|
/// This method is called when a search has completed. By default, this
|
|
/// does nothing.
|
|
///
|
|
/// If this returns an error, the error is bubbled back up to the caller of
|
|
/// the searcher.
|
|
#[inline]
|
|
fn finish(
|
|
&mut self,
|
|
_searcher: &Searcher,
|
|
_: &SinkFinish,
|
|
) -> Result<(), Self::Error> {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl<'a, S: Sink> Sink for &'a mut S {
|
|
type Error = S::Error;
|
|
|
|
#[inline]
|
|
fn matched(
|
|
&mut self,
|
|
searcher: &Searcher,
|
|
mat: &SinkMatch<'_>,
|
|
) -> Result<bool, S::Error> {
|
|
(**self).matched(searcher, mat)
|
|
}
|
|
|
|
#[inline]
|
|
fn context(
|
|
&mut self,
|
|
searcher: &Searcher,
|
|
context: &SinkContext<'_>,
|
|
) -> Result<bool, S::Error> {
|
|
(**self).context(searcher, context)
|
|
}
|
|
|
|
#[inline]
|
|
fn context_break(
|
|
&mut self,
|
|
searcher: &Searcher,
|
|
) -> Result<bool, S::Error> {
|
|
(**self).context_break(searcher)
|
|
}
|
|
|
|
#[inline]
|
|
fn binary_data(
|
|
&mut self,
|
|
searcher: &Searcher,
|
|
binary_byte_offset: u64,
|
|
) -> Result<bool, S::Error> {
|
|
(**self).binary_data(searcher, binary_byte_offset)
|
|
}
|
|
|
|
#[inline]
|
|
fn begin(&mut self, searcher: &Searcher) -> Result<bool, S::Error> {
|
|
(**self).begin(searcher)
|
|
}
|
|
|
|
#[inline]
|
|
fn finish(
|
|
&mut self,
|
|
searcher: &Searcher,
|
|
sink_finish: &SinkFinish,
|
|
) -> Result<(), S::Error> {
|
|
(**self).finish(searcher, sink_finish)
|
|
}
|
|
}
|
|
|
|
impl<S: Sink + ?Sized> Sink for Box<S> {
|
|
type Error = S::Error;
|
|
|
|
#[inline]
|
|
fn matched(
|
|
&mut self,
|
|
searcher: &Searcher,
|
|
mat: &SinkMatch<'_>,
|
|
) -> Result<bool, S::Error> {
|
|
(**self).matched(searcher, mat)
|
|
}
|
|
|
|
#[inline]
|
|
fn context(
|
|
&mut self,
|
|
searcher: &Searcher,
|
|
context: &SinkContext<'_>,
|
|
) -> Result<bool, S::Error> {
|
|
(**self).context(searcher, context)
|
|
}
|
|
|
|
#[inline]
|
|
fn context_break(
|
|
&mut self,
|
|
searcher: &Searcher,
|
|
) -> Result<bool, S::Error> {
|
|
(**self).context_break(searcher)
|
|
}
|
|
|
|
#[inline]
|
|
fn binary_data(
|
|
&mut self,
|
|
searcher: &Searcher,
|
|
binary_byte_offset: u64,
|
|
) -> Result<bool, S::Error> {
|
|
(**self).binary_data(searcher, binary_byte_offset)
|
|
}
|
|
|
|
#[inline]
|
|
fn begin(&mut self, searcher: &Searcher) -> Result<bool, S::Error> {
|
|
(**self).begin(searcher)
|
|
}
|
|
|
|
#[inline]
|
|
fn finish(
|
|
&mut self,
|
|
searcher: &Searcher,
|
|
sink_finish: &SinkFinish,
|
|
) -> Result<(), S::Error> {
|
|
(**self).finish(searcher, sink_finish)
|
|
}
|
|
}
|
|
|
|
/// Summary data reported at the end of a search.
|
|
///
|
|
/// This reports data such as the total number of bytes searched and the
|
|
/// absolute offset of the first occurrence of binary data, if any were found.
|
|
///
|
|
/// A searcher that stops early because of an error does not call `finish`.
|
|
/// A searcher that stops early because the `Sink` implementor instructed it
|
|
/// to will still call `finish`.
|
|
#[derive(Clone, Debug)]
|
|
pub struct SinkFinish {
|
|
pub(crate) byte_count: u64,
|
|
pub(crate) binary_byte_offset: Option<u64>,
|
|
}
|
|
|
|
impl SinkFinish {
|
|
/// Return the total number of bytes searched.
|
|
#[inline]
|
|
pub fn byte_count(&self) -> u64 {
|
|
self.byte_count
|
|
}
|
|
|
|
/// If binary detection is enabled and if binary data was found, then this
|
|
/// returns the absolute byte offset of the first detected byte of binary
|
|
/// data.
|
|
///
|
|
/// Note that since this is an absolute byte offset, it cannot be relied
|
|
/// upon to index into any addressable memory.
|
|
#[inline]
|
|
pub fn binary_byte_offset(&self) -> Option<u64> {
|
|
self.binary_byte_offset
|
|
}
|
|
}
|
|
|
|
/// A type that describes a match reported by a searcher.
|
|
#[derive(Clone, Debug)]
|
|
pub struct SinkMatch<'b> {
|
|
pub(crate) line_term: LineTerminator,
|
|
pub(crate) bytes: &'b [u8],
|
|
pub(crate) absolute_byte_offset: u64,
|
|
pub(crate) line_number: Option<u64>,
|
|
pub(crate) buffer: &'b [u8],
|
|
pub(crate) bytes_range_in_buffer: std::ops::Range<usize>,
|
|
}
|
|
|
|
impl<'b> SinkMatch<'b> {
|
|
/// Returns the bytes for all matching lines, including the line
|
|
/// terminators, if they exist.
|
|
#[inline]
|
|
pub fn bytes(&self) -> &'b [u8] {
|
|
self.bytes
|
|
}
|
|
|
|
/// Return an iterator over the lines in this match.
|
|
///
|
|
/// If multi line search is enabled, then this may yield more than one
|
|
/// line (but always at least one line). If multi line search is disabled,
|
|
/// then this always reports exactly one line (but may consist of just
|
|
/// the line terminator).
|
|
///
|
|
/// Lines yielded by this iterator include their terminators.
|
|
#[inline]
|
|
pub fn lines(&self) -> LineIter<'b> {
|
|
LineIter::new(self.line_term.as_byte(), self.bytes)
|
|
}
|
|
|
|
/// Returns the absolute byte offset of the start of this match. This
|
|
/// offset is absolute in that it is relative to the very beginning of the
|
|
/// input in a search, and can never be relied upon to be a valid index
|
|
/// into an in-memory slice.
|
|
#[inline]
|
|
pub fn absolute_byte_offset(&self) -> u64 {
|
|
self.absolute_byte_offset
|
|
}
|
|
|
|
/// Returns the line number of the first line in this match, if available.
|
|
///
|
|
/// Line numbers are only available when the search builder is instructed
|
|
/// to compute them.
|
|
#[inline]
|
|
pub fn line_number(&self) -> Option<u64> {
|
|
self.line_number
|
|
}
|
|
|
|
/// Exposes as much of the underlying buffer that was search as possible.
|
|
#[inline]
|
|
pub fn buffer(&self) -> &'b [u8] {
|
|
self.buffer
|
|
}
|
|
|
|
/// Returns a range that corresponds to where [`SinkMatch::bytes`] appears
|
|
/// in [`SinkMatch::buffer`].
|
|
#[inline]
|
|
pub fn bytes_range_in_buffer(&self) -> std::ops::Range<usize> {
|
|
self.bytes_range_in_buffer.clone()
|
|
}
|
|
}
|
|
|
|
/// The type of context reported by a searcher.
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
pub enum SinkContextKind {
|
|
/// The line reported occurred before a match.
|
|
Before,
|
|
/// The line reported occurred after a match.
|
|
After,
|
|
/// Any other type of context reported, e.g., as a result of a searcher's
|
|
/// "passthru" mode.
|
|
Other,
|
|
}
|
|
|
|
/// A type that describes a contextual line reported by a searcher.
|
|
#[derive(Clone, Debug)]
|
|
pub struct SinkContext<'b> {
|
|
#[cfg(test)]
|
|
pub(crate) line_term: LineTerminator,
|
|
pub(crate) bytes: &'b [u8],
|
|
pub(crate) kind: SinkContextKind,
|
|
pub(crate) absolute_byte_offset: u64,
|
|
pub(crate) line_number: Option<u64>,
|
|
}
|
|
|
|
impl<'b> SinkContext<'b> {
|
|
/// Returns the context bytes, including line terminators.
|
|
#[inline]
|
|
pub fn bytes(&self) -> &'b [u8] {
|
|
self.bytes
|
|
}
|
|
|
|
/// Returns the type of context.
|
|
#[inline]
|
|
pub fn kind(&self) -> &SinkContextKind {
|
|
&self.kind
|
|
}
|
|
|
|
/// Return an iterator over the lines in this match.
|
|
///
|
|
/// This always yields exactly one line (and that one line may contain just
|
|
/// the line terminator).
|
|
///
|
|
/// Lines yielded by this iterator include their terminators.
|
|
#[cfg(test)]
|
|
pub(crate) fn lines(&self) -> LineIter<'b> {
|
|
LineIter::new(self.line_term.as_byte(), self.bytes)
|
|
}
|
|
|
|
/// Returns the absolute byte offset of the start of this context. This
|
|
/// offset is absolute in that it is relative to the very beginning of the
|
|
/// input in a search, and can never be relied upon to be a valid index
|
|
/// into an in-memory slice.
|
|
#[inline]
|
|
pub fn absolute_byte_offset(&self) -> u64 {
|
|
self.absolute_byte_offset
|
|
}
|
|
|
|
/// Returns the line number of the first line in this context, if
|
|
/// available.
|
|
///
|
|
/// Line numbers are only available when the search builder is instructed
|
|
/// to compute them.
|
|
#[inline]
|
|
pub fn line_number(&self) -> Option<u64> {
|
|
self.line_number
|
|
}
|
|
}
|
|
|
|
/// A collection of convenience implementations of `Sink`.
|
|
///
|
|
/// Each implementation in this module makes some kind of sacrifice in the name
|
|
/// of making common cases easier to use. Most frequently, each type is a
|
|
/// wrapper around a closure specified by the caller that provides limited
|
|
/// access to the full suite of information available to implementors of
|
|
/// `Sink`.
|
|
///
|
|
/// For example, the `UTF8` sink makes the following sacrifices:
|
|
///
|
|
/// * All matches must be UTF-8. An arbitrary `Sink` does not have this
|
|
/// restriction and can deal with arbitrary data. If this sink sees invalid
|
|
/// UTF-8, then an error is returned and searching stops. (Use the `Lossy`
|
|
/// sink instead to suppress this error.)
|
|
/// * The searcher must be configured to report line numbers. If it isn't,
|
|
/// an error is reported at the first match and searching stops.
|
|
/// * Context lines, context breaks and summary data reported at the end of
|
|
/// a search are all ignored.
|
|
/// * Implementors are forced to use `std::io::Error` as their error type.
|
|
///
|
|
/// If you need more flexibility, then you're advised to implement the `Sink`
|
|
/// trait directly.
|
|
pub mod sinks {
|
|
use std::io;
|
|
|
|
use crate::searcher::Searcher;
|
|
|
|
use super::{Sink, SinkError, SinkMatch};
|
|
|
|
/// A sink that provides line numbers and matches as strings while ignoring
|
|
/// everything else.
|
|
///
|
|
/// This implementation will return an error if a match contains invalid
|
|
/// UTF-8 or if the searcher was not configured to count lines. Errors
|
|
/// on invalid UTF-8 can be suppressed by using the `Lossy` sink instead
|
|
/// of this one.
|
|
///
|
|
/// The closure accepts two parameters: a line number and a UTF-8 string
|
|
/// containing the matched data. The closure returns a
|
|
/// `Result<bool, std::io::Error>`. If the `bool` is `false`, then the
|
|
/// search stops immediately. Otherwise, searching continues.
|
|
///
|
|
/// If multi line mode was enabled, the line number refers to the line
|
|
/// number of the first line in the match.
|
|
#[derive(Clone, Debug)]
|
|
pub struct UTF8<F>(pub F)
|
|
where
|
|
F: FnMut(u64, &str) -> Result<bool, io::Error>;
|
|
|
|
impl<F> Sink for UTF8<F>
|
|
where
|
|
F: FnMut(u64, &str) -> Result<bool, io::Error>,
|
|
{
|
|
type Error = io::Error;
|
|
|
|
fn matched(
|
|
&mut self,
|
|
_searcher: &Searcher,
|
|
mat: &SinkMatch<'_>,
|
|
) -> Result<bool, io::Error> {
|
|
let matched = match std::str::from_utf8(mat.bytes()) {
|
|
Ok(matched) => matched,
|
|
Err(err) => return Err(io::Error::error_message(err)),
|
|
};
|
|
let line_number = match mat.line_number() {
|
|
Some(line_number) => line_number,
|
|
None => {
|
|
let msg = "line numbers not enabled";
|
|
return Err(io::Error::error_message(msg));
|
|
}
|
|
};
|
|
(self.0)(line_number, &matched)
|
|
}
|
|
}
|
|
|
|
/// A sink that provides line numbers and matches as (lossily converted)
|
|
/// strings while ignoring everything else.
|
|
///
|
|
/// This is like `UTF8`, except that if a match contains invalid UTF-8,
|
|
/// then it will be lossily converted to valid UTF-8 by substituting
|
|
/// invalid UTF-8 with Unicode replacement characters.
|
|
///
|
|
/// This implementation will return an error on the first match if the
|
|
/// searcher was not configured to count lines.
|
|
///
|
|
/// The closure accepts two parameters: a line number and a UTF-8 string
|
|
/// containing the matched data. The closure returns a
|
|
/// `Result<bool, std::io::Error>`. If the `bool` is `false`, then the
|
|
/// search stops immediately. Otherwise, searching continues.
|
|
///
|
|
/// If multi line mode was enabled, the line number refers to the line
|
|
/// number of the first line in the match.
|
|
#[derive(Clone, Debug)]
|
|
pub struct Lossy<F>(pub F)
|
|
where
|
|
F: FnMut(u64, &str) -> Result<bool, io::Error>;
|
|
|
|
impl<F> Sink for Lossy<F>
|
|
where
|
|
F: FnMut(u64, &str) -> Result<bool, io::Error>,
|
|
{
|
|
type Error = io::Error;
|
|
|
|
fn matched(
|
|
&mut self,
|
|
_searcher: &Searcher,
|
|
mat: &SinkMatch<'_>,
|
|
) -> Result<bool, io::Error> {
|
|
use std::borrow::Cow;
|
|
|
|
let matched = match std::str::from_utf8(mat.bytes()) {
|
|
Ok(matched) => Cow::Borrowed(matched),
|
|
// TODO: In theory, it should be possible to amortize
|
|
// allocation here, but `std` doesn't provide such an API.
|
|
// Regardless, this only happens on matches with invalid UTF-8,
|
|
// which should be pretty rare.
|
|
Err(_) => String::from_utf8_lossy(mat.bytes()),
|
|
};
|
|
let line_number = match mat.line_number() {
|
|
Some(line_number) => line_number,
|
|
None => {
|
|
let msg = "line numbers not enabled";
|
|
return Err(io::Error::error_message(msg));
|
|
}
|
|
};
|
|
(self.0)(line_number, &matched)
|
|
}
|
|
}
|
|
|
|
/// A sink that provides line numbers and matches as raw bytes while
|
|
/// ignoring everything else.
|
|
///
|
|
/// This implementation will return an error on the first match if the
|
|
/// searcher was not configured to count lines.
|
|
///
|
|
/// The closure accepts two parameters: a line number and a raw byte string
|
|
/// containing the matched data. The closure returns a
|
|
/// `Result<bool, std::io::Error>`. If the `bool` is `false`, then the
|
|
/// search stops immediately. Otherwise, searching continues.
|
|
///
|
|
/// If multi line mode was enabled, the line number refers to the line
|
|
/// number of the first line in the match.
|
|
#[derive(Clone, Debug)]
|
|
pub struct Bytes<F>(pub F)
|
|
where
|
|
F: FnMut(u64, &[u8]) -> Result<bool, io::Error>;
|
|
|
|
impl<F> Sink for Bytes<F>
|
|
where
|
|
F: FnMut(u64, &[u8]) -> Result<bool, io::Error>,
|
|
{
|
|
type Error = io::Error;
|
|
|
|
fn matched(
|
|
&mut self,
|
|
_searcher: &Searcher,
|
|
mat: &SinkMatch<'_>,
|
|
) -> Result<bool, io::Error> {
|
|
let line_number = match mat.line_number() {
|
|
Some(line_number) => line_number,
|
|
None => {
|
|
let msg = "line numbers not enabled";
|
|
return Err(io::Error::error_message(msg));
|
|
}
|
|
};
|
|
(self.0)(line_number, mat.bytes())
|
|
}
|
|
}
|
|
}
|