api_bones/serde/
maybe_string.rs1#[cfg(all(not(feature = "std"), feature = "alloc"))]
39use alloc::string::{String, ToString};
40#[cfg(feature = "std")]
41use std::string::String;
42
43use core::fmt::Display;
44use core::str::FromStr;
45use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
46
47pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
53where
54 T: Serialize,
55 S: Serializer,
56{
57 value.serialize(serializer)
58}
59
60pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
70where
71 T: Deserialize<'de> + FromStr,
72 <T as FromStr>::Err: Display,
73 D: Deserializer<'de>,
74{
75 deserializer.deserialize_any(MaybeStringVisitor::<T>(core::marker::PhantomData))
76}
77
78struct MaybeStringVisitor<T>(core::marker::PhantomData<T>);
79
80impl<'de, T> de::Visitor<'de> for MaybeStringVisitor<T>
81where
82 T: Deserialize<'de> + FromStr,
83 <T as FromStr>::Err: Display,
84{
85 type Value = T;
86
87 fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
88 formatter.write_str("a string or a number")
89 }
90
91 fn visit_str<E: de::Error>(self, v: &str) -> Result<T, E> {
92 v.parse::<T>().map_err(de::Error::custom)
93 }
94
95 fn visit_string<E: de::Error>(self, v: String) -> Result<T, E> {
96 self.visit_str(&v)
97 }
98
99 fn visit_bool<E: de::Error>(self, v: bool) -> Result<T, E> {
100 self.visit_str(if v { "true" } else { "false" })
101 }
102
103 fn visit_i64<E: de::Error>(self, v: i64) -> Result<T, E> {
104 self.visit_str(&v.to_string())
105 }
106
107 fn visit_u64<E: de::Error>(self, v: u64) -> Result<T, E> {
108 self.visit_str(&v.to_string())
109 }
110
111 fn visit_f64<E: de::Error>(self, v: f64) -> Result<T, E> {
112 self.visit_str(&v.to_string())
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use serde::{Deserialize, Serialize};
119
120 #[derive(Debug, PartialEq, Serialize, Deserialize)]
121 struct WithU64 {
122 #[serde(with = "super")]
123 value: u64,
124 }
125
126 #[derive(Debug, PartialEq, Serialize, Deserialize)]
127 struct WithI64 {
128 #[serde(with = "super")]
129 value: i64,
130 }
131
132 #[derive(Debug, PartialEq, Serialize, Deserialize)]
133 struct WithF64 {
134 #[serde(with = "super")]
135 value: f64,
136 }
137
138 #[derive(Debug, PartialEq, Serialize, Deserialize)]
139 struct WithBool {
140 #[serde(with = "super")]
141 value: bool,
142 }
143
144 #[test]
147 fn serialize_u64() {
148 let w = WithU64 { value: 99 };
149 let json = serde_json::to_string(&w).unwrap();
150 assert_eq!(json, r#"{"value":99}"#);
151 }
152
153 #[test]
154 fn serialize_bool() {
155 let w = WithBool { value: true };
156 let json = serde_json::to_string(&w).unwrap();
157 assert_eq!(json, r#"{"value":true}"#);
158 }
159
160 #[test]
163 fn deserialize_u64_from_string() {
164 let w: WithU64 = serde_json::from_str(r#"{"value":"42"}"#).unwrap();
165 assert_eq!(w.value, 42);
166 }
167
168 #[test]
169 fn deserialize_i64_from_string() {
170 let w: WithI64 = serde_json::from_str(r#"{"value":"-7"}"#).unwrap();
171 assert_eq!(w.value, -7);
172 }
173
174 #[test]
175 fn deserialize_f64_from_string() {
176 let w: WithF64 = serde_json::from_str(r#"{"value":"1.5"}"#).unwrap();
177 assert!((w.value - 1.5_f64).abs() < 1e-9);
178 }
179
180 #[test]
181 fn deserialize_bool_from_string_true() {
182 let w: WithBool = serde_json::from_str(r#"{"value":"true"}"#).unwrap();
183 assert!(w.value);
184 }
185
186 #[test]
187 fn deserialize_bool_from_string_false() {
188 let w: WithBool = serde_json::from_str(r#"{"value":"false"}"#).unwrap();
189 assert!(!w.value);
190 }
191
192 #[test]
195 fn deserialize_u64_from_number() {
196 let w: WithU64 = serde_json::from_str(r#"{"value":100}"#).unwrap();
197 assert_eq!(w.value, 100);
198 }
199
200 #[test]
203 fn deserialize_i64_from_negative_number() {
204 let w: WithI64 = serde_json::from_str(r#"{"value":-5}"#).unwrap();
205 assert_eq!(w.value, -5);
206 }
207
208 #[test]
211 fn deserialize_f64_from_float() {
212 let w: WithF64 = serde_json::from_str(r#"{"value":1.5}"#).unwrap();
213 assert!((w.value - 1.5_f64).abs() < 1e-9);
214 }
215
216 #[test]
219 fn deserialize_bool_from_true() {
220 let w: WithBool = serde_json::from_str(r#"{"value":true}"#).unwrap();
221 assert!(w.value);
222 }
223
224 #[test]
225 fn deserialize_bool_from_false() {
226 let w: WithBool = serde_json::from_str(r#"{"value":false}"#).unwrap();
227 assert!(!w.value);
228 }
229
230 #[test]
233 fn deserialize_u64_from_value_string() {
234 let val = serde_json::json!({"value": "55"});
235 let w: WithU64 = serde_json::from_value(val).unwrap();
236 assert_eq!(w.value, 55);
237 }
238
239 #[test]
242 fn deserialize_u64_invalid_string() {
243 let result: Result<WithU64, _> = serde_json::from_str(r#"{"value":"not_a_number"}"#);
244 assert!(result.is_err());
245 }
246
247 #[test]
250 fn deserialize_error_message_contains_expectation() {
251 let result: Result<WithU64, _> = serde_json::from_str(r#"{"value":[1,2,3]}"#);
253 assert!(result.is_err());
254 }
255}