1use crate::typenum::{Integer, U10};
2use crate::Fix;
3
4use anchor_lang::error::ErrorCode::InvalidNumericConversion;
5use anchor_lang::prelude::{borsh, AnchorDeserialize, AnchorSerialize, InitSpace, Result};
6use paste::paste;
7use serde::{Deserialize, Serialize};
8
9macro_rules! impl_fix_value {
10 ($sign:ident, $bits:expr) => {
11 paste! {
12 #[derive(PartialEq, Eq, Copy, Clone, Debug, Serialize, Deserialize, AnchorSerialize, AnchorDeserialize, InitSpace)]
15 pub struct [<$sign FixValue $bits>] {
16 pub bits: [<$sign:lower $bits>],
17 pub exp: i8,
18 }
19
20 impl [<$sign FixValue $bits>] {
21 pub fn new(bits: [<$sign:lower $bits>], exp: i8) -> Self {
22 Self { bits, exp }
23 }
24 }
25
26 impl<Bits, Exp> From<Fix<Bits, U10, Exp>> for [<$sign FixValue $bits>]
27 where
28 Bits: Into<[<$sign:lower $bits>]>,
29 Exp: Integer,
30 {
31 fn from(fix: Fix<Bits, U10, Exp>) -> Self {
32 Self {
33 bits: fix.bits.into(),
34 exp: Exp::to_i8(),
35 }
36 }
37 }
38
39 impl<Bits, Exp> TryFrom<[<$sign FixValue $bits>]> for Fix<Bits, U10, Exp>
40 where
41 Bits: From<[<$sign:lower $bits>]>,
42 Exp: Integer,
43 {
44 type Error = anchor_lang::error::Error;
45 fn try_from(value: [<$sign FixValue $bits>]) -> Result<Fix<Bits, U10, Exp>> {
46 if value.exp == Exp::to_i8() {
47 Ok(Fix::new(value.bits.into()))
48 } else {
49 Err(InvalidNumericConversion.into())
50 }
51 }
52 }
53 }
54 };
55}
56
57impl_fix_value!(U, 8);
58impl_fix_value!(U, 16);
59impl_fix_value!(U, 32);
60impl_fix_value!(U, 64);
61impl_fix_value!(U, 128);
62impl_fix_value!(I, 8);
63impl_fix_value!(I, 16);
64impl_fix_value!(I, 32);
65impl_fix_value!(I, 64);
66impl_fix_value!(I, 128);
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71 use crate::aliases::si::Kilo;
72 use anyhow::Result;
73 use borsh::to_vec;
74
75 macro_rules! fix_value_tests {
76 ($sign:ident, $bits:expr) => {
77 paste! {
78 #[test]
79 fn [<roundtrip_into_ $sign:lower $bits>]() -> Result<()> {
80 let start = Kilo::new([<69 $sign:lower $bits>]);
81 let there: [<$sign FixValue $bits>] = start.into();
82 let back: Kilo<[<$sign:lower $bits>]> = there.try_into()?;
83 assert_eq!(there, [<$sign FixValue $bits>]::new(69, 3));
84 Ok(assert_eq!(start, back))
85 }
86
87 #[test]
88 fn [<roundtrip_serialize_ $sign:lower $bits>]() -> Result<()> {
89 let start = [<$sign FixValue $bits>]::new(20, -2);
90 let bytes = to_vec(&start)?;
91 let back = AnchorDeserialize::deserialize(&mut bytes.as_slice())?;
92 Ok(assert_eq!(start, back))
93 }
94
95 #[test]
96 fn [<wrong_exp_should_fail_ $sign:lower $bits>]() -> Result<()> {
97 let pow11 = [<$sign FixValue $bits>]::new(42, -11);
98 let wrong = TryInto::<Kilo<[<$sign:lower $bits>]>>::try_into(pow11);
99 Ok(assert_eq!(Err(InvalidNumericConversion.into()), wrong))
100 }
101 }
102 };
103 }
104
105 fix_value_tests!(U, 8);
106 fix_value_tests!(U, 16);
107 fix_value_tests!(U, 32);
108 fix_value_tests!(U, 64);
109 fix_value_tests!(U, 128);
110 fix_value_tests!(I, 8);
111 fix_value_tests!(I, 16);
112 fix_value_tests!(I, 32);
113 fix_value_tests!(I, 64);
114 fix_value_tests!(I, 128);
115}