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}