pencil_box/array/
compact.rs

1/// A trait defining an `is_empty` method for various types.
2///
3/// This trait provides a generic way to determine if a value of a given type
4/// can be considered "empty". The definition of "empty" is context-specific
5/// and is implemented for common Rust types like collections, strings,
6/// booleans, and numeric types.
7///
8/// # Implementations:
9/// - `String` and `&str`: Returns `true` if the string contains no characters.
10/// - `Vec<T>`: Returns `true` if the vector contains no elements.
11/// - `bool`: Returns `true` if the boolean value is `false`.
12/// - Numeric types (integers and floats): Returns `true` if the value is `0` or `0.0`.
13/// - `Option<T>`: Returns `true` if the `Option` is `None` or if `Some(value)` and `value` is `is_empty()`.
14///
15/// # Usage
16/// This trait is particularly useful for filtering or compacting collections
17/// where the concept of "emptiness" applies to the elements.
18pub trait IsEmpty {
19    /// Checks if the value is considered empty.
20    ///
21    /// The specific definition of "empty" depends on the type implementing this trait.
22    ///
23    /// # Returns
24    /// `true` if the value is empty, `false` otherwise.
25    fn is_empty(&self) -> bool;
26}
27
28// --- Implementations for common Rust types ---
29
30/// Implements `IsEmpty` for `String`.
31///
32/// A `String` is considered empty if its length is zero.
33///
34/// # Performance
35/// This implementation directly calls the `String::is_empty()` method,
36/// which is an efficient O(1) operation.
37impl IsEmpty for String {
38    fn is_empty(&self) -> bool {
39        self.is_empty()
40    }
41}
42
43/// Implements `IsEmpty` for string slices (`&str`).
44///
45/// A `&str` is considered empty if its length is zero.
46///
47/// # Performance
48/// This implementation directly calls the `&str::is_empty()` method,
49/// which is an efficient O(1) operation.
50impl IsEmpty for &str {
51    fn is_empty(&self) -> bool {
52        self.len() == 0
53    }
54}
55
56/// Implements `IsEmpty` for dynamic vectors (`Vec<T>`).
57///
58/// A `Vec<T>` is considered empty if it contains no elements.
59///
60/// # Type Parameters
61/// - `T`: The type of elements within the vector.
62///
63/// # Performance
64/// This implementation directly calls the `Vec::is_empty()` method,
65/// which is an efficient O(1) operation.
66impl<T> IsEmpty for Vec<T> {
67    fn is_empty(&self) -> bool {
68        self.is_empty()
69    }
70}
71
72/// Implements `IsEmpty` for boolean values (`bool`).
73///
74/// A `bool` is considered empty if its value is `false`.
75/// This aligns with the common interpretation of `false` as "nothing" or "non-existent"
76/// in a logical context.
77///
78/// # Performance
79/// This is a direct comparison, an efficient O(1) operation.
80impl IsEmpty for bool {
81    fn is_empty(&self) -> bool {
82        // Explicit comparison for clarity, though `!*self` would also work.
83        *self == false
84    }
85}
86
87/// Implements `IsEmpty` for `Option<T>`.
88///
89/// An `Option<T>` is considered empty if it is `None` or if it is `Some(value)`
90/// and the `value` itself is `is_empty()`. This provides a recursive check for emptiness.
91///
92/// # Type Parameters
93/// - `T`: The type contained within the `Option`, which must also implement `IsEmpty`.
94///
95/// # Performance
96/// The performance depends on the `is_empty()` implementation of the inner type `T`.
97impl<T: IsEmpty> IsEmpty for Option<T> {
98    fn is_empty(&self) -> bool {
99        match self {
100            None => true,
101            Some(value) => value.is_empty(),
102        }
103    }
104}
105
106/// A macro to automatically implement `IsEmpty` for various numeric types.
107///
108/// This macro reduces boilerplate by generating `IsEmpty` implementations
109/// for both integer and floating-point types.
110///
111/// # How "Empty" is Defined for Numerics
112/// - For integers, a value is considered empty if it is `0`.
113/// - For floating-point numbers, a value is considered empty if it is `0.0`.
114///
115/// # Arguments
116/// - `ints`: A comma-separated list of integer types (e.g., `i8, u16`).
117/// - `floats`: A comma-separated list of floating-point types (e.g., `f32, f64`).
118///
119/// # Performance
120/// All generated implementations are direct comparisons, which are efficient O(1) operations.
121macro_rules! impl_is_empty_for_numerics {
122    (
123        ints: [$($int_ty:ty),*],
124        floats: [$($float_ty:ty),*]
125    ) => {
126        $(
127            /// Implements `IsEmpty` for integer type `$int_ty`.
128            /// An integer is considered empty if its value is `0`.
129            impl IsEmpty for $int_ty {
130                fn is_empty(&self) -> bool {
131                    *self == 0
132                }
133            }
134        )*
135        $(
136            /// Implements `IsEmpty` for floating-point type `$float_ty`.
137            /// A float is considered empty if its value is `0.0`.
138            impl IsEmpty for $float_ty {
139                fn is_empty(&self) -> bool {
140                    *self == 0.0
141                }
142            }
143        )*
144    };
145}
146
147// Apply the macro to a comprehensive list of standard numeric types.
148impl_is_empty_for_numerics!(
149    ints: [i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize],
150    floats: [f32, f64]
151);
152
153/// ๐Ÿšฎ Compacts a mutable vector by removing all elements that are considered "empty".
154///
155/// This function iterates through the vector and retains only those elements
156/// for which the `is_empty()` method returns `false`.
157///
158/// # Type Parameters
159/// - `T`: The type of elements in the vector. Must implement the [`IsEmpty`] trait.
160///
161/// # Arguments
162/// - `values`: A mutable reference to the `Vec<T>` to be compacted.
163///
164/// # Behavior
165/// - Modifies the input vector **in-place**, removing elements for which `is_empty()` is true.
166/// - If the vector is initially empty, it remains empty.
167/// - If all elements are empty, the result is an empty vector.
168/// - If no elements are empty, the vector remains unchanged.
169///
170/// # Performance
171/// - Runs in **O(n)** time, where `n` is the number of elements.
172/// - Uses `Vec::retain()` under the hood โ€” efficient, no reallocations.
173/// - Each element is checked once. For types where `is_empty()` is O(1), overall cost is linear and very fast.
174///
175/// # Supported Types
176/// This function works with any type that implements the `IsEmpty` trait, such as:
177/// - `String`, `&str`
178/// - All integers and floats (`0`, `0.0` are "empty")
179/// - `bool` (`false` is "empty")
180/// - `Vec<T>` where `T: IsEmpty`
181/// - `Option<T>` where `T: IsEmpty`
182///
183/// # Examples
184///
185/// ### ๐Ÿ“œ Remove empty strings
186/// ```
187/// use pencil_box::array::compact::compact;
188/// use pencil_box::traits::IsEmpty;
189///
190/// let mut items = vec!["hello".to_string(), "".to_string(), "world".to_string()];
191/// compact(&mut items);
192/// assert_eq!(items, vec!["hello", "world"]);
193/// ```
194///
195/// ### ๐Ÿ“ฆ Remove empty vectors
196/// ```
197/// let mut data = vec![vec![1], vec![], vec![2, 3]];
198/// compact(&mut data);
199/// assert_eq!(data, vec![vec![1], vec![2, 3]]);
200/// ```
201///
202/// ### ๐Ÿงน Remove zero values
203/// ```
204/// let mut nums = vec![0, 1, 0, 2, 3];
205/// compact(&mut nums);
206/// assert_eq!(nums, vec![1, 2, 3]);
207/// ```
208///
209/// ### โ“ Remove `None` and empty `Some`s
210/// ```
211/// let mut opts = vec![Some("hi"), None, Some("")];
212/// compact(&mut opts);
213/// assert_eq!(opts, vec![Some("hi")]);
214/// ```
215///
216/// ### ๐Ÿ” Leave non-empty values untouched
217/// ```
218/// let mut flags = vec![true, true, true];
219/// compact(&mut flags);
220/// assert_eq!(flags, vec![true, true, true]);
221/// ```
222///
223/// ### ๐Ÿ“ญ No-op on empty input
224/// ```
225/// let mut empty: Vec<String> = vec![];
226/// compact(&mut empty);
227/// assert!(empty.is_empty());
228/// ```
229pub fn compact<T: IsEmpty>(values: &mut Vec<T>) {
230    values.retain(|v| !v.is_empty());
231}
232