Skip to main content

cheetah_string/cheetah_string/
pattern.rs

1use alloc::string::String;
2use core::str;
3
4// Sealed trait pattern to support both &str and char in starts_with/ends_with/contains.
5mod private {
6    use alloc::string::String;
7
8    pub trait Sealed {}
9    impl Sealed for char {}
10    impl Sealed for &str {}
11    impl Sealed for &String {}
12
13    pub trait SplitSealed {}
14    impl SplitSealed for char {}
15    impl SplitSealed for &str {}
16}
17
18/// A pattern that can be used with `starts_with` and `ends_with` methods.
19pub trait StrPattern: private::Sealed {
20    #[doc(hidden)]
21    fn as_str_pattern(&self) -> StrPatternImpl<'_>;
22}
23
24#[doc(hidden)]
25pub enum StrPatternImpl<'a> {
26    Char(char),
27    Str(&'a str),
28}
29
30impl StrPattern for char {
31    #[inline]
32    fn as_str_pattern(&self) -> StrPatternImpl<'_> {
33        StrPatternImpl::Char(*self)
34    }
35}
36
37impl StrPattern for &str {
38    #[inline]
39    fn as_str_pattern(&self) -> StrPatternImpl<'_> {
40        StrPatternImpl::Str(self)
41    }
42}
43
44impl StrPattern for &String {
45    #[inline]
46    fn as_str_pattern(&self) -> StrPatternImpl<'_> {
47        StrPatternImpl::Str(self.as_str())
48    }
49}
50
51/// A pattern that can be used with `split` method.
52pub trait SplitPattern<'a>: private::SplitSealed {
53    #[doc(hidden)]
54    fn split_str(self, s: &'a str) -> SplitWrapper<'a>;
55}
56
57impl SplitPattern<'_> for char {
58    fn split_str(self, s: &str) -> SplitWrapper<'_> {
59        SplitWrapper::Char(s.split(self))
60    }
61}
62
63impl<'a> SplitPattern<'a> for &'a str {
64    fn split_str(self, s: &'a str) -> SplitWrapper<'a> {
65        let inner = match single_char_pattern(self) {
66            Some(ch) => SplitStrInner::Char(s.split(ch)),
67            None => SplitStrInner::Str(s.split(self)),
68        };
69
70        SplitWrapper::Str(SplitStr(inner))
71    }
72}
73
74/// Helper struct for splitting strings by a string pattern.
75pub struct SplitStr<'a>(SplitStrInner<'a>);
76
77enum SplitStrInner<'a> {
78    Str(str::Split<'a, &'a str>),
79    Char(str::Split<'a, char>),
80}
81
82#[inline]
83fn single_char_pattern(pattern: &str) -> Option<char> {
84    let mut chars = pattern.chars();
85    let ch = chars.next()?;
86
87    if chars.next().is_none() {
88        Some(ch)
89    } else {
90        None
91    }
92}
93
94impl<'a> Iterator for SplitStr<'a> {
95    type Item = &'a str;
96
97    fn next(&mut self) -> Option<Self::Item> {
98        match &mut self.0 {
99            SplitStrInner::Str(iter) => iter.next(),
100            SplitStrInner::Char(iter) => iter.next(),
101        }
102    }
103}
104
105/// Wrapper for split iterator that supports both char and str patterns.
106pub enum SplitWrapper<'a> {
107    #[doc(hidden)]
108    Char(str::Split<'a, char>),
109    #[doc(hidden)]
110    Str(SplitStr<'a>),
111}
112
113impl<'a> Iterator for SplitWrapper<'a> {
114    type Item = &'a str;
115
116    fn next(&mut self) -> Option<Self::Item> {
117        match self {
118            SplitWrapper::Char(iter) => iter.next(),
119            SplitWrapper::Str(iter) => iter.next(),
120        }
121    }
122}
123
124impl<'a> DoubleEndedIterator for SplitWrapper<'a> {
125    fn next_back(&mut self) -> Option<Self::Item> {
126        match self {
127            SplitWrapper::Char(iter) => iter.next_back(),
128            SplitWrapper::Str(_) => {
129                // String pattern split doesn't support reverse iteration.
130                panic!("split with string pattern does not support reverse iteration")
131            }
132        }
133    }
134}