helium_api/models/
values.rs1use 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 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}