string_iter/
interval.rs

1use core::num::NonZeroUsize;
2
3use crate::{pattern::{Pattern, Never}, prelude::Sep};
4
5
6/// Defines a repeating pattern [`Interval`](crate::patterns::Interval)
7/// 
8/// `interval!(4)` means every `4` [`char`]s
9/// 
10/// `interval!(4, 1)` means every `4` [`char`]s,
11/// then every `1` [`char`]
12/// 
13/// `interval!(4 => 3)` means every `4` [`char`]s, 
14/// starting from `3`.
15/// 
16/// `interval!(4 => -10)` means match 10 [`char`]s, 
17/// then every `4` [`char`].
18/// 
19/// `interval!(4, 2 => 1)` means every `4` [`char`]s,
20/// then every `2` [`char`]s,
21/// starting from `1`
22/// 
23/// Note that unlike most other patterns, 
24/// this uses `[Sep::Yield]` by default.
25#[macro_export]
26macro_rules! interval {
27    ($n: expr) => {
28        ::string_iter::patterns::Interval::new(0, [$n])
29    };
30    ($a: expr, $($b: expr),*) => {
31        ::string_iter::patterns::Interval::new(0, [$a, $($b),*])
32    };
33    ($n: expr => $adv: expr) => {
34        ::string_iter::patterns::Interval::new($adv, [$n])
35    };
36    ($a: expr, $($b: expr),* => $adv: expr) => {
37        ::string_iter::patterns::Interval::new($adv, [$a, $($b),*])
38    };
39}
40
41/// A pattern of substrings with repeating lengths.
42/// 
43/// See the [`interval`] macro for more information.
44pub struct Interval<const N: usize> {
45    cursor: isize,
46    interval: [NonZeroUsize; N],
47}
48
49impl<const N: usize> Interval<N> {
50    #[doc(hidden)]
51    pub const fn new(mut cursor: isize, lengths: [usize; N]) -> Self {
52        assert!(N != 0);
53        let mut count = 0;
54        let mut sum = 0;
55        let mut interval = [NonZeroUsize::MIN; N];
56        while count < N {
57            assert!(lengths[count] > 0, "expected non-zero length");
58            sum += lengths[count];
59            interval[count] = match NonZeroUsize::new(sum){
60                Some(v) => v,
61                None => panic!("expected non-zero length")
62            };
63            count += 1;
64        }
65        if cursor.is_positive() {
66            cursor = cursor % interval[N-1].get() as isize;
67        }
68        Self {
69            cursor, 
70            interval,
71        }
72    }
73
74    #[doc(hidden)]
75    const fn len(&self) -> isize{
76        self.interval[N-1].get() as isize
77    }
78}
79
80impl<const N: usize> Pattern for Interval<N> {
81    type Err = Never;
82
83    fn sep(&self) -> Sep {
84        // using Retain is not logically consistent here
85        Sep::Yield
86    }
87
88    fn matches(&mut self, _: char, _: &str) -> Result<bool, Self::Err> {
89        self.cursor += 1;
90        if self.cursor.is_negative() {
91            return Ok(false);
92        }
93        self.cursor = self.cursor % self.len();
94        Ok(self.cursor == 0 || 
95            self.interval.iter().any(|x| x.get() == self.cursor as usize))
96    }
97}