1use std::{cmp, fmt, mem};
2
3use crate::{Error, Result};
4
5const DENOM_INT_SHIFT: u8 = 8;
6const DENOM_EXP_MAX: u32 = 19;
7const DENOM_BASE: u64 = 10;
8
9pub const DENOM_LEN: usize = 2;
11
12#[repr(C)]
24#[derive(Clone, Copy, Debug, Eq, PartialEq)]
25pub struct Denomination(u16);
26
27impl Denomination {
28 pub const fn new() -> Self {
30 Self(0x0100)
31 }
32
33 pub const fn integer(&self) -> u8 {
35 (self.0 >> DENOM_INT_SHIFT) as u8
36 }
37
38 pub const fn exponent(&self) -> u8 {
40 self.0 as u8
41 }
42
43 pub fn value(&self) -> u64 {
61 let exp = cmp::min(self.exponent() as u32, DENOM_EXP_MAX);
62 (self.integer() as u64).saturating_mul(DENOM_BASE.pow(exp))
63 }
64
65 pub fn from_value(val: u64) -> Self {
78 match val {
79 v if v <= u8::MAX as u64 => Self((val << 8) as u16),
80 v if v % 10 == 0 => {
81 let exp = (val as f64).log10().floor() as u32;
82 let (int, exp) = match val.saturating_div(10u64.pow(exp)) {
83 i if i == 1 || i == 2 => (i * 100, exp - 2),
84 i if i == 5 || i == 25 => (i * 10, exp - 1),
85 i => (i, exp),
86 };
87
88 Self(((int << 8) as u16) | exp as u16)
89 }
90 _ => Self(0),
91 }
92 }
93
94 pub const fn len() -> usize {
96 mem::size_of::<u16>()
97 }
98
99 pub const fn is_empty(&self) -> bool {
101 self.0 == 0
102 }
103
104 pub const fn is_valid(&self) -> bool {
106 matches!(self.integer(), 1 | 2 | 5 | 10 | 20 | 50 | 100 | 200 | 250)
107 }
108
109 pub const fn to_u16(&self) -> u16 {
111 self.0
112 }
113
114 pub const fn into_u16(self) -> u16 {
116 self.0
117 }
118
119 pub fn from_bytes(val: &[u8]) -> Self {
121 match val.len() {
122 0 => Self(0),
123 1 => Self((val[0] as u16) << DENOM_INT_SHIFT),
124 _ => Self(((val[0] as u16) << DENOM_INT_SHIFT) | val[1] as u16),
125 }
126 }
127
128 pub fn valid_value(val: u64) -> bool {
130 [1, 2, 5, 10, 20, 50, 100, 200, 250]
131 .into_iter()
132 .any(|v| val % v == 0 && (val <= 10 || val % 10 == 0))
133 }
134
135 pub fn to_bytes(&self, buf: &mut [u8]) -> Result<()> {
137 let len = Self::len();
138 let buf_len = buf.len();
139
140 if buf_len < len {
141 Err(Error::InvalidDenominationLen((buf_len, len)))
142 } else {
143 buf.copy_from_slice(self.to_u16().to_be_bytes().as_ref());
144 Ok(())
145 }
146 }
147
148 pub const fn into_bytes(self) -> [u8; DENOM_LEN] {
150 self.to_u16().to_be_bytes()
151 }
152}
153
154impl TryFrom<u64> for Denomination {
155 type Error = Error;
156
157 fn try_from(val: u64) -> Result<Self> {
158 match Self::from_value(val) {
159 d if d.is_valid() => Ok(d),
160 d => Err(Error::InvalidDenomination((d.integer(), d.exponent()))),
161 }
162 }
163}
164
165impl TryFrom<u32> for Denomination {
166 type Error = Error;
167
168 fn try_from(val: u32) -> Result<Self> {
169 (val as u64).try_into()
170 }
171}
172
173impl TryFrom<u16> for Denomination {
174 type Error = Error;
175
176 fn try_from(val: u16) -> Result<Self> {
177 (val as u64).try_into()
178 }
179}
180
181impl TryFrom<u8> for Denomination {
182 type Error = Error;
183
184 fn try_from(val: u8) -> Result<Self> {
185 (val as u64).try_into()
186 }
187}
188
189impl TryFrom<&[u8]> for Denomination {
190 type Error = Error;
191
192 fn try_from(val: &[u8]) -> Result<Self> {
193 match Self::from_bytes(val) {
194 d if d.is_valid() => Ok(d),
195 d => Err(Error::InvalidDenomination((d.integer(), d.exponent()))),
196 }
197 }
198}
199
200impl<const N: usize> TryFrom<[u8; N]> for Denomination {
201 type Error = Error;
202
203 fn try_from(val: [u8; N]) -> Result<Self> {
204 val.as_ref().try_into()
205 }
206}
207
208impl<const N: usize> TryFrom<&[u8; N]> for Denomination {
209 type Error = Error;
210
211 fn try_from(val: &[u8; N]) -> Result<Self> {
212 val.as_ref().try_into()
213 }
214}
215
216impl Default for Denomination {
217 fn default() -> Self {
218 Self::new()
219 }
220}
221
222impl fmt::Display for Denomination {
223 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
224 write!(f, "{{")?;
225 write!(f, r#""integer": {:#x}, "#, self.integer())?;
226 write!(f, r#""exponent": {:#x}, "#, self.exponent())?;
227 write!(f, r#""value": {:#x}"#, self.value())?;
228 write!(f, "}}")
229 }
230}
231
232#[cfg(test)]
233mod tests {
234 use super::*;
235
236 #[test]
237 fn test_denomination() {
238 let raw_vals = [1, 2, 5, 10, 20, 50, 100, 250, 500, 1000, 10_000u64];
239 let exp_ints = [1, 2, 5, 10, 20, 50, 100, 250, 50, 100, 100];
240 let exp_exps = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2];
241 let exp_denoms = [
242 Denomination(0x0100),
243 Denomination(0x0200),
244 Denomination(0x0500),
245 Denomination(0x0a00),
246 Denomination(0x1400),
247 Denomination(0x3200),
248 Denomination(0x6400),
249 Denomination(0xfa00),
250 Denomination(0x3201),
251 Denomination(0x6401),
252 Denomination(0x6402),
253 ];
254
255 raw_vals.into_iter().enumerate().for_each(|(i, val)| {
256 assert_eq!(Denomination::try_from(val), Ok(exp_denoms[i]));
257 assert_eq!(
258 Denomination::try_from([exp_ints[i], exp_exps[i]]),
259 Ok(exp_denoms[i])
260 );
261
262 let denom = Denomination::from_value(val);
263
264 assert_eq!(denom, exp_denoms[i]);
265 assert_eq!(denom.integer(), exp_ints[i]);
266 assert_eq!(denom.exponent(), exp_exps[i]);
267 assert_eq!(denom.value(), val);
268
269 assert!(denom.is_valid());
270 assert!(!denom.is_empty());
271 });
272 }
273
274 #[test]
275 fn test_denomination_invalid() {
276 let zero_denom = Denomination::from_value(0);
277
278 assert!(zero_denom.is_empty());
279 assert!(!zero_denom.is_valid());
280
281 (0..=u16::MAX)
282 .filter(|&v| !Denomination::valid_value(v as u64))
283 .for_each(|val| {
284 assert!(!Denomination::from_value(val as u64).is_valid());
285 assert!(Denomination::try_from(val).is_err());
286 });
287 }
288}