helium_api/models/
values.rs

1use crate::{Error, Result};
2use core::fmt;
3use rust_decimal::prelude::*;
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5use std::str::FromStr;
6
7macro_rules! decimal_scalar {
8    ($stype:ident, $scalar:literal, $scale:literal) => {
9        #[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
10        pub struct $stype(Decimal);
11
12        impl FromStr for $stype {
13            type Err = Error;
14
15            fn from_str(s: &str) -> Result<Self> {
16                match Decimal::from_str(s).or_else(|_| Decimal::from_scientific(s)) {
17                    Ok(data) if data.scale() > 8 => Err(Error::decimals(s)),
18                    Ok(data) => Ok(Self(data)),
19                    Err(_) => Err(Error::decimals(s)),
20                }
21            }
22        }
23
24        impl Serialize for $stype {
25            fn serialize<S>(&self, s: S) -> std::result::Result<S::Ok, S::Error>
26            where
27                S: Serializer,
28            {
29                let u: u64 = u64::from(*self);
30                s.serialize_u64(u)
31            }
32        }
33
34        impl<'de> Deserialize<'de> for $stype {
35            fn deserialize<D>(d: D) -> std::result::Result<Self, D::Error>
36            where
37                D: Deserializer<'de>,
38            {
39                let val = u64::deserialize(d)?;
40                Ok(Self::from(val))
41            }
42        }
43
44        impl fmt::Display for $stype {
45            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46                self.0.fmt(f)
47            }
48        }
49
50        impl $stype {
51            pub fn new(d: Decimal) -> Self {
52                Self(d)
53            }
54
55            pub fn get_decimal(&self) -> Decimal {
56                self.0
57            }
58
59            pub fn to_f64(&self) -> f64 {
60                // The rust_decimal crate unwraps like this when implementing serde-with-float.
61                // Presumably, to_f64() would never return none.
62                use num_traits::ToPrimitive;
63                self.0.to_f64().unwrap()
64            }
65
66            pub fn deserialize_option<'de, D>(d: D) -> std::result::Result<Option<Self>, D::Error>
67            where
68                D: Deserializer<'de>,
69            {
70                let v: Option<u64> = Option::deserialize(d)?;
71                if let Some(val) = v {
72                    Ok(Some(Self::from(val)))
73                } else {
74                    Ok(None)
75                }
76            }
77        }
78
79        impl From<u64> for $stype {
80            fn from(v: u64) -> Self {
81                if let Some(mut data) = Decimal::from_u64(v) {
82                    data.set_scale($scale).unwrap();
83                    return Self(data);
84                }
85                panic!("u64 could not be converted into Decimal")
86            }
87        }
88
89        impl From<$stype> for u64 {
90            fn from(v: $stype) -> Self {
91                if let Some(scaled_dec) = v.0.checked_mul($scalar.into()) {
92                    if let Some(num) = scaled_dec.to_u64() {
93                        return num;
94                    }
95                }
96                panic!("Invalid scaled decimal construction")
97            }
98        }
99
100        impl From<i32> for $stype {
101            fn from(v: i32) -> Self {
102                if let Some(mut data) = Decimal::from_i32(v) {
103                    data.set_scale($scale).unwrap();
104                    return Self(data);
105                }
106                panic!("u64 could not be converted into Decimal")
107            }
108        }
109
110        impl From<$stype> for i32 {
111            fn from(v: $stype) -> Self {
112                if let Some(scaled_dec) = v.0.checked_mul($scalar.into()) {
113                    if let Some(num) = scaled_dec.to_i32() {
114                        return num;
115                    }
116                }
117                panic!("Invalid scaled decimal construction")
118            }
119        }
120    };
121}
122
123decimal_scalar!(Hnt, 100_000_000, 8);
124decimal_scalar!(Hst, 100_000_000, 8);
125decimal_scalar!(Iot, 100_000_000, 8);
126decimal_scalar!(Mobile, 100_000_000, 8);
127decimal_scalar!(Token, 100_000_000, 8);
128decimal_scalar!(Usd, 100_000_000, 8);
129decimal_scalar!(Dbi, 10, 1);
130
131#[cfg(test)]
132mod tests {
133    use serde_test::{assert_tokens, Token};
134
135    use super::*;
136
137    #[test]
138    fn test_ser_hnt() {
139        let hnt = Hnt::from(5500);
140
141        assert_tokens(&hnt, &[Token::U64(5500)]);
142    }
143}