use fixed_decimal::Decimal;
use icu_decimal::options::DecimalFormatterOptions;
use icu_decimal::preferences::DecimalFormatterPreferences;
use icu_decimal::DecimalFormatter;
use icu_locale_core::Locale;
use crate::CollateError;
pub struct IcuNumberFormatter {
inner: DecimalFormatter,
}
impl IcuNumberFormatter {
pub fn new(locale: &str) -> Result<Self, CollateError> {
let loc: Locale = locale
.parse()
.map_err(|e| CollateError::InvalidLocale(format!("{e}")))?;
let prefs = DecimalFormatterPreferences::from(loc);
let opts = DecimalFormatterOptions::default();
let inner = DecimalFormatter::try_new(prefs, opts)
.map_err(|e| CollateError::Icu(format!("{e}")))?;
Ok(Self { inner })
}
pub fn format_int(&self, value: i64) -> String {
let dec = Decimal::from(value);
self.inner.format(&dec).to_string()
}
pub fn format_uint(&self, value: u64) -> String {
let dec = Decimal::from(value);
self.inner.format(&dec).to_string()
}
pub fn format(&self, value: f64) -> String {
let repr = format!("{value:.6}");
match repr.parse::<Decimal>() {
Ok(dec) => self.inner.format(&dec).to_string(),
Err(_) => repr,
}
}
pub fn format_with_precision(&self, value: f64, frac_digits: usize) -> String {
let repr = format!("{value:.frac_digits$}");
match repr.parse::<Decimal>() {
Ok(dec) => self.inner.format(&dec).to_string(),
Err(_) => repr,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn format_integer_en() {
let fmt = IcuNumberFormatter::new("en").expect("English number formatter");
let s = fmt.format_int(1234);
assert!(!s.is_empty(), "formatted string should not be empty");
assert!(
s.contains('1') && s.contains('4'),
"should contain digits: {s}"
);
}
#[test]
fn format_negative_integer() {
let fmt = IcuNumberFormatter::new("en").expect("English number formatter");
let s = fmt.format_int(-42);
assert!(!s.is_empty());
assert!(
s.contains('4') && s.contains('2'),
"should contain digits: {s}"
);
}
#[test]
fn format_uint() {
let fmt = IcuNumberFormatter::new("en").expect("formatter");
let s = fmt.format_uint(9_999_999);
assert!(!s.is_empty());
}
#[test]
fn format_float_basic() {
let fmt = IcuNumberFormatter::new("en").expect("formatter");
let s = fmt.format(1.75);
assert!(!s.is_empty(), "should format float: {s}");
}
#[test]
fn format_with_precision_two_decimals() {
let fmt = IcuNumberFormatter::new("en").expect("formatter");
let s = fmt.format_with_precision(1.5, 2);
assert!(!s.is_empty());
assert!(s.contains('1') && s.contains('5'), "unexpected format: {s}");
}
#[test]
fn format_zero() {
let fmt = IcuNumberFormatter::new("en").expect("formatter");
let s = fmt.format_int(0);
assert!(!s.is_empty());
assert!(s.contains('0'), "zero should contain '0': {s}");
}
#[test]
fn invalid_locale_returns_error() {
let result = IcuNumberFormatter::new("not-a-valid-locale!!!");
assert!(result.is_err(), "invalid locale should produce error");
}
}