bybit/serde_helpers/mod.rs
1use crate::prelude::*;
2
3/// Custom deserialization module for handling strings as optional `f64`.
4///
5/// Used for fields like `avg_price` or `leverage` that may be empty or absent in API responses. Empty strings are treated as `None`, ensuring proper handling of optional numerical fields.
6pub mod string_to_float_optional {
7 use super::*;
8
9 /// Serializes an `Option<f64>` as a string.
10 ///
11 /// Converts the `Option<f64>` to a string, using an empty string for `None`. This aligns with Bybit’s API expectations for optional fields.
12 pub fn serialize<S>(value: &Option<f64>, serializer: S) -> Result<S::Ok, S::Error>
13 where
14 S: Serializer,
15 {
16 match value {
17 Some(v) => serializer.serialize_str(&v.to_string()),
18 None => serializer.serialize_str(""),
19 }
20 }
21
22 /// Deserializes a string to an `Option<f64>`, returning `None` for empty strings.
23 ///
24 /// Parses the string to an `Option<f64>`, treating empty strings as `None`. Bots use this to handle optional numerical fields robustly.
25 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<f64>, D::Error>
26 where
27 D: Deserializer<'de>,
28 {
29 let s: Option<String> = Option::deserialize(deserializer)?;
30 match s {
31 Some(s) if s.trim().is_empty() => Ok(None),
32 Some(s) => f64::from_str(&s)
33 .map(Some)
34 .map_err(serde::de::Error::custom),
35 None => Ok(None),
36 }
37 }
38}
39
40/// Module for serializing and deserializing optional u64 values as strings.
41pub mod string_to_u64_optional {
42 use super::*;
43
44 /// Serializes an Option<u64> as a string, using an empty string for None.
45 pub fn serialize<S>(value: &Option<u64>, serializer: S) -> Result<S::Ok, S::Error>
46 where
47 S: Serializer,
48 {
49 match value {
50 Some(v) => serializer.serialize_str(&v.to_string()),
51 None => serializer.serialize_str(""),
52 }
53 }
54
55 /// Deserializes a string to an Option<u64>, returning None for empty strings.
56 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error>
57 where
58 D: Deserializer<'de>,
59 {
60 let s: Option<String> = Option::deserialize(deserializer)?;
61 match s {
62 Some(s) if s.trim().is_empty() => Ok(None),
63 Some(s) => u64::from_str(&s)
64 .map(Some)
65 .map_err(serde::de::Error::custom),
66 None => Ok(None),
67 }
68 }
69}
70
71/// Deserializes a string to a u64, handling empty strings and invalid formats.
72///
73/// Used for fields like timestamps (`time_second`, `start_time`) that are returned as strings by the Bybit API but need to be converted to `u64` for Rust. Bots should rely on this to handle edge cases, such as empty strings, which are treated as `0`.
74pub mod string_to_u64 {
75 use super::*;
76
77 // Serialize a u64 as a string.
78 pub fn serialize<S>(value: &u64, serializer: S) -> Result<S::Ok, S::Error>
79 where
80 S: Serializer,
81 {
82 let s = value.to_string();
83 serializer.serialize_str(&s)
84 }
85
86 // Deserialize a string to a u64.
87 pub fn deserialize<'de, D>(deserializer: D) -> Result<u64, D::Error>
88 where
89 D: Deserializer<'de>,
90 {
91 let s = String::deserialize(deserializer)?;
92 s.parse::<u64>().map_err(serde::de::Error::custom)
93 }
94}
95
96/// Deserializes a string to a u32, handling empty strings and invalid formats.
97///
98/// Used for fields like numeric IDs or enum values that are returned as strings by the Bybit API but need to be converted to `u32` for Rust. Bots should rely on this to handle edge cases, such as empty strings, which are treated as `0`.
99pub mod string_to_u32 {
100 use super::*;
101
102 // Serialize a u32 as a string.
103 pub fn serialize<S>(value: &u32, serializer: S) -> Result<S::Ok, S::Error>
104 where
105 S: Serializer,
106 {
107 let s = value.to_string();
108 serializer.serialize_str(&s)
109 }
110
111 // Deserialize a string to a u32.
112 pub fn deserialize<'de, D>(deserializer: D) -> Result<u32, D::Error>
113 where
114 D: Deserializer<'de>,
115 {
116 let s = String::deserialize(deserializer)?;
117 s.parse::<u32>().map_err(serde::de::Error::custom)
118 }
119}
120
121/// Deserializes a string to an f64, handling empty strings and invalid formats.
122///
123/// Used for fields like prices (`last_price`, `open_price`) and quantities (`size`, `open_interest`) that are returned as strings to preserve precision but need to be converted to `f64` for calculations. Bots should use this to ensure accurate numerical processing and handle edge cases like empty strings.
124pub mod string_to_float {
125 use super::*;
126
127 // Serialize a u64 as a string.
128 pub fn serialize<S>(value: &f64, serializer: S) -> Result<S::Ok, S::Error>
129 where
130 S: Serializer,
131 {
132 let s = value.to_string();
133 serializer.serialize_str(&s)
134 }
135
136 // Deserialize a string as an f64.
137 pub fn deserialize<'de, D>(deserializer: D) -> Result<f64, D::Error>
138 where
139 D: Deserializer<'de>,
140 {
141 let s = String::deserialize(deserializer)?;
142 s.parse::<f64>().map_err(serde::de::Error::custom)
143 }
144}
145
146/// Checks if an optional string is empty or None.
147///
148/// Used in serialization to skip fields like `min_price` or `max_order_qty` when they are empty or `None`, ensuring cleaner JSON output that complies with Bybit’s API expectations.
149pub fn is_empty_or_none(s: &Option<String>) -> bool {
150 s.as_ref().is_none_or(|s| s.is_empty())
151}
152
153/// Custom deserialization function to treat empty strings as `None`.
154///
155/// Used for fields that may receive empty strings from the Bybit API but should be treated as absent values in Rust. This ensures proper handling of optional fields like `block_trade_id` or `order_iv` in order responses, preventing empty strings from being misinterpreted as valid data. Bots should use this to maintain data integrity when processing API responses.
156pub fn empty_string_as_none<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
157where
158 D: Deserializer<'de>,
159 T: Deserialize<'de>,
160{
161 let s: Option<String> = Option::deserialize(deserializer)?;
162 match s {
163 Some(ref s) if s.trim().is_empty() => Ok(None),
164 Some(s) => {
165 let kind = T::deserialize(serde_json::Value::String(s))
166 .map(Some)
167 .map_err(serde::de::Error::custom)?;
168 Ok(kind)
169 }
170 None => Ok(None),
171 }
172}