Skip to main content

ct_regex_internal/haystack/
interface.rs

1use std::fmt::Debug;
2use std::ops::Range;
3
4use crate::haystack::HaystackItem;
5
6/// A trait representing a slice of the underlying haystack for various [`Haystack`] types.
7///
8/// The implementer of this trait is usually but not always, the only implementer of
9/// [`IntoHaystack`] for a haystack type.
10///
11/// It should be noted that this trait is often implemented of a reference to the type in question,
12/// e.g. `&str` or `&[u8]` rather than `str` or `[u8]` themselves, so that the implementing type can
13/// be cloned as required.
14pub trait HaystackSlice<'a>: Debug + Clone + Sized + ToOwned {
15    /// The `HaystackItem` contained within this slice.
16    type Item: HaystackItem;
17
18    /// Slices the underlying slice with the provided (half-open) `range`, used for retrieving
19    /// values of capture groups.
20    fn slice_with(&self, range: Range<usize>) -> Self;
21
22    fn as_bytes(&self) -> &[u8];
23}
24
25/// A trait used to interface the haystack types use when matching of capturing against a
26/// [`Regex`](crate::expr::Regex), including tracking progression and slicing captures.
27///
28/// It is rare that users will have to interact with this trait, apart from Trait bounds. All public
29/// methods will take an `impl IntoHaystack<'a, H>` as an argument.
30///
31/// `Haystack` is accompanied by another trait, [`HaystackItem`], representing items that can be
32/// matched against a [`Regex`](crate::expr::Regex).
33///
34/// `Haystack`s are stateful and therefore can't be matched against multiple times without being
35/// [`reset`](Self::reset) first, or they will continue where the first pattern finished. They store
36/// their state as a `usize`, which can be obtained via [`index`](Self::index) and restored via
37/// [`rollback`](Self::rollback). Additionally, `Haystack`s are cheap to clone, relying on shallow
38/// clones or reference counting.
39///
40/// # Implementing
41///
42/// `Haystack` can be implemented for other types to allow searching, matching and capturing within
43/// other string and byte slice-like types.
44///
45/// For unicode-based haystacks like [`&str`](str), the implementing type needs to be able to deal
46/// with the contained variable width code points.
47///
48/// This trait requires that implementers also implement
49/// [`Iterator<Item = Self::Slice::Item>`](Iterator). When [`Iterator::next`] is called, on a
50/// `Haystack` it should return the same value that previous calls to [`item`](Self::item) have,
51/// before progressing the index to the next item. When the last item has been returned by `next`,
52/// the iterators should return None. Any future calls should avoid incrementing the index.
53///
54/// Additionally, `Haystack`s should be cheap to clone and able to produce and restore an index
55/// representing the current position.
56///
57/// Although possible, there is no point implementing a `Haystack` that shares a `Slice` with
58/// another `Haystack`.
59pub trait Haystack<'a>: Debug + Clone + Iterator<Item = <Self::Slice as HaystackSlice<'a>>::Item> {
60    /// The `HaystackSlice` returned by this type when slicing the underlying haystack. This type is
61    /// usually also contained within the implementer used to create an instance via
62    /// [`IntoHaystack`].
63    type Slice: HaystackSlice<'a>;
64
65    /// Returns the item currently being matched in the haystack. Repeatedly calling this method
66    /// should return the same item, until progressed with [`Iterator::next`].
67    fn item(&self) -> Option<Self::Item>;
68
69    /// Returns the item last matched in the haystack without making any changes.
70    fn prev_item(&self) -> Option<Self::Item>;
71
72    /// Returns the index of the current item in the original haystack. The returned value should be
73    /// valid to pass to [`Self::go_to`] without causing a panic.
74    fn index(&self) -> usize;
75
76    // Progression is only completed by elements which explicitly check the byte and succeed.
77    fn progress(&mut self) {
78        self.next();
79    }
80
81    /// Returns the underlying slice, as it was when this `Haystack` was created - representing
82    /// the entire haystack being matched against.
83    fn inner_slice(&self) -> Self::Slice;
84
85    fn slice_with(&self, range: Range<usize>) -> Self::Slice {
86        self.inner_slice().slice_with(range)
87    }
88
89    /// Returns the remaining contents of this haystack, as a `Slice`. For slice based haystacks,
90    /// this is can be implemented as `&self.inner[self.index..]`.
91    fn remainder_as_slice(&self) -> Self::Slice;
92
93    /// Restores the `index` of the haystack to the provided one. This should only be called with
94    /// indexes obtained by calling [`index`](Self::index) on this `Haystack`.
95    fn go_to(&mut self, index: usize);
96
97    fn rollback(&mut self, state: usize) -> &mut Self {
98        self.go_to(state);
99        self
100    }
101
102    fn skip(&mut self, count: usize) {
103        self.go_to(self.index() + count);
104    }
105
106    fn reset(&mut self) {
107        self.go_to(0);
108    }
109
110    fn is_start(&self) -> bool {
111        self.index() == 0
112    }
113
114    fn is_end(&self) -> bool {
115        self.item().is_none()
116    }
117
118    fn is_line_start(&self) -> bool {
119        self.prev_item().is_none_or(HaystackItem::is_newline)
120    }
121
122    fn is_line_end(&self) -> bool {
123        self.item().is_none_or(HaystackItem::is_newline)
124    }
125
126    fn is_crlf_start(&self) -> bool {
127        match self.prev_item() {
128            Some(n) if n.is_newline() => true,
129            Some(r) if r.is_return() => !self.item().is_some_and(HaystackItem::is_newline),
130            Some(_) => false,
131            None => true,
132        }
133    }
134
135    fn is_crlf_end(&self) -> bool {
136        // TODO: Clarify semantics surrounding "\r?(EndCRLF)"
137        match self.item() {
138            Some(n) if n.is_newline() => !self.prev_item().is_some_and(HaystackItem::is_return),
139            Some(r) if r.is_return() => true,
140            Some(_) => false,
141            None => true,
142        }
143    }
144}
145
146/// This trait is exactly the same as [`Haystack`], except that it simplifies bounds by requiring
147/// that `Item = I`.
148///
149/// It is also blanket-implemented for all types that implement `Haystack<Item = I>`.
150pub trait HaystackOf<'a, I: HaystackItem>: Haystack<'a, Slice: HaystackSlice<'a, Item = I>> {}
151
152impl<'a, I, T> HaystackOf<'a, I> for T
153where
154    I: HaystackItem,
155    T: Haystack<'a, Slice<>: HaystackSlice<'a, Item = I>>
156{}
157
158/// A trait that is responsible for converting a slice into a stateful [`Haystack`], of type `H`.
159/// The primary intent of this trait is to allow users to avoid creating their own `Haystack`,
160/// instead passing a slice to methods on [`Regex`](crate::expr::Regex).
161///
162/// If creating a new `Haystack` type, this trait should be implemented manually so that all types
163/// can be inferred properly.
164pub trait IntoHaystack<'a, H: Haystack<'a>> {
165    /// Creates a new [`Haystack`] from self. The result should be initialized at index 0.
166    fn into_haystack(self) -> H;
167}
168
169impl<'a, H: Haystack<'a>> IntoHaystack<'a, H> for H {
170    fn into_haystack(self) -> H {
171        self
172    }
173}
174
175// Avoid a blanket implementation here so that users don't have to specify types.
176// impl<'a, I: HaystackItem, H: Haystack<'a, I>> IntoHaystack<'a, I, H> for H::Slice {
177//     fn into_haystack(self) -> H {
178//         <H as Haystack>::from_slice(self)
179//     }
180// }
181
182/// A trait representing an owned, mutable type that can be converted into a [`Haystack`] as
183/// required. This allows for [`Regex`](crate::expr::Regex) methods that replace matches or captures
184/// from the original `Haystack`.
185///
186/// It is also used as the return type of the closures take by a couple of `Regex` replace methods.
187pub trait OwnedHaystackable<I: HaystackItem> {
188    type Hay<'a>: HaystackOf<'a, I> where Self: 'a;
189
190    /// Replaces the substring at the position indicated by `range` with the `replacement`
191    /// [`HaystackSlice`].
192    fn replace_range<'a>(
193        &mut self,
194        range: Range<usize>,
195        replacement: <Self::Hay<'a> as Haystack<'a>>::Slice
196    ) where Self: 'a;
197
198    /// Creates a temporary [`Haystack`] out of the underlying slice. This should usually be done by
199    /// borrowing (or cloning if reference counted) and calling [`IntoHaystack::into_haystack`].
200    fn as_haystack<'a>(&'a self) -> Self::Hay<'a>;
201
202    /// Borrows the underlying [`HaystackSlice`] without creating a haystack. Used for slicing
203    /// substrings. Note that `HaystackSlice` is inherently borrowed and probably be implemented for
204    /// a reference.
205    fn as_slice<'a>(&'a self) -> <Self::Hay<'a> as Haystack<'a>>::Slice;
206
207    /// Returns the length of the underlying slice.
208    fn len(&self) -> usize;
209
210    /// Returns true if the underlying slice is empty.
211    fn is_empty(&self) -> bool {
212        self.len() == 0
213    }
214}