pyth_lazer_protocol/
rate.rs

1#[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            // Mul/div is reversed for converting mantissa to value
76            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}