fluent_static_value/
number.rs

1use std::str::FromStr;
2
3pub mod format;
4use crate::Value;
5
6#[derive(Debug, Clone, Copy)]
7pub enum Number {
8    I64(i64),
9    U64(u64),
10    I128(i128),
11    U128(u128),
12    F64(f64),
13}
14
15impl PartialEq for Number {
16    fn eq(&self, other: &Self) -> bool {
17        match self.ordered_tuple(other) {
18            (Number::I64(i1), Number::I64(i2)) => i1 == i2,
19            (Number::U64(u1), Number::I64(i2)) if *i2 >= 0 => *u1 == *i2 as u64,
20            (Number::U64(u1), Number::U64(u2)) => u1 == u2,
21            (Number::I128(i1), Number::I64(i2)) => *i1 == *i2 as i128,
22            (Number::I128(i1), Number::U64(u2)) => *i1 == *u2 as i128,
23            (Number::I128(i1), Number::I128(i2)) => i1 == i2,
24            (Number::U128(u1), Number::I64(i2)) if *i2 >= 0 => *u1 == *i2 as u128,
25            (Number::U128(u1), Number::U64(u2)) => *u1 == *u2 as u128,
26            (Number::U128(u1), Number::I128(i2)) if *i2 >= 0 => *u1 == *i2 as u128,
27            (Number::U128(u1), Number::U128(u2)) => u1 == u2,
28            (Number::F64(f1), n2) => f64::abs(f1 - n2.as_f64()) < f64::EPSILON,
29
30            _ => false,
31        }
32    }
33}
34
35impl Number {
36    fn ord(&self) -> usize {
37        match self {
38            Number::I64(_) => 0,
39            Number::U64(_) => 1,
40            Number::I128(_) => 2,
41            Number::U128(_) => 3,
42            Number::F64(_) => 4,
43        }
44    }
45
46    fn ordered_tuple<'a>(&'a self, other: &'a Self) -> (&'a Self, &'a Self) {
47        if self.ord() > other.ord() {
48            (self, other)
49        } else {
50            (other, self)
51        }
52    }
53
54    pub fn as_f64(&self) -> f64 {
55        match self {
56            Number::I64(v) => *v as f64,
57            Number::U64(v) => *v as f64,
58            Number::I128(v) => *v as f64,
59            Number::U128(v) => *v as f64,
60            Number::F64(v) => *v,
61        }
62    }
63
64    pub fn as_string(&self) -> String {
65        match self {
66            Number::I64(n) => n.to_string(),
67            Number::U64(n) => n.to_string(),
68            Number::I128(n) => n.to_string(),
69            Number::U128(n) => n.to_string(),
70            Number::F64(n) => n.to_string(),
71        }
72    }
73}
74
75impl FromStr for Number {
76    type Err = std::num::ParseFloatError;
77
78    fn from_str(s: &str) -> Result<Self, Self::Err> {
79        if s.contains('.') {
80            // If the string contains a decimal point, parse as f64
81            match s.parse::<f64>() {
82                Ok(f) => Ok(Number::F64(f)),
83                Err(e) => Err(e),
84            }
85        } else {
86            // Try parsing as integers in order: i64, u64, i128, u128
87            if let Ok(i) = s.parse::<i64>() {
88                Ok(Number::I64(i))
89            } else if let Ok(u) = s.parse::<u64>() {
90                Ok(Number::U64(u))
91            } else if let Ok(i) = s.parse::<i128>() {
92                Ok(Number::I128(i))
93            } else if let Ok(u) = s.parse::<u128>() {
94                Ok(Number::U128(u))
95            } else {
96                // If all integer parsing fails, try f64 as a fallback
97                match s.parse::<f64>() {
98                    Ok(f) => Ok(Number::F64(f)),
99                    Err(e) => Err(e),
100                }
101            }
102        }
103    }
104}
105
106impl<'a> From<Number> for Value<'a> {
107    fn from(value: Number) -> Self {
108        Self::Number {
109            value,
110            format: None,
111        }
112    }
113}
114
115macro_rules! impl_from_for_number {
116    ($($t:ty => $variant:ident),*) => {
117        $(
118            impl From<$t> for Number {
119                fn from(value: $t) -> Self {
120                    Number::$variant(value as _)
121                }
122            }
123            impl From<&$t> for Number {
124                fn from(value: &$t) -> Self {
125                    Number::$variant(*value as _)
126                }
127            }
128            impl From<$t> for Value<'_> {
129                fn from(value: $t) -> Self {
130                    Value::Number {
131                        value: Number::$variant(value as _),
132                        format: None
133                    }
134                }
135            }
136            impl From<&$t> for Value<'_> {
137                fn from(value: &$t) -> Self {
138                    Value::Number{
139                        value: Number::$variant(*value as _),
140                        format: None
141                    }
142                }
143            }
144        )*
145    };
146}
147
148impl_from_for_number! {
149    i8 => I64,
150    i16 => I64,
151    i32 => I64,
152    i64 => I64,
153    i128 => I128,
154    isize => I64,
155    u8 => U64,
156    u16 => U64,
157    u32 => U64,
158    u64 => U64,
159    u128 => U128,
160    usize => U64,
161    f32 => F64,
162    f64 => F64
163}
164
165#[cfg(test)]
166mod test {
167    use super::Number;
168
169    #[test]
170    fn test_number_equality() {
171        let n1 = Number::I64(0);
172        let n2 = Number::F64(0f64);
173        assert_eq!(n1, n2);
174    }
175}