Skip to main content

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)]
23#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
24#[cfg_attr(feature = "utoipa", schema(value_type = i64))]
25pub struct Rate(i64);
26
27impl Rate {
28    pub fn from_integer(value: i64, exponent: i16) -> Result<Self, RateError> {
29        let mantissa = match ExponentFactor::get(exponent).ok_or(RateError::Overflow)? {
30            ExponentFactor::Mul(coef) => value.checked_mul(coef).ok_or(RateError::Overflow)?,
31            ExponentFactor::Div(coef) => value.checked_div(coef).ok_or(RateError::Overflow)?,
32        };
33        Ok(Self(mantissa))
34    }
35
36    pub fn parse_str(value: &str, exponent: i16) -> Result<Self, RateError> {
37        let value: Decimal = value.parse()?;
38        let mantissa = match ExponentFactor::get(exponent).ok_or(RateError::Overflow)? {
39            ExponentFactor::Mul(coef) => value
40                .checked_mul(Decimal::from_i64(coef).ok_or(RateError::Overflow)?)
41                .ok_or(RateError::Overflow)?,
42            ExponentFactor::Div(coef) => value
43                .checked_div(Decimal::from_i64(coef).ok_or(RateError::Overflow)?)
44                .ok_or(RateError::Overflow)?,
45        };
46        if !mantissa.is_integer() {
47            return Err(RateError::TooPrecise);
48        }
49        let mantissa: i64 = mantissa.try_into().map_err(|_| RateError::Overflow)?;
50        Ok(Self(mantissa))
51    }
52
53    pub const fn from_mantissa(mantissa: i64) -> Self {
54        Self(mantissa)
55    }
56
57    pub fn from_f64(value: f64, exponent: i16) -> Result<Self, RateError> {
58        let value = Decimal::from_f64(value).ok_or(RateError::Overflow)?;
59        let mantissa = match ExponentFactor::get(exponent).ok_or(RateError::Overflow)? {
60            ExponentFactor::Mul(coef) => value
61                .checked_mul(Decimal::from_i64(coef).ok_or(RateError::Overflow)?)
62                .ok_or(RateError::Overflow)?,
63            ExponentFactor::Div(coef) => value
64                .checked_div(Decimal::from_i64(coef).ok_or(RateError::Overflow)?)
65                .ok_or(RateError::Overflow)?,
66        };
67        let mantissa: i64 = mantissa.try_into().map_err(|_| RateError::Overflow)?;
68        Ok(Self(mantissa))
69    }
70
71    pub fn mantissa(self) -> i64 {
72        self.0
73    }
74
75    pub fn to_f64(self, exponent: i16) -> Result<f64, RateError> {
76        match ExponentFactor::get(exponent).ok_or(RateError::Overflow)? {
77            // Mul/div is reversed for converting mantissa to value
78            ExponentFactor::Mul(coef) => Ok(self.0 as f64 / coef as f64),
79            ExponentFactor::Div(coef) => Ok(self.0 as f64 * coef as f64),
80        }
81    }
82}