use alloc::string::String;
use azul_css::props::style::lists::StyleListStyleType;
#[must_use]
pub fn format_counter(value: i32, style: StyleListStyleType) -> String {
match style {
StyleListStyleType::None => String::new(),
StyleListStyleType::Disc => "•".to_string(),
StyleListStyleType::Circle => "◦".to_string(),
StyleListStyleType::Square => "▪".to_string(),
StyleListStyleType::Decimal => value.to_string(),
StyleListStyleType::DecimalLeadingZero => format!("{value:02}"),
StyleListStyleType::LowerAlpha => decimal_fallback(value, with_sign(value, |n| to_alphabetic(n, false))),
StyleListStyleType::UpperAlpha => decimal_fallback(value, with_sign(value, |n| to_alphabetic(n, true))),
StyleListStyleType::LowerRoman => with_sign(value, |n| to_roman(n, false)),
StyleListStyleType::UpperRoman => with_sign(value, |n| to_roman(n, true)),
StyleListStyleType::LowerGreek => decimal_fallback(value, with_sign(value, |n| to_greek(n, false))),
StyleListStyleType::UpperGreek => decimal_fallback(value, with_sign(value, |n| to_greek(n, true))),
}
}
fn decimal_fallback(value: i32, formatted: String) -> String {
if formatted.is_empty() {
value.to_string()
} else {
formatted
}
}
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] fn with_sign<F: Fn(usize) -> String>(value: i32, format: F) -> String {
if value < 0 {
let magnitude = i64::from(value).unsigned_abs() as usize;
format!("-{}", format(magnitude))
} else {
format(value as usize)
}
}
#[allow(clippy::cast_possible_truncation)] pub(crate) fn to_alphabetic(mut num: usize, uppercase: bool) -> String {
if num == 0 {
return String::new();
}
let mut result = String::new();
let base = if uppercase { b'A' } else { b'a' };
while num > 0 {
let remainder = ((num - 1) % 26) as u8;
result.insert(0, (base + remainder) as char);
num = (num - 1) / 26;
}
result
}
pub(crate) fn to_roman(mut num: usize, uppercase: bool) -> String {
const MAX_ROMAN: usize = 3999;
if num == 0 {
return "0".to_string();
}
if num > MAX_ROMAN {
return num.to_string();
}
let numerals = [
(1000, "m"),
(900, "cm"),
(500, "d"),
(400, "cd"),
(100, "c"),
(90, "xc"),
(50, "l"),
(40, "xl"),
(10, "x"),
(9, "ix"),
(5, "v"),
(4, "iv"),
(1, "i"),
];
let mut result = String::new();
for (value, numeral) in &numerals {
while num >= *value {
result.push_str(numeral);
num -= value;
}
}
if uppercase {
result.to_uppercase()
} else {
result
}
}
pub(crate) fn to_greek(num: usize, uppercase: bool) -> String {
const GREEK_LOWER: &[char] = &[
'α', 'β', 'γ', 'δ', 'ε', 'ζ', 'η', 'θ', 'ι', 'κ', 'λ', 'μ', 'ν', 'ξ', 'ο', 'π', 'ρ', 'σ',
'τ', 'υ', 'φ', 'χ', 'ψ', 'ω',
];
const GREEK_UPPER: &[char] = &[
'Α', 'Β', 'Γ', 'Δ', 'Ε', 'Ζ', 'Η', 'Θ', 'Ι', 'Κ', 'Λ', 'Μ', 'Ν', 'Ξ', 'Ο', 'Π', 'Ρ', 'Σ',
'Τ', 'Υ', 'Φ', 'Χ', 'Ψ', 'Ω',
];
if num == 0 {
return String::new();
}
let letters = if uppercase { GREEK_UPPER } else { GREEK_LOWER };
if num <= letters.len() {
return letters[num - 1].to_string();
}
let mut result = String::new();
let mut remaining = num;
while remaining > 0 {
remaining -= 1;
result.insert(0, letters[remaining % letters.len()]);
remaining /= letters.len();
}
result
}