irox_units/units/
mod.rs

1// SPDX-License-Identifier: MIT
2// Copyright 2025 IROX Contributors
3//
4
5///
6/// Matches (struct, units, default) to make a new basic struct
7macro_rules! impl_unitstruct {
8    ($struct_type:ident, $units_type: ident, $($slf:ty)+) => {
9        impl $crate::units::UnitStruct<$units_type> for $($slf)+ {
10            type Output = $struct_type;
11
12            fn new(value: f64, units: $units_type) -> $struct_type {
13                $struct_type { value, units }
14            }
15
16            fn value(&self) -> f64 {
17                self.value
18            }
19
20            fn units(&self) -> $units_type {
21                self.units
22            }
23        }
24        impl Unit<$units_type> for $($slf)+ {
25            type Output = $struct_type;
26            fn as_unit(&self, units: $units_type) -> $struct_type
27            where
28                Self: Sized,
29            {
30                $struct_type {
31                    value: units.from(self.value, self.units),
32                    units,
33                }
34            }
35        }
36    };
37}
38
39macro_rules! impl_sub {
40    ($($out:ty)+, $units_type:ident, $($sub:ty)+, $($slf:ty)+) => {
41        impl<'a> core::ops::Sub<$($sub)+> for $($slf)+ {
42            type Output = $($out)+;
43
44            fn sub(self, rhs: $($sub)+) -> $($out)+ {
45                let val = <$($sub)+ as $crate::units::Unit::<$units_type>>::as_unit(&rhs, self.units()).value();
46                <$($slf)+ as $crate::units::UnitStruct::<$units_type>>::new(self.value() - val, self.units())
47            }
48        }
49    };
50}
51macro_rules! impl_add {
52    ($($out:ty)+, $units_type:ident, $($add:ty)+, $($slf:ty)+) => {
53        impl<'a> core::ops::Add<$($add)+> for $($slf)+ {
54            type Output = $($out)+;
55
56            fn add(self, rhs: $($add)+) -> $($out)+ {
57                let val = <$($add)+ as $crate::units::Unit::<$units_type>>::as_unit(&rhs, self.units()).value();
58                <$($slf)+ as $crate::units::UnitStruct::<$units_type>>::new(self.value() + val, self.units())
59            }
60        }
61    };
62}
63macro_rules! impl_subassign {
64    ($($out:ty)+, $units_type:ident, $($sub:ty)+, $($slf:ty)+) => {
65        impl<'a> core::ops::SubAssign<$($sub)+> for $($slf)+ {
66            fn sub_assign(&mut self, rhs: $($sub)+) {
67                let val = <$($sub)+ as $crate::units::Unit::<$units_type>>::as_unit(&rhs, self.units()).value();
68                self.value -= val;
69            }
70
71        }
72    };
73}
74macro_rules! impl_addassign {
75    ($($out:ty)+, $units_type:ident, $($add:ty)+, $($slf:ty)+) => {
76        impl<'a> core::ops::AddAssign<$($add)+> for $($slf)+ {
77            fn add_assign(&mut self, rhs: $($add)+) {
78                let val = <$($add)+ as $crate::units::Unit::<$units_type>>::as_unit(&rhs, self.units()).value();
79                self.value += val;
80            }
81        }
82    };
83}
84macro_rules! impl_div {
85    ($($out:ty)+, $units_type:ident, $($div:ty)+, $($slf:ty)+) => {
86        impl<'a> core::ops::Div<$($div)+> for $($slf)+ {
87            type Output = $($out)+ ;
88            fn div(self, rhs: $($div)+) -> $($out)+  {
89                <$($slf)+ as $crate::units::UnitStruct::<$units_type>>::new(self.value() / rhs, self.units())
90            }
91        }
92        impl<'a> core::ops::Div<$($slf)+> for $($slf)+ {
93            type Output = f64;
94            fn div(self, rhs: $($slf)+) -> Self::Output {
95                let upper = self.value();
96                let lower = <$($slf)+ as $crate::units::Unit::<$units_type>>::as_unit(&rhs, self.units()).value();
97                upper / lower
98            }
99        }
100    };
101}
102
103macro_rules! impl_divassign {
104    ($($out:ty)+, $units_type:ident, $($div:ty)+, $($slf:ty)+) => {
105        impl<'a> core::ops::DivAssign<$($div)+> for $($slf)+ {
106            fn div_assign(&mut self, rhs: $($div)+) {
107                self.value /= rhs;
108            }
109        }
110    };
111}
112
113macro_rules! impl_mul {
114    ($($out:ty)+, $units_type:ident, $($mul:ty)+, $($slf:ty)+) => {
115         impl<'a> core::ops::Mul<$($mul)+> for $($slf)+ {
116            type Output = $($out)+ ;
117            fn mul(self, rhs: $($mul)+) -> $($out)+  {
118                <$($slf)+ as $crate::units::UnitStruct::<$units_type>>::new(self.value() * rhs, self.units())
119            }
120        }
121        impl<'a> core::ops::Mul<$($slf)+> for $($slf)+ {
122            type Output = $($mul)+;
123            fn mul(self, rhs: $($slf)+) -> Self::Output {
124                let upper = self.value();
125                let lower = <$($slf)+ as $crate::units::Unit::<$units_type>>::as_unit(&rhs, self.units()).value();
126                upper * lower
127            }
128        }
129        impl<'a> core::ops::Mul<$($slf)+> for f64 {
130            type Output = $($out)+;
131            fn mul(self, rhs: $($slf)+) -> Self::Output {
132                rhs * self
133            }
134        }
135    };
136}
137macro_rules! impl_mulassign {
138    ($($out:ty)+, $units_type:ident, $($mul:ty)+, $($slf:ty)+) => {
139        impl<'a> core::ops::MulAssign<$($mul)+> for $($slf)+ {
140            fn mul_assign(&mut self, rhs: $($mul)+) {
141                self.value *= rhs;
142            }
143        }
144        // impl core::ops::MulAssign<f64> for $struct_type {
145        //     fn mul_assign(&mut self, rhs: f64) {
146        //         self.value *= rhs
147        //     }
148        // }
149    };
150}
151
152macro_rules! impl_op {
153    ($op:ident, $units_type:ident, $($operand:ty)+) => {
154        $op!($($operand)+, $units_type, $($operand)+, $($operand)+);
155        $op!($($operand)+, $units_type, $($operand)+, &$($operand)+);
156        $op!($($operand)+, $units_type, $($operand)+, &mut $($operand)+);
157        $op!($($operand)+, $units_type, &$($operand)+, $($operand)+);
158        $op!($($operand)+, $units_type, &$($operand)+, &$($operand)+);
159        $op!($($operand)+, $units_type, &$($operand)+, &mut $($operand)+);
160        $op!($($operand)+, $units_type, &mut $($operand)+, $($operand)+);
161        $op!($($operand)+, $units_type, &mut $($operand)+, &$($operand)+);
162        $op!($($operand)+, $units_type, &mut $($operand)+, &mut $($operand)+);
163    };
164}
165macro_rules! impl_mutop {
166    ($op:ident, $units_type:ident, $($operand:ty)+) => {
167        $op!($($operand)+, $units_type, $($operand)+, $($operand)+);
168        $op!($($operand)+, $units_type, $($operand)+, &mut $($operand)+);
169        $op!($($operand)+, $units_type, &$($operand)+, $($operand)+);
170        $op!($($operand)+, $units_type, &$($operand)+, &mut $($operand)+);
171        $op!($($operand)+, $units_type, &mut $($operand)+, $($operand)+);
172        $op!($($operand)+, $units_type, &mut $($operand)+, &mut $($operand)+);
173    };
174}
175
176#[macro_export]
177macro_rules! basic_unit {
178    ($struct_type:ident, $units_type: ident, $default_units: ident) => {
179        #[derive(Debug, Clone, Copy, Default)]
180        pub struct $struct_type {
181            value: f64,
182            units: $units_type,
183        }
184
185        impl $struct_type {
186            #[must_use]
187            pub const fn new(value: f64, units: $units_type) -> Self {
188                Self { value, units }
189            }
190
191            #[must_use]
192            pub fn value(&self) -> f64 {
193                self.value
194            }
195
196            #[must_use]
197            pub fn units(&self) -> $units_type {
198                self.units
199            }
200        }
201
202        impl_unitstruct!($struct_type, $units_type, $struct_type);
203        impl_unitstruct!($struct_type, $units_type, &$struct_type);
204        impl_unitstruct!($struct_type, $units_type, &mut $struct_type);
205
206        impl_op!(impl_sub, $units_type, $struct_type);
207        impl_mutop!(impl_subassign, $units_type, $struct_type);
208        impl_op!(impl_add, $units_type, $struct_type);
209        impl_mutop!(impl_addassign, $units_type, $struct_type);
210        impl_div!($struct_type, $units_type, f64, $struct_type);
211        impl_div!($struct_type, $units_type, f64, &$struct_type);
212        impl_div!($struct_type, $units_type, f64, &mut $struct_type);
213        impl_divassign!($struct_type, $units_type, f64, $struct_type);
214        impl_divassign!($struct_type, $units_type, f64, &mut $struct_type);
215        impl_mul!($struct_type, $units_type, f64, $struct_type);
216        impl_mul!($struct_type, $units_type, f64, &$struct_type);
217        impl_mul!($struct_type, $units_type, f64, &mut $struct_type);
218        impl_mulassign!($struct_type, $units_type, f64, $struct_type);
219        impl_mulassign!($struct_type, $units_type, f64, &mut $struct_type);
220
221        impl core::cmp::PartialOrd<$struct_type> for $struct_type {
222            fn partial_cmp(&self, rhs: &$struct_type) -> Option<core::cmp::Ordering> {
223                Some(self.cmp(rhs))
224            }
225        }
226        impl core::cmp::Ord for $struct_type {
227            fn cmp(&self, other: &Self) -> core::cmp::Ordering {
228                self.value.total_cmp(&other.as_unit(self.units).value)
229            }
230        }
231        impl core::cmp::Eq for $struct_type {}
232
233        impl core::cmp::PartialEq<$struct_type> for $struct_type {
234            fn eq(&self, rhs: &$struct_type) -> bool {
235                if let Some(val) = self.partial_cmp(rhs) {
236                    return val == core::cmp::Ordering::Equal;
237                }
238                false
239            }
240        }
241
242        pub const ZERO: $struct_type = $struct_type::new(0.0, $units_type::$default_units);
243    };
244}
245
246///
247/// Trait to allow the direct conversion of a unit scalar to another unit scalar
248pub trait FromUnits<T> {
249    /// Converts the value with unit units into this (self) unit
250    fn from(&self, value: T, units: Self) -> T;
251}
252
253///
254/// Represents a Value/Unit pairing
255pub trait UnitStruct<T>: Unit<T> {
256    type Output;
257
258    /// Creates a new type
259    fn new(value: f64, units: T) -> <Self as UnitStruct<T>>::Output;
260
261    /// Returns the value of this struct
262    fn value(&self) -> f64;
263
264    /// Returns the unit type of this struct
265    fn units(&self) -> T;
266}
267
268///
269/// Trait to provide access to unit conversions
270pub trait Unit<T> {
271    type Output;
272    #[must_use]
273    fn as_unit(&self, unit: T) -> Self::Output
274    where
275        Self: Sized;
276}
277
278pub mod angle;
279pub mod compass;
280pub mod datasize;
281pub mod duration;
282pub mod length;
283pub mod speed;
284pub mod temperature;