#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PluralCategory {
Zero,
One,
Two,
Few,
Many,
Other,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct PluralOperands {
pub n: f64,
pub i: u64,
pub v: u32,
pub w: u32,
pub f: u64,
pub t: u64,
pub c: u32,
}
impl PluralOperands {
#[must_use]
pub const fn from_int(value: i64) -> Self {
let i = value.unsigned_abs();
PluralOperands {
n: i as f64,
i,
v: 0,
w: 0,
f: 0,
t: 0,
c: 0,
}
}
#[must_use]
pub fn parse(s: &str) -> Option<Self> {
let s = s.trim();
let s = s.strip_prefix(['-', '+']).unwrap_or(s);
let (int_str, frac_str) = match s.split_once('.') {
Some((a, b)) => (a, b),
None => (s, ""),
};
if int_str.is_empty() || !int_str.bytes().all(|b| b.is_ascii_digit()) {
return None;
}
if !frac_str.bytes().all(|b| b.is_ascii_digit()) {
return None;
}
let i: u64 = int_str.parse().ok()?;
let v = frac_str.len() as u32;
let f: u64 = if frac_str.is_empty() {
0
} else {
frac_str.parse().ok()?
};
let trimmed = frac_str.trim_end_matches('0');
let w = trimmed.len() as u32;
let t: u64 = if trimmed.is_empty() {
0
} else {
trimmed.parse().ok()?
};
let n: f64 = s.parse().ok()?;
Some(PluralOperands {
n,
i,
v,
w,
f,
t,
c: 0,
})
}
}
#[doc(hidden)]
#[must_use]
pub fn in_set(x: f64, ranges: &[(f64, f64)]) -> bool {
x % 1.0 == 0.0 && ranges.iter().any(|&(a, b)| x >= a && x <= b)
}
#[must_use]
pub fn plural_category(lang: &str, operands: &PluralOperands) -> PluralCategory {
select(
operands,
lang,
crate::unicode::generated::plurals::plural_category,
)
}
#[must_use]
pub fn ordinal_category(lang: &str, operands: &PluralOperands) -> PluralCategory {
select(
operands,
lang,
crate::unicode::generated::plurals::ordinal_category,
)
}
fn select(
operands: &PluralOperands,
lang: &str,
lookup: fn(&str, &PluralOperands) -> Option<PluralCategory>,
) -> PluralCategory {
let mut buf = [0u8; 40];
let bytes = lang.as_bytes();
let len = bytes.len().min(buf.len());
for k in 0..len {
let b = bytes[k].to_ascii_lowercase();
buf[k] = if b == b'_' { b'-' } else { b };
}
let norm = core::str::from_utf8(&buf[..len]).unwrap_or("");
let mut end = norm.len();
loop {
if let Some(cat) = lookup(&norm[..end], operands) {
return cat;
}
match norm[..end].rfind('-') {
Some(i) => end = i,
None => return PluralCategory::Other,
}
}
}