nautilus_hyperliquid/common/
parse.rs1use std::str::FromStr;
17
18use rust_decimal::Decimal;
19use serde::{Deserialize, Deserializer, Serializer};
20
21pub fn serialize_decimal_as_str<S>(decimal: &Decimal, serializer: S) -> Result<S::Ok, S::Error>
23where
24 S: Serializer,
25{
26 serializer.serialize_str(&decimal.normalize().to_string())
27}
28
29pub fn deserialize_decimal_from_str<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
31where
32 D: Deserializer<'de>,
33{
34 let s = String::deserialize(deserializer)?;
35 Decimal::from_str(&s).map_err(serde::de::Error::custom)
36}
37
38pub fn serialize_optional_decimal_as_str<S>(
40 decimal: &Option<Decimal>,
41 serializer: S,
42) -> Result<S::Ok, S::Error>
43where
44 S: Serializer,
45{
46 match decimal {
47 Some(d) => serializer.serialize_str(&d.normalize().to_string()),
48 None => serializer.serialize_none(),
49 }
50}
51
52pub fn deserialize_optional_decimal_from_str<'de, D>(
54 deserializer: D,
55) -> Result<Option<Decimal>, D::Error>
56where
57 D: Deserializer<'de>,
58{
59 let opt = Option::<String>::deserialize(deserializer)?;
60 match opt {
61 Some(s) => {
62 let decimal = Decimal::from_str(&s).map_err(serde::de::Error::custom)?;
63 Ok(Some(decimal))
64 }
65 None => Ok(None),
66 }
67}
68
69#[cfg(test)]
74mod tests {
75 use rstest::rstest;
76 use serde::{Deserialize, Serialize};
77
78 use super::*;
79
80 #[derive(Serialize, Deserialize)]
81 struct TestStruct {
82 #[serde(
83 serialize_with = "serialize_decimal_as_str",
84 deserialize_with = "deserialize_decimal_from_str"
85 )]
86 value: Decimal,
87 #[serde(
88 serialize_with = "serialize_optional_decimal_as_str",
89 deserialize_with = "deserialize_optional_decimal_from_str"
90 )]
91 optional_value: Option<Decimal>,
92 }
93
94 #[rstest]
95 fn test_decimal_serialization_roundtrip() {
96 let original = TestStruct {
97 value: Decimal::from_str("123.456789012345678901234567890").unwrap(),
98 optional_value: Some(Decimal::from_str("0.000000001").unwrap()),
99 };
100
101 let json = serde_json::to_string(&original).unwrap();
102 println!("Serialized: {}", json);
103
104 assert!(json.contains("\"123.45678901234567890123456789\""));
106 assert!(json.contains("\"0.000000001\""));
107
108 let deserialized: TestStruct = serde_json::from_str(&json).unwrap();
109 assert_eq!(original.value, deserialized.value);
110 assert_eq!(original.optional_value, deserialized.optional_value);
111 }
112
113 #[rstest]
114 fn test_decimal_precision_preservation() {
115 let test_cases = [
116 "0",
117 "1",
118 "0.1",
119 "0.01",
120 "0.001",
121 "123.456789012345678901234567890",
122 "999999999999999999.999999999999999999",
123 ];
124
125 for case in test_cases {
126 let decimal = Decimal::from_str(case).unwrap();
127 let test_struct = TestStruct {
128 value: decimal,
129 optional_value: Some(decimal),
130 };
131
132 let json = serde_json::to_string(&test_struct).unwrap();
133 let parsed: TestStruct = serde_json::from_str(&json).unwrap();
134
135 assert_eq!(decimal, parsed.value, "Failed for case: {}", case);
136 assert_eq!(
137 Some(decimal),
138 parsed.optional_value,
139 "Failed for case: {}",
140 case
141 );
142 }
143 }
144
145 #[rstest]
146 fn test_optional_none_handling() {
147 let test_struct = TestStruct {
148 value: Decimal::from_str("42.0").unwrap(),
149 optional_value: None,
150 };
151
152 let json = serde_json::to_string(&test_struct).unwrap();
153 assert!(json.contains("null"));
154
155 let parsed: TestStruct = serde_json::from_str(&json).unwrap();
156 assert_eq!(test_struct.value, parsed.value);
157 assert_eq!(None, parsed.optional_value);
158 }
159}