1use crate::Nullable;
10#[cfg(feature = "bytemuck")]
11use bytemuck::{Pod, Zeroable};
12#[cfg(feature = "serde")]
13use serde::{Deserialize, Deserializer, Serialize, Serializer};
14#[cfg(feature = "wincode")]
15use wincode::{SchemaRead, SchemaWrite};
16#[cfg(feature = "borsh")]
17use {
18 alloc::format,
19 borsh::{BorshDeserialize, BorshSchema, BorshSerialize},
20};
21
22#[repr(transparent)]
28#[cfg_attr(
29 feature = "borsh",
30 derive(BorshDeserialize, BorshSerialize, BorshSchema)
31)]
32#[cfg_attr(feature = "wincode", derive(SchemaRead, SchemaWrite))]
33#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
34pub struct MaybeNull<T: Nullable>(T);
35
36#[cfg(feature = "wincode")]
40unsafe impl<T, C> wincode::config::ZeroCopy<C> for MaybeNull<T>
41where
42 C: wincode::config::ConfigCore,
43 T: Nullable + wincode::config::ZeroCopy<C>,
44{
45}
46
47impl<T: Nullable> Default for MaybeNull<T> {
48 fn default() -> Self {
49 Self(T::NONE)
50 }
51}
52
53impl<T: Nullable> MaybeNull<T> {
54 #[inline]
56 pub fn get(self) -> Option<T> {
57 if self.0.is_none() {
58 None
59 } else {
60 Some(self.0)
61 }
62 }
63
64 #[inline]
66 pub fn as_ref(&self) -> Option<&T> {
67 if self.0.is_none() {
68 None
69 } else {
70 Some(&self.0)
71 }
72 }
73
74 #[inline]
76 pub fn as_mut(&mut self) -> Option<&mut T> {
77 if self.0.is_none() {
78 None
79 } else {
80 Some(&mut self.0)
81 }
82 }
83
84 #[inline]
86 pub fn copied(&self) -> Option<T>
87 where
88 T: Copy,
89 {
90 self.as_ref().copied()
91 }
92
93 #[inline]
95 pub fn cloned(&self) -> Option<T>
96 where
97 T: Clone,
98 {
99 self.as_ref().cloned()
100 }
101}
102
103impl<T: Nullable> From<T> for MaybeNull<T> {
104 fn from(value: T) -> Self {
105 MaybeNull(value)
106 }
107}
108
109impl<T: Nullable> From<MaybeNull<T>> for Option<T> {
110 fn from(value: MaybeNull<T>) -> Self {
111 value.get()
112 }
113}
114
115impl<T: Nullable> TryFrom<Option<T>> for MaybeNull<T> {
116 type Error = MaybeNullError;
117
118 fn try_from(value: Option<T>) -> Result<Self, Self::Error> {
119 match value {
120 Some(value) if value.is_none() => Err(MaybeNullError::NoneValueInSome),
121 Some(value) => Ok(MaybeNull(value)),
122 None => Ok(MaybeNull(T::NONE)),
123 }
124 }
125}
126
127#[derive(Clone, Copy, Debug, PartialEq, Eq)]
129pub enum MaybeNullError {
130 NoneValueInSome,
132}
133
134impl core::fmt::Display for MaybeNullError {
135 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
136 match self {
137 Self::NoneValueInSome => {
138 write!(f, "cannot wrap None-equivalent value in Some")
139 }
140 }
141 }
142}
143
144#[cfg(feature = "bytemuck")]
149unsafe impl<T: Nullable + Pod> Pod for MaybeNull<T> {}
150
151#[cfg(feature = "bytemuck")]
156unsafe impl<T: Nullable + Zeroable> Zeroable for MaybeNull<T> {}
157
158#[cfg(feature = "serde")]
159impl<T> Serialize for MaybeNull<T>
160where
161 T: Nullable + Serialize,
162{
163 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
164 where
165 S: Serializer,
166 {
167 if self.0.is_none() {
168 serializer.serialize_none()
169 } else {
170 serializer.serialize_some(&self.0)
171 }
172 }
173}
174
175#[cfg(feature = "serde")]
176impl<'de, T> Deserialize<'de> for MaybeNull<T>
177where
178 T: Nullable + Deserialize<'de>,
179{
180 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
181 where
182 D: Deserializer<'de>,
183 {
184 let option = Option::<T>::deserialize(deserializer)?;
185 match option {
186 Some(value) if value.is_none() => Err(serde::de::Error::custom(
187 "Invalid MaybeNull encoding: Some(value) cannot equal the None marker.",
188 )),
189 Some(value) => Ok(MaybeNull(value)),
190 None => Ok(MaybeNull(T::NONE)),
191 }
192 }
193}
194
195#[cfg(test)]
196mod tests {
197 use super::*;
198
199 #[test]
200 fn test_try_from_option() {
201 let some = Some(42u64);
202 assert_eq!(MaybeNull::try_from(some).unwrap(), MaybeNull(42u64));
203
204 let none: Option<u64> = None;
205 assert_eq!(MaybeNull::try_from(none).unwrap(), MaybeNull::from(0u64));
206
207 let invalid = Some(0u64);
208 assert_eq!(
209 MaybeNull::try_from(invalid).unwrap_err(),
210 MaybeNullError::NoneValueInSome,
211 );
212 }
213
214 #[test]
215 fn test_from_maybe_null() {
216 let some = MaybeNull::from(42u64);
217 let none = MaybeNull::from(0u64);
218
219 assert_eq!(Option::<u64>::from(some), Some(42));
220 assert_eq!(Option::<u64>::from(none), None);
221 }
222
223 #[test]
224 fn test_default() {
225 let def = MaybeNull::<u64>::default();
226 assert_eq!(def, MaybeNull(0u64));
227 assert_eq!(def.get(), None);
228 }
229
230 #[test]
231 fn test_copied() {
232 let some = MaybeNull::from(42u64);
233 assert_eq!(some.copied(), Some(42));
234
235 let none = MaybeNull::from(0u64);
236 assert_eq!(none.copied(), None);
237 }
238
239 #[test]
240 fn test_nullable_predicates() {
241 assert!(u64::NONE.is_none());
242 assert!(!u64::NONE.is_some());
243 assert!(8u64.is_some());
244 assert!(!8u64.is_none());
245 }
246
247 #[test]
248 fn test_as_ref() {
249 let some = MaybeNull::from(8u64);
250 assert_eq!(some.as_ref(), Some(&8u64));
251
252 let none = MaybeNull::from(u64::NONE);
253 assert_eq!(none.as_ref(), None);
254 }
255
256 #[test]
257 fn test_as_mut() {
258 let mut some = MaybeNull::from(3u64);
259 assert!(some.as_mut().is_some());
260 *some.as_mut().unwrap() = 4;
261 assert_eq!(some.get(), Some(4));
262
263 let mut none = MaybeNull::from(0u64);
264 assert!(none.as_mut().is_none());
265 }
266
267 #[derive(Clone, Debug, PartialEq)]
268 struct TestNonCopyNullable([u8; 4]);
269
270 impl Nullable for TestNonCopyNullable {
271 const NONE: Self = Self([0u8; 4]);
272 }
273
274 #[test]
275 fn test_cloned_with_non_copy_nullable() {
276 let some = MaybeNull::from(TestNonCopyNullable([1, 2, 3, 4]));
277 assert_eq!(some.cloned(), Some(TestNonCopyNullable([1, 2, 3, 4])));
278
279 let none = MaybeNull::from(TestNonCopyNullable::NONE);
280 assert_eq!(none.cloned(), None);
281 }
282
283 #[cfg(feature = "borsh")]
284 mod borsh_tests {
285 use {super::*, alloc::vec};
286
287 #[test]
288 fn test_borsh_roundtrip_u64() {
289 let some = MaybeNull::from(42u64);
290 let none = MaybeNull::from(0u64);
291
292 let some_bytes = borsh::to_vec(&some).unwrap();
293 let none_bytes = borsh::to_vec(&none).unwrap();
294
295 assert_eq!(some_bytes, 42u64.to_le_bytes().to_vec());
296 assert_eq!(none_bytes, vec![0; 8]);
297 assert_eq!(
298 borsh::from_slice::<MaybeNull<u64>>(&some_bytes).unwrap(),
299 some
300 );
301 assert_eq!(
302 borsh::from_slice::<MaybeNull<u64>>(&none_bytes).unwrap(),
303 none
304 );
305 assert!(borsh::from_slice::<MaybeNull<u64>>(&[]).is_err());
306 }
307 }
308
309 #[cfg(feature = "wincode")]
310 mod wincode_tests {
311 use {super::*, wincode::ZeroCopy};
312
313 #[test]
314 fn test_wincode_maybe_null_roundtrip_and_size() {
315 let some = MaybeNull::from(9u64);
316 let none = MaybeNull::from(0u64);
317
318 let some_bytes = wincode::serialize(&some).unwrap();
319 let none_bytes = wincode::serialize(&none).unwrap();
320
321 assert_eq!(some_bytes.len(), core::mem::size_of::<u64>());
322 assert_eq!(none_bytes.len(), core::mem::size_of::<u64>());
323 assert_eq!(some_bytes.as_slice(), &9u64.to_le_bytes());
324 assert_eq!(none_bytes.as_slice(), &0u64.to_le_bytes());
325
326 let some_roundtrip: MaybeNull<u64> = wincode::deserialize(&some_bytes).unwrap();
327 let none_roundtrip: MaybeNull<u64> = wincode::deserialize(&none_bytes).unwrap();
328 assert_eq!(some_roundtrip, some);
329 assert_eq!(none_roundtrip, none);
330
331 let some_zero_copy = MaybeNull::<u64>::from_bytes(&some_bytes).unwrap();
332 let none_zero_copy = MaybeNull::<u64>::from_bytes(&none_bytes).unwrap();
333 assert_eq!(some_zero_copy, &some);
334 assert_eq!(none_zero_copy, &none);
335 }
336
337 #[test]
338 fn test_wincode_maybe_null_rejects_truncated_input() {
339 assert!(wincode::deserialize::<MaybeNull<u64>>(&[]).is_err());
340 assert!(wincode::deserialize::<MaybeNull<u64>>(&[0; 7]).is_err());
341 }
342 }
343
344 #[cfg(feature = "serde")]
345 mod serde_tests {
346 use {super::*, alloc::string::ToString};
347
348 #[test]
349 fn test_serde_u64_some() {
350 let some = MaybeNull::from(7u64);
351 let serialized = serde_json::to_string(&some).unwrap();
352 assert_eq!(serialized, "7");
353 let deserialized = serde_json::from_str::<MaybeNull<u64>>(&serialized).unwrap();
354 assert_eq!(deserialized, some);
355 }
356
357 #[test]
358 fn test_serde_u64_none() {
359 let deserialized = serde_json::from_str::<MaybeNull<u64>>("null").unwrap();
360 assert_eq!(deserialized, MaybeNull::from(0));
361 }
362
363 #[test]
364 fn test_serde_u64_none_marker_error_message() {
365 let err = serde_json::from_str::<MaybeNull<u64>>("0").unwrap_err();
366 let message = err.to_string();
367 assert!(message.contains("MaybeNull encoding"));
368 assert!(message.contains("None marker"));
369 }
370
371 #[test]
372 fn test_serde_u64_reject_invalid_input() {
373 assert!(serde_json::from_str::<MaybeNull<u64>>("\"abc\"").is_err());
374 assert!(serde_json::from_str::<MaybeNull<u64>>("{}").is_err());
375 }
376 }
377
378 #[cfg(feature = "bytemuck")]
379 mod bytemuck_tests {
380 use super::*;
381
382 #[test]
383 fn test_maybe_null_u64() {
384 let some = MaybeNull::from(42u64);
385 assert_eq!(some.get(), Some(42));
386
387 let none = MaybeNull::from(0u64);
388 assert_eq!(none.get(), None);
389
390 let bytes = 42u64.to_le_bytes();
391 let value: &MaybeNull<u64> = bytemuck::from_bytes(&bytes);
392 assert_eq!(*value, MaybeNull::from(42u64));
393
394 let zero_bytes = 0u64.to_le_bytes();
395 let value: &MaybeNull<u64> = bytemuck::from_bytes(&zero_bytes);
396 assert_eq!(*value, MaybeNull::from(0u64));
397 assert_eq!(value.get(), None);
398 }
399
400 #[test]
401 fn test_maybe_null_from_bytes_errors() {
402 assert!(bytemuck::try_from_bytes::<MaybeNull<u64>>(&[]).is_err());
403 assert!(bytemuck::try_from_bytes::<MaybeNull<u64>>(&[0; 1]).is_err());
404 }
405 }
406}