is_empty/
lib.rs

1//! Easily check if the struct is empty.  
2//!
3//! Say you have a struct full of `Option<T>` fields, and you want to know if all the fields are `None`.
4//! ```
5//! struct Foo {
6//!     client_ip: Option<String>,
7//!     client_country: Option<String>,
8//! }
9//! ```
10//! You can manually check for each field like this:
11//! ```
12//!# struct Foo {
13//!#     client_ip: Option<String>,
14//!#     client_country: Option<String>,
15//!# }
16//! impl Foo {
17//!     fn is_empty(&self) -> bool {
18//!        self.client_ip.is_none() && self.client_country.is_none()
19//!     }
20//! }
21//! ```
22//! But this becomes tedious as more and more fields are added to the struct.
23//!
24//! With this crate, you can derive the `IsEmpty` trait, and then call is_empty() on the struct.
25//! ```
26//! use is_empty::IsEmpty;
27//!
28//! #[derive(IsEmpty)]
29//! struct Foo {
30//!     client_ip: Option<String>,
31//!     client_country: Option<String>,
32//! }
33//!
34//! let empty_foo = Foo { client_ip: None, client_country: None };
35//! assert!(empty_foo.is_empty());
36//! ```
37//!
38//! You can also nest other `IsEmpty`-deriving struct inside the struct.
39//! ```
40//! use is_empty::IsEmpty;
41//!
42//! #[derive(IsEmpty)]
43//! struct Foo {
44//!     bar: Bar,
45//!     baz: Option<u8>
46//! }
47//! #[derive(IsEmpty)]
48//! struct Bar {
49//!     client_ip: Option<String>,
50//!     client_country: Option<String>,
51//! }
52//!
53//! let empty_foo = Foo { bar: Bar { client_ip: None, client_country: None }, baz: None };
54//! assert!(empty_foo.is_empty());
55//! ```
56//!
57//! If you want to customize the logic for determining if the field is empty, you can use the `#[is_empty(if = "some_fn")]` attribute.
58//! ```
59//! use is_empty::IsEmpty;
60//!
61//! #[derive(IsEmpty)]
62//! struct Foo {
63//!     #[is_empty(if = "Vec::is_empty")]
64//!     bar: Vec<u8>,
65//! }
66//!
67//! let empty_foo = Foo { bar: vec![] };
68//! assert!(empty_foo.is_empty());
69//! ```
70//!
71//! This crate pairs well with serde's `#[serde(skip_serializing_if = "condition")]` attribute.
72//! ```
73//! use is_empty::IsEmpty;
74//! use serde::{Serialize, Deserialize};
75//!
76//! #[derive(Serialize, Deserialize, IsEmpty)]
77//! struct Foo {
78//!     client_ip: Option<String>,
79//!     client_country: Option<String>,
80//! }
81//!
82//! #[derive(Serialize, Deserialize)]
83//! struct Root {
84//!     #[serde(skip_serializing_if = "is_empty::is_empty")]
85//!     foo: Foo,
86//! }
87//!
88//! let empty_foo = Foo { client_ip: None, client_country: None };
89//! let root = Root { foo: empty_foo };
90//! assert_eq!(serde_json::to_string(&root).unwrap(), "{}");
91//! ```
92//!
93//! For support for additional types, enable "std_impls" feature:
94//!```
95//! # mod test_std_impl {
96//! #![cfg(feature = "std_impls")]
97//! # use std::collections::{HashSet, HashMap};
98//! # use is_empty::IsEmpty;
99//! # use serde::{Serialize, Deserialize};
100//!
101//! # fn main() {
102//! #[derive(Serialize, Deserialize, IsEmpty)]
103//! struct Bar {
104//!     owned_string: String,
105//!     os_string: std::ffi::OsString,
106//!
107//!     vec: Vec<String>,
108//!     set: HashSet<String>,
109//!     map: HashMap<String, String>
110//! }
111//!
112//! let bar = Bar {
113//!   owned_string: String::new(),
114//!   os_string: std::ffi::OsString::new(),
115//!   vec: vec![],
116//!   set: HashSet::new(),
117//!   map: HashMap::new(),
118//! };
119//!
120//! assert!(bar.is_empty())
121//! # }
122//! # }
123//! # #[cfg(not(feature = "std_impls"))]
124//! # mod test_std_impl {
125//! # fn main() {}
126//! # }
127//!
128//!
129//!
130
131pub use is_empty_derive::*;
132
133#[cfg(feature = "std_impls")]
134pub mod std_impls;
135
136/// A trait for checking if a struct is empty.
137/// See the [crate-level documentation](crate) for more information.
138pub trait IsEmpty {
139    /// Check if the struct is empty.
140    /// Returns true if all the fields are considered empty.
141    fn is_empty(&self) -> bool;
142}
143
144impl<T> IsEmpty for std::option::Option<T> {
145    fn is_empty(&self) -> bool {
146        self.is_none()
147    }
148}
149
150/// Thin wrapper function around [IsEmpty::is_empty]. Use it with serde's skip_serializing_if attribute.
151/// ```
152/// use is_empty::IsEmpty;
153/// use serde::{Serialize, Deserialize};
154///
155/// #[derive(Serialize, Deserialize, IsEmpty)]
156/// struct Foo {
157///     client_ip: Option<String>,
158///     client_country: Option<String>,
159/// }
160///
161/// #[derive(Serialize, Deserialize)]
162/// struct Root {
163///     #[serde(skip_serializing_if = "is_empty::is_empty")]
164///     foo: Foo,
165/// }
166///
167/// let empty_foo = Foo { client_ip: None, client_country: None };
168/// let root = Root { foo: empty_foo };
169/// assert_eq!(serde_json::to_string(&root).unwrap(), "{}");
170/// ```
171pub fn is_empty<T>(t: &T) -> bool
172where
173    T: IsEmpty,
174{
175    t.is_empty()
176}
177
178/// Check if the `Option<T>` is really empty.  
179///
180/// [is_empty] returns false for `Some(T)`, even if `T` is empty.
181/// This function inspects the struct wrapped inside `Option::Some<T>`, and returns true if it is empty.
182/// (Of course, it returns true for `None`.)
183///
184/// This function can be used in two ways:
185/// - in a `#[is_empty(if = "is_empty::is_option_really_empty")]` attribute,
186/// - in a `#[serde(skip_serializing_if = "is_empty::is_option_really_empty")]` attribute.
187pub fn is_option_really_empty<T>(t: &Option<T>) -> bool
188where
189    T: IsEmpty,
190{
191    match t {
192        None => true,
193        Some(t) => t.is_empty(),
194    }
195}