dive_deco/common/
depth.rs1use core::{
2 fmt,
3 ops::{Add, AddAssign, Div, Mul, Sub},
4};
5
6#[cfg(feature = "serde")]
7use serde::{Deserialize, Serialize};
8
9#[cfg(test)]
10use alloc::vec;
11
12pub type DepthType = f64;
13pub enum Units {
14 Metric,
15 Imperial,
16}
17
18pub trait Unit<T = f64>: Sized {
19 fn from_units(val: T, units: Units) -> Self;
20 fn to_units(&self, units: Units) -> T;
21 fn base_unit(&self) -> T;
22}
23
24#[derive(Clone, Copy, Debug)]
25#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
26pub struct Depth {
27 m: DepthType,
28}
29
30impl Default for Depth {
31 fn default() -> Self {
32 Self { m: 0. }
33 }
34}
35
36impl fmt::Display for Depth {
37 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
38 write!(f, r"{}m \ {}ft", self.as_meters(), self.as_feet())
39 }
40}
41
42impl PartialEq<Self> for Depth {
43 fn eq(&self, other: &Self) -> bool {
44 self.m == other.m
45 }
46}
47
48impl PartialOrd<Self> for Depth {
49 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
50 self.m.partial_cmp(&other.m)
51 }
52}
53
54impl Add<Self> for Depth {
55 type Output = Self;
56
57 fn add(self, rhs: Self) -> Self::Output {
58 Self { m: self.m + rhs.m }
59 }
60}
61
62impl Sub<Self> for Depth {
63 type Output = Self;
64
65 fn sub(self, rhs: Self) -> Self::Output {
66 Self { m: self.m - rhs.m }
67 }
68}
69
70impl Mul<Self> for Depth {
71 type Output = Self;
72
73 fn mul(self, rhs: Self) -> Self::Output {
74 Self { m: self.m * rhs.m }
75 }
76}
77
78impl Mul<f64> for Depth {
79 type Output = Self;
80
81 fn mul(self, rhs: f64) -> Self::Output {
82 Self { m: self.m * rhs }
83 }
84}
85
86impl Div<Self> for Depth {
87 type Output = Self;
88
89 fn div(self, rhs: Self) -> Self::Output {
90 Self { m: self.m / rhs.m }
91 }
92}
93
94impl Div<f64> for Depth {
95 type Output = Self;
96
97 fn div(self, rhs: f64) -> Self::Output {
98 Self { m: self.m / rhs }
99 }
100}
101
102impl AddAssign for Depth {
103 fn add_assign(&mut self, rhs: Self) {
104 *self = Self { m: self.m + rhs.m }
105 }
106}
107
108impl Unit for Depth {
109 fn from_units(val: DepthType, units: Units) -> Self {
110 match units {
111 Units::Metric => Self::from_meters(val),
112 Units::Imperial => Self::from_feet(val),
113 }
114 }
115 fn to_units(&self, units: Units) -> DepthType {
116 match units {
117 Units::Metric => self.as_meters(),
118 Units::Imperial => self.as_feet(),
119 }
120 }
121 fn base_unit(&self) -> f64 {
122 self.m
123 }
124}
125
126impl Depth {
127 pub fn zero() -> Self {
128 Self { m: 0. }
129 }
130 pub fn from_meters<T: Into<DepthType>>(val: T) -> Self {
131 Self { m: val.into() }
132 }
133 pub fn from_feet<T: Into<DepthType>>(val: T) -> Self {
134 Self {
135 m: Self::ft_to_m(val.into()),
136 }
137 }
138 pub fn as_meters(&self) -> DepthType {
139 self.m
140 }
141 pub fn as_feet(&self) -> DepthType {
142 Self::m_to_ft(self.m)
143 }
144 fn m_to_ft(m: DepthType) -> DepthType {
145 m * 3.28084
146 }
147 fn ft_to_m(ft: DepthType) -> DepthType {
148 ft * 0.3048
149 }
150}
151
152#[cfg(test)]
153mod tests {
154 use super::*;
155
156 #[test]
157 fn m_to_ft() {
158 let depth = Depth::from_meters(1.);
159 let ft = depth.as_feet();
160 assert_eq!(ft, 3.28084);
161 }
162
163 #[test]
164 fn ft_to_m() {
165 let depth = Depth::from_feet(100.);
166 let m = depth.as_meters();
167 assert_eq!(m, 30.48);
168 }
169
170 #[test]
171 fn depth_conversion_factors() {
172 let depth = Depth::from_meters(1.);
173 let ft = depth.as_feet();
174 let new_depth = Depth::from_feet(ft);
175 let m = new_depth.as_meters();
176 assert_eq!(with_precision(m, 5), 1.);
177 }
178
179 #[test]
180 fn from_units_constructor() {
181 let depth_m = Depth::from_units(1., Units::Metric);
182 assert_eq!(depth_m.as_meters(), 1.);
183 assert_eq!(depth_m.as_feet(), 3.28084);
184
185 let depth_ft = Depth::from_units(1., Units::Imperial);
186 assert_eq!(with_precision(depth_ft.as_feet(), 5), 1.);
187 assert_eq!(depth_ft.as_meters(), 0.3048);
188 }
189
190 #[test]
191 fn test_depth_param_type_conversion() {
192 vec![Depth::from_meters(1.), Depth::from_meters(1)]
193 .into_iter()
194 .reduce(|a, b| {
195 assert_eq!(a, b);
196 Depth::zero()
197 });
198
199 vec![Depth::from_feet(1.), Depth::from_feet(1)]
200 .into_iter()
201 .reduce(|a, b| {
202 assert_eq!(a, b);
203 Depth::zero()
204 });
205 }
206
207 fn with_precision(x: f64, precision: u32) -> f64 {
208 let d = 10_u32.pow(precision) as f64;
209 (x * d).round() / d
210 }
211}