reda_unit/unit/
mod.rs

1mod units;
2mod ops;
3
4use core::fmt;
5use std::{fmt::Debug, marker::PhantomData, str::FromStr};
6use crate::Number;
7use serde::{Deserialize, Deserializer, Serialize, Serializer};
8pub use units::*;
9
10pub trait Unit : PartialEq + Eq + Clone + Copy + Debug {
11    fn name() -> &'static str;
12}
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub struct UnitNumber<U> {
16    number: Number,
17    unit: PhantomData<U>,
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub struct UnitComplex<U> {
22    re: UnitNumber<U>,
23    im: UnitNumber<U>,
24    unit: PhantomData<U>,
25}
26
27impl<U: Unit> UnitNumber<U> {
28    pub fn new<N: Into<Number>>(number: N) -> Self {
29        Self { number: number.into(), unit: PhantomData }
30    }
31
32    pub fn to_f64(&self) -> f64 {
33        self.number.to_f64()
34    }
35
36    pub fn value(&self) -> Number {
37        self.number
38    }
39
40    pub fn is_nan(self) -> bool {
41        self.number.is_nan()
42    }
43
44    pub fn is_finite(self) -> bool {
45        self.number.is_finite()
46    }
47
48    pub fn powf(self, exp: f64) -> Self {
49        Self::new(self.number.powf(exp))
50    }
51
52    pub fn atan2(self, other: Self) -> Number {
53        self.number.atan2(other.number)
54    }
55}
56
57macro_rules! impl_f64_like_method {
58    ($f:ident) => {
59        pub fn $f(&self) -> Self {
60            Self::new(self.number.$f())
61        }
62    };
63}
64
65impl<U: Unit> UnitNumber<U> {
66    impl_f64_like_method!(abs);
67    impl_f64_like_method!(ceil);
68    impl_f64_like_method!(floor);
69    impl_f64_like_method!(round);
70    impl_f64_like_method!(trunc);
71    impl_f64_like_method!(fract);
72
73    impl_f64_like_method!(sqrt);
74    impl_f64_like_method!(exp);
75    impl_f64_like_method!(ln);
76    impl_f64_like_method!(log10);
77    impl_f64_like_method!(log2);
78    impl_f64_like_method!(recip);
79    
80    impl_f64_like_method!(sin);
81    impl_f64_like_method!(cos);
82    impl_f64_like_method!(tan);
83    impl_f64_like_method!(asin);
84    impl_f64_like_method!(acos);
85    impl_f64_like_method!(atan);
86
87    impl_f64_like_method!(sinh);
88    impl_f64_like_method!(cosh);
89    impl_f64_like_method!(tanh);
90
91    impl_f64_like_method!(to_degrees);
92    impl_f64_like_method!(to_radians);
93}
94
95impl<U: Unit> UnitComplex<U> {
96    pub fn new<N1: Into<UnitNumber<U>>, N2: Into<UnitNumber<U>>>(re: N1, im: N2) -> Self {
97        Self { re: re.into(), im: im.into(), unit: PhantomData }
98    }
99
100    pub fn parts(&self) -> (UnitNumber<U>, UnitNumber<U>) {
101        (self.re, self.im)
102    }
103
104    pub fn conjugate(&self) -> Self {
105        Self {
106            re: self.re,
107            im: -self.im,
108            unit: self.unit
109        }
110    }
111
112    pub fn abs(&self) -> UnitNumber<U> {
113        let abs_value = (self.re.number.powf(2.) + self.im.number.powf(2.)).sqrt();
114        UnitNumber::new(abs_value)
115    }
116
117    pub fn arg(&self) -> Number {
118        self.im.atan2(self.re)
119    }
120}
121
122impl<U: Unit> fmt::Display for UnitNumber<U> {
123    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124        if let Some(p) = f.precision() {
125            write!(f, "{:.*}{}", p, self.number, U::name())
126        } else {
127            write!(f, "{}{}", self.number, U::name())
128        }
129    }
130}
131
132impl<U: Unit> fmt::Display for UnitComplex<U> {
133    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134        if let Some(p) = f.precision() {
135            write!(f, "{:.*} + {:.*}j", p, self.re, p, self.im)
136        } else {
137            write!(f, "{} + {}j", self.re, self.im)
138        }
139    }
140}
141
142impl<U: Unit> FromStr for UnitNumber<U> {
143    type Err = String;
144    fn from_str(s: &str) -> Result<Self, Self::Err> {
145        let s = s.trim();
146        if s.ends_with(U::name()) {
147            let number_len = s.len() - U::name().len();
148            let number_str = &s[..number_len];
149            let number: Number = FromStr::from_str(number_str)?;
150            Ok(Self::new(number))
151        } else {
152            Err(format!("Expect end with '{}'", U::name()))
153        }
154    }
155}
156
157impl<U: Unit> Serialize for UnitNumber<U> {
158    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
159    where
160        S: Serializer,
161    {
162        let s = self.to_string();
163        serializer.serialize_str(&s)
164    }
165}
166
167impl<'de, U: Unit> Deserialize<'de> for UnitNumber<U> {
168    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
169    where
170        D: Deserializer<'de>,
171    {
172        let s = String::deserialize(deserializer)?;
173        Self::from_str(&s).map_err(serde::de::Error::custom)
174    }
175}
176
177impl<U: Unit> From<Number> for UnitNumber<U> {
178    fn from(value: Number) -> Self {
179        Self::new(value)
180    }
181}
182
183macro_rules! impl_from {
184    ($t:ty) => {
185        impl<U: Unit> From<$t> for UnitNumber<U> {
186            fn from(value: $t) -> Self {
187                Self::new(Number::from(value as f64))    
188            }
189        }
190    };
191}
192
193impl_from!(f64);
194impl_from!(f32);
195impl_from!(u32);
196impl_from!(i32);