mod table;
use table::{ALPHABETIC, DECIMAL, DIGIT, NUMERIC, SPACE, XID_CONTINUE, XID_START};
use crate::casing;
use crate::printable::is_printable;
use crate::ranges::among;
#[must_use]
pub fn is_alpha(points: &[u32]) -> bool {
every(points, |cp| among(&ALPHABETIC, cp))
}
#[must_use]
pub fn is_alnum(points: &[u32]) -> bool {
every(points, |cp| among(&ALPHABETIC, cp) || among(&NUMERIC, cp))
}
#[must_use]
pub fn is_decimal(points: &[u32]) -> bool {
every(points, |cp| among(&DECIMAL, cp))
}
#[must_use]
pub fn is_digit(points: &[u32]) -> bool {
every(points, |cp| among(&DIGIT, cp))
}
#[must_use]
pub fn is_numeric(points: &[u32]) -> bool {
every(points, |cp| among(&NUMERIC, cp))
}
#[must_use]
pub fn is_space(points: &[u32]) -> bool {
every(points, |cp| among(&SPACE, cp))
}
#[must_use]
pub fn is_space_point(cp: u32) -> bool {
among(&SPACE, cp)
}
#[must_use]
pub fn is_ascii(points: &[u32]) -> bool {
points.iter().all(|&cp| cp < 0x80)
}
#[must_use]
pub fn is_printable_str(points: &[u32]) -> bool {
points
.iter()
.all(|&cp| char::from_u32(cp).is_some_and(is_printable))
}
#[must_use]
pub fn is_lower(points: &[u32]) -> bool {
leaning(points, casing::is_lowercase, casing::is_uppercase)
}
#[must_use]
pub fn is_upper(points: &[u32]) -> bool {
leaning(points, casing::is_uppercase, casing::is_lowercase)
}
fn leaning(points: &[u32], wanted: fn(u32) -> bool, against: fn(u32) -> bool) -> bool {
let mut found = false;
for &cp in points {
if against(cp) || casing::is_titlecase(cp) {
return false;
}
found |= wanted(cp);
}
found
}
#[must_use]
pub fn is_title(points: &[u32]) -> bool {
let mut found = false;
let mut inside = false;
for &cp in points {
let starts = casing::is_uppercase(cp) || casing::is_titlecase(cp);
let carries = casing::is_lowercase(cp);
if starts && inside || carries && !inside {
return false;
}
if starts || carries {
inside = true;
found = true;
} else {
inside = false;
}
}
found
}
#[must_use]
pub fn is_identifier(points: &[u32]) -> bool {
let Some((&first, rest)) = points.split_first() else {
return false;
};
among(&XID_START, first) && rest.iter().all(|&cp| among(&XID_CONTINUE, cp))
}
fn every(points: &[u32], holds: impl Fn(u32) -> bool) -> bool {
!points.is_empty() && points.iter().all(|&cp| holds(cp))
}