use alloc::string::String;
#[derive(Debug, Clone, Copy)]
pub struct Pattern {
pub prefix: &'static str,
pub suffix: &'static str,
pub min_int: u8,
pub min_frac: u8,
pub max_frac: u8,
pub primary_group: u8,
pub secondary_group: u8,
}
#[derive(Debug, Clone, Copy)]
pub struct NumberSpec {
pub decimal: &'static str,
pub group: &'static str,
pub minus: &'static str,
pub plus: &'static str,
pub percent: &'static str,
pub dec: Pattern,
pub pct: Pattern,
}
fn spec(lang: &str) -> NumberSpec {
use crate::unicode::generated::numbers::number_spec;
let norm: String = lang
.chars()
.map(|c| {
if c == '_' {
'-'
} else {
c.to_ascii_lowercase()
}
})
.collect();
let mut end = norm.len();
loop {
if let Some(s) = number_spec(&norm[..end]) {
return s;
}
match norm[..end].rfind('-') {
Some(i) => end = i,
None => return number_spec("en").expect("root spec present"),
}
}
}
#[must_use]
pub fn format_decimal(lang: &str, value: f64) -> String {
let s = spec(lang);
format_with(&s.dec, value, s.decimal, s.group, s.minus)
}
#[must_use]
pub fn format_percent(lang: &str, value: f64) -> String {
let s = spec(lang);
format_with(&s.pct, value * 100.0, s.decimal, s.group, s.minus)
}
fn format_with(p: &Pattern, value: f64, decimal: &str, group: &str, minus: &str) -> String {
let neg = value.is_sign_negative() && value != 0.0;
let abs = if value < 0.0 { -value } else { value };
let formatted = alloc::format!("{:.*}", p.max_frac as usize, abs);
let (int_str, frac_full) = match formatted.split_once('.') {
Some((a, b)) => (a, b),
None => (formatted.as_str(), ""),
};
let mut int_owned;
let int_str: &str = if (int_str.len() as u8) < p.min_int {
int_owned = String::new();
for _ in 0..(p.min_int as usize - int_str.len()) {
int_owned.push('0');
}
int_owned.push_str(int_str);
&int_owned
} else {
int_str
};
let mut frac = frac_full;
while frac.len() > p.min_frac as usize && frac.ends_with('0') {
frac = &frac[..frac.len() - 1];
}
let grouped = group_digits(int_str, p.primary_group, p.secondary_group, group);
let mut out = String::new();
if neg {
out.push_str(minus);
}
out.push_str(p.prefix);
out.push_str(&grouped);
if !frac.is_empty() {
out.push_str(decimal);
out.push_str(frac);
}
out.push_str(p.suffix);
out
}
fn group_digits(digits: &str, primary: u8, secondary: u8, sep: &str) -> String {
if primary == 0 || digits.len() <= primary as usize {
return String::from(digits);
}
let chars: alloc::vec::Vec<char> = digits.chars().collect();
let mut rev: alloc::vec::Vec<char> = alloc::vec::Vec::new();
let mut count = 0u8;
let mut limit = primary;
for &c in chars.iter().rev() {
if count == limit {
for sc in sep.chars().rev() {
rev.push(sc);
}
count = 0;
limit = secondary;
}
rev.push(c);
count += 1;
}
rev.iter().rev().collect()
}