1use core::{iter::FusedIterator, str};
2
3use crate::{Char, OwnedChar};
4
5#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
6pub struct CharRefs<'a> {
20 s: &'a [u8],
21}
22
23impl<'a> CharRefs<'a> {
24 #[must_use]
25 #[inline]
26 pub const fn as_str(&self) -> &'a str {
28 unsafe { str::from_utf8_unchecked(self.s) }
31 }
32
33 #[inline]
34 pub fn owned(self) -> core::iter::Map<Self, fn(&Char) -> OwnedChar> {
36 self.map(Char::as_owned)
37 }
38}
39
40impl<'a> From<&'a str> for CharRefs<'a> {
41 #[inline]
42 fn from(value: &'a str) -> Self {
43 Self {
44 s: value.as_bytes(),
45 }
46 }
47}
48
49impl<'a> Iterator for CharRefs<'a> {
50 type Item = &'a Char;
51
52 fn next(&mut self) -> Option<Self::Item> {
53 let char = unsafe { str::from_utf8_unchecked(self.s) }.chars().next()?;
56
57 let (char_slice, remaining) = self.s.split_at(char.len_utf8());
58 self.s = remaining;
59
60 Some(unsafe { &*Char::new_unchecked(char_slice.as_ptr()) })
64 }
65
66 #[inline]
67 fn size_hint(&self) -> (usize, Option<usize>) {
68 let len = self.s.len();
69 ((len + 3) / 4, Some(len))
70 }
71
72 #[inline]
73 fn count(self) -> usize
74 where
75 Self: Sized,
76 {
77 unsafe { str::from_utf8_unchecked(self.s) }.chars().count()
80 }
81
82 #[inline]
83 fn last(mut self) -> Option<Self::Item>
84 where
85 Self: Sized,
86 {
87 self.next_back()
88 }
89
90 fn nth(&mut self, n: usize) -> Option<Self::Item> {
91 let (index, char) = unsafe { str::from_utf8_unchecked(self.s) }
94 .char_indices()
95 .nth(n)?;
96
97 let (prefix, remaining) = self.s.split_at(index + char.len_utf8());
98 self.s = remaining;
99 let char_slice = &prefix[index..];
100
101 Some(unsafe { &*Char::new_unchecked(char_slice.as_ptr()) })
105 }
106}
107
108impl<'a> DoubleEndedIterator for CharRefs<'a> {
109 fn next_back(&mut self) -> Option<Self::Item> {
110 let char = unsafe { str::from_utf8_unchecked(self.s) }
113 .chars()
114 .next_back()?;
115
116 let (remaining, char_slice) = self.s.split_at(self.s.len() - char.len_utf8());
117 self.s = remaining;
118
119 Some(unsafe { &*Char::new_unchecked(char_slice.as_ptr()) })
123 }
124
125 fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
126 let (index, char) = unsafe { str::from_utf8_unchecked(self.s) }
129 .char_indices()
130 .nth_back(n)?;
131
132 let (remaining, prefix) = self.s.split_at(index);
133 self.s = remaining;
134 let char_slice = &prefix[..char.len_utf8()];
135
136 Some(unsafe { &*Char::new_unchecked(char_slice.as_ptr()) })
140 }
141}
142
143impl<'a> FusedIterator for CharRefs<'a> {}
144
145#[cfg(test)]
146mod test {
147 use crate::test::TEST_STR;
148
149 use super::CharRefs;
150
151 #[test]
152 fn test_forwards() {
153 let mut iter = CharRefs::from(TEST_STR);
154
155 for expected in TEST_STR.chars() {
156 let actual = iter.next().expect("expected a character ref");
157
158 assert_eq!(actual.len(), expected.len_utf8());
159 assert_eq!(actual.as_char(), expected);
160 }
161
162 assert!(iter.next().is_none(), "expected no more character refs");
163
164 let size_hint = iter.size_hint();
165 assert_eq!(size_hint.0, 0);
166 assert_eq!(size_hint.1, Some(0));
167 }
168
169 #[test]
170 fn test_nth() {
171 for step in 0..4 {
172 let mut iter = CharRefs::from(TEST_STR);
173 let mut expected_chars = TEST_STR.chars();
174
175 while let Some(expected) = expected_chars.nth(step) {
176 let actual = iter.nth(step).expect("expected a character ref");
177
178 assert_eq!(actual.len(), expected.len_utf8());
179 assert_eq!(actual.as_char(), expected);
180 }
181
182 assert!(iter.nth(step).is_none(), "expected no more character refs");
183 }
184
185 assert!(CharRefs::from(TEST_STR).nth(4).is_none());
186 }
187
188 #[test]
189 fn test_backwards() {
190 let mut iter = CharRefs::from(TEST_STR);
191
192 for expected in TEST_STR.chars().rev() {
193 let actual = iter.next_back().expect("expected a character ref");
194
195 assert_eq!(actual.len(), expected.len_utf8());
196 assert_eq!(actual.as_char(), expected);
197 }
198
199 assert!(
200 iter.next_back().is_none(),
201 "expected no more character refs"
202 );
203
204 let size_hint = iter.size_hint();
205 assert_eq!(size_hint.0, 0);
206 assert_eq!(size_hint.1, Some(0));
207 }
208
209 #[test]
210 fn test_nth_backwards() {
211 for step in 0..4 {
212 let mut iter = CharRefs::from(TEST_STR);
213 let mut expected_chars = TEST_STR.chars();
214
215 while let Some(expected) = expected_chars.nth_back(step) {
216 let actual = iter.nth_back(step).expect("expected a character ref");
217
218 assert_eq!(actual.len(), expected.len_utf8());
219 assert_eq!(actual.as_char(), expected);
220 }
221
222 assert!(
223 iter.nth_back(step).is_none(),
224 "expected no more character refs"
225 );
226 }
227
228 assert!(CharRefs::from(TEST_STR).nth_back(4).is_none());
229 }
230}