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