vec_of_enum/lib.rs
1//! A helper struct to manage a `Vec` of `enum` values. Reduces boilerplate, implements useful traits.
2//!
3//! ```rust
4//! # use derive_more::{Constructor, From};
5//! # use serde::{Deserialize, Serialize};
6//! #
7//! # // Define some sample validation error structs
8//! # #[derive(Constructor, Serialize, Deserialize)]
9//! # pub struct PasswordMinLengthError {
10//! # min_length: usize,
11//! # }
12//! #
13//! # #[derive(Constructor, Serialize, Deserialize)]
14//! # pub struct InvalidEmailError {
15//! # email: String,
16//! # reason: String,
17//! # }
18//! #
19//! # // Define an enum that can contain any validation error
20//! # #[derive(From, Serialize, Deserialize)]
21//! # pub enum ValidationError {
22//! # PasswordMinLength(PasswordMinLengthError),
23//! # InvalidEmail(InvalidEmailError),
24//! # }
25//! #
26//! # // Convenience conversion
27//! # impl From<(&str, &str)> for ValidationError {
28//! # fn from((email, reason): (&str, &str)) -> Self {
29//! # Self::InvalidEmail(InvalidEmailError::new(email.into(), reason.into()))
30//! # }
31//! # }
32//! #
33//! # // Define a typed vector wrapper for ValidationErrors
34//! # vec_of_enum::define!(
35//! # #[derive(Serialize, Deserialize)]
36//! # pub struct ValidationErrors(Vec<ValidationError>);
37//! # );
38//! #
39//! # // Define a typed vector wrapper that also automatically converts from variant types
40//! # vec_of_enum::define!(
41//! # #[derive(Serialize, Deserialize)]
42//! # pub struct ValidationErrorsWithVariants(Vec<ValidationError>);
43//! # variants = [PasswordMinLengthError, InvalidEmailError];
44//! # );
45//! #
46//! let mut errors = ValidationErrors::default();
47//!
48//! // ❌ Without `vec-of-enum`: too verbose
49//! errors.push(ValidationError::InvalidEmail(InvalidEmailError::new("user@example.com".into(), "domain is blocked".into())));
50//!
51//! // ✅ With `vec-of-enum`: very concise
52//! errors.push(("user@example.com", "domain is blocked"));
53//! ```
54//!
55//! # Full example
56//!
57//! ```rust
58//! use derive_more::{Constructor, From};
59//! use serde::{Deserialize, Serialize};
60//!
61//! // Define some sample validation error structs
62//! #[derive(Constructor, Serialize, Deserialize)]
63//! pub struct PasswordMinLengthError {
64//! min_length: usize,
65//! }
66//!
67//! #[derive(Constructor, Serialize, Deserialize)]
68//! pub struct InvalidEmailError {
69//! email: String,
70//! reason: String,
71//! }
72//!
73//! // Define an enum that can contain any validation error
74//! #[derive(From, Serialize, Deserialize)]
75//! pub enum ValidationError {
76//! PasswordMinLength(PasswordMinLengthError),
77//! InvalidEmail(InvalidEmailError),
78//! }
79//!
80//! // Convenience conversion
81//! impl From<(&str, &str)> for ValidationError {
82//! fn from((email, reason): (&str, &str)) -> Self {
83//! Self::InvalidEmail(InvalidEmailError::new(email.into(), reason.into()))
84//! }
85//! }
86//!
87//! // Define a typed vector wrapper for ValidationErrors
88//! vec_of_enum::define!(
89//! #[derive(Serialize, Deserialize)]
90//! pub struct ValidationErrors(Vec<ValidationError>);
91//! );
92//!
93//! // Define a typed vector wrapper that also automatically converts from variant types
94//! vec_of_enum::define!(
95//! #[derive(Serialize, Deserialize)]
96//! pub struct ValidationErrorsWithVariants(Vec<ValidationError>);
97//! variants = [PasswordMinLengthError, InvalidEmailError];
98//! );
99//!
100//! let mut errors = ValidationErrors::default();
101//!
102//! // ❌ Without `vec-of-enum`: too verbose
103//! errors.push(ValidationError::InvalidEmail(InvalidEmailError::new("user@example.com".into(), "domain is blocked".into())));
104//!
105//! // ✅ With `vec-of-enum`: very concise
106//! errors.push(("user@example.com", "domain is blocked"));
107//! ```
108//!
109//! # Features
110//!
111//! The wrapper struct created using the `define!` macro:
112//!
113//! - Is `#[repr(transparent)]` for zero-cost abstraction
114//! - Implements `Deref` and `DerefMut` to `Vec<T>` for access to all Vec methods
115//! - Provides `new()`, `push()`, and `extend_from()` methods
116//! - Implements `Default`, `Extend`, `IntoIterator`, `From<Vec<T>>`, and `Into<Vec<T>>`
117//! - Supports automatic conversions from variant types when using the `variants = [...]` option
118//!
119//! # Custom Derives
120//!
121//! You can add any derive macros to your struct definition, and they will be applied to
122//! the generated struct. For example:
123//!
124//! ```rust
125//! use serde::{Deserialize, Serialize};
126//!
127//! #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
128//! pub enum MyEnum {}
129//!
130//! vec_of_enum::define!(
131//! #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
132//! pub struct MyVec(Vec<MyEnum>);
133//! );
134//! ```
135//!
136//! This allows you to add any necessary derives that your application requires.
137
138#![cfg_attr(not(test), deny(unused_crate_dependencies))]
139
140#[macro_export]
141macro_rules! define {
142 (
143 $(#[$meta:meta])*
144 $vis:vis struct $name:ident(Vec<$inner:ty>)
145 $(where [$($where_clause:tt)*])?;
146 $(variants = [$($variant:ty),+];)?
147 ) => {
148 $crate::define_struct!(
149 $(#[$meta])*
150 $vis struct $name(Vec<$inner>)
151 $(where [$($where_clause)*])?;
152 );
153 $crate::impl_self!($name, $inner);
154 $crate::impl_default!($name);
155 $crate::impl_extend!($name, $inner);
156 $crate::impl_into_iter_own!($name, $inner);
157 $crate::impl_into_iter_ref!($name, $inner);
158 $crate::impl_deref!($name, $inner);
159 $crate::impl_deref_mut!($name, $inner);
160 $crate::impl_from_vec!($name, $inner);
161 $crate::impl_into_vec!($name, $inner);
162 $($crate::impl_from_value!($name, [$($variant),+]);)?
163 };
164}
165
166#[macro_export]
167macro_rules! define_struct {
168 (
169 $(#[$meta:meta])*
170 $vis:vis struct $name:ident(Vec<$inner:ty>)
171 $(where [$($where_clause:tt)*])?;
172 ) => {
173 #[repr(transparent)]
174 $(#[$meta])*
175 $vis struct $name(Vec<$inner>)
176 $(where $($where_clause)*)?;
177 };
178}
179
180#[macro_export]
181macro_rules! impl_self {
182 ($name:ident, $inner:ty) => {
183 impl $name {
184 pub fn new(inner: impl Into<Vec<$inner>>) -> Self {
185 Self(inner.into())
186 }
187
188 pub fn push(&mut self, value: impl Into<$inner>) {
189 self.0.push(value.into())
190 }
191
192 pub fn extend_from<T: Into<$inner>>(&mut self, iter: impl IntoIterator<Item = T>) {
193 self.extend(iter.into_iter().map(T::into))
194 }
195 }
196 };
197}
198
199#[macro_export]
200macro_rules! impl_default {
201 ($name:ident) => {
202 impl Default for $name {
203 fn default() -> Self {
204 Self(Default::default())
205 }
206 }
207 };
208}
209
210#[macro_export]
211macro_rules! impl_extend {
212 ($name:ident, $inner:ty) => {
213 impl Extend<$inner> for $name {
214 fn extend<I: IntoIterator<Item = $inner>>(&mut self, iter: I) {
215 self.0.extend(iter);
216 }
217 }
218 };
219}
220
221#[macro_export]
222macro_rules! impl_into_iter_own {
223 ($name:ident, $inner:ty) => {
224 impl IntoIterator for $name {
225 type Item = $inner;
226 type IntoIter = std::vec::IntoIter<Self::Item>;
227
228 fn into_iter(self) -> Self::IntoIter {
229 self.0.into_iter()
230 }
231 }
232 };
233}
234
235#[macro_export]
236macro_rules! impl_into_iter_ref {
237 ($name:ident, $inner:ty) => {
238 impl<'a> IntoIterator for &'a $name {
239 type Item = &'a $inner;
240 type IntoIter = std::slice::Iter<'a, $inner>;
241
242 fn into_iter(self) -> Self::IntoIter {
243 self.0.iter()
244 }
245 }
246 };
247}
248
249#[macro_export]
250macro_rules! impl_deref {
251 ($name:ident, $inner:ty) => {
252 impl std::ops::Deref for $name {
253 type Target = Vec<$inner>;
254
255 fn deref(&self) -> &Self::Target {
256 &self.0
257 }
258 }
259 };
260}
261
262#[macro_export]
263macro_rules! impl_deref_mut {
264 ($name:ident, $inner:ty) => {
265 impl std::ops::DerefMut for $name {
266 fn deref_mut(&mut self) -> &mut Self::Target {
267 &mut self.0
268 }
269 }
270 };
271}
272
273#[macro_export]
274macro_rules! impl_from_vec {
275 ($name:ident, $inner:ty) => {
276 impl From<Vec<$inner>> for $name {
277 fn from(vec: Vec<$inner>) -> Self {
278 Self(vec)
279 }
280 }
281 };
282}
283
284#[macro_export]
285macro_rules! impl_from_value {
286 ($name:ident, [$($value_source:ty),+]) => {
287 $(
288 $crate::impl_from_value!($name, $value_source);
289 )+
290 };
291 ($name:ident, $value_source:ty) => {
292 impl From<$value_source> for $name {
293 fn from(source: $value_source) -> Self {
294 Self(vec![source.into()])
295 }
296 }
297 };
298}
299
300#[macro_export]
301macro_rules! impl_into_vec {
302 ($name:ident, $inner:ty) => {
303 impl From<$name> for Vec<$inner> {
304 fn from(value: $name) -> Self {
305 value.0
306 }
307 }
308 };
309}