use core::fmt;
use core::mem;
use core::ops::RangeBounds;
use crate::range::CharsRange;
use crate::utf8::{self, REPLACEMENT_CHAR};
use crate::{PartialHandling, StrQueue};
#[derive(Debug, Clone)]
pub struct IntoChars {
inner: alloc::collections::vec_deque::IntoIter<u8>,
should_print_incomplete_bytes: bool,
}
impl IntoChars {
#[inline]
#[must_use]
pub(crate) fn new(mut q: StrQueue, partial_handling: PartialHandling) -> Self {
let should_print_incomplete_bytes = partial_handling.is_emit() && (q.len_incomplete() != 0);
q.inner.truncate(q.len_complete());
Self {
inner: q.inner.into_iter(),
should_print_incomplete_bytes,
}
}
}
impl Iterator for IntoChars {
type Item = char;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
utf8::take_char(&mut self.inner)
.map(|(c, _len)| c)
.or_else(|| {
if mem::replace(&mut self.should_print_incomplete_bytes, false) {
Some(REPLACEMENT_CHAR)
} else {
None
}
})
}
}
impl fmt::Display for IntoChars {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.clone().try_for_each(|c| c.fmt(f))
}
}
#[derive(Debug, Clone)]
pub struct Chars<'a> {
range: CharsRange<'a>,
}
impl<'a> Chars<'a> {
#[inline]
#[must_use]
pub(crate) fn new<R>(queue: &'a StrQueue, range: R, partial_handling: PartialHandling) -> Self
where
R: RangeBounds<usize>,
{
let mut range = queue.chars_range(range);
if partial_handling.is_ignore() {
range.trim_last_incomplete_char();
}
Self { range }
}
#[inline]
#[must_use]
pub(crate) fn from_range(range: CharsRange<'a>) -> Self {
Self { range }
}
}
impl Iterator for Chars<'_> {
type Item = char;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.range.pop_char_replaced()
}
}
impl fmt::Display for Chars<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.range.fmt(f)
}
}
#[cfg(test)]
mod tests {
use crate::{PartialHandling, StrQueue};
use alloc::string::String;
fn assert_consistent_with_std(bytes: &[u8]) {
let mut queue = StrQueue::new();
queue.push_bytes(bytes);
assert_eq!(
queue.chars(PartialHandling::Emit).collect::<String>(),
String::from_utf8_lossy(bytes)
);
assert_eq!(
queue.into_chars(PartialHandling::Emit).collect::<String>(),
String::from_utf8_lossy(bytes)
);
}
#[test]
fn consistency_with_std_from_utf8_lossy() {
assert_consistent_with_std(b"hello");
assert_consistent_with_std(b"\xffhello");
assert_consistent_with_std(b"\xff\xffhello");
assert_consistent_with_std(b"\xff\xff\xffhello");
assert_consistent_with_std(b"\xff\xff\xff\xffhello");
assert_consistent_with_std(b"\xff\xff\xff\xff\xffhello");
assert_consistent_with_std(b"\xe3\x81\x82hello");
assert_consistent_with_std(b"\xe3\xe3\x81\x82hello");
assert_consistent_with_std(b"\xe3z\xe3\x81\x82hello");
assert_consistent_with_std(b"\xe3\x81\xe3\x81\x82hello");
assert_consistent_with_std(b"\xe3z\xe3\x81\x82hello");
assert_consistent_with_std(b"z\x81\xe3\x81\x82hello");
}
}