Skip to main content

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}