derive_utils/
lib.rs

1// SPDX-License-Identifier: Apache-2.0 OR MIT
2
3/*!
4A 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]
9like deriving trait to enum so long as all variants are implemented that trait.
10
11```rust
12# extern crate proc_macro;
13use derive_utils::quick_derive;
14use proc_macro::TokenStream;
15
16# #[cfg(any())]
17#[proc_macro_derive(Iterator)]
18# pub fn _derive_iterator(_: TokenStream) -> TokenStream { unimplemented!() }
19pub 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!() }
36pub 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
53When deriving for enum like the following:
54
55```rust
56# #[cfg(any())]
57#[derive(Iterator, ExactSizeIterator, Future)]
58# struct _Enum<A>(A);
59enum Enum<A, B> {
60    A(A),
61    B(B),
62}
63```
64
65Code like this will be generated:
66
67```rust
68enum Enum<A, B> {
69    A(A),
70    B(B),
71}
72
73#[automatically_derived]
74impl<A, B> std::iter::Iterator for Enum<A, B>
75where
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]
95impl<A, B> std::iter::ExactSizeIterator for Enum<A, B>
96where
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]
144mod error;
145
146mod ast;
147mod parse;
148
149pub 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]
159macro_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)]
201pub 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}