pyth_lazer_protocol/
price.rs1#[cfg(test)]
2mod tests;
3
4use {
5 crate::ExponentFactor,
6 rust_decimal::{prelude::FromPrimitive, Decimal},
7 serde::{Deserialize, Serialize},
8 std::num::NonZeroI64,
9 thiserror::Error,
10};
11
12#[derive(Debug, Error)]
13pub enum PriceError {
14 #[error("decimal parse error: {0}")]
15 DecimalParse(#[from] rust_decimal::Error),
16 #[error("price value is more precise than available exponent")]
17 TooPrecise,
18 #[error("zero price is unsupported")]
19 ZeroPriceUnsupported,
20 #[error("overflow")]
21 Overflow,
22}
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
25#[repr(transparent)]
26pub struct Price(NonZeroI64);
27
28impl Price {
29 pub fn from_integer(value: i64, exponent: i16) -> Result<Price, PriceError> {
30 let mantissa = match ExponentFactor::get(exponent).ok_or(PriceError::Overflow)? {
31 ExponentFactor::Mul(coef) => value.checked_mul(coef).ok_or(PriceError::Overflow)?,
32 ExponentFactor::Div(coef) => value.checked_div(coef).ok_or(PriceError::Overflow)?,
33 };
34 let mantissa = NonZeroI64::new(mantissa).ok_or(PriceError::ZeroPriceUnsupported)?;
35 Ok(Self(mantissa))
36 }
37
38 pub fn parse_str(value: &str, exponent: i16) -> Result<Price, PriceError> {
39 let value: Decimal = value.parse()?;
40 let mantissa = match ExponentFactor::get(exponent).ok_or(PriceError::Overflow)? {
41 ExponentFactor::Mul(coef) => value
42 .checked_mul(Decimal::from_i64(coef).ok_or(PriceError::Overflow)?)
43 .ok_or(PriceError::Overflow)?,
44 ExponentFactor::Div(coef) => value
45 .checked_div(Decimal::from_i64(coef).ok_or(PriceError::Overflow)?)
46 .ok_or(PriceError::Overflow)?,
47 };
48 if !mantissa.is_integer() {
49 return Err(PriceError::TooPrecise);
50 }
51 let mantissa: i64 = mantissa.try_into().map_err(|_| PriceError::Overflow)?;
52 let mantissa = NonZeroI64::new(mantissa).ok_or(PriceError::Overflow)?;
53 Ok(Self(mantissa))
54 }
55
56 pub const fn from_nonzero_mantissa(mantissa: NonZeroI64) -> Self {
57 Self(mantissa)
58 }
59
60 pub const fn from_mantissa(mantissa: i64) -> Result<Self, PriceError> {
61 if let Some(mantissa) = NonZeroI64::new(mantissa) {
62 Ok(Self(mantissa))
63 } else {
64 Err(PriceError::ZeroPriceUnsupported)
65 }
66 }
67
68 pub fn mantissa(self) -> NonZeroI64 {
69 self.0
70 }
71
72 pub fn mantissa_i64(self) -> i64 {
73 self.0.get()
74 }
75
76 pub fn to_f64(self, exponent: i16) -> Result<f64, PriceError> {
77 match ExponentFactor::get(exponent).ok_or(PriceError::Overflow)? {
78 ExponentFactor::Mul(coef) => Ok(self.0.get() as f64 / coef as f64),
80 ExponentFactor::Div(coef) => Ok(self.0.get() as f64 * coef as f64),
81 }
82 }
83
84 pub fn from_f64(value: f64, exponent: i16) -> Result<Self, PriceError> {
85 let value = Decimal::from_f64(value).ok_or(PriceError::Overflow)?;
86 let mantissa = match ExponentFactor::get(exponent).ok_or(PriceError::Overflow)? {
87 ExponentFactor::Mul(coef) => value
88 .checked_mul(Decimal::from_i64(coef).ok_or(PriceError::Overflow)?)
89 .ok_or(PriceError::Overflow)?,
90 ExponentFactor::Div(coef) => value
91 .checked_div(Decimal::from_i64(coef).ok_or(PriceError::Overflow)?)
92 .ok_or(PriceError::Overflow)?,
93 };
94 let mantissa: i64 = mantissa.try_into().map_err(|_| PriceError::Overflow)?;
95 Ok(Self(
96 NonZeroI64::new(mantissa).ok_or(PriceError::ZeroPriceUnsupported)?,
97 ))
98 }
99
100 pub fn add_with_same_exponent(self, other: Price) -> Result<Self, PriceError> {
101 let mantissa = self
102 .0
103 .get()
104 .checked_add(other.0.get())
105 .ok_or(PriceError::Overflow)?;
106 Self::from_mantissa(mantissa).map_err(|_| PriceError::ZeroPriceUnsupported)
107 }
108
109 pub fn sub_with_same_exponent(self, other: Price) -> Result<Self, PriceError> {
110 let mantissa = self
111 .0
112 .get()
113 .checked_sub(other.0.get())
114 .ok_or(PriceError::Overflow)?;
115 Self::from_mantissa(mantissa).map_err(|_| PriceError::ZeroPriceUnsupported)
116 }
117
118 pub fn mul_integer(self, factor: i64) -> Result<Self, PriceError> {
119 let mantissa = self
120 .0
121 .get()
122 .checked_mul(factor)
123 .ok_or(PriceError::Overflow)?;
124 Self::from_mantissa(mantissa).map_err(|_| PriceError::ZeroPriceUnsupported)
125 }
126
127 pub fn div_integer(self, factor: i64) -> Result<Self, PriceError> {
128 let mantissa = self
129 .0
130 .get()
131 .checked_div(factor)
132 .ok_or(PriceError::Overflow)?;
133 Self::from_mantissa(mantissa).map_err(|_| PriceError::ZeroPriceUnsupported)
134 }
135
136 pub fn mul_decimal(self, mantissa: i64, exponent: i16) -> Result<Self, PriceError> {
137 let left_mantissa = i128::from(self.0.get());
138 let right_mantissa = i128::from(mantissa);
139
140 let multiplied_mantissas = left_mantissa
142 .checked_mul(right_mantissa)
143 .ok_or(PriceError::Overflow)?;
144
145 let result_mantissa = match ExponentFactor::get(exponent).ok_or(PriceError::Overflow)? {
148 ExponentFactor::Mul(coef) => multiplied_mantissas
149 .checked_div(coef.into())
150 .ok_or(PriceError::Overflow)?,
151 ExponentFactor::Div(coef) => multiplied_mantissas
152 .checked_mul(coef.into())
153 .ok_or(PriceError::Overflow)?,
154 };
155 let result_mantissa: i64 = result_mantissa
156 .try_into()
157 .map_err(|_| PriceError::Overflow)?;
158 Self::from_mantissa(result_mantissa).map_err(|_| PriceError::ZeroPriceUnsupported)
159 }
160}