serde_nothing/
lib.rs

1//!
2//! This crate defines "nothing" in terms of `serde` data model
3//! and allows checking values and create such values using only `Serialize` and `Deserialize` traits.
4//!
5//! # Motivation
6//!
7//! This crate is designed to generalize serialization pattern
8//! where struct fields with None/empty/default values are skipped on serialization
9//! and constructed on deserialization when missing.
10//!
11//! Usually this pattern is coded using `#[serde(default, skip_serializing_if = "Option::is_none/Vec::is_empty/is_default")]`.
12//! Where `is_default` is a function defined as
13//! ```rust
14//! fn is_default<T: Default + PartialEq>(value: &T) -> bool {
15//!     *value == T::default()
16//! }
17//! ```
18//!
19//! The pattern works very well for field with concrete types and generic wrappers like `Option`, `Vec` and similar.
20//!
21//! But using `#[serde(default)]` on field with generic type `T` would require `Default` bound added to the `Deserialize` impl.
22//! And `#[serde(skip_serializing_if = "is_default")]` would require `T: Default + PartialEq` bound added to the `Serialize` impl.
23//!
24//! This crate allows to implement this pattern without additional bounds.
25//! Even more, it allows specialize for types that can be skipped.
26//! That is, if field has type `T` that does not even have a value that would be appropriate to skip,
27//! the code will continue work correctly and would simply always serialize the field and require the data for deserialization.
28//!
29//! # Magic? No, science!
30//!
31//! This crate provides stateless `Nothing` type which is a special kind of `serde::Serializer` or `serde::Deserializer`.
32//! `Nothing` can be used to serialize and deserialize "nothing" values.
33//! "Nothing" values are `None`, empty collections, units,
34//! structs and tuples where all fields are "nothing"
35//!
36//! Serializing a "non-nothing" value with `Nothing` always fails.
37//!
38//! As deserializer `Nothing` would visit most appropriate `Visitor` method
39//! with matching kind of nothingness, like `None`, empty slice/sequence/map,
40//! single-variant enum with nothing in discriminant and nothing in payload,
41//! `0` numeric value etc.
42//!
43//! User would most probably want to use a shortcut and utilize `is_nothing` function for serialization
44//! and `from_nothing` function for deserialization.
45//!
46
47#![cfg_attr(not(feature = "std"), no_std)]
48
49mod de;
50mod ser;
51
52pub use self::{de::NothingDeserializeError, ser::NothingSerializeError};
53
54/// Serializer to serialize values into and from nothing.
55#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
56pub struct Nothing;
57
58/// Returns true if the value matches definition of "nothing".
59/// Typically deserializing from `Nothing` would yield same value.
60#[inline]
61pub fn is_nothing<T: ?Sized>(value: &T) -> bool
62where
63    T: serde::ser::Serialize,
64{
65    value.serialize(Nothing).is_ok()
66}
67
68/// Returns some "nothing" value of the type.
69/// Or none if failed to create one.
70#[inline]
71pub fn from_nothing<'de, T>() -> Option<T>
72where
73    T: serde::de::Deserialize<'de>,
74{
75    T::deserialize(Nothing).ok()
76}
77
78#[cfg(test)]
79mod tests {
80    use core::fmt::Debug;
81
82    use serde::{Deserialize, Serialize};
83
84    use crate::{is_nothing, Nothing};
85
86    #[derive(Debug, PartialEq, Eq, serde_derive::Serialize, serde_derive::Deserialize)]
87    struct Struct {
88        number: u32,
89        string: &'static str,
90    }
91
92    #[cfg(test)]
93    fn check_roundtrip<'de, T: ?Sized + Serialize + Deserialize<'de> + PartialEq + Debug>(
94        value: &T,
95    ) {
96        assert!(is_nothing(value));
97        let () = value.serialize(Nothing).unwrap();
98        assert_eq!(Ok(value), T::deserialize(Nothing).as_ref());
99    }
100
101    #[test]
102    fn test_unit() {
103        check_roundtrip(&());
104    }
105
106    #[test]
107    fn test_numbers() {
108        check_roundtrip(&0);
109    }
110
111    #[test]
112    #[should_panic]
113    fn test_numbers_fail() {
114        check_roundtrip(&1);
115    }
116
117    #[test]
118    fn test_string() {
119        check_roundtrip(&"");
120    }
121
122    #[test]
123    #[should_panic]
124    fn test_string_fail() {
125        check_roundtrip(&"a");
126    }
127
128    #[test]
129    fn test_slice() {
130        check_roundtrip(&&[0u8][..0]);
131    }
132
133    #[test]
134    #[should_panic]
135    fn test_slice_fail() {
136        check_roundtrip(&&[0u8][..]);
137    }
138
139    #[test]
140    fn test_array() {
141        check_roundtrip(&[0, 0, 0]);
142    }
143
144    #[test]
145    #[should_panic]
146    fn test_array_fail() {
147        check_roundtrip(&[0, 0, 1]);
148    }
149
150    #[test]
151    fn test_tuple() {
152        check_roundtrip(&(0, ""))
153    }
154
155    #[test]
156    #[should_panic]
157    fn test_tuple_fail() {
158        check_roundtrip(&(0, "a"))
159    }
160
161    #[test]
162    fn test_struct() {
163        check_roundtrip(&Struct {
164            number: 0,
165            string: "",
166        })
167    }
168
169    #[test]
170    #[should_panic]
171    fn test_struct_fail() {
172        check_roundtrip(&Struct {
173            number: 1,
174            string: "",
175        })
176    }
177}