1use core::fmt;
4use core::mem;
5use core::ops::RangeBounds;
6
7use crate::range::CharsRange;
8use crate::utf8::{self, REPLACEMENT_CHAR};
9use crate::{PartialHandling, StrQueue};
10
11#[derive(Debug, Clone)]
13pub struct IntoChars {
14 inner: alloc::collections::vec_deque::IntoIter<u8>,
16 should_print_incomplete_bytes: bool,
18}
19
20impl IntoChars {
21 #[inline]
23 #[must_use]
24 pub(crate) fn new(mut q: StrQueue, partial_handling: PartialHandling) -> Self {
25 let should_print_incomplete_bytes = partial_handling.is_emit() && (q.len_incomplete() != 0);
26 q.inner.truncate(q.len_complete());
27
28 Self {
29 inner: q.inner.into_iter(),
30 should_print_incomplete_bytes,
31 }
32 }
33}
34
35impl Iterator for IntoChars {
36 type Item = char;
37
38 #[inline]
39 fn next(&mut self) -> Option<Self::Item> {
40 utf8::take_char(&mut self.inner)
41 .map(|(c, _len)| c)
42 .or_else(|| {
43 if mem::replace(&mut self.should_print_incomplete_bytes, false) {
44 Some(REPLACEMENT_CHAR)
45 } else {
46 None
47 }
48 })
49 }
50}
51
52impl fmt::Display for IntoChars {
54 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59 self.clone().try_for_each(|c| c.fmt(f))
60 }
61}
62
63#[derive(Debug, Clone)]
65pub struct Chars<'a> {
66 range: CharsRange<'a>,
68}
69
70impl<'a> Chars<'a> {
71 #[inline]
77 #[must_use]
78 pub(crate) fn new<R>(queue: &'a StrQueue, range: R, partial_handling: PartialHandling) -> Self
79 where
80 R: RangeBounds<usize>,
81 {
82 let mut range = queue.chars_range(range);
83 if partial_handling.is_ignore() {
84 range.trim_last_incomplete_char();
85 }
86 Self { range }
87 }
88
89 #[inline]
91 #[must_use]
92 pub(crate) fn from_range(range: CharsRange<'a>) -> Self {
93 Self { range }
94 }
95}
96
97impl Iterator for Chars<'_> {
98 type Item = char;
99
100 #[inline]
101 fn next(&mut self) -> Option<Self::Item> {
102 self.range.pop_char_replaced()
103 }
104}
105
106impl fmt::Display for Chars<'_> {
107 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108 self.range.fmt(f)
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use crate::{PartialHandling, StrQueue};
115
116 use alloc::string::String;
117
118 fn assert_consistent_with_std(bytes: &[u8]) {
120 let mut queue = StrQueue::new();
121 queue.push_bytes(bytes);
122
123 assert_eq!(
124 queue.chars(PartialHandling::Emit).collect::<String>(),
125 String::from_utf8_lossy(bytes)
126 );
127 assert_eq!(
128 queue.into_chars(PartialHandling::Emit).collect::<String>(),
129 String::from_utf8_lossy(bytes)
130 );
131 }
132
133 #[test]
142 fn consistency_with_std_from_utf8_lossy() {
143 assert_consistent_with_std(b"hello");
144 assert_consistent_with_std(b"\xffhello");
145 assert_consistent_with_std(b"\xff\xffhello");
146 assert_consistent_with_std(b"\xff\xff\xffhello");
147 assert_consistent_with_std(b"\xff\xff\xff\xffhello");
148 assert_consistent_with_std(b"\xff\xff\xff\xff\xffhello");
149
150 assert_consistent_with_std(b"\xe3\x81\x82hello");
151 assert_consistent_with_std(b"\xe3\xe3\x81\x82hello");
152 assert_consistent_with_std(b"\xe3z\xe3\x81\x82hello");
153 assert_consistent_with_std(b"\xe3\x81\xe3\x81\x82hello");
154 assert_consistent_with_std(b"\xe3z\xe3\x81\x82hello");
155 assert_consistent_with_std(b"z\x81\xe3\x81\x82hello");
156 }
157}