1use core::{iter::FusedIterator, mem::transmute, str};
2
3use crate::{Char, OwnedChar};
4
5#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
6pub struct CharMutRefs<'a> {
21 s: &'a mut [u8],
22}
23
24#[allow(clippy::needless_lifetimes)]
25impl<'a> CharMutRefs<'a> {
26 #[must_use]
27 #[inline]
28 pub const fn as_str<'b>(&'b self) -> &'b str {
30 unsafe { str::from_utf8_unchecked(self.s) }
33 }
34
35 #[must_use]
36 #[inline]
37 pub fn as_str_mut<'b>(&'b mut self) -> &'b mut str {
39 unsafe { str::from_utf8_unchecked_mut(self.s) }
42 }
43
44 #[inline]
45 pub fn owned(self) -> core::iter::Map<Self, fn(&mut Char) -> OwnedChar> {
47 self.map(|x| x.as_owned())
48 }
49}
50
51impl<'a> From<&'a mut str> for CharMutRefs<'a> {
52 #[inline]
53 fn from(value: &'a mut str) -> Self {
54 Self {
55 s: unsafe { value.as_bytes_mut() },
58 }
59 }
60}
61
62impl<'a> Iterator for CharMutRefs<'a> {
63 type Item = &'a mut Char;
64
65 fn next<'b>(&'b mut self) -> Option<Self::Item> {
66 let char = unsafe { str::from_utf8_unchecked(self.s) }.chars().next()?;
69
70 let (char_slice, remaining) = unsafe {
75 transmute::<(&'b mut [u8], &'b mut [u8]), (&'a mut [u8], &'a mut [u8])>(
76 self.s.split_at_mut(char.len_utf8()),
77 )
78 };
79
80 self.s = remaining;
81
82 Some(unsafe { &mut *Char::new_unchecked_mut(char_slice.as_mut_ptr()) })
86 }
87
88 #[inline]
89 fn size_hint(&self) -> (usize, Option<usize>) {
90 let len = self.s.len();
91 ((len + 3) / 4, Some(len))
92 }
93
94 #[inline]
95 fn count(self) -> usize
96 where
97 Self: Sized,
98 {
99 unsafe { str::from_utf8_unchecked(self.s) }.chars().count()
102 }
103
104 #[inline]
105 fn last(mut self) -> Option<Self::Item>
106 where
107 Self: Sized,
108 {
109 self.next_back()
110 }
111
112 fn nth<'b>(&'b mut self, n: usize) -> Option<Self::Item> {
113 let (index, char) = unsafe { str::from_utf8_unchecked(self.s) }
116 .char_indices()
117 .nth(n)?;
118
119 let (prefix, remaining) = unsafe {
124 transmute::<(&'b mut [u8], &'b mut [u8]), (&'a mut [u8], &'a mut [u8])>(
125 self.s.split_at_mut(index + char.len_utf8()),
126 )
127 };
128
129 self.s = remaining;
130 let char_slice = &mut prefix[index..];
131
132 Some(unsafe { &mut *Char::new_unchecked_mut(char_slice.as_mut_ptr()) })
136 }
137}
138
139impl<'a> DoubleEndedIterator for CharMutRefs<'a> {
140 fn next_back<'b>(&'b mut self) -> Option<Self::Item> {
141 let char = unsafe { str::from_utf8_unchecked(self.s) }
144 .chars()
145 .next_back()?;
146
147 let (remaining, char_slice) = unsafe {
152 transmute::<(&'b mut [u8], &'b mut [u8]), (&'a mut [u8], &'a mut [u8])>(
153 self.s.split_at_mut(self.s.len() - char.len_utf8()),
154 )
155 };
156
157 self.s = remaining;
158
159 Some(unsafe { &mut *Char::new_unchecked_mut(char_slice.as_mut_ptr()) })
163 }
164
165 fn nth_back<'b>(&'b mut self, n: usize) -> Option<Self::Item> {
166 let (index, char) = unsafe { str::from_utf8_unchecked(self.s) }
169 .char_indices()
170 .nth_back(n)?;
171
172 let (remaining, prefix) = unsafe {
177 transmute::<(&'b mut [u8], &'b mut [u8]), (&'a mut [u8], &'a mut [u8])>(
178 self.s.split_at_mut(index),
179 )
180 };
181
182 self.s = remaining;
183 let char_slice = &mut prefix[..char.len_utf8()];
184
185 Some(unsafe { &mut *Char::new_unchecked_mut(char_slice.as_mut_ptr()) })
189 }
190}
191
192impl<'a> FusedIterator for CharMutRefs<'a> {}
193
194#[cfg(test)]
195mod test {
196 use crate::{
197 test::{test_str_owned, TEST_STR},
198 StrExt,
199 };
200
201 use super::CharMutRefs;
202
203 #[test]
204 fn test_forwards() {
205 let mut s = test_str_owned();
206 let mut iter = CharMutRefs::from(&mut *s);
207
208 for expected in TEST_STR.chars() {
209 let actual = iter.next().expect("expected a character ref");
210
211 assert_eq!(actual.len(), expected.len_utf8());
212 assert_eq!(actual.as_char(), expected);
213 }
214
215 assert!(iter.next().is_none(), "expected no more character refs");
216
217 let size_hint = iter.size_hint();
218 assert_eq!(size_hint.0, 0);
219 assert_eq!(size_hint.1, Some(0));
220 }
221
222 #[test]
223 fn test_nth() {
224 let mut s = test_str_owned();
225
226 for step in 0..4 {
227 let mut iter = CharMutRefs::from(&mut *s);
228 let mut expected_chars = TEST_STR.chars();
229
230 while let Some(expected) = expected_chars.nth(step) {
231 let actual = iter.nth(step).expect("expected a character ref");
232
233 assert_eq!(actual.len(), expected.len_utf8());
234 assert_eq!(actual.as_char(), expected);
235 }
236
237 assert!(iter.nth(step).is_none(), "expected no more character refs");
238 }
239
240 assert!(CharMutRefs::from(&mut *s).nth(4).is_none());
241 }
242
243 #[test]
244 fn test_backwards() {
245 let mut s = test_str_owned();
246 let mut iter = CharMutRefs::from(&mut *s);
247
248 for expected in TEST_STR.chars().rev() {
249 let actual = iter.next_back().expect("expected a character ref");
250
251 assert_eq!(actual.len(), expected.len_utf8());
252 assert_eq!(actual.as_char(), expected);
253 }
254
255 assert!(
256 iter.next_back().is_none(),
257 "expected no more character refs"
258 );
259
260 let size_hint = iter.size_hint();
261 assert_eq!(size_hint.0, 0);
262 assert_eq!(size_hint.1, Some(0));
263 }
264
265 #[test]
266 fn test_nth_backwards() {
267 let mut s = test_str_owned();
268
269 for step in 0..4 {
270 let mut iter = CharMutRefs::from(&mut *s);
271 let mut expected_chars = TEST_STR.chars();
272
273 while let Some(expected) = expected_chars.nth_back(step) {
274 let actual = iter.nth_back(step).expect("expected a character ref");
275
276 assert_eq!(actual.len(), expected.len_utf8());
277 assert_eq!(actual.as_char(), expected);
278 }
279
280 assert!(
281 iter.nth_back(step).is_none(),
282 "expected no more character refs"
283 );
284 }
285
286 assert!(CharMutRefs::from(&mut *s).nth_back(4).is_none());
287 }
288
289 #[test]
290 fn test_mut() {
291 let s = "abcd";
292 let mut s2 = Box::<str>::from(s);
293
294 for char in s2.mut_iter() {
295 char.make_ascii_uppercase();
296 }
297
298 assert_eq!(&*s2, s.to_ascii_uppercase());
299 }
300}