Skip to main content

fff_grep/
sink.rs

1use std::io;
2
3use crate::searcher::Searcher;
4
5/// A trait that describes errors that can be reported by searchers and
6/// implementations of `Sink`.
7pub trait SinkError: Sized {
8    /// A constructor for converting any value that satisfies the
9    /// `std::fmt::Display` trait into an error.
10    fn error_message<T: std::fmt::Display>(message: T) -> Self;
11
12    /// A constructor for converting I/O errors that occur while searching into
13    /// an error of this type.
14    fn error_io(err: io::Error) -> Self {
15        Self::error_message(err)
16    }
17}
18
19impl SinkError for io::Error {
20    fn error_message<T: std::fmt::Display>(message: T) -> io::Error {
21        io::Error::other(message.to_string())
22    }
23
24    fn error_io(err: io::Error) -> io::Error {
25        err
26    }
27}
28
29/// A trait that defines how results from searchers are handled.
30///
31/// The searcher follows the "push" model: the searcher drives execution and
32/// pushes results back to the caller via this trait.
33pub trait Sink {
34    /// The type of an error that should be reported by a searcher.
35    type Error: SinkError;
36
37    /// This method is called whenever a match is found.
38    ///
39    /// If this returns `true`, then searching continues. If this returns
40    /// `false`, then searching is stopped immediately and `finish` is called.
41    fn matched(&mut self, _searcher: &Searcher, _mat: &SinkMatch<'_>) -> Result<bool, Self::Error>;
42
43    /// This method is called when a search has begun, before any search is
44    /// executed. By default, this does nothing.
45    #[inline]
46    fn begin(&mut self, _searcher: &Searcher) -> Result<bool, Self::Error> {
47        Ok(true)
48    }
49
50    /// This method is called when a search has completed. By default, this
51    /// does nothing.
52    #[inline]
53    fn finish(&mut self, _searcher: &Searcher, _: &SinkFinish) -> Result<(), Self::Error> {
54        Ok(())
55    }
56}
57
58impl<S: Sink> Sink for &mut S {
59    type Error = S::Error;
60
61    #[inline]
62    fn matched(&mut self, searcher: &Searcher, mat: &SinkMatch<'_>) -> Result<bool, S::Error> {
63        (**self).matched(searcher, mat)
64    }
65
66    #[inline]
67    fn begin(&mut self, searcher: &Searcher) -> Result<bool, S::Error> {
68        (**self).begin(searcher)
69    }
70
71    #[inline]
72    fn finish(&mut self, searcher: &Searcher, sink_finish: &SinkFinish) -> Result<(), S::Error> {
73        (**self).finish(searcher, sink_finish)
74    }
75}
76
77/// Summary data reported at the end of a search.
78#[derive(Clone, Debug)]
79pub struct SinkFinish {
80    pub(crate) byte_count: u64,
81}
82
83impl SinkFinish {
84    /// Return the total number of bytes searched.
85    #[inline]
86    pub fn byte_count(&self) -> u64 {
87        self.byte_count
88    }
89}
90
91/// A type that describes a match reported by a searcher.
92#[derive(Clone, Debug)]
93pub struct SinkMatch<'b> {
94    pub(crate) bytes: &'b [u8],
95    pub(crate) absolute_byte_offset: u64,
96    pub(crate) line_number: Option<u64>,
97    pub(crate) buffer: &'b [u8],
98    pub(crate) bytes_range_in_buffer: std::ops::Range<usize>,
99}
100
101impl<'b> SinkMatch<'b> {
102    /// Returns the bytes for all matching lines, including the line
103    /// terminators, if they exist.
104    #[inline]
105    pub fn bytes(&self) -> &'b [u8] {
106        self.bytes
107    }
108
109    /// Returns the absolute byte offset of the start of this match. This
110    /// offset is absolute in that it is relative to the very beginning of the
111    /// input in a search.
112    #[inline]
113    pub fn absolute_byte_offset(&self) -> u64 {
114        self.absolute_byte_offset
115    }
116
117    /// Returns the line number of the first line in this match, if available.
118    ///
119    /// Line numbers are only available when the search builder is instructed
120    /// to compute them.
121    #[inline]
122    pub fn line_number(&self) -> Option<u64> {
123        self.line_number
124    }
125
126    /// Exposes as much of the underlying buffer that was searched as possible.
127    #[inline]
128    pub fn buffer(&self) -> &'b [u8] {
129        self.buffer
130    }
131
132    /// Returns a range that corresponds to where [`SinkMatch::bytes`] appears
133    /// in [`SinkMatch::buffer`].
134    #[inline]
135    pub fn bytes_range_in_buffer(&self) -> std::ops::Range<usize> {
136        self.bytes_range_in_buffer.clone()
137    }
138}