#![no_std]
pub trait Constr {
const STR: &'static str;
}
pub unsafe trait Ascii: Constr {}
pub unsafe trait Caseless: Constr {}
pub unsafe trait Nonempty: Constr {}
#[allow(missing_debug_implementations)]
pub struct Empty;
impl Constr for Empty {
const STR: &'static str = "";
}
unsafe impl Caseless for Empty {}
unsafe impl Ascii for Empty {}
mod utf8 {
const CONT_MASK: u8 = 0b0011_1111;
const fn utf8_first_byte(byte: u8, width: u32) -> u32 {
(byte & (0x7F >> width)) as u32
}
const fn utf8_acc_cont_byte(ch: u32, byte: u8) -> u32 {
(ch << 6) | (byte & CONT_MASK) as u32
}
pub(super) const unsafe fn next_code_point(bytes: &mut &[u8]) -> Option<char> {
let Some((x, xs)) = bytes.split_first() else {
return None;
};
*bytes = xs;
let x = *x;
if x < 128 {
return Some(x as char);
}
let init = utf8_first_byte(x, 2);
let (y, ys) = unsafe { bytes.split_first().unwrap_unchecked() };
*bytes = ys;
let y = *y;
let mut ch = utf8_acc_cont_byte(init, y);
if x >= 0xE0 {
let (z, zs) = unsafe { bytes.split_first().unwrap_unchecked() };
*bytes = zs;
let z = *z;
let y_z = utf8_acc_cont_byte((y & CONT_MASK) as u32, z);
ch = init << 12 | y_z;
if x >= 0xF0 {
let (w, ws) = unsafe { bytes.split_first().unwrap_unchecked() };
*bytes = ws;
let w = *w;
ch = (init & 7) << 18 | utf8_acc_cont_byte(y_z, w);
}
}
Some(unsafe { char::from_u32_unchecked(ch) })
}
}
const fn is_titlecase(c: char) -> bool {
matches!(c,
| '\u{01C5}'
| '\u{01C8}'
| '\u{01CB}'
| '\u{01F2}'
| '\u{1F88}'..='\u{1F8F}'
| '\u{1F98}'..='\u{1F9F}'
| '\u{1FA8}'..='\u{1FAF}'
| '\u{1FBC}'
| '\u{1FCC}'
| '\u{1FFC}'
)
}
#[doc(hidden)]
#[allow(non_snake_case)]
pub mod verify {
#[doc(hidden)]
pub const fn Ascii(s: &'static str) -> bool {
s.is_ascii()
}
#[doc(hidden)]
pub const fn Caseless(s: &'static str) -> bool {
let mut bytes = s.as_bytes();
loop {
match unsafe { crate::utf8::next_code_point(&mut bytes) } {
Some(c) if c.is_lowercase() || c.is_uppercase() || crate::is_titlecase(c) => {
return false;
}
Some(_) => (),
None => return true,
}
}
}
}
#[macro_export]
macro_rules! constr {
(
$(
$(#[$attr:meta])*
$vis:vis type $name:ident = $(($($flag:ident),* $(,)?))? $lit:literal;
)*
) => {
$(
$(#[$attr])*
#[allow(missing_debug_implementations)]
$vis struct $name;
impl $crate::Constr for $name {
const STR: &'static str = $lit;
}
unsafe impl $crate::Nonempty for $name {}
const _: () = {
assert!(
!$lit.is_empty(),
concat!(stringify!($lit), " was empty"),
);
};
$(
$(
unsafe impl $crate::$flag for $name {}
const _: () = {
assert!(
$crate::verify::$flag($lit),
concat!(stringify!($lit), " did not satisfy ", stringify!($flag)),
);
};
)*
)*
)*
};
}