sea_core/primitives/
quantity.rs

1use crate::units::Dimension;
2#[cfg(feature = "formatting")]
3use fixed_decimal::FixedDecimal;
4#[cfg(feature = "formatting")]
5use icu_decimal::FixedDecimalFormatter;
6#[cfg(feature = "formatting")]
7use icu_locid::Locale;
8use serde::{Deserialize, Serialize};
9#[cfg(feature = "formatting")]
10use std::str::FromStr;
11
12#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
13pub struct Quantity {
14    pub value: f64,
15    pub unit: String,
16    pub dimension: Dimension,
17}
18
19impl Quantity {
20    pub fn new(value: f64, unit: String, dimension: Dimension) -> Result<Self, String> {
21        if !value.is_finite() {
22            return Err(format!("Quantity value must be finite, got {}", value));
23        }
24        Ok(Self {
25            value,
26            unit,
27            dimension,
28        })
29    }
30}
31
32#[cfg(feature = "formatting")]
33pub struct QuantityFormatter {
34    formatter: FixedDecimalFormatter,
35    // locale is kept for future use (e.g. currency formatting)
36    #[allow(dead_code)]
37    locale: Locale,
38}
39
40#[cfg(feature = "formatting")]
41impl QuantityFormatter {
42    pub fn new(locale: Locale) -> Self {
43        let formatter = FixedDecimalFormatter::try_new(&(&locale).into(), Default::default())
44            .expect("Failed to create FixedDecimalFormatter");
45        Self { formatter, locale }
46    }
47
48    pub fn format(&self, quantity: &Quantity) -> Result<String, String> {
49        // Convert f64 to FixedDecimal for formatting
50        // FixedDecimal::from_str handles float string representation
51        let decimal = FixedDecimal::from_str(&quantity.value.to_string())
52            .map_err(|e| format!("Failed to convert quantity value to decimal: {}", e))?;
53        let formatted_value = self.formatter.format(&decimal).to_string();
54        Ok(format!("{} \"{}\"", formatted_value, quantity.unit))
55    }
56}