if_empty/lib.rs
1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5//! For use with defensive programming where context specific defaults are needed.
6//!
7//! While using an [`Option`] is preferrably in most circumstances there are situations where a function call doesn't return an
8//! [`Option`] and the [`Default`] of a type isn't helpful either.
9//!
10//! [`Option`]: https://doc.rust-lang.org/std/option/enum.Option.html
11//! [`Default`]: https://doc.rust-lang.org/std/default/trait.Default.html
12//! [`str`]: https://doc.rust-lang.org/std/primitive.str.html
13//! [`String`]: https://doc.rust-lang.org/std/string/struct.String.html
14//! [`String::is_empty()`]: https://doc.rust-lang.org/nightly/alloc/string/struct.String.html#method.is_empty
15//! [`str::is_empty()`]: https://doc.rust-lang.org/std/primitive.str.html#method.is_empty
16//!
17//! # Examples
18//!
19//! ```
20//! # struct Bar {}
21//! # impl Bar {
22//! # fn is_empty(&self) -> bool { true }
23//! # }
24//! # fn bar() -> Bar {
25//! # Bar {}
26//! # }
27//! // Converting
28//! let foo = {
29//! let b = bar();
30//! if b.is_empty() {
31//! Bar {
32//! // set the default values for your context here
33//! }
34//! } else {
35//! b
36//! }
37//! };
38//! // into
39//! use if_empty::IfEmpty;
40//! impl IfEmpty for Bar {
41//! fn if_empty(self, value: Self) -> Self {
42//! // implement
43//! # if self.is_empty() {
44//! # value
45//! # } else {
46//! # self
47//! # }
48//! }
49//! }
50//!
51//! let foo = bar().if_empty(Bar { /* ... */ });
52//! ```
53//!
54//! # Implementation
55//!
56//! In this example we're using the obvious `is_empty()` function for `Foo` but we could also
57//! do more elaborate checks.
58//!
59//! ```
60//! use if_empty::IfEmpty;
61//!
62//! struct Foo {
63//! val: bool,
64//! }
65//!
66//! impl Foo {
67//! fn is_empty(&self) -> bool { !self.val }
68//! }
69//!
70//! impl IfEmpty for Foo {
71//! fn if_empty(self, value: Foo) -> Foo {
72//! if self.is_empty() {
73//! value
74//! } else {
75//! self
76//! }
77//! }
78//! }
79//! ```
80
81pub use if_empty_derive::IfEmpty;
82
83/// For checking IfEmpty on value semantics
84pub trait IfEmpty {
85 /// Returns `val` if the `self` is empty
86 fn if_empty(self, val: Self) -> Self;
87}
88
89/// For checking IfEmpty on borrowed objects
90pub trait IfEmptyBorrowed {
91 /// Return `val` if `self` is empty
92 fn if_empty<'a>(&'a self, val: &'a Self) -> &'a Self;
93}
94
95/// Implementation of `IfEmptyBorrowed` for [`str`]
96impl IfEmptyBorrowed for str {
97 /// Returns `input` if [`str::is_empty()`] returns true.
98 /// Otherwise `self` is returned.
99 fn if_empty<'a>(&'a self, input: &'a Self) -> &'a Self {
100 if self.is_empty() {
101 input
102 } else {
103 self
104 }
105 }
106}
107
108/// Implementation of `IfEmpty` for [`String`]
109impl IfEmpty for String {
110 /// Returns `input` if [`String::is_empty()`] returns true.
111 /// Otherwise `self` is returned.
112 fn if_empty(self, input: Self) -> Self {
113 if self.is_empty() {
114 input
115 } else {
116 self
117 }
118 }
119}
120
121/// [`OsStr`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html
122/// Implementation of `IfEmptyBorrowed` for [`OsStr`]
123impl IfEmptyBorrowed for std::ffi::OsStr {
124 /// [`OsStr::is_empty()`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html#method.is_empty
125 /// Returns `input` if [`OsStr::is_empty()`] returns true.
126 /// Otherwise `self` is returned.
127 fn if_empty<'a>(&'a self, input: &'a Self) -> &'a Self {
128 if self.is_empty() {
129 input
130 } else {
131 self
132 }
133 }
134}
135
136/// [`OsString`]: https://doc.rust-lang.org/std/ffi/struct.OsString.html
137/// Implementation of `IfEmpty` for [`OsString`]
138impl IfEmpty for std::ffi::OsString {
139 /// [`OsString::is_empty()`]: https://doc.rust-lang.org/std/ffi/struct.OsString.html#method.is_empty
140 /// Returns `input` if [`OsString::is_empty()`] returns true.
141 /// Otherwise `self` is returned.
142 fn if_empty(self, input: Self) -> Self {
143 if self.is_empty() {
144 input
145 } else {
146 self
147 }
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use std::ffi::{OsStr, OsString};
154
155 use crate::{IfEmpty, IfEmptyBorrowed};
156
157 #[test]
158 fn string() {
159 let string = String::default();
160 assert!(string.is_empty());
161 let replacement = "text".to_string();
162 let replaced = string.if_empty(replacement.clone());
163 assert!(!replaced.is_empty());
164 assert_eq!(replacement, replaced);
165
166 let string = "not empty".to_string();
167 assert!(!string.is_empty());
168 assert_eq!("not empty", string.if_empty("should not be returned".to_string()));
169 }
170 #[test]
171 fn str() {
172 let string: &str = "";
173 assert!(string.is_empty());
174 let replacement = "text";
175 let replaced = string.if_empty(replacement);
176 assert!(!replaced.is_empty());
177 assert_eq!(replacement, replaced);
178
179 let string: &str = "not empty";
180 assert!(!string.is_empty());
181 assert_eq!("not empty", string.if_empty("should not be returned"));
182 }
183 #[test]
184 fn os_string() {
185 let string = OsString::default();
186 assert!(string.is_empty());
187 let replacement = OsString::from("text");
188 let replaced = string.if_empty(replacement.clone());
189 assert!(!replaced.is_empty());
190 assert_eq!(replacement, replaced);
191
192 let string = OsString::from("not empty");
193 assert!(!string.is_empty());
194 assert_eq!(
195 OsString::from("not empty"),
196 string.if_empty(OsString::from("should not be returned"))
197 );
198 }
199 #[test]
200 fn os_str() {
201 let string = OsStr::new("");
202 assert!(string.is_empty());
203 let replacement = OsStr::new("text");
204 let replaced = string.if_empty(replacement);
205 assert!(!replaced.is_empty());
206 assert_eq!(replacement, replaced);
207
208 let string = OsStr::new("not empty");
209 assert!(!string.is_empty());
210 }
211 #[test]
212 fn custom() {
213 struct Fake {
214 value: bool,
215 }
216
217 impl IfEmpty for Fake {
218 fn if_empty(self, value: Self) -> Self {
219 if self.value {
220 self
221 } else {
222 value
223 }
224 }
225 }
226
227 let f = Fake {
228 value: false,
229 };
230 assert!(
231 f.if_empty(Fake {
232 value: true
233 })
234 .value
235 );
236
237 let f = Fake {
238 value: true,
239 };
240 assert!(
241 f.if_empty(Fake {
242 value: false
243 })
244 .value
245 );
246 }
247
248 #[test]
249 fn derive_macro() {
250 #[derive(IfEmpty)]
251 struct Example {
252 value: String,
253 }
254
255 impl Example {
256 fn is_empty(&self) -> bool {
257 self.value.is_empty()
258 }
259 }
260
261 let e = Example {
262 value: String::new(),
263 };
264 assert_eq!(
265 e.if_empty(Example {
266 value: "not empty".to_string(),
267 })
268 .value,
269 "not empty"
270 );
271
272 let e = Example {
273 value: "a string".to_string(),
274 };
275 assert_eq!(
276 e.if_empty(Example {
277 value: "not empty".to_string(),
278 })
279 .value,
280 "a string"
281 );
282 }
283}