libhaystack/haystack/val/
number.rs

1// Copyright (C) 2020 - 2022, J2 Innovations
2
3//! Haystack Number
4
5use crate::{haystack::val::Value, units::Unit, units::DEFAULT_UNIT};
6use std::{
7    cmp::Ordering,
8    convert::{From, TryFrom},
9    hash::Hash,
10    ops::{Add, Div, Mul, Sub},
11};
12
13/// Haystack `Number` floating point value and optional unit
14///
15/// # Example
16/// Create `Number` value
17/// ```
18/// use libhaystack::val::*;
19/// use libhaystack::units::Unit;
20/// use libhaystack::units::{get_unit, get_unit_or_default};
21///
22/// // Create number from `f64` primitive
23/// let number_value = Value::from(42.0);
24/// assert!(number_value.is_number());
25/// assert_eq!(f64::try_from(&number_value), Ok(42.0));
26///
27/// // Create `Number` from `i32` primitive
28/// let num = Number::from(100);
29/// assert_eq!(num.value, 100.0);
30/// assert!(Value::from(num).is_number());
31///
32/// // Number with unit
33/// let num_unit_value = Value::from(Number::make_with_unit(100.0, "sec".into()));
34/// assert_eq!(Number::try_from(&num_unit_value).unwrap().unit, get_unit("sec"));
35///```
36#[derive(Copy, Clone, Debug, Default)]
37pub struct Number {
38    pub value: f64,
39    pub unit: Option<&'static Unit>,
40}
41
42impl Number {
43    /// Makes number without unit
44    pub fn make(value: f64) -> Number {
45        Number { value, unit: None }
46    }
47
48    /// Makes number with specified unit
49    pub fn make_with_unit(value: f64, unit: &'static Unit) -> Number {
50        Number {
51            value,
52            unit: if unit != &*DEFAULT_UNIT {
53                Some(unit)
54            } else {
55                None
56            },
57        }
58    }
59}
60
61// Make a Haystack `Number` from a float value
62impl From<f64> for Number {
63    fn from(value: f64) -> Self {
64        Number { value, unit: None }
65    }
66}
67// Make a Haystack `Number` from an int value
68impl From<i32> for Number {
69    fn from(value: i32) -> Self {
70        Number {
71            value: value as f64,
72            unit: None,
73        }
74    }
75}
76
77/// Converts from `Number` to a `Number` `Value`
78impl From<Number> for Value {
79    fn from(value: Number) -> Self {
80        Value::Number(value)
81    }
82}
83
84/// Converts from `f64` to a `Number` `Value`
85impl From<f64> for Value {
86    fn from(value: f64) -> Self {
87        Value::Number(Number::from(value))
88    }
89}
90
91/// Converts from `i32` to a `Number` `Value`
92impl From<i32> for Value {
93    fn from(value: i32) -> Self {
94        Value::Number(Number::from(value))
95    }
96}
97
98/// Tries to convert from `Number` `Value` to a `f64`
99impl TryFrom<&Value> for f64 {
100    type Error = &'static str;
101    fn try_from(value: &Value) -> Result<Self, Self::Error> {
102        match value {
103            Value::Number(v) => Ok(v.value),
104            _ => Err("Value is not a `Number`"),
105        }
106    }
107}
108
109/// Tries to convert from `Number` `Value` to a `Number`
110impl TryFrom<&Value> for Number {
111    type Error = &'static str;
112    fn try_from(value: &Value) -> Result<Self, Self::Error> {
113        match value {
114            Value::Number(v) => Ok(*v),
115            _ => Err("Value is not a `Number`"),
116        }
117    }
118}
119
120impl Hash for Number {
121    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
122        self.value.to_bits().hash(state);
123        self.unit.hash(state);
124    }
125}
126
127impl PartialEq for Number {
128    fn eq(&self, other: &Self) -> bool {
129        self.value == other.value && self.unit == other.unit
130    }
131}
132
133impl Eq for Number {}
134
135#[allow(clippy::non_canonical_partial_ord_impl)]
136impl PartialOrd for Number {
137    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
138        if self.unit == other.unit {
139            self.value.partial_cmp(&other.value)
140        } else {
141            None
142        }
143    }
144}
145
146impl Ord for Number {
147    fn cmp(&self, other: &Self) -> Ordering {
148        if self.value < other.value {
149            Ordering::Less
150        } else if self.value == other.value {
151            Ordering::Equal
152        } else {
153            Ordering::Greater
154        }
155    }
156}
157
158/// Addition operator
159impl Add<Number> for Number {
160    type Output = Result<Number, String>;
161
162    fn add(self, other: Number) -> Self::Output {
163        let unit = if self.unit == other.unit {
164            self.unit
165        } else if self.unit.is_none() {
166            other.unit
167        } else if other.unit.is_none() {
168            self.unit
169        } else {
170            return Err(format!(
171                "Invalid addition units: {this:?}, {other:?}",
172                this = self.unit,
173                other = other.unit
174            ));
175        };
176
177        Ok(Number::make_with_unit(
178            self.value + other.value,
179            unit.unwrap_or(&*DEFAULT_UNIT),
180        ))
181    }
182}
183
184/// Subtraction operator
185impl Sub<Number> for Number {
186    type Output = Result<Number, String>;
187
188    fn sub(self, other: Number) -> Self::Output {
189        let unit = if self.unit == other.unit {
190            self.unit
191        } else if self.unit.is_none() {
192            other.unit
193        } else if other.unit.is_none() {
194            self.unit
195        } else {
196            return Err(format!(
197                "Invalid subtraction units: {this:?}, {other:?}",
198                this = self.unit,
199                other = other.unit
200            ));
201        };
202
203        Ok(Number::make_with_unit(
204            self.value - other.value,
205            unit.unwrap_or(&*DEFAULT_UNIT),
206        ))
207    }
208}
209
210/// Multiplication operator
211impl Mul<Number> for Number {
212    type Output = Result<Number, String>;
213
214    fn mul(self, other: Number) -> Self::Output {
215        let unit = if self.unit.is_none() {
216            other.unit
217        } else if other.unit.is_none() {
218            self.unit
219        } else {
220            match self.unit.unwrap_or(&*DEFAULT_UNIT) * other.unit.unwrap_or(&*DEFAULT_UNIT) {
221                Ok(u) => Some(u),
222                Err(err) => return Err(err),
223            }
224        };
225
226        Ok(Number::make_with_unit(
227            self.value * other.value,
228            unit.unwrap_or(&*DEFAULT_UNIT),
229        ))
230    }
231}
232
233/// Division operator
234impl Div<Number> for Number {
235    type Output = Result<Number, String>;
236
237    fn div(self, other: Number) -> Self::Output {
238        let unit = if self.unit.is_none() {
239            other.unit
240        } else if other.unit.is_none() {
241            self.unit
242        } else {
243            match self.unit.unwrap_or(&*DEFAULT_UNIT) / other.unit.unwrap_or(&*DEFAULT_UNIT) {
244                Ok(u) => Some(u),
245                Err(err) => return Err(err),
246            }
247        };
248
249        Ok(Number::make_with_unit(
250            self.value / other.value,
251            unit.unwrap_or(&*DEFAULT_UNIT),
252        ))
253    }
254}