Skip to main content

ig_client/presentation/
serialization.rs

1/// Module for handling the conversion between string and optional float values
2///
3/// This module provides serialization and deserialization functions for converting
4/// between `Option<f64>` and string representations used in the IG Markets API.
5pub mod string_as_float_opt {
6    use serde::{self, Deserialize, Deserializer, Serializer};
7    use serde_json::Value;
8
9    /// Serializes an optional float value to its string representation
10    ///
11    /// # Arguments
12    /// * `value` - The optional float value to serialize
13    /// * `serializer` - The serializer to use
14    ///
15    /// # Returns
16    /// A Result containing the serialized value or an error
17    pub fn serialize<S>(value: &Option<f64>, serializer: S) -> Result<S::Ok, S::Error>
18    where
19        S: Serializer,
20    {
21        match value {
22            Some(v) => serializer.serialize_f64(*v), // Serialize as a number
23            None => serializer.serialize_none(),
24        }
25    }
26
27    /// Deserializes a string representation to an optional float value
28    ///
29    /// # Arguments
30    /// * `deserializer` - The deserializer to use
31    ///
32    /// # Returns
33    /// A Result containing the deserialized optional float value or an error
34    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<f64>, D::Error>
35    where
36        D: Deserializer<'de>,
37    {
38        let value = Value::deserialize(deserializer)?;
39
40        match value {
41            Value::Null => Ok(None),
42            Value::Number(num) => {
43                if let Some(float) = num.as_f64() {
44                    Ok(Some(float))
45                } else {
46                    Err(serde::de::Error::custom("Expected a float"))
47                }
48            }
49            Value::String(s) => {
50                if s.is_empty() {
51                    return Ok(None);
52                }
53                s.parse::<f64>().map(Some).map_err(|_| {
54                    serde::de::Error::custom(format!("Failed to parse string as float: {s}"))
55                })
56            }
57            _ => Err(serde::de::Error::custom("Expected null, number or string")),
58        }
59    }
60}
61
62/// Module for handling the conversion between string and optional integer values
63///
64/// This module provides serialization and deserialization functions for converting
65/// between `Option<i64>` and string representations used in the IG Markets API.
66/// It is the integer counterpart of [`string_as_float_opt`] and is intended for
67/// wire values that are conceptually integers (for example epoch-millisecond
68/// timestamps) where float parsing would risk silent rounding.
69pub mod string_as_int_opt {
70    use serde::{self, Deserialize, Deserializer, Serializer};
71    use serde_json::Value;
72
73    /// Serializes an optional integer value to a JSON number
74    ///
75    /// # Arguments
76    /// * `value` - The optional integer value to serialize
77    /// * `serializer` - The serializer to use
78    ///
79    /// # Returns
80    /// A Result containing the serialized value or an error
81    pub fn serialize<S>(value: &Option<i64>, serializer: S) -> Result<S::Ok, S::Error>
82    where
83        S: Serializer,
84    {
85        match value {
86            Some(v) => serializer.serialize_i64(*v), // Serialize as a number
87            None => serializer.serialize_none(),
88        }
89    }
90
91    /// Deserializes a string or number representation to an optional integer value
92    ///
93    /// Accepts a JSON `null` (mapped to `None`), a JSON integer, or a string
94    /// holding an integer (an empty string maps to `None`). A non-integer or
95    /// otherwise malformed value yields a deserialization error.
96    ///
97    /// # Arguments
98    /// * `deserializer` - The deserializer to use
99    ///
100    /// # Returns
101    /// A Result containing the deserialized optional integer value or an error
102    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<i64>, D::Error>
103    where
104        D: Deserializer<'de>,
105    {
106        let value = Value::deserialize(deserializer)?;
107
108        match value {
109            Value::Null => Ok(None),
110            Value::Number(num) => {
111                if let Some(int) = num.as_i64() {
112                    Ok(Some(int))
113                } else {
114                    Err(serde::de::Error::custom("Expected an integer"))
115                }
116            }
117            Value::String(s) => {
118                if s.is_empty() {
119                    return Ok(None);
120                }
121                s.parse::<i64>().map(Some).map_err(|_| {
122                    serde::de::Error::custom(format!("Failed to parse string as integer: {s}"))
123                })
124            }
125            _ => Err(serde::de::Error::custom("Expected null, number or string")),
126        }
127    }
128}
129
130/// Module for handling the conversion between string and optional boolean values
131///
132/// This module provides serialization and deserialization functions for converting
133/// between `Option<bool>` and string representations ("0" and "1") used in the IG Markets API.
134pub mod string_as_bool_opt {
135    use serde::{self, Deserialize, Deserializer, Serializer};
136
137    /// Serializes an optional boolean value to its string representation
138    ///
139    /// Converts true to "1" and false to "0"
140    ///
141    /// # Arguments
142    /// * `value` - The optional boolean value to serialize
143    /// * `serializer` - The serializer to use
144    ///
145    /// # Returns
146    /// A Result containing the serialized value or an error
147    pub fn serialize<S>(value: &Option<bool>, serializer: S) -> Result<S::Ok, S::Error>
148    where
149        S: Serializer,
150    {
151        match value {
152            Some(v) => {
153                let s = if *v { "1" } else { "0" };
154                serializer.serialize_str(s)
155            }
156            None => serializer.serialize_none(),
157        }
158    }
159
160    /// Deserializes a string representation to an optional boolean value
161    ///
162    /// Converts "1" to true and "0" to false
163    ///
164    /// # Arguments
165    /// * `deserializer` - The deserializer to use
166    ///
167    /// # Returns
168    /// A Result containing the deserialized optional boolean value or an error
169    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<bool>, D::Error>
170    where
171        D: Deserializer<'de>,
172    {
173        let opt = Option::<String>::deserialize(deserializer)?;
174        match opt {
175            Some(s) => {
176                // Handle empty strings as None
177                if s.is_empty() {
178                    return Ok(None);
179                }
180                match s.as_str() {
181                    "0" => Ok(Some(false)),
182                    "1" => Ok(Some(true)),
183                    _ => Err(serde::de::Error::custom(format!(
184                        "Invalid boolean value: {s}"
185                    ))),
186                }
187            }
188            None => Ok(None),
189        }
190    }
191}
192
193/// Module for handling empty strings as None in `Option<String>` fields
194///
195/// This module provides serialization and deserialization functions for converting
196/// between empty strings and None values in `Option<String>` fields.
197pub mod option_string_empty_as_none {
198    use serde::{self, Deserialize, Deserializer, Serialize, Serializer};
199
200    /// Serializes an optional string value, treating empty strings as None
201    ///
202    /// # Arguments
203    /// * `value` - The optional string value to serialize
204    /// * `serializer` - The serializer to use
205    ///
206    /// # Returns
207    /// A Result containing the serialized value or an error
208    pub fn serialize<S>(value: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
209    where
210        S: Serializer,
211    {
212        match value {
213            Some(s) if s.is_empty() => serializer.serialize_none(),
214            _ => value.serialize(serializer),
215        }
216    }
217
218    /// Deserializes a value to an optional string, treating empty strings as None
219    ///
220    /// # Arguments
221    /// * `deserializer` - The deserializer to use
222    ///
223    /// # Returns
224    /// A Result containing the deserialized optional string value or an error
225    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
226    where
227        D: Deserializer<'de>,
228    {
229        let opt = Option::<String>::deserialize(deserializer)?;
230        match opt {
231            Some(s) if s.is_empty() => Ok(None),
232            _ => Ok(opt),
233        }
234    }
235}