use core::marker::PhantomData;
use crate::unit::Unit;
pub mod sealed {
pub trait Sealed {}
impl Sealed for crate::utf8::Utf8 {}
impl<const FOREIGN: bool> Sealed for crate::utf16::Utf16<FOREIGN> {}
impl<const FOREIGN: bool> Sealed for crate::utf32::Utf32<FOREIGN> {}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Kind {
Utf8,
Utf16,
Utf32,
}
pub trait Encoding: sealed::Sealed + 'static {
type Unit: Unit;
const KIND: Kind;
const FOREIGN: bool;
const MAX_UNITS: usize;
fn run_length(front: Self::Unit) -> usize;
fn encoded_length(cp: u32) -> usize;
fn encode(cp: u32, out: &mut [Self::Unit]) -> usize;
fn decode(input: &mut &[Self::Unit]) -> u32;
}
#[inline(always)]
pub const fn same_encoding<A: Encoding, B: Encoding>() -> bool {
A::KIND as u8 == B::KIND as u8 && A::FOREIGN == B::FOREIGN
}
pub struct Codepoints<'a, E: Encoding> {
rest: &'a [E::Unit],
_encoding: PhantomData<E>,
}
impl<E: Encoding> Clone for Codepoints<'_, E> {
#[inline(always)]
fn clone(&self) -> Self {
Self { rest: self.rest, _encoding: PhantomData }
}
}
impl<E: Encoding> Iterator for Codepoints<'_, E> {
type Item = u32;
#[inline]
fn next(&mut self) -> Option<u32> {
if self.rest.is_empty() {
None
} else {
Some(E::decode(&mut self.rest))
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
(self.rest.len().div_ceil(E::MAX_UNITS), Some(self.rest.len()))
}
}
#[inline]
pub const fn codepoints<E: Encoding>(input: &[E::Unit]) -> Codepoints<'_, E> {
Codepoints { rest: input, _encoding: PhantomData }
}
#[inline]
pub fn chars<E: Encoding>(input: &[E::Unit]) -> impl Iterator<Item = char> + '_ {
codepoints::<E>(input).map(|cp| char::from_u32(cp).unwrap_or(char::REPLACEMENT_CHARACTER))
}