irox_units/units/
length.rs1use core::fmt::{Display, Formatter};
8
9use crate::units::{FromUnits, Unit};
10
11#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)]
14#[non_exhaustive]
15pub enum LengthUnits {
16 #[default]
18 Meters,
19
20 Kilometers,
22
23 Feet,
25
26 Mile,
28
29 NauticalMile,
31
32 USSurveyFoot,
34}
35macro_rules! from_units_length {
36 ($type:ident) => {
37 impl crate::units::FromUnits<$type> for LengthUnits {
38 fn from(&self, value: $type, source_unit: Self) -> $type {
39 match self {
40 LengthUnits::Meters => match source_unit {
42 LengthUnits::Meters => value as $type,
44 LengthUnits::Feet => value * FEET_TO_METERS as $type,
45 LengthUnits::Kilometers => value * KILOMETERS_TO_METERS as $type,
46 LengthUnits::Mile => value * MILES_TO_METERS as $type,
47 LengthUnits::NauticalMile => value * NAUTICAL_MILES_TO_METERS as $type,
48 LengthUnits::USSurveyFoot => value * SURVEYFOOT_TO_METER as $type,
49 },
50 LengthUnits::Feet => match source_unit {
51 LengthUnits::Meters => value * METERS_TO_FEET as $type,
52 LengthUnits::Feet => value as $type,
53 LengthUnits::Kilometers => {
54 FromUnits::<$type>::from(&LengthUnits::Meters, value, source_unit)
55 * METERS_TO_KILOMETERS as $type
56 }
57 LengthUnits::Mile => {
58 FromUnits::<$type>::from(&LengthUnits::Meters, value, source_unit)
59 * METERS_TO_MILES as $type
60 }
61 LengthUnits::NauticalMile => {
62 FromUnits::<$type>::from(&LengthUnits::Meters, value, source_unit)
63 * METERS_TO_NAUTICAL_MILE as $type
64 }
65 LengthUnits::USSurveyFoot => {
66 value * (SURVEYFOOT_TO_METER * METERS_TO_FEET) as $type
67 }
68 },
69 LengthUnits::Kilometers => match source_unit {
70 LengthUnits::Meters => value * METERS_TO_KILOMETERS as $type,
71 LengthUnits::Kilometers => value,
72 LengthUnits::Feet => {
73 value * (FEET_TO_METERS * METERS_TO_KILOMETERS) as $type
74 }
75 LengthUnits::Mile => {
76 value * (MILES_TO_METERS * METERS_TO_KILOMETERS) as $type
77 }
78 LengthUnits::NauticalMile => {
79 value * (NAUTICAL_MILES_TO_METERS * METERS_TO_KILOMETERS) as $type
80 }
81 LengthUnits::USSurveyFoot => {
82 value * (SURVEYFOOT_TO_METER * METERS_TO_KILOMETERS) as $type
83 }
84 },
85 LengthUnits::Mile => match source_unit {
86 LengthUnits::Meters => value * METERS_TO_MILES as $type,
87 LengthUnits::Kilometers => {
88 value * (KILOMETERS_TO_METERS * METERS_TO_MILES) as $type
89 }
90 LengthUnits::Feet => value * (FEET_TO_METERS * METERS_TO_MILES) as $type,
91 LengthUnits::Mile => value,
92 LengthUnits::NauticalMile => {
93 value * (NAUTICAL_MILES_TO_METERS * METERS_TO_MILES) as $type
94 }
95 LengthUnits::USSurveyFoot => {
96 value * (SURVEYFOOT_TO_METER * METERS_TO_MILES) as $type
97 }
98 },
99 LengthUnits::NauticalMile => match source_unit {
100 LengthUnits::Meters => value * METERS_TO_NAUTICAL_MILE as $type,
101 LengthUnits::Kilometers => {
102 value * (KILOMETERS_TO_METERS * METERS_TO_NAUTICAL_MILE) as $type
103 }
104 LengthUnits::Feet => {
105 value * (FEET_TO_METERS * METERS_TO_NAUTICAL_MILE) as $type
106 }
107 LengthUnits::Mile => {
108 value * (MILES_TO_METERS * METERS_TO_NAUTICAL_MILE) as $type
109 }
110 LengthUnits::NauticalMile => value,
111 LengthUnits::USSurveyFoot => {
112 value * (SURVEYFOOT_TO_METER * METERS_TO_NAUTICAL_MILE) as $type
113 }
114 },
115 LengthUnits::USSurveyFoot => match source_unit {
116 LengthUnits::Meters => value * METER_TO_SURVEYFOOT as $type,
117 LengthUnits::Kilometers => {
118 value * (KILOMETERS_TO_METERS * METER_TO_SURVEYFOOT) as $type
119 }
120 LengthUnits::Feet => {
121 value * (FEET_TO_METERS * METER_TO_SURVEYFOOT) as $type
122 }
123 LengthUnits::Mile => {
124 value * (MILES_TO_METERS * METER_TO_SURVEYFOOT) as $type
125 }
126 LengthUnits::NauticalMile => {
127 value * (NAUTICAL_MILES_TO_METERS * METER_TO_SURVEYFOOT) as $type
128 }
129 LengthUnits::USSurveyFoot => value,
130 },
131 }
132 }
133 }
134 };
135}
136basic_unit!(Length, LengthUnits, Meters);
137from_units_length!(f32);
138from_units_length!(f64);
139
140impl LengthUnits {
141 pub const fn short_name(&self) -> &'static str {
142 match self {
143 LengthUnits::Meters => "m",
144 LengthUnits::Kilometers => "km",
145 LengthUnits::Feet => "ft",
146 LengthUnits::Mile => "mi",
147 LengthUnits::NauticalMile => "nmi",
148 LengthUnits::USSurveyFoot => "ussft",
149 }
150 }
151}
152
153impl Length {
156 pub const ZERO: Length = Self::new_meters(0.0);
157
158 #[must_use]
159 pub const fn new_meters(value: f64) -> Length {
160 Self {
161 value,
162 units: LengthUnits::Meters,
163 }
164 }
165
166 #[must_use]
167 pub const fn new_feet(value: f64) -> Length {
168 Self {
169 value,
170 units: LengthUnits::Feet,
171 }
172 }
173
174 #[must_use]
175 pub fn as_meters(&self) -> Length {
176 self.as_unit(LengthUnits::Meters)
177 }
178
179 #[must_use]
180 pub fn as_feet(&self) -> Length {
181 self.as_unit(LengthUnits::Feet)
182 }
183}
184
185impl Display for Length {
186 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
187 f.write_fmt(format_args!(
188 "{:02.3}{}",
189 self.value,
190 self.units.short_name()
191 ))
192 }
193}
194
195pub const FEET_TO_METERS: f64 = 3.048E-01; pub const METERS_TO_FEET: f64 = 1. / FEET_TO_METERS;
197pub const MILES_TO_METERS: f64 = 1.609_344E3; pub const METERS_TO_MILES: f64 = 1. / MILES_TO_METERS;
199pub const KILOMETERS_TO_METERS: f64 = 1000.;
200pub const METERS_TO_KILOMETERS: f64 = 1. / KILOMETERS_TO_METERS;
201pub const NAUTICAL_MILES_TO_METERS: f64 = 1.852E3;
202pub const METERS_TO_NAUTICAL_MILE: f64 = 1. / NAUTICAL_MILES_TO_METERS;
203
204#[allow(clippy::excessive_precision)]
205pub const SURVEYFOOT_TO_METER: f64 = 3.04800609601219241E-1;
206pub const METER_TO_SURVEYFOOT: f64 = 1. / SURVEYFOOT_TO_METER;
207
208#[macro_export]
209macro_rules! assert_length_eq_eps {
210 ($left:expr, $right:expr, $eps:expr) => {
211 match (&$left, &$right) {
212 (left_val, right_val) => {
213 let delta = (*left_val - *right_val).value().abs();
214 if !(delta <= $eps) {
215 panic!(
216 "Assertion failed, {} - {} = {} > {} (error: {})",
217 &*left_val,
218 &*right_val,
219 delta,
220 $eps,
221 delta - $eps
222 )
223 }
224 }
225 }
226 };
227}
228
229#[cfg(test)]
230mod tests {
231 use crate::units::length::LengthUnits;
232 use crate::units::FromUnits;
233
234 #[test]
235 pub fn test_feet_meters() {
236 assert_eq!(
237 LengthUnits::Feet.from(1.0, LengthUnits::Meters),
238 1.0 / 0.3048
239 );
240 assert_eq!(LengthUnits::Meters.from(1.0, LengthUnits::Feet), 0.3048);
241 }
242
243 #[test]
244 pub fn test_meters_kilometers() {
245 assert_eq!(
246 LengthUnits::Meters.from(1.0, LengthUnits::Kilometers),
247 1000.
248 );
249 assert_eq!(
250 LengthUnits::Kilometers.from(1000.0, LengthUnits::Meters),
251 1.
252 );
253 }
254}