elevator_core/components/
units.rs1use serde::{Deserialize, Serialize};
4use std::fmt;
5
6#[derive(Debug, Clone, PartialEq)]
19pub struct UnitError {
20 pub unit: &'static str,
22 pub value: f64,
24}
25
26impl fmt::Display for UnitError {
27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28 write!(
29 f,
30 "invalid {} value: {} (must be finite and non-negative)",
31 self.unit, self.value
32 )
33 }
34}
35
36impl std::error::Error for UnitError {}
37
38#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Serialize, Deserialize)]
49#[serde(transparent)]
50pub struct Weight {
51 pub(crate) value: f64,
53}
54
55impl Weight {
56 pub const ZERO: Self = Self { value: 0.0 };
58
59 pub fn try_new(value: f64) -> Result<Self, UnitError> {
72 if value.is_finite() && value >= 0.0 {
73 Ok(Self { value })
74 } else {
75 Err(UnitError {
76 unit: "Weight",
77 value,
78 })
79 }
80 }
81
82 #[must_use]
84 pub const fn value(self) -> f64 {
85 self.value
86 }
87}
88
89impl fmt::Display for Weight {
90 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91 write!(f, "{:.2}kg", self.value)
92 }
93}
94
95#[allow(clippy::panic)]
96impl From<f64> for Weight {
97 fn from(value: f64) -> Self {
98 Self::try_new(value).unwrap_or_else(|e| panic!("{e}"))
99 }
100}
101
102impl std::ops::Add for Weight {
103 type Output = Self;
104 fn add(self, rhs: Self) -> Self {
105 Self {
106 value: self.value + rhs.value,
107 }
108 }
109}
110
111impl std::ops::AddAssign for Weight {
112 fn add_assign(&mut self, rhs: Self) {
113 self.value += rhs.value;
114 }
115}
116
117impl std::ops::Sub for Weight {
118 type Output = Self;
119 fn sub(self, rhs: Self) -> Self {
120 Self {
121 value: (self.value - rhs.value).max(0.0),
122 }
123 }
124}
125
126impl std::ops::SubAssign for Weight {
127 fn sub_assign(&mut self, rhs: Self) {
128 self.value = (self.value - rhs.value).max(0.0);
129 }
130}
131
132#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Serialize, Deserialize)]
140#[serde(transparent)]
141pub struct Speed {
142 pub(crate) value: f64,
144}
145
146impl Speed {
147 pub fn try_new(value: f64) -> Result<Self, UnitError> {
159 if value.is_finite() && value >= 0.0 {
160 Ok(Self { value })
161 } else {
162 Err(UnitError {
163 unit: "Speed",
164 value,
165 })
166 }
167 }
168
169 #[must_use]
171 pub const fn value(self) -> f64 {
172 self.value
173 }
174}
175
176impl fmt::Display for Speed {
177 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178 write!(f, "{:.2}m/s", self.value)
179 }
180}
181
182#[allow(clippy::panic)]
183impl From<f64> for Speed {
184 fn from(value: f64) -> Self {
185 Self::try_new(value).unwrap_or_else(|e| panic!("{e}"))
186 }
187}
188
189#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Serialize, Deserialize)]
197#[serde(transparent)]
198pub struct Accel {
199 pub(crate) value: f64,
201}
202
203impl Accel {
204 pub fn try_new(value: f64) -> Result<Self, UnitError> {
216 if value.is_finite() && value >= 0.0 {
217 Ok(Self { value })
218 } else {
219 Err(UnitError {
220 unit: "Accel",
221 value,
222 })
223 }
224 }
225
226 #[must_use]
228 pub const fn value(self) -> f64 {
229 self.value
230 }
231}
232
233impl fmt::Display for Accel {
234 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
235 write!(f, "{:.2}m/s²", self.value)
236 }
237}
238
239#[allow(clippy::panic)]
240impl From<f64> for Accel {
241 fn from(value: f64) -> Self {
242 Self::try_new(value).unwrap_or_else(|e| panic!("{e}"))
243 }
244}