solana_nullable/
serde_with.rs1use {
50 crate::{MaybeNull, Nullable},
51 serde::{Deserialize, Deserializer, Serialize, Serializer},
52 serde_with::{de::DeserializeAsWrap, ser::SerializeAsWrap, DeserializeAs, SerializeAs},
53};
54
55impl<T, U> SerializeAs<MaybeNull<T>> for Option<U>
56where
57 T: Nullable,
58 U: SerializeAs<T>,
59{
60 fn serialize_as<S>(source: &MaybeNull<T>, serializer: S) -> Result<S::Ok, S::Error>
61 where
62 S: Serializer,
63 {
64 source
65 .as_ref()
66 .map(SerializeAsWrap::<T, U>::new)
67 .serialize(serializer)
68 }
69}
70
71impl<'de, T, U> DeserializeAs<'de, MaybeNull<T>> for Option<U>
72where
73 T: Nullable,
74 U: DeserializeAs<'de, T>,
75{
76 fn deserialize_as<D>(deserializer: D) -> Result<MaybeNull<T>, D::Error>
77 where
78 D: Deserializer<'de>,
79 {
80 Option::<DeserializeAsWrap<T, U>>::deserialize(deserializer)?
81 .map(DeserializeAsWrap::into_inner)
82 .try_into()
83 .map_err(serde::de::Error::custom)
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use {
90 super::*,
91 crate::Nullable,
92 alloc::string::ToString,
93 serde_derive::{Deserialize, Serialize},
94 serde_with::{serde_as, DisplayFromStr},
95 };
96
97 #[derive(Clone, Copy, Debug, PartialEq)]
98 struct Id(u32);
99
100 impl Nullable for Id {
101 const NONE: Self = Id(0);
102 }
103
104 impl core::fmt::Display for Id {
105 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
106 write!(f, "{}", self.0)
107 }
108 }
109
110 impl core::str::FromStr for Id {
111 type Err = core::num::ParseIntError;
112 fn from_str(s: &str) -> Result<Self, Self::Err> {
113 Ok(Id(s.parse()?))
114 }
115 }
116
117 #[serde_as]
118 #[derive(Debug, PartialEq, Serialize, Deserialize)]
119 struct TestStruct {
120 #[serde_as(as = "Option<DisplayFromStr>")]
121 pub value: MaybeNull<Id>,
122 }
123
124 #[test]
125 fn serialize_some_as_display_string() {
126 let s = TestStruct {
127 value: MaybeNull::from(Id(42)),
128 };
129 let json = serde_json::to_string(&s).unwrap();
130 assert_eq!(json, r#"{"value":"42"}"#);
131 }
132
133 #[test]
134 fn serialize_none_as_null() {
135 let s = TestStruct {
136 value: MaybeNull::default(),
137 };
138 let json = serde_json::to_string(&s).unwrap();
139 assert_eq!(json, r#"{"value":null}"#);
140 }
141
142 #[test]
143 fn deserialize_string_to_some() {
144 let json = r#"{"value":"42"}"#;
145 let s: TestStruct = serde_json::from_str(json).unwrap();
146 assert_eq!(s.value, MaybeNull::from(Id(42)));
147 }
148
149 #[test]
150 fn deserialize_null_to_none() {
151 let json = r#"{"value":null}"#;
152 let s: TestStruct = serde_json::from_str(json).unwrap();
153 assert_eq!(s.value, MaybeNull::default());
154 }
155
156 #[test]
157 fn deserialize_none_marker_in_some_is_rejected() {
158 let json = r#"{"value":"0"}"#;
160 let err = serde_json::from_str::<TestStruct>(json).unwrap_err();
161 let msg = err.to_string();
162 assert!(msg.contains("None-equivalent"));
163 }
164
165 #[test]
166 fn deserialize_malformed_string_propagates_error() {
167 let json = r#"{"value":"not_a_number"}"#;
168 assert!(serde_json::from_str::<TestStruct>(json).is_err());
169 }
170
171 #[test]
172 fn deserialize_wrong_json_type_propagates_error() {
173 let json = r#"{"value":42}"#;
174 assert!(serde_json::from_str::<TestStruct>(json).is_err());
175 }
176
177 #[test]
178 fn roundtrip_some() {
179 let original = TestStruct {
180 value: MaybeNull::from(Id(99)),
181 };
182 let json = serde_json::to_string(&original).unwrap();
183 let deserialized: TestStruct = serde_json::from_str(&json).unwrap();
184 assert_eq!(original, deserialized);
185 }
186
187 #[test]
188 fn deserialize_missing_field_is_error() {
189 let json = r#"{}"#;
191 assert!(serde_json::from_str::<TestStruct>(json).is_err());
192 }
193
194 #[test]
195 fn roundtrip_none() {
196 let original = TestStruct {
197 value: MaybeNull::default(),
198 };
199 let json = serde_json::to_string(&original).unwrap();
200 let deserialized: TestStruct = serde_json::from_str(&json).unwrap();
201 assert_eq!(original, deserialized);
202 }
203
204 #[derive(Debug, PartialEq, Serialize, Deserialize)]
206 struct TestWithSyntax {
207 #[serde(with = "serde_with::As::<Option<DisplayFromStr>>")]
208 pub value: MaybeNull<Id>,
209 }
210
211 #[test]
212 fn serde_with_syntax_serialize_some() {
213 let s = TestWithSyntax {
214 value: MaybeNull::from(Id(7)),
215 };
216 let json = serde_json::to_string(&s).unwrap();
217 assert_eq!(json, r#"{"value":"7"}"#);
218 }
219
220 #[test]
221 fn serde_with_syntax_serialize_none() {
222 let s = TestWithSyntax {
223 value: MaybeNull::default(),
224 };
225 let json = serde_json::to_string(&s).unwrap();
226 assert_eq!(json, r#"{"value":null}"#);
227 }
228
229 #[test]
230 fn serde_with_syntax_deserialize_some() {
231 let json = r#"{"value":"7"}"#;
232 let s: TestWithSyntax = serde_json::from_str(json).unwrap();
233 assert_eq!(s.value, MaybeNull::from(Id(7)));
234 }
235
236 #[test]
237 fn serde_with_syntax_deserialize_null() {
238 let json = r#"{"value":null}"#;
239 let s: TestWithSyntax = serde_json::from_str(json).unwrap();
240 assert_eq!(s.value, MaybeNull::default());
241 }
242
243 #[derive(Clone, Copy, Debug, PartialEq)]
245 struct Score(u32);
246
247 impl Nullable for Score {
248 const NONE: Self = Score(0);
249 }
250
251 struct DoubledScore;
253
254 impl SerializeAs<Score> for DoubledScore {
255 fn serialize_as<S>(source: &Score, serializer: S) -> Result<S::Ok, S::Error>
256 where
257 S: Serializer,
258 {
259 serializer.serialize_u32(source.0.saturating_mul(2))
260 }
261 }
262
263 impl<'de> DeserializeAs<'de, Score> for DoubledScore {
264 fn deserialize_as<D>(deserializer: D) -> Result<Score, D::Error>
265 where
266 D: Deserializer<'de>,
267 {
268 let v = u32::deserialize(deserializer)?;
269 Ok(Score(v / 2))
270 }
271 }
272
273 #[serde_as]
274 #[derive(Debug, PartialEq, Serialize, Deserialize)]
275 struct CustomAdapterStruct {
276 #[serde_as(as = "Option<DoubledScore>")]
277 pub value: MaybeNull<Score>,
278 }
279
280 #[test]
281 fn custom_adapter_serialize_some() {
282 let s = CustomAdapterStruct {
283 value: MaybeNull::from(Score(21)),
284 };
285 let json = serde_json::to_string(&s).unwrap();
286 assert_eq!(json, r#"{"value":42}"#);
287 }
288
289 #[test]
290 fn custom_adapter_serialize_none() {
291 let s = CustomAdapterStruct {
292 value: MaybeNull::default(),
293 };
294 let json = serde_json::to_string(&s).unwrap();
295 assert_eq!(json, r#"{"value":null}"#);
296 }
297
298 #[test]
299 fn custom_adapter_deserialize_some() {
300 let json = r#"{"value":42}"#;
301 let s: CustomAdapterStruct = serde_json::from_str(json).unwrap();
302 assert_eq!(
303 s,
304 CustomAdapterStruct {
305 value: MaybeNull::from(Score(21)),
306 }
307 );
308 }
309
310 #[test]
311 fn custom_adapter_deserialize_null() {
312 let json = r#"{"value":null}"#;
313 let s: CustomAdapterStruct = serde_json::from_str(json).unwrap();
314 assert_eq!(
315 s,
316 CustomAdapterStruct {
317 value: MaybeNull::default(),
318 }
319 );
320 }
321
322 #[test]
323 fn custom_adapter_rejects_none_marker_after_transform() {
324 let json = r#"{"value":1}"#;
327 let err = serde_json::from_str::<CustomAdapterStruct>(json).unwrap_err();
328 let msg = err.to_string();
329 assert!(msg.contains("None-equivalent"));
330 }
331}