string_iter/
pattern.rs

1use core::{fmt::Debug, num::NonZeroUsize};
2use core::ops::{RangeTo, RangeInclusive};
3
4/// A never type that cannot be instanciated.
5#[derive(Debug)]
6pub enum Never{}
7
8/// This proves Pattern is object safe.
9const _: Option<&dyn Pattern<Err = Never>> = None;
10
11#[derive(Clone, Copy, PartialEq, Eq, Debug)]
12/// Determines what to do with a matched [`char`] on string
13/// separation. By default [`Retain`](Sep::Retain).
14pub enum Sep{
15    /// Keep the [`char`] in the iterator.
16    Retain,
17    /// Yield the [`char`] with the output.
18    Yield,
19    /// Discard the [`char`].
20    Split,
21    /// Yield the [`char`] but keep it in the iterator.
22    Conjoin,
23}
24
25/// A pattern for use in 
26/// [`slice`](crate::StringIter::try_next_slice), 
27/// [`split`](crate::StringIter::into_substrs) and 
28/// [`trim`](crate::StringIter::trim_by)
29/// functions.
30pub trait Pattern{
31    type Err: Debug;
32    /// Try matching a char in a pattern
33    /// 
34    /// # Arguments
35    ///
36    /// * `c` - The current [`char`], guaranteed to be the first [`char`] in `s`
37    /// * `s` - The current [`&str`], a substring of some length after `c`
38    /// 
39    /// In [`slice`](StringIter::try_next_slice) or 
40    /// [`split`](StringIter::into_substrs): match until the first `true` result
41    /// 
42    /// In [`trim_by`](StringIter::trim_by): match until the first `false` result
43    /// 
44    /// [`len()`](Pattern::len) is a suggestion for `s.len()`.
45    /// However `s` is not guaranteed to have the same length as `self.len()`,
46    /// since joining multiple patterns can increase `s.len()`, 
47    /// and corner cases will decrease `s.len()`.
48    fn matches(&mut self, c: char, s: &str) -> Result<bool, Self::Err>;
49    /// Determines how many [`char`]s to look ahead, default `1`.
50    /// 
51    /// The iterator will not stop prematurely because of look-ahead.
52    fn len(&self) -> NonZeroUsize { NonZeroUsize::new(1).unwrap() }
53    /// Determines what to do with the matched [char] on separation.
54    /// 
55    /// See also [`sep_with`](SetSep::sep_with)
56    fn sep(&self) -> Sep { Sep::Retain }
57}
58
59impl Pattern for isize {
60    type Err = Never;
61    /// similar to peekn
62    fn matches(&mut self, _: char, _: &str) -> Result<bool, Self::Err> {
63        *self -= 1;
64        Ok(*self == -1)
65    }
66}
67
68impl Pattern for RangeTo<isize> {
69    type Err = Never;
70    fn matches(&mut self, _: char, _: &str) -> Result<bool, Self::Err> {
71        self.end -= 1;
72        Ok(self.contains(&-1))
73    }
74}
75
76impl Pattern for RangeInclusive<char> {
77    type Err = Never;
78    fn matches(&mut self, c: char, _: &str) -> Result<bool, Self::Err> {
79        Ok(self.contains(&c))
80    }
81}
82
83impl Pattern for char {
84    type Err = Never;
85    fn matches(&mut self, c: char, _: &str) -> Result<bool, Self::Err> {
86        Ok(c == *self)
87    }
88}
89
90impl Pattern for &str {
91    type Err = Never;
92    fn matches(&mut self, _: char, s: &str) -> Result<bool, Self::Err> {
93        Ok(s.starts_with(*self))
94    }
95    fn len(&self) -> NonZeroUsize {
96        NonZeroUsize::new(self.chars().count())
97            .expect("\"\" is not a valid pattern")
98    }
99}
100
101impl Pattern for &[char] {
102    type Err = Never;
103    fn matches(&mut self, c: char, _: &str) -> Result<bool, Self::Err> {
104        Ok(self.contains(&c))
105    }
106}
107
108impl<const N: usize> Pattern for [char; N] {
109    type Err = Never;
110    fn matches(&mut self, c: char, _: &str) -> Result<bool, Self::Err> {
111        Ok(self.contains(&c))
112    }
113}
114
115mod private{
116    pub trait Sealed {}
117}
118
119/// A generalized fallible boolean result.
120pub trait FallibleBool: private::Sealed {
121    type Err: Debug;
122    fn get(self) -> Result<bool, Self::Err>;
123}
124
125impl private::Sealed for bool {}
126
127impl FallibleBool for bool {
128    type Err = Never;
129
130    fn get(self) -> Result<bool, Self::Err> {
131        Ok(self)
132    }
133}
134
135impl private::Sealed for Option<bool> {}
136
137impl FallibleBool for Option<bool> {
138    type Err = ();
139
140    fn get(self) -> Result<bool, ()> {
141        self.ok_or(())
142    }
143}
144
145impl<E: Debug> private::Sealed for Result<bool, E> {}
146
147impl<E: Debug> FallibleBool for Result<bool, E> {
148    type Err = E;
149
150    fn get(self) -> Result<bool, E> {
151        self
152    }
153}
154
155impl<F, B> Pattern for F where F: FnMut(char) -> B, B: FallibleBool {
156    type Err = B::Err;
157    fn matches(&mut self, c: char, _: &str) -> Result<bool, Self::Err> {
158        self(c).get()
159    }
160}
161
162pub struct SizedStrPredicate<P: FnMut(&str) -> B, B: FallibleBool> {
163    pattern: P,
164    len: NonZeroUsize
165}
166
167/// Convert `FnMut(&str) -> FalliableBool` into a pattern 
168/// by specifying a look-ahead length.
169pub trait StrPredicate<B: FallibleBool>: FnMut(&str) -> B + Sized{
170
171    /// Returns a pattern by giving a length hint on a string predicate
172    /// 
173    /// # Note
174    /// 
175    /// * All chars above position zero are obtained through peeking
176    /// * Do not expect all incoming strings to be length `len`,
177    /// the user is expected to handle edge cases.
178    /// 
179    /// # Panics
180    /// 
181    /// if `len` is `0`
182    fn expecting(self, len: usize) -> SizedStrPredicate<Self, B> {
183        let len = NonZeroUsize::new(len)
184            .expect("pattern cannot have length 0");
185        SizedStrPredicate { pattern: self, len }
186    }
187}
188
189impl<P: FnMut(&str) -> B, B: FallibleBool> Pattern for SizedStrPredicate<P, B> {
190    type Err = B::Err;
191    fn matches(&mut self, _: char, s: &str) -> Result<bool, Self::Err> {
192        (self.pattern)(s).get()
193    }
194    fn len(&self) -> NonZeroUsize { self.len }
195}
196
197
198pub struct SizedCharStrPredicate<P: FnMut(char, &str) -> B, B: FallibleBool> {
199    pattern: P,
200    len: NonZeroUsize
201}
202
203/// Convert `FnMut(char, &str) -> FalliableBool` into a pattern 
204/// by specifying a look-ahead length.
205pub trait CharStrPredicate<B: FallibleBool>: FnMut(char, &str) -> B + Sized{
206
207    /// Returns a pattern by giving a length hint on a string predicate
208    /// 
209    /// # Note
210    /// 
211    /// * All chars above position zero are obtained through peeking
212    /// * Do not expect all incoming strings to be length `len`,
213    /// the user is expected to handle edge cases.
214    /// 
215    /// # Panics
216    /// 
217    /// if `len` is `0`
218    fn expecting(self, len: usize) -> SizedCharStrPredicate<Self, B> {
219        let len = NonZeroUsize::new(len)
220            .expect("pattern cannot have length 0");
221        SizedCharStrPredicate { pattern: self, len }
222    }
223}
224
225impl<P: FnMut(char, &str) -> B, B: FallibleBool> Pattern for SizedCharStrPredicate<P, B> {
226    type Err = B::Err;
227    
228    fn matches(&mut self, c: char, s: &str) -> Result<bool, Self::Err> {
229        (self.pattern)(c, s).get()
230    }
231    fn len(&self) -> NonZeroUsize { self.len }
232}
233
234
235pub(crate) struct PatRef<'t, T: Pattern>(pub(crate) &'t mut T);
236
237impl<'t, T: Pattern> Pattern for PatRef<'t, T> {
238    type Err = T::Err;
239    fn len(&self) -> NonZeroUsize {
240        self.0.len()
241    }
242    fn matches(&mut self, c: char, s: &str) -> Result<bool, Self::Err> {
243        self.0.matches(c, s)
244    }
245    fn sep(&self) -> Sep {
246        self.0.sep()
247    }
248}
249
250#[cfg(feature="std")]
251const _:() = {
252    extern crate alloc;
253    use alloc::boxed::Box;
254
255    impl<E: Debug> Pattern for Box<dyn Pattern<Err = E>> {
256        type Err = E;
257        #[doc(hidden)]
258        fn len(&self) -> NonZeroUsize {
259            self.as_ref().len()
260        }
261        #[doc(hidden)]
262        fn matches(&mut self, c: char, s: &str) -> Result<bool, Self::Err> {
263            self.as_mut().matches(c, s)
264        }
265        #[doc(hidden)]
266        fn sep(&self) -> Sep {
267            self.as_ref().sep()
268        }
269    }
270};
271
272#[cfg(feature = "std")]
273const _: () = {
274    extern crate alloc;
275
276    use alloc::string::String;
277
278    impl Pattern for String {
279        type Err = Never;
280        fn matches(&mut self, _: char, s: &str) -> Result<bool, Self::Err> {
281            Ok(self == &s)
282        }
283        fn len(&self) -> NonZeroUsize {
284            NonZeroUsize::new(self.chars().count())
285                .expect("\"\" is not a valid pattern")
286        }
287    }
288
289    impl Pattern for &String {
290        type Err = Never;
291        fn matches(&mut self, _: char, s: &str) -> Result<bool, Self::Err> {
292            Ok(self == &s)
293        }
294        fn len(&self) -> NonZeroUsize {
295            NonZeroUsize::new(self.chars().count())
296                .expect("\"\" is not a valid pattern")
297        }
298    }
299};
300
301
302pub struct SepConfig<P: SetSep> {
303    pattern: P,
304    config: Sep,
305}
306
307impl<P: SetSep> Pattern for SepConfig<P> {
308    type Err = P::Err;
309
310    fn matches(&mut self, c: char, s: &str) -> Result<bool, Self::Err> {
311        self.pattern.matches(c, s)
312    }
313
314    fn len(&self) -> NonZeroUsize { self.pattern.len() }
315
316    fn sep(&self) -> Sep {
317        self.config
318    }
319}
320
321/// Allows a pattern to edit its [`sep_method`](crate::Pattern::sep_method).
322pub trait SetSep: Pattern + Sized {
323
324    /// Set the [Sep] for this pattern. 
325    /// By default this is [Retain](Sep::Retain).
326    /// 
327    /// # Sep Methods:
328    /// 
329    /// * [Retain](Sep::Retain): Keep the matched [`char`] in the iterator.
330    /// 
331    /// * [Yield](Sep::Yield): Yield the matched [`char`] with the output.
332    /// 
333    /// * [Split](Sep::Split): Discard the matched [`char`].
334    /// 
335    /// * [Conjoin](Sep::Conjoin): Yield the matched [`char`] while keeping it in the iterator.
336    fn sep_with(self, sep: Sep) -> SepConfig<Self> {
337        SepConfig { 
338            pattern: self, 
339            config: sep,
340        }
341    }
342}
343
344impl<P> SetSep for P where P: Pattern + Sized {}
345
346/// Convert a `char` or `&str` match pattern into a `Pattern` 
347#[macro_export]
348macro_rules! pat {
349    ($p: pat) => {
350        |c: char| matches!(c, $p)
351    };
352    (! $p: pat) => {
353        |c: char| !matches!(c, $p)
354    };
355    ($e: expr => $p: pat ) => {
356        (|s: str| matches!(c, $p)).expecting($e)
357    };
358    ($e: expr => ! $p: pat ) => {
359        (|s: str| !matches!(c, $p)).expecting($e)
360    };
361}