df_hyperliquid_core/utils/dec/
wire.rs1use std::{fmt, str::FromStr};
5
6use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
7
8use crate::utils::dec::{Dec, MAX_SCALE};
9
10#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
11pub enum BadDec {
12 #[error("`{0}` is not a decimal number")]
13 NotANumber(String),
14 #[error("`{0}` has more than eighteen decimal places")]
15 TooFine(String),
16 #[error("`{0}` is larger than this build can count")]
17 OutOfRange(String),
18}
19
20impl Dec {
21 pub fn parse(text: &str) -> Result<Dec, BadDec> {
24 let bad = || BadDec::NotANumber(text.to_owned());
25 let body = text.trim();
26 let (negative, body) = match body.strip_prefix('-') {
27 Some(rest) => (true, rest),
28 None => (false, body.strip_prefix('+').unwrap_or(body)),
29 };
30 let (whole, fraction) = body.split_once('.').unwrap_or((body, ""));
31 if whole.is_empty() && fraction.is_empty() {
32 return Err(bad());
33 }
34 if !whole.bytes().all(|b| b.is_ascii_digit()) || !fraction.bytes().all(|b| b.is_ascii_digit()) {
35 return Err(bad());
36 }
37 let fraction = fraction.trim_end_matches('0');
38 let scale = u32::try_from(fraction.len()).map_err(|_| BadDec::TooFine(text.to_owned()))?;
39 if scale > MAX_SCALE {
40 return Err(BadDec::TooFine(text.to_owned()));
41 }
42 let mut units: i128 = 0;
43 for digit in whole.bytes().chain(fraction.bytes()) {
44 units = units
45 .checked_mul(10)
46 .and_then(|u| u.checked_add(i128::from(digit - b'0')))
47 .ok_or_else(|| BadDec::OutOfRange(text.to_owned()))?;
48 }
49 Ok(Dec { units: if negative { -units } else { units }, scale }.normal())
50 }
51
52 #[must_use]
55 pub fn to_wire(self) -> String {
56 self.to_string()
57 }
58}
59
60impl fmt::Display for Dec {
61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 let normal = self.normal();
63 let sign = if normal.units < 0 { "-" } else { "" };
64 let magnitude = normal.units.unsigned_abs();
65 if normal.scale == 0 {
66 return write!(f, "{sign}{magnitude}");
67 }
68 let step = 10u128.pow(normal.scale);
69 let places = normal.scale as usize;
70 write!(f, "{sign}{}.{:0places$}", magnitude / step, magnitude % step)
71 }
72}
73
74impl FromStr for Dec {
75 type Err = BadDec;
76
77 fn from_str(text: &str) -> Result<Self, Self::Err> {
78 Dec::parse(text)
79 }
80}
81
82impl Serialize for Dec {
83 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
84 serializer.collect_str(self)
85 }
86}
87
88impl<'de> Deserialize<'de> for Dec {
91 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
92 struct Visitor;
93
94 impl de::Visitor<'_> for Visitor {
95 type Value = Dec;
96
97 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98 f.write_str("a decimal string or an integer")
99 }
100
101 fn visit_str<E: de::Error>(self, text: &str) -> Result<Dec, E> {
102 Dec::parse(text).map_err(E::custom)
103 }
104
105 fn visit_u64<E: de::Error>(self, value: u64) -> Result<Dec, E> {
106 Ok(Dec { units: i128::from(value), scale: 0 })
107 }
108
109 fn visit_i64<E: de::Error>(self, value: i64) -> Result<Dec, E> {
110 Ok(Dec { units: i128::from(value), scale: 0 })
111 }
112 }
113
114 deserializer.deserialize_any(Visitor)
115 }
116}
117
118#[cfg(test)]
119#[path = "tests/wire.rs"]
120mod tests;