libhaystack/haystack/val/
number.rs1use 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#[derive(Copy, Clone, Debug, Default)]
37pub struct Number {
38 pub value: f64,
39 pub unit: Option<&'static Unit>,
40}
41
42impl Number {
43 pub fn make(value: f64) -> Number {
45 Number { value, unit: None }
46 }
47
48 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
61impl From<f64> for Number {
63 fn from(value: f64) -> Self {
64 Number { value, unit: None }
65 }
66}
67impl From<i32> for Number {
69 fn from(value: i32) -> Self {
70 Number {
71 value: value as f64,
72 unit: None,
73 }
74 }
75}
76
77impl From<Number> for Value {
79 fn from(value: Number) -> Self {
80 Value::Number(value)
81 }
82}
83
84impl From<f64> for Value {
86 fn from(value: f64) -> Self {
87 Value::Number(Number::from(value))
88 }
89}
90
91impl From<i32> for Value {
93 fn from(value: i32) -> Self {
94 Value::Number(Number::from(value))
95 }
96}
97
98impl 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
109impl 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
158impl 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
184impl 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
210impl 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
233impl 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}