hipparchus_az/
unit.rs

1use std::fmt::Display;
2use std::str::FromStr;
3use num_enum::{IntoPrimitive, TryFromPrimitive};
4
5/// Unit of angle measurement.
6#[repr(i8)]
7#[derive(Debug, PartialEq, Copy, Clone, IntoPrimitive, TryFromPrimitive)]
8pub enum Unit
9{
10    /// Degree, the default unit.
11    Degree = 0,
12
13    /// Minute, 1/60 of degree.
14    Minute = 1,
15
16    /// Second, 1/60 of minute.
17    Second = 2, 
18}
19
20impl Unit
21{
22    /// Get the abbreviation of the unit.
23    pub fn abbr(self) -> &'static str
24    {
25        match self
26        {
27            Self::Degree => "°",
28            Self::Minute => "'",
29            Self::Second => "\"",
30        }
31    }
32
33    /// Get the coefficient of the unit.
34    pub fn coefficient(self) -> f64
35    {
36        match self
37        {
38            Self::Degree => 1.0,
39            Self::Minute => 60.0,
40            Self::Second => 3600.0,
41        }
42    }
43
44    /// Convert the value from one unit to another. 
45    pub fn convert(value: f64, from: Unit, to: Unit) -> f64
46    {
47        value / from.coefficient() * to.coefficient()
48    }
49}
50
51/// Display `Unit` enum as a single character.
52impl Display for Unit
53{
54    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
55    {
56        write!(f, "{}", self.abbr())
57    }
58}
59
60/// Parse `Unit` enum from a single character.
61impl FromStr for Unit
62{
63    type Err = ();
64
65    fn from_str(s: &str) -> Result<Self, Self::Err>
66    {
67        match s
68        {
69            "°" => Ok(Unit::Degree),
70            "'" => Ok(Unit::Minute),
71            "\"" => Ok(Unit::Second),
72            _ => Err(()),
73        }
74    }
75}
76
77#[cfg(test)]
78mod tests 
79{
80    use super::*;
81    use rstest::*;
82    use float_cmp::assert_approx_eq;
83
84    #[rstest]
85    #[case(100.0, Unit::Degree)]
86    #[case(100.0, Unit::Minute)]
87    #[case(100.0, Unit::Second)]
88    fn test_unit_convert_same(#[case] value: f64, #[case] unit: Unit)
89    {
90        let actual = Unit::convert(value, unit, unit);
91        assert_approx_eq!(f64, value, actual);
92    }
93
94    #[rstest]
95    #[case(100.0, Unit::Degree, Unit::Minute, 6000.0)]
96    #[case(100.0, Unit::Degree, Unit::Second, 360000.0)]
97    #[case(100.0, Unit::Minute, Unit::Second, 6000.0)]
98    fn test_unit_convert_dms(#[case] value: f64, #[case] from: Unit, #[case] to: Unit, #[case] expected: f64)
99    {
100        let actual = Unit::convert(value, from, to);
101        assert_approx_eq!(f64, expected, actual);
102
103        let actual = Unit::convert(actual, to, from);
104        assert_approx_eq!(f64, value, actual);
105    }
106
107    #[rstest]
108    #[case(Unit::Degree, "°")]
109    #[case(Unit::Minute, "'")]
110    #[case(Unit::Second, "\"")]
111    fn test_unit_str(#[case] unit: Unit, #[case] text: String)
112    {
113        assert_eq!(text, unit.to_string());
114        assert_eq!(unit, Unit::from_str(text.as_str()).unwrap());
115    }
116
117    #[rstest]
118    #[case("")]
119    #[case("ABCD")]
120    fn test_unit_str_error(#[case] text: String)
121    {
122        let unit = Unit::from_str(text.as_str());
123        assert!(unit.is_err());
124    }
125}