dive_deco/common/
depth.rs

1use 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}