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