1use core::error::Error;
2use core::fmt::{self, Display, Formatter};
3
4#[cfg(feature = "std")]
5use std::string::String;
6#[cfg(feature = "std")]
7use std::{format, vec};
8
9#[cfg(feature = "anchor")]
10use anchor_lang::error::Error as AnchorError;
11#[cfg(feature = "anchor")]
12use anchor_lang::error::ErrorCode::InvalidNumericConversion;
13#[cfg(feature = "anchor")]
14use anchor_lang::prelude::{borsh, AnchorDeserialize, AnchorSerialize, InitSpace};
15use paste::paste;
16use serde::{Deserialize, Serialize};
17
18use crate::typenum::{Integer, U10};
19use crate::Fix;
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub struct ExponentMismatch {
24 pub expected: i8,
25 pub actual: i8,
26}
27
28impl Display for ExponentMismatch {
29 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
30 write!(
31 f,
32 "Exponent mismatch converting `FixValue` to `Fix`: expected: {}, got: {}.",
33 self.expected, self.actual
34 )
35 }
36}
37
38impl Error for ExponentMismatch {}
39
40#[cfg(feature = "anchor")]
41impl From<ExponentMismatch> for AnchorError {
42 fn from(_: ExponentMismatch) -> AnchorError {
43 InvalidNumericConversion.into()
44 }
45}
46
47macro_rules! impl_fix_value {
48 ($sign:ident, $bits:expr) => {
49 paste! {
50 #[derive(PartialEq, Eq, Copy, Clone, Debug, Default, Serialize, Deserialize)]
53 #[cfg_attr(
54 feature = "anchor",
55 derive(AnchorSerialize, AnchorDeserialize, InitSpace)
56 )]
57 pub struct [<$sign FixValue $bits>] {
58 pub bits: [<$sign:lower $bits>],
59 pub exp: i8,
60 }
61
62 impl [<$sign FixValue $bits>] {
63 #[must_use] pub fn new(bits: [<$sign:lower $bits>], exp: i8) -> Self {
64 Self { bits, exp }
65 }
66 }
67
68 impl<Bits, Exp> From<Fix<Bits, U10, Exp>> for [<$sign FixValue $bits>]
69 where
70 Bits: Into<[<$sign:lower $bits>]>,
71 Exp: Integer,
72 {
73 fn from(fix: Fix<Bits, U10, Exp>) -> Self {
74 Self {
75 bits: fix.bits.into(),
76 exp: Exp::to_i8(),
77 }
78 }
79 }
80
81 impl<Bits, Exp> TryFrom<[<$sign FixValue $bits>]> for Fix<Bits, U10, Exp>
82 where
83 Bits: From<[<$sign:lower $bits>]>,
84 Exp: Integer,
85 {
86 type Error = ExponentMismatch;
87 fn try_from(
88 value: [<$sign FixValue $bits>],
89 ) -> Result<Fix<Bits, U10, Exp>, ExponentMismatch> {
90 if value.exp == Exp::to_i8() {
91 Ok(Fix::new(value.bits.into()))
92 } else {
93 Err(ExponentMismatch { expected: Exp::to_i8(), actual: value.exp })
94 }
95 }
96 }
97 }
98 };
99}
100
101impl_fix_value!(U, 8);
102impl_fix_value!(U, 16);
103impl_fix_value!(U, 32);
104impl_fix_value!(U, 64);
105impl_fix_value!(U, 128);
106impl_fix_value!(I, 8);
107impl_fix_value!(I, 16);
108impl_fix_value!(I, 32);
109impl_fix_value!(I, 64);
110impl_fix_value!(I, 128);
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115 use crate::aliases::si::Kilo;
116 use anyhow::Result;
117 #[cfg(feature = "anchor")]
118 use borsh::to_vec;
119
120 macro_rules! fix_value_tests {
121 ($sign:ident, $bits:expr) => {
122 paste! {
123 #[test]
124 fn [<roundtrip_into_ $sign:lower $bits>]() -> Result<()> {
125 let start = Kilo::new([<69 $sign:lower $bits>]);
126 let there: [<$sign FixValue $bits>] = start.into();
127 let back: Kilo<[<$sign:lower $bits>]> = there.try_into()?;
128 assert_eq!(there, [<$sign FixValue $bits>]::new(69, 3));
129 Ok(assert_eq!(start, back))
130 }
131
132 #[cfg(feature = "anchor")]
133 #[test]
134 fn [<roundtrip_serialize_ $sign:lower $bits>]() -> Result<()> {
135 let start = [<$sign FixValue $bits>]::new(20, -2);
136 let bytes = to_vec(&start)?;
137 let back = AnchorDeserialize::deserialize(&mut bytes.as_slice())?;
138 Ok(assert_eq!(start, back))
139 }
140
141 #[test]
142 fn [<wrong_exp_should_fail_ $sign:lower $bits>]() -> Result<()> {
143 let pow11 = [<$sign FixValue $bits>]::new(42, -11);
144 let wrong = TryInto::<Kilo<[<$sign:lower $bits>]>>::try_into(pow11);
145 Ok(assert_eq!(
146 Err(ExponentMismatch { expected: 3, actual: -11 }),
147 wrong
148 ))
149 }
150 }
151 };
152 }
153
154 fix_value_tests!(U, 8);
155 fix_value_tests!(U, 16);
156 fix_value_tests!(U, 32);
157 fix_value_tests!(U, 64);
158 fix_value_tests!(U, 128);
159 fix_value_tests!(I, 8);
160 fix_value_tests!(I, 16);
161 fix_value_tests!(I, 32);
162 fix_value_tests!(I, 64);
163 fix_value_tests!(I, 128);
164}