use std::{
cmp::Ordering,
num::NonZeroU32,
str::Chars,
};
include!(concat!(env!("OUT_DIR"), "/adbyss-idna.rs"));
#[repr(u8)]
#[derive(Clone, Copy)]
pub(super) enum CharKind {
Valid,
Ignored,
Mapped(u8, u8, u8),
}
impl CharKind {
pub(super) fn from_char(ch: char) -> Option<Self> {
if let '-'..='.' | '0'..='9' | 'a'..='z' = ch { Some(Self::Valid) }
else { map_get(ch as u32) }
}
#[inline]
pub(super) fn is_valid(ch: char) -> bool {
matches!(Self::from_char(ch), Some(Self::Valid))
}
}
pub(super) struct IdnaChars<'a> {
chars: Chars<'a>,
remap: Option<(usize, u8)>,
error: &'a mut bool,
}
impl<'a> IdnaChars<'a> {
pub(super) fn new(src: &'a str, error: &'a mut bool) -> Self {
Self {
chars: src.chars(),
remap: None,
error,
}
}
}
impl<'a> Iterator for IdnaChars<'a> {
type Item = char;
fn next(&mut self) -> Option<Self::Item> {
if let Some((pos, len)) = &mut self.remap {
let ch = MAP_STR[*pos];
if *len > 1 {
*pos += 1;
*len -= 1;
}
else { self.remap = None; }
return Some(ch);
}
let ch = self.chars.next()?;
if let '-'..='.' | '0'..='9' | 'a'..='z' = ch { return Some(ch); }
match CharKind::from_char(ch) {
Some(CharKind::Valid) => Some(ch),
Some(CharKind::Mapped(a, b, l)) => {
let pos = u16::from_le_bytes([a, b]) as usize;
let ch = MAP_STR[pos];
if l > 1 { self.remap.replace((pos + 1, l - 1)); }
Some(ch)
},
Some(CharKind::Ignored) => self.next(),
None => {
*self.error = true;
None
},
}
}
}