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)]
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 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}