pyth_lazer_protocol/
rate.rs1#[cfg(test)]
2mod tests;
3
4use {
5 crate::ExponentFactor,
6 rust_decimal::{prelude::FromPrimitive, Decimal},
7 serde::{Deserialize, Serialize},
8 thiserror::Error,
9};
10
11#[derive(Debug, Error)]
12pub enum RateError {
13 #[error("decimal parse error: {0}")]
14 DecimalParse(#[from] rust_decimal::Error),
15 #[error("price value is more precise than available exponent")]
16 TooPrecise,
17 #[error("overflow")]
18 Overflow,
19}
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
22#[repr(transparent)]
23pub struct Rate(i64);
24
25impl Rate {
26 pub fn from_integer(value: i64, exponent: i16) -> Result<Self, RateError> {
27 let mantissa = match ExponentFactor::get(exponent).ok_or(RateError::Overflow)? {
28 ExponentFactor::Mul(coef) => value.checked_mul(coef).ok_or(RateError::Overflow)?,
29 ExponentFactor::Div(coef) => value.checked_div(coef).ok_or(RateError::Overflow)?,
30 };
31 Ok(Self(mantissa))
32 }
33
34 pub fn parse_str(value: &str, exponent: i16) -> Result<Self, RateError> {
35 let value: Decimal = value.parse()?;
36 let mantissa = match ExponentFactor::get(exponent).ok_or(RateError::Overflow)? {
37 ExponentFactor::Mul(coef) => value
38 .checked_mul(Decimal::from_i64(coef).ok_or(RateError::Overflow)?)
39 .ok_or(RateError::Overflow)?,
40 ExponentFactor::Div(coef) => value
41 .checked_div(Decimal::from_i64(coef).ok_or(RateError::Overflow)?)
42 .ok_or(RateError::Overflow)?,
43 };
44 if !mantissa.is_integer() {
45 return Err(RateError::TooPrecise);
46 }
47 let mantissa: i64 = mantissa.try_into().map_err(|_| RateError::Overflow)?;
48 Ok(Self(mantissa))
49 }
50
51 pub const fn from_mantissa(mantissa: i64) -> Self {
52 Self(mantissa)
53 }
54
55 pub fn from_f64(value: f64, exponent: i16) -> Result<Self, RateError> {
56 let value = Decimal::from_f64(value).ok_or(RateError::Overflow)?;
57 let mantissa = match ExponentFactor::get(exponent).ok_or(RateError::Overflow)? {
58 ExponentFactor::Mul(coef) => value
59 .checked_mul(Decimal::from_i64(coef).ok_or(RateError::Overflow)?)
60 .ok_or(RateError::Overflow)?,
61 ExponentFactor::Div(coef) => value
62 .checked_div(Decimal::from_i64(coef).ok_or(RateError::Overflow)?)
63 .ok_or(RateError::Overflow)?,
64 };
65 let mantissa: i64 = mantissa.try_into().map_err(|_| RateError::Overflow)?;
66 Ok(Self(mantissa))
67 }
68
69 pub fn mantissa(self) -> i64 {
70 self.0
71 }
72
73 pub fn to_f64(self, exponent: i16) -> Result<f64, RateError> {
74 match ExponentFactor::get(exponent).ok_or(RateError::Overflow)? {
75 ExponentFactor::Mul(coef) => Ok(self.0 as f64 / coef as f64),
77 ExponentFactor::Div(coef) => Ok(self.0 as f64 * coef as f64),
78 }
79 }
80}