use core::convert::{TryFrom, TryInto};
use core::iter::FusedIterator;
use core::str::FromStr;
#[cfg(feature = "alloc")]
use alloc::borrow::Cow;
#[cfg(feature = "alloc")]
use alloc::collections::TryReserveError;
use crate::InvalidCaseMappingMode;
use crate::unicode::mapping::Mode;
mod ascii;
mod full;
pub use ascii::make_ascii_uppercase;
#[cfg(feature = "alloc")]
pub use ascii::to_ascii_uppercase;
#[derive(Debug, Clone)]
#[allow(variant_size_differences)]
enum Inner<'a> {
Empty,
Full(full::Uppercase<'a>),
Ascii(ascii::Uppercase<'a>),
}
#[derive(Debug, Clone)]
#[must_use = "Uppercase is a Iterator and must be used"]
pub struct Uppercase<'a> {
iter: Inner<'a>,
}
impl Default for Uppercase<'_> {
fn default() -> Self {
Self::new()
}
}
impl<'a> Uppercase<'a> {
pub const fn new() -> Self {
Self { iter: Inner::Empty }
}
pub const fn with_slice(slice: &'a [u8]) -> Self {
if slice.is_ascii() {
return Self::with_ascii_slice(slice);
}
Self {
iter: Inner::Full(full::Uppercase::with_slice(slice)),
}
}
pub const fn with_ascii_slice(slice: &'a [u8]) -> Self {
Self {
iter: Inner::Ascii(ascii::Uppercase::with_slice(slice)),
}
}
pub(crate) const fn with_mode(slice: &'a [u8], mode: Mode) -> Self {
if !mode.is_turkic() && slice.is_ascii() {
return Self::with_ascii_slice(slice);
}
Self {
iter: Inner::Full(full::Uppercase::with_mode(slice, mode)),
}
}
}
impl Iterator for Uppercase<'_> {
type Item = u8;
fn next(&mut self) -> Option<Self::Item> {
match self.iter {
Inner::Empty => None,
Inner::Full(ref mut iter) => iter.next(),
Inner::Ascii(ref mut iter) => iter.next(),
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
match self.iter {
Inner::Empty => (0, Some(0)),
Inner::Full(ref iter) => iter.size_hint(),
Inner::Ascii(ref iter) => iter.size_hint(),
}
}
fn count(self) -> usize {
match self.iter {
Inner::Empty => 0,
Inner::Full(iter) => iter.count(),
Inner::Ascii(iter) => iter.count(),
}
}
}
impl FusedIterator for Uppercase<'_> {}
#[derive(Default, Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum UppercaseMode {
#[default]
Full,
Ascii,
Turkic,
Lithuanian,
}
impl TryFrom<&str> for UppercaseMode {
type Error = InvalidCaseMappingMode;
#[inline]
fn try_from(value: &str) -> Result<Self, Self::Error> {
value.as_bytes().try_into()
}
}
impl TryFrom<Option<&str>> for UppercaseMode {
type Error = InvalidCaseMappingMode;
#[inline]
fn try_from(value: Option<&str>) -> Result<Self, Self::Error> {
value.map(str::as_bytes).try_into()
}
}
impl TryFrom<&[u8]> for UppercaseMode {
type Error = InvalidCaseMappingMode;
#[inline]
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
match value {
b"ascii" => Ok(Self::Ascii),
b"turkic" => Ok(Self::Turkic),
b"lithuanian" => Ok(Self::Lithuanian),
_ => Err(InvalidCaseMappingMode::new()),
}
}
}
impl TryFrom<Option<&[u8]>> for UppercaseMode {
type Error = InvalidCaseMappingMode;
#[inline]
fn try_from(value: Option<&[u8]>) -> Result<Self, Self::Error> {
match value {
None => Ok(Self::Full),
Some(b"ascii") => Ok(Self::Ascii),
Some(b"turkic") => Ok(Self::Turkic),
Some(b"lithuanian") => Ok(Self::Lithuanian),
Some(_) => Err(InvalidCaseMappingMode::new()),
}
}
}
impl FromStr for UppercaseMode {
type Err = InvalidCaseMappingMode;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.try_into()
}
}
pub const fn uppercase(slice: &[u8], options: UppercaseMode) -> Uppercase<'_> {
match options {
UppercaseMode::Full | UppercaseMode::Lithuanian => Uppercase::with_slice(slice),
UppercaseMode::Ascii => Uppercase::with_ascii_slice(slice),
UppercaseMode::Turkic => Uppercase::with_mode(slice, Mode::TurkicUpper),
}
}
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
#[inline]
pub fn try_to_uppercase(
slice: &[u8],
options: UppercaseMode,
) -> Result<Cow<'_, [u8]>, TryReserveError> {
crate::collect::try_collect(slice, uppercase(slice, options))
}
#[cfg(test)]
mod tests {
use core::{convert::TryInto, str::FromStr};
use super::UppercaseMode;
use crate::InvalidCaseMappingMode;
use alloc::vec::Vec;
use bstr::ByteSlice;
use super::Uppercase;
#[test]
fn empty() {
let iter = Uppercase::new();
assert_eq!(iter.collect::<Vec<_>>().as_bstr(), b"".as_bstr());
let iter = Uppercase::default();
assert_eq!(iter.collect::<Vec<_>>().as_bstr(), b"".as_bstr());
let iter = Uppercase::with_slice(b"");
assert_eq!(iter.collect::<Vec<_>>().as_bstr(), b"".as_bstr());
let iter = Uppercase::with_ascii_slice(b"");
assert_eq!(iter.collect::<Vec<_>>().as_bstr(), b"".as_bstr());
}
#[test]
fn size_hint() {
assert_eq!(Uppercase::new().size_hint(), (0, Some(0)));
assert_eq!(Uppercase::default().size_hint(), (0, Some(0)));
assert_eq!(Uppercase::with_slice(b"").size_hint(), (0, Some(0)));
assert_eq!(Uppercase::with_slice(b"abc, xyz").size_hint(), (8, Some(8)));
assert_eq!(
Uppercase::with_slice(b"abc, \xFF\xFE, xyz").size_hint(),
(3, Some(144))
);
assert_eq!(
Uppercase::with_slice("�".as_bytes()).size_hint(),
(1, Some(36))
);
assert_eq!(
Uppercase::with_slice("Έτος".as_bytes()).size_hint(),
(2, Some(96))
);
assert_eq!(
Uppercase::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!(
Uppercase::with_slice(&utf8_with_invalid_bytes).size_hint(),
(3, Some(120))
);
assert_eq!(
Uppercase::with_ascii_slice(b"abc, xyz").size_hint(),
(8, Some(8))
);
assert_eq!(
Uppercase::with_ascii_slice(b"abc, \xFF\xFE, xyz").size_hint(),
(12, Some(12))
);
assert_eq!(
Uppercase::with_ascii_slice("�".as_bytes()).size_hint(),
(3, Some(3))
);
assert_eq!(
Uppercase::with_ascii_slice("Έτος".as_bytes()).size_hint(),
(8, Some(8))
);
assert_eq!(
Uppercase::with_ascii_slice("ZȺȾ".as_bytes()).size_hint(),
(5, Some(5))
);
let mut utf8_with_invalid_bytes = b"\xFF\xFE".to_vec();
utf8_with_invalid_bytes.extend_from_slice("Έτος".as_bytes());
assert_eq!(
Uppercase::with_ascii_slice(&utf8_with_invalid_bytes).size_hint(),
(10, Some(10))
);
}
#[test]
fn count() {
assert_eq!(Uppercase::new().count(), 0);
assert_eq!(Uppercase::default().count(), 0);
assert_eq!(Uppercase::with_slice(b"").count(), 0);
assert_eq!(Uppercase::with_slice(b"abc, xyz").count(), 8);
assert_eq!(Uppercase::with_slice(b"abc, \xFF\xFE, xyz").count(), 12);
assert_eq!(Uppercase::with_slice("�".as_bytes()).count(), 3);
assert_eq!(Uppercase::with_slice("Έτος".as_bytes()).count(), 8);
assert_eq!(Uppercase::with_slice("zⱥⱦ".as_bytes()).count(), 5);
let mut utf8_with_invalid_bytes = b"\xFF\xFE".to_vec();
utf8_with_invalid_bytes.extend_from_slice("Έτος".as_bytes());
assert_eq!(Uppercase::with_slice(&utf8_with_invalid_bytes).count(), 10);
assert_eq!(Uppercase::with_ascii_slice(b"abc, xyz").count(), 8);
assert_eq!(
Uppercase::with_ascii_slice(b"abc, \xFF\xFE, xyz").count(),
12
);
assert_eq!(Uppercase::with_ascii_slice("�".as_bytes()).count(), 3);
assert_eq!(Uppercase::with_ascii_slice("Έτος".as_bytes()).count(), 8);
assert_eq!(Uppercase::with_ascii_slice("ZȺȾ".as_bytes()).count(), 5);
let mut utf8_with_invalid_bytes = b"\xFF\xFE".to_vec();
utf8_with_invalid_bytes.extend_from_slice("Έτος".as_bytes());
assert_eq!(
Uppercase::with_ascii_slice(&utf8_with_invalid_bytes).count(),
10
);
}
#[test]
fn size_hint_covers_count() {
let iter = Uppercase::new();
let (min, max) = iter.size_hint();
let count = iter.count();
assert!(min <= count);
assert!(count <= max.unwrap());
}
#[test]
fn test_uppercase_mode_parsing() {
assert_eq!(UppercaseMode::from_str("ascii"), Ok(UppercaseMode::Ascii));
assert_eq!(UppercaseMode::from_str("turkic"), Ok(UppercaseMode::Turkic));
assert_eq!(
UppercaseMode::from_str("lithuanian"),
Ok(UppercaseMode::Lithuanian)
);
assert_eq!(
UppercaseMode::from_str("full"),
Err(InvalidCaseMappingMode::new())
);
}
#[test]
fn test_uppercase_mode_conversion() {
let mut mode: UppercaseMode;
mode = "turkic".try_into().unwrap();
assert_eq!(mode, UppercaseMode::Turkic);
mode = Some("turkic").try_into().unwrap();
assert_eq!(mode, UppercaseMode::Turkic);
mode = b"turkic"[..].try_into().unwrap();
assert_eq!(mode, UppercaseMode::Turkic);
mode = Some(&b"turkic"[..]).try_into().unwrap();
assert_eq!(mode, UppercaseMode::Turkic);
}
}