1 | // SPDX-License-Identifier: Apache-2.0 OR MIT |
2 | |
3 | /*! |
4 | A procedural macro helper for easily writing [derives macros][proc-macro-derive] for enums. |
5 | |
6 | ## Examples |
7 | |
8 | [`quick_derive!`] macro make easy to write [`proc_macro_derive`][proc-macro-derive] |
9 | like deriving trait to enum so long as all variants are implemented that trait. |
10 | |
11 | ```rust |
12 | # extern crate proc_macro; |
13 | use derive_utils::quick_derive; |
14 | use proc_macro::TokenStream; |
15 | |
16 | # #[cfg (any())] |
17 | #[proc_macro_derive(Iterator)] |
18 | # pub fn _derive_iterator(_: TokenStream) -> TokenStream { unimplemented!() } |
19 | pub fn derive_iterator(input: TokenStream) -> TokenStream { |
20 | quick_derive! { |
21 | input, |
22 | // trait path |
23 | std::iter::Iterator, |
24 | // trait definition |
25 | trait Iterator { |
26 | type Item; |
27 | fn next(&mut self) -> Option<Self::Item>; |
28 | fn size_hint(&self) -> (usize, Option<usize>); |
29 | } |
30 | } |
31 | } |
32 | |
33 | # #[cfg (any())] |
34 | #[proc_macro_derive(ExactSizeIterator)] |
35 | # pub fn _derive_exact_size_iterator(_: TokenStream) -> TokenStream { unimplemented!() } |
36 | pub fn derive_exact_size_iterator(input: TokenStream) -> TokenStream { |
37 | quick_derive! { |
38 | input, |
39 | // trait path |
40 | std::iter::ExactSizeIterator, |
41 | // super trait's associated types |
42 | <Item>, |
43 | // trait definition |
44 | trait ExactSizeIterator: Iterator { |
45 | fn len(&self) -> usize; |
46 | } |
47 | } |
48 | } |
49 | ``` |
50 | |
51 | ### Generated code |
52 | |
53 | When deriving for enum like the following: |
54 | |
55 | ```rust |
56 | # #[cfg (any())] |
57 | #[derive(Iterator, ExactSizeIterator, Future)] |
58 | # struct _Enum<A>(A); |
59 | enum Enum<A, B> { |
60 | A(A), |
61 | B(B), |
62 | } |
63 | ``` |
64 | |
65 | Code like this will be generated: |
66 | |
67 | ```rust |
68 | enum Enum<A, B> { |
69 | A(A), |
70 | B(B), |
71 | } |
72 | |
73 | #[automatically_derived] |
74 | impl<A, B> std::iter::Iterator for Enum<A, B> |
75 | where |
76 | A: std::iter::Iterator, |
77 | B: std::iter::Iterator<Item = <A as std::iter::Iterator>::Item>, |
78 | { |
79 | type Item = <A as std::iter::Iterator>::Item; |
80 | fn next(&mut self) -> Option<Self::Item> { |
81 | match self { |
82 | Enum::A(x) => x.next(), |
83 | Enum::B(x) => x.next(), |
84 | } |
85 | } |
86 | fn size_hint(&self) -> (usize, Option<usize>) { |
87 | match self { |
88 | Enum::A(x) => x.size_hint(), |
89 | Enum::B(x) => x.size_hint(), |
90 | } |
91 | } |
92 | } |
93 | |
94 | #[automatically_derived] |
95 | impl<A, B> std::iter::ExactSizeIterator for Enum<A, B> |
96 | where |
97 | A: std::iter::ExactSizeIterator, |
98 | B: std::iter::ExactSizeIterator<Item = <A as Iterator>::Item>, |
99 | { |
100 | fn len(&self) -> usize { |
101 | match self { |
102 | Enum::A(x) => x.len(), |
103 | Enum::B(x) => x.len(), |
104 | } |
105 | } |
106 | } |
107 | ``` |
108 | |
109 | ## Related Projects |
110 | |
111 | - [auto_enums]: A library for to allow multiple return types by automatically generated enum. |
112 | - [io-enum]: \#\[derive(Read, Write, Seek, BufRead)\] for enums. |
113 | - [iter-enum]: \#\[derive(Iterator, DoubleEndedIterator, ExactSizeIterator, FusedIterator, Extend)\] for enums. |
114 | |
115 | [auto_enums]: https://github.com/taiki-e/auto_enums |
116 | [io-enum]: https://github.com/taiki-e/io-enum |
117 | [iter-enum]: https://github.com/taiki-e/iter-enum |
118 | [proc-macro-derive]: https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros |
119 | */ |
120 | |
121 | #![doc (test( |
122 | no_crate_inject, |
123 | attr( |
124 | deny(warnings, rust_2018_idioms, single_use_lifetimes), |
125 | allow(dead_code, unused_variables) |
126 | ) |
127 | ))] |
128 | #![forbid (unsafe_code)] |
129 | #![warn ( |
130 | // Lints that may help when writing public library. |
131 | // missing_debug_implementations, |
132 | // missing_docs, |
133 | clippy::alloc_instead_of_core, |
134 | clippy::exhaustive_enums, |
135 | clippy::exhaustive_structs, |
136 | clippy::impl_trait_in_params, |
137 | // clippy::missing_inline_in_public_items, |
138 | // clippy::std_instead_of_alloc, |
139 | clippy::std_instead_of_core, |
140 | )] |
141 | #![allow (clippy::must_use_candidate)] |
142 | |
143 | #[macro_use ] |
144 | mod error; |
145 | |
146 | mod ast; |
147 | mod parse; |
148 | |
149 | pub use crate::{ |
150 | ast::EnumData, |
151 | parse::{derive_trait, EnumImpl}, |
152 | }; |
153 | |
154 | /// A macro for making easy to write `proc_macro_derive` like deriving trait to |
155 | /// enum so long as all variants are implemented that trait. |
156 | /// |
157 | /// See crate level documentation for details. |
158 | #[macro_export ] |
159 | macro_rules! quick_derive { |
160 | ($input:expr, $trait_path:expr, <$super:ident>, $($trait_def:tt)*) => { |
161 | $crate::__private::parse_input($input, |data| { |
162 | $crate::derive_trait( |
163 | &data, |
164 | &$crate::__private::parse_quote!($trait_path), |
165 | $crate::__private::Some( |
166 | $crate::__private::format_ident!($crate::__private::stringify!($super)) |
167 | ), |
168 | $crate::__private::parse_quote!($($trait_def)*), |
169 | ) |
170 | }) |
171 | .into() |
172 | }; |
173 | ($input:expr, $trait_path:expr, <$($super:ident),+ $(,)?>, $($trait_def:tt)*) => { |
174 | $crate::__private::parse_input($input, |data| { |
175 | $crate::derive_trait( |
176 | &data, |
177 | &$crate::__private::parse_quote!($trait_path), |
178 | $crate::__private::vec![ |
179 | $( $crate::__private::format_ident!($crate::__private::stringify!($super)) ),+ |
180 | ], |
181 | $crate::__private::parse_quote!($($trait_def)*), |
182 | ) |
183 | }) |
184 | .into() |
185 | }; |
186 | ($input:expr, $trait_path:expr, $($trait_def:tt)*) => { |
187 | $crate::__private::parse_input($input, |data| { |
188 | $crate::derive_trait( |
189 | &data, |
190 | &$crate::__private::parse_quote!($trait_path), |
191 | $crate::__private::None, |
192 | $crate::__private::parse_quote!($($trait_def)*), |
193 | ) |
194 | }) |
195 | .into() |
196 | }; |
197 | } |
198 | |
199 | // Not public API. |
200 | #[doc (hidden)] |
201 | pub mod __private { |
202 | #[doc (hidden)] |
203 | pub use core::{ |
204 | option::Option::{None, Some}, |
205 | stringify, |
206 | }; |
207 | #[doc (hidden)] |
208 | pub use std::vec; |
209 | |
210 | use proc_macro2::TokenStream; |
211 | #[doc (hidden)] |
212 | pub use quote::{format_ident, quote}; |
213 | use syn::Error; |
214 | #[doc (hidden)] |
215 | pub use syn::{parse2, parse_quote, ItemTrait, Path}; |
216 | |
217 | use crate::EnumData; |
218 | |
219 | #[doc (hidden)] |
220 | pub fn parse_input<T: Into<TokenStream>, F: Fn(EnumData) -> TokenStream>( |
221 | input: T, |
222 | f: F, |
223 | ) -> TokenStream { |
224 | parse2::<EnumData>(input.into()).map_or_else(Error::into_compile_error, f) |
225 | } |
226 | } |
227 | |