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#[macro_export]
139macro_rules! define {
140 (
141 $(#[$meta:meta])*
142 $vis:vis struct $name:ident(Vec<$inner:ty>)
143 $(where [$($where_clause:tt)*])?;
144 $(variants = [$($variant:ty),+];)?
145 ) => {
146 $crate::define_struct!(
147 $(#[$meta])*
148 $vis struct $name(Vec<$inner>)
149 $(where [$($where_clause)*])?;
150 );
151 $crate::impl_self!($name, $inner);
152 $crate::impl_default!($name);
153 $crate::impl_extend!($name, $inner);
154 $crate::impl_into_iter_own!($name, $inner);
155 $crate::impl_into_iter_ref!($name, $inner);
156 $crate::impl_deref!($name, $inner);
157 $crate::impl_deref_mut!($name, $inner);
158 $crate::impl_from_vec!($name, $inner);
159 $crate::impl_into_vec!($name, $inner);
160 $($crate::impl_from_value!($name, [$($variant),+]);)?
161 };
162}
163
164#[macro_export]
165macro_rules! define_struct {
166 (
167 $(#[$meta:meta])*
168 $vis:vis struct $name:ident(Vec<$inner:ty>)
169 $(where [$($where_clause:tt)*])?;
170 ) => {
171 #[repr(transparent)]
172 $(#[$meta])*
173 $vis struct $name(Vec<$inner>)
174 $(where $($where_clause)*)?;
175 };
176}
177
178#[macro_export]
179macro_rules! impl_self {
180 ($name:ident, $inner:ty) => {
181 impl $name {
182 pub fn new(inner: impl Into<Vec<$inner>>) -> Self {
183 Self(inner.into())
184 }
185
186 pub fn push(&mut self, value: impl Into<$inner>) {
187 self.0.push(value.into())
188 }
189
190 pub fn extend_from<T: Into<$inner>>(&mut self, iter: impl IntoIterator<Item = T>) {
191 self.extend(iter.into_iter().map(T::into))
192 }
193 }
194 };
195}
196
197#[macro_export]
198macro_rules! impl_default {
199 ($name:ident) => {
200 impl Default for $name {
201 fn default() -> Self {
202 Self(Default::default())
203 }
204 }
205 };
206}
207
208#[macro_export]
209macro_rules! impl_extend {
210 ($name:ident, $inner:ty) => {
211 impl Extend<$inner> for $name {
212 fn extend<I: IntoIterator<Item = $inner>>(&mut self, iter: I) {
213 self.0.extend(iter);
214 }
215 }
216 };
217}
218
219#[macro_export]
220macro_rules! impl_into_iter_own {
221 ($name:ident, $inner:ty) => {
222 impl IntoIterator for $name {
223 type Item = $inner;
224 type IntoIter = std::vec::IntoIter<Self::Item>;
225
226 fn into_iter(self) -> Self::IntoIter {
227 self.0.into_iter()
228 }
229 }
230 };
231}
232
233#[macro_export]
234macro_rules! impl_into_iter_ref {
235 ($name:ident, $inner:ty) => {
236 impl<'a> IntoIterator for &'a $name {
237 type Item = &'a $inner;
238 type IntoIter = std::slice::Iter<'a, $inner>;
239
240 fn into_iter(self) -> Self::IntoIter {
241 self.0.iter()
242 }
243 }
244 };
245}
246
247#[macro_export]
248macro_rules! impl_deref {
249 ($name:ident, $inner:ty) => {
250 impl std::ops::Deref for $name {
251 type Target = Vec<$inner>;
252
253 fn deref(&self) -> &Self::Target {
254 &self.0
255 }
256 }
257 };
258}
259
260#[macro_export]
261macro_rules! impl_deref_mut {
262 ($name:ident, $inner:ty) => {
263 impl std::ops::DerefMut for $name {
264 fn deref_mut(&mut self) -> &mut Self::Target {
265 &mut self.0
266 }
267 }
268 };
269}
270
271#[macro_export]
272macro_rules! impl_from_vec {
273 ($name:ident, $inner:ty) => {
274 impl From<Vec<$inner>> for $name {
275 fn from(vec: Vec<$inner>) -> Self {
276 Self(vec)
277 }
278 }
279 };
280}
281
282#[macro_export]
283macro_rules! impl_from_value {
284 ($name:ident, [$($value_source:ty),+]) => {
285 $(
286 $crate::impl_from_value!($name, $value_source);
287 )+
288 };
289 ($name:ident, $value_source:ty) => {
290 impl From<$value_source> for $name {
291 fn from(source: $value_source) -> Self {
292 Self(vec![source.into()])
293 }
294 }
295 };
296}
297
298#[macro_export]
299macro_rules! impl_into_vec {
300 ($name:ident, $inner:ty) => {
301 impl From<$name> for Vec<$inner> {
302 fn from(value: $name) -> Self {
303 value.0
304 }
305 }
306 };
307}