use core::fmt;
use core::iter::FusedIterator;
use core::ops::Range;
use bstr::ByteSlice;
use crate::unicode::mapping::{Mode, lookup};
use crate::unicode::std_case_mapping_iter::CaseMappingIter;
#[derive(Clone)]
#[must_use = "Capitalize is an Iterator and must be used"]
pub struct Capitalize<'a> {
slice: &'a [u8],
next_bytes: [u8; 4],
next_range: Range<usize>,
case_iter: Option<CaseMappingIter>,
mode: Mode,
first: bool,
}
impl fmt::Debug for Capitalize<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Capitalize")
.field("slice", &self.slice.as_bstr())
.field("next_bytes", &self.next_bytes)
.field("next_range", &self.next_range)
.field("case", &self.case_iter)
.field("first", &self.first)
.field("mode", &self.mode)
.finish()
}
}
impl<'a> From<&'a [u8]> for Capitalize<'a> {
fn from(slice: &'a [u8]) -> Self {
Self::with_slice(slice)
}
}
impl<'a> Capitalize<'a> {
pub const fn with_slice(slice: &'a [u8]) -> Self {
Self::with_mode(slice, Mode::Title)
}
pub const fn with_mode(slice: &'a [u8], mode: Mode) -> Self {
Self {
slice,
next_bytes: [0; 4],
next_range: 0..0,
case_iter: None,
first: true,
mode,
}
}
fn buffered_len(&self) -> usize {
let mapped = self
.case_iter
.clone()
.map_or(0, |iter| iter.map(char::len_utf8).sum());
self.next_range.len() + mapped
}
}
impl Iterator for Capitalize<'_> {
type Item = u8;
fn next(&mut self) -> Option<Self::Item> {
if let Some(idx) = self.next_range.next() {
debug_assert!(self.next_bytes.get(idx).is_some());
return Some(self.next_bytes[idx]);
}
if let Some(ch) = self.case_iter.as_mut().and_then(Iterator::next) {
let enc = ch.encode_utf8(&mut self.next_bytes);
self.next_range = 1..enc.len();
debug_assert!(self.next_bytes.get(self.next_range.clone()).is_some());
return Some(self.next_bytes[0]);
}
self.case_iter = None;
if let Some((&byte, remainder)) = self.slice.split_first() {
let special_i = if self.first { b'i' } else { b'I' };
if byte.is_ascii() && !(self.mode.is_turkic() && byte == special_i) {
let mapped = if self.first {
byte.to_ascii_uppercase()
} else {
byte.to_ascii_lowercase()
};
self.slice = remainder;
self.first = false;
return Some(mapped);
}
}
match bstr::decode_utf8(self.slice) {
(_, 0) => None,
(Some(ch), size) => {
self.slice = &self.slice[size..];
let mode = if self.first {
self.first = false;
self.mode
} else if self.mode.is_turkic() {
Mode::TurkicLower
} else {
Mode::Lower
};
let mode = super::georgian::capitalization_mode(ch, mode);
let mut case_iter = lookup(ch, mode);
let ch = case_iter
.next()
.expect("case mapping yields at least one char");
let enc = ch.encode_utf8(&mut self.next_bytes);
self.next_range = 1..enc.len();
debug_assert!(self.next_bytes.get(self.next_range.clone()).is_some());
self.case_iter = Some(case_iter);
Some(self.next_bytes[0])
}
(None, size) => {
let (bytes, remainder) = self.slice.split_at(size);
self.slice = remainder;
debug_assert!(self.next_bytes.get(..bytes.len()).is_some());
self.next_bytes[..bytes.len()].copy_from_slice(bytes);
self.next_range = 1..bytes.len();
Some(self.next_bytes[0])
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
const CASE_MAPPING_MAX_BYTES: usize = 3 * 4;
let buffered = self.buffered_len();
let len = self.slice.len();
if self.slice.is_ascii() && !self.mode.is_turkic() {
let len = buffered + len;
(len, Some(len))
} else {
let min = buffered.saturating_add(len.div_ceil(4));
let max = len
.checked_mul(CASE_MAPPING_MAX_BYTES)
.and_then(|len| len.checked_add(buffered));
(min, max)
}
}
fn count(self) -> usize {
if self.slice.is_ascii() && !self.mode.is_turkic() {
self.buffered_len() + self.slice.len()
} else {
self.fold(0, |acc, _| acc + 1)
}
}
}
impl FusedIterator for Capitalize<'_> {}
#[cfg(test)]
mod tests {
use alloc::{format, vec::Vec};
use bstr::ByteSlice;
use super::Capitalize;
#[test]
fn empty() {
let iter = Capitalize::from(&b""[..]);
assert_eq!(iter.collect::<Vec<_>>().as_bstr(), b"".as_bstr());
}
#[test]
fn ascii() {
let iter = Capitalize::from(&b"abc"[..]);
assert_eq!(iter.collect::<Vec<_>>().as_bstr(), b"Abc".as_bstr());
let iter = Capitalize::from(&b"aBC"[..]);
assert_eq!(iter.collect::<Vec<_>>().as_bstr(), b"Abc".as_bstr());
let iter = Capitalize::from(&b"ABC"[..]);
assert_eq!(iter.collect::<Vec<_>>().as_bstr(), b"Abc".as_bstr());
let iter = Capitalize::from(&b"aBC, 123, ABC, baby you and me girl"[..]);
assert_eq!(
iter.collect::<Vec<_>>().as_bstr(),
b"Abc, 123, abc, baby you and me girl".as_bstr()
);
}
#[test]
fn utf8() {
let s = "ß".as_bytes();
let iter = Capitalize::from(s);
assert_eq!(
iter.collect::<Vec<_>>().as_bstr(),
"Ss".as_bytes().as_bstr()
);
let s = "Αύριο".as_bytes();
let iter = Capitalize::from(s);
assert_eq!(
iter.collect::<Vec<_>>().as_bstr(),
"Αύριο".as_bytes().as_bstr()
);
let s = "Έτος".as_bytes();
let iter = Capitalize::from(s);
assert_eq!(
iter.collect::<Vec<_>>().as_bstr(),
"Έτος".as_bytes().as_bstr()
);
let s = "𐐜 𐐔𐐇𐐝𐐀𐐡𐐇𐐓 𐐙𐐊𐐡𐐝𐐓/𐐝𐐇𐐗𐐊𐐤𐐔 𐐒𐐋𐐗 𐐒𐐌 𐐜 𐐡𐐀𐐖𐐇𐐤𐐓𐐝 𐐱𐑂 𐑄 𐐔𐐇𐐝𐐀𐐡𐐇𐐓 𐐏𐐆𐐅𐐤𐐆𐐚𐐊𐐡𐐝𐐆𐐓𐐆".as_bytes();
let iter = Capitalize::from(s);
assert_eq!(
iter.collect::<Vec<_>>().as_bstr(),
"𐐜 𐐼𐐯𐑅𐐨𐑉𐐯𐐻 𐑁𐐲𐑉𐑅𐐻/𐑅𐐯𐐿𐐲𐑌𐐼 𐐺𐐳𐐿 𐐺𐐴 𐑄 𐑉𐐨𐐾𐐯𐑌𐐻𐑅 𐐱𐑂 𐑄 𐐼𐐯𐑅𐐨𐑉𐐯𐐻 𐐷𐐮𐐭𐑌𐐮𐑂𐐲𐑉𐑅𐐮𐐻𐐮"
.as_bytes()
.as_bstr()
);
let s = "ⱥȾȾZ".as_bytes();
let titlecased = Capitalize::from(s).collect::<Vec<_>>();
assert_eq!(titlecased.as_bstr(), "Ⱥⱦⱦz".as_bytes().as_bstr());
assert_ne!(s.len(), titlecased.len());
}
#[test]
fn invalid_utf8() {
let iter = Capitalize::from(&b"\xFF\xFE"[..]);
assert_eq!(iter.collect::<Vec<u8>>().as_bstr(), b"\xFF\xFE".as_bstr());
let iter = Capitalize::from(&b"ABC\xFF\xFEXYZ"[..]);
assert_eq!(
iter.collect::<Vec<u8>>().as_bstr(),
b"Abc\xFF\xFExyz".as_bstr()
);
let iter = Capitalize::from(&b"abc\xFF\xFEXYZ"[..]);
assert_eq!(
iter.collect::<Vec<u8>>().as_bstr(),
b"Abc\xFF\xFExyz".as_bstr()
);
let iter = Capitalize::from(&b"aB\xF0\x9F\x87Yz"[..]);
assert_eq!(
iter.collect::<Vec<_>>().as_bstr(),
b"Ab\xF0\x9F\x87yz".as_bstr()
);
}
#[test]
fn unicode_replacement_character() {
let s = "�".as_bytes();
let iter = Capitalize::from(s);
assert_eq!(iter.collect::<Vec<_>>().as_bstr(), "�".as_bytes().as_bstr());
}
#[test]
fn dz_to_titlecase() {
let s = "Dž".as_bytes();
let iter = Capitalize::from(s);
assert_eq!(iter.collect::<Vec<_>>().as_bstr(), "Dž".as_bytes().as_bstr());
let s = "DŽ".as_bytes();
let iter = Capitalize::from(s);
assert_eq!(iter.collect::<Vec<_>>().as_bstr(), "Dž".as_bytes().as_bstr());
let s = "dž".as_bytes();
let iter = Capitalize::from(s);
assert_eq!(iter.collect::<Vec<_>>().as_bstr(), "Dž".as_bytes().as_bstr());
}
#[test]
fn latin_capital_i_with_dot_above() {
let s = "İ".as_bytes();
let iter = Capitalize::from(s);
assert_eq!(iter.collect::<Vec<_>>().as_bstr(), "İ");
}
#[test]
fn case_map_to_one_char() {
let s = "ᾂ".as_bytes();
let iter = Capitalize::from(s);
assert_eq!(iter.collect::<Vec<_>>().as_bstr(), "ᾊ".as_bytes().as_bstr());
}
#[test]
fn case_map_to_two_chars() {
let s = "և".as_bytes();
let iter = Capitalize::from(s);
assert_eq!(
iter.collect::<Vec<_>>().as_bstr(),
"Եւ".as_bytes().as_bstr()
);
let s = "ẙ".as_bytes();
let iter = Capitalize::from(s);
assert_eq!(
iter.collect::<Vec<_>>().as_bstr(),
"Y\u{30a}".as_bytes().as_bstr()
);
let s = "ﬗ".as_bytes();
let iter = Capitalize::from(s);
assert_eq!(
iter.collect::<Vec<_>>().as_bstr(),
"Մխ".as_bytes().as_bstr()
);
}
#[test]
fn case_map_to_three_chars() {
let s = "ffi".as_bytes();
let iter = Capitalize::from(s);
assert_eq!(iter.collect::<Vec<_>>().as_bstr(), b"Ffi".as_bstr());
let s = "ὖ".as_bytes();
let iter = Capitalize::from(s);
assert_eq!(
iter.collect::<Vec<_>>().as_bstr(),
"Υ\u{313}\u{342}".as_bytes().as_bstr()
);
let s = "ῷ".as_bytes();
let iter = Capitalize::from(s);
assert_eq!(
iter.collect::<Vec<_>>().as_bstr(),
"Ω\u{342}\u{345}".as_bytes().as_bstr()
);
}
#[test]
fn size_hint() {
assert_eq!(Capitalize::with_slice(b"").size_hint(), (0, Some(0)));
assert_eq!(
Capitalize::with_slice(b"abc, xyz").size_hint(),
(8, Some(8))
);
assert_eq!(
Capitalize::with_slice(b"abc, \xFF\xFE, xyz").size_hint(),
(3, Some(144))
);
assert_eq!(
Capitalize::with_slice("�".as_bytes()).size_hint(),
(1, Some(36))
);
assert_eq!(
Capitalize::with_slice("Έτος".as_bytes()).size_hint(),
(2, Some(96))
);
assert_eq!(
Capitalize::with_slice("ZȺȾ".as_bytes()).size_hint(),
(2, Some(60))
);
let mut utf8_with_invalid_bytes = b"\xFF\xFE".to_vec();
utf8_with_invalid_bytes.extend_from_slice("Έτος".as_bytes());
assert_eq!(
Capitalize::with_slice(&utf8_with_invalid_bytes).size_hint(),
(3, Some(120))
);
}
#[test]
fn count() {
assert_eq!(Capitalize::with_slice(b"").count(), 0);
assert_eq!(Capitalize::with_slice(b"abc, xyz").count(), 8);
assert_eq!(Capitalize::with_slice(b"abc, \xFF\xFE, xyz").count(), 12);
assert_eq!(Capitalize::with_slice("�".as_bytes()).count(), 3);
assert_eq!(Capitalize::with_slice("Έτος".as_bytes()).count(), 8);
assert_eq!(Capitalize::with_slice("ZȺȾ".as_bytes()).count(), 7);
let mut utf8_with_invalid_bytes = b"\xFF\xFE".to_vec();
utf8_with_invalid_bytes.extend_from_slice("Έτος".as_bytes());
assert_eq!(Capitalize::with_slice(&utf8_with_invalid_bytes).count(), 10);
}
#[test]
fn size_hint_covers_count() {
let iter = Capitalize::with_slice(b"");
let (min, max) = iter.size_hint();
let count = iter.count();
assert!(min <= count);
assert!(count <= max.unwrap());
let iter = Capitalize::with_slice(b"abc, xyz");
let (min, max) = iter.size_hint();
let count = iter.count();
assert!(min <= count);
assert!(count <= max.unwrap());
let iter = Capitalize::with_slice(b"abc, \xFF\xFE, xyz");
let (min, max) = iter.size_hint();
let count = iter.count();
assert!(min <= count);
assert!(count <= max.unwrap());
let iter = Capitalize::with_slice("�".as_bytes());
let (min, max) = iter.size_hint();
let count = iter.count();
assert!(min <= count);
assert!(count <= max.unwrap());
let iter = Capitalize::with_slice("Έτος".as_bytes());
let (min, max) = iter.size_hint();
let count = iter.count();
assert!(min <= count);
assert!(count <= max.unwrap());
let iter = Capitalize::with_slice("ZȺȾ".as_bytes());
let (min, max) = iter.size_hint();
let count = iter.count();
assert!(min <= count);
assert!(count <= max.unwrap());
let mut utf8_with_invalid_bytes = b"\xFF\xFE".to_vec();
utf8_with_invalid_bytes.extend_from_slice("Έτος".as_bytes());
let iter = Capitalize::with_slice(&utf8_with_invalid_bytes);
let (min, max) = iter.size_hint();
let count = iter.count();
assert!(min <= count);
assert!(count <= max.unwrap());
}
#[test]
fn test_fmt() {
let s = "Αύριο".as_bytes();
let iter = Capitalize::from(s);
assert_eq!(
format!("{iter:?}"),
"Capitalize { slice: \"Αύριο\", next_bytes: [0, 0, 0, 0], next_range: 0..0, case: None, first: true, mode: Title }"
);
}
}