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}