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}