Skip to main content

ignorable/
lib.rs

1#![doc = concat!("[![crates.io](https://img.shields.io/crates/v/", env!("CARGO_PKG_NAME"), "?style=flat-square&logo=rust)](https://crates.io/crates/", env!("CARGO_PKG_NAME"), ")")]
2#![doc = concat!("[![docs.rs](https://img.shields.io/docsrs/", env!("CARGO_PKG_NAME"), "?style=flat-square&logo=docs.rs)](https://docs.rs/", env!("CARGO_PKG_NAME"), ")")]
3#![doc = "![license](https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue?style=flat-square)"]
4#![doc = concat!("![msrv](https://img.shields.io/badge/msrv-", env!("CARGO_PKG_RUST_VERSION"), "-blue?style=flat-square&logo=rust)")]
5#![doc = concat!("[![github](https://img.shields.io/github/stars/nik-rev/", env!("CARGO_PKG_NAME"), ")](https://github.com/nik-rev/", env!("CARGO_PKG_NAME"), ")")]
6//!
7//! This crate provides 5 derives that are just like the standard library's, but they allow
8//! to ignore fields when deriving. Inspired by [RFC 3869](https://github.com/rust-lang/rfcs/pull/3869)
9//!
10//! ```toml
11#![doc = concat!(env!("CARGO_PKG_NAME"), " = ", "\"", env!("CARGO_PKG_VERSION_MAJOR"), ".", env!("CARGO_PKG_VERSION_MINOR"), "\"")]
12//! ```
13//!
14//! # Usage
15//!
16//! This crate provides 5 derive macros:
17//!
18//! - `PartialEq`
19//! - `PartialOrd`
20//! - `Ord`
21//! - `Debug`
22//! - `Hash`
23//!
24//! The advantage of these derives over the standard library's is that they support
25//! the `#[ignored]` attribute to ignore individual fields when deriving the respective traits.
26//!
27//! ```rust
28//! use ignorable::{PartialEq, Hash};
29//!
30//! // `PartialEq` and `Hash` impls will only check
31//! // the `id` field of 2 `User`s
32//! #[derive(Clone, PartialEq, Eq, Hash)]
33//! struct User {
34//!     #[ignored(PartialEq, Hash)]
35//!     name: String,
36//!     #[ignored(PartialEq, Hash)]
37//!     age: u8,
38//!     id: u64
39//! }
40//! ```
41//!
42//! Advantages:
43//!
44//! - **Significantly** less boilerplate
45//! - Less maintenance overhead, it's not your responsibility to remember to update manual implementations of traits,
46//!   keep traits like `Hash` and `PartialEq` in sync. We've got that covered!
47//! - This might become a language feature in the future ([RFC 3869](https://github.com/rust-lang/rfcs/pull/3869)),
48//!   so you'll be able to transition away from this crate once that time comes!
49//!
50//! Remember that it is a [logic error](https://doc.rust-lang.org/stable/std/hash/trait.Hash.html#hash-and-eq)
51//! for the implementations of `Hash` and `PartialEq` to differ, and if you need to manually implement the traits
52//! to skip certain fields, **you** must remember to keep them in sync because you can't use the `derive` anymore.
53//!
54//! # With `ignorable`
55//!
56//! Uses derives provided by this crate.
57//!
58//! ```rust
59//! # use std::marker::PhantomData;
60//! # use std::cell::RefCell;
61//! # use std::rc::Rc;
62//! #
63//! # #[derive(PartialEq, Debug, Hash, Clone)]
64//! # struct Symbol;
65//! # #[derive(PartialEq, Debug, Hash, Clone)]
66//! # struct Value;
67//! #
68//! # mod protocols {
69//! #     #[derive(PartialEq, Debug, Hash, Clone)]
70//! #     pub struct IPersistentMap;
71//! # };
72//! use ignorable::{Debug, PartialEq, Hash};
73//!
74//! #[derive(Clone, Debug, PartialEq, Hash)]
75//! pub struct Var<T> {
76//!     pub ns: Symbol,
77//!     pub sym: Symbol,
78//!     #[ignored(PartialEq, Hash)]
79//!     meta: RefCell<protocols::IPersistentMap>,
80//!     #[ignored(PartialEq, Hash)]
81//!     pub root: RefCell<Rc<Value>>,
82//!     #[ignored(Debug)]
83//!     _phantom: PhantomData<T>
84//! }
85//! ```
86//!
87//! # Without
88//!
89//! You must manually implement each trait.
90//!
91//! ```rust
92//! # use std::marker::PhantomData;
93//! # use std::hash::{Hasher, Hash};
94//! # use std::fmt;
95//! # use std::cell::RefCell;
96//! # use std::rc::Rc;
97//! #
98//! # #[derive(PartialEq, Debug, Hash, Clone)]
99//! # struct Symbol;
100//! # #[derive(PartialEq, Debug, Hash, Clone)]
101//! # struct Value;
102//! #
103//! # mod protocols {
104//! #     #[derive(PartialEq, Debug, Hash, Clone)]
105//! #     pub struct IPersistentMap;
106//! # };
107//! #[derive(Clone)]
108//! pub struct Var<T> {
109//!     pub ns: Symbol,
110//!     pub sym: Symbol,
111//!     meta: RefCell<protocols::IPersistentMap>,
112//!     pub root: RefCell<Rc<Value>>,
113//!     _phantom: PhantomData<T>
114//! }
115//!
116//! impl<T> fmt::Debug for Var<T> {
117//!     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118//!         f.debug_struct("Var")
119//!             .field("ns", &self.ns)
120//!             .field("sym", &self.sym)
121//!             .field("meta", &self.meta)
122//!             .field("root", &self.root)
123//!             .finish()
124//!     }
125//! }
126//!
127//! impl<T> PartialEq for Var<T> {
128//!     fn eq(&self, other: &Self) -> bool {
129//!         self.ns == other.ns && self.sym == other.sym
130//!     }
131//! }
132//!
133//! impl<T> Hash for Var<T> {
134//!     fn hash<H: Hasher>(&self, state: &mut H) {
135//!         (&self.ns, &self.sym).hash(state);
136//!     }
137//! }
138//! ```
139//!
140//! Notes:
141//!
142//! - It is logically incorrect for `Hash` and `PartialEq` implementations
143//!   to differ, so you must remember to keep them in sync if `Var` changes
144//! - You must remember to update the string names of the `Debug` impl if you
145//!   ever rename the fields or `Var` itself
146use proc_macro2::{Span, TokenStream};
147use quote::{format_ident, quote, ToTokens};
148use std::cmp::Ordering;
149use syn::{parse_macro_input, DeriveInput};
150use syn::{parse_quote, Index, Member};
151use syn::{punctuated::Punctuated, token::Comma, Error, Field, Ident, Variant};
152
153create_derive!(PartialEq);
154create_derive!(PartialOrd);
155create_derive!(Ord);
156create_derive!(Debug);
157create_derive!(Hash);
158
159fn generate(
160    input: &mut syn::DeriveInput,
161    deriving: Deriving,
162    errors: &mut Vec<Error>,
163) -> TokenStream {
164    let body = match &input.data {
165        syn::Data::Struct(data) => {
166            let handle_struct_fields = data
167                .fields
168                .iter()
169                .enumerate()
170                .filter(|(_, field)| !deriving.is_ignored_in(field, errors))
171                .map(|(i, field)| {
172                    let member = field
173                        .ident
174                        .clone()
175                        .map(Member::Named)
176                        .unwrap_or_else(|| Member::Unnamed(Index::from(i)));
177
178                    deriving.handle_struct_field(member)
179                });
180
181            deriving.handle_struct(
182                handle_struct_fields,
183                &input.ident,
184                matches!(data.fields, syn::Fields::Unnamed(_)),
185            )
186        }
187        syn::Data::Enum(data) => 'body: {
188            if data.variants.is_empty() {
189                break 'body quote! { match *self {} };
190            }
191
192            let handle_variants = data.variants.iter().map(|variant| {
193                let (members, (fields_patterns, handle_variant_fields)): (
194                    Vec<_>,
195                    (Vec<_>, Vec<_>),
196                ) = variant
197                    .fields
198                    .iter()
199                    .enumerate()
200                    .filter(|(_, field)| !deriving.is_ignored_in(field, errors))
201                    .map(|(i, field)| {
202                        let member = field
203                            .ident
204                            .clone()
205                            .map(Member::Named)
206                            .unwrap_or_else(|| Member::Unnamed(Index::from(i)));
207
208                        (member.clone(), deriving.handle_variant_field(member))
209                    })
210                    .unzip();
211
212                let handle_variant = deriving.handle_struct(handle_variant_fields.into_iter(), &variant.ident, matches!(variant.fields, syn::Fields::Unnamed(_)));
213                let variant_name = &variant.ident;
214
215                if matches!(deriving, Deriving::Debug | Deriving::Hash) {
216                    let patterns = fields_patterns.into_iter().map(|x| match x {
217                        EnumPatternField::One(ident) => ident,
218                        EnumPatternField::Two(..) => unreachable!(
219                            "variant depends on variant of `deriving`, but it is constant in this arm"
220                        ),
221                    });
222
223                    // match self {
224                    quote! {
225                        Self::#variant_name { #(#members: #patterns,)* .. } => {
226                            #handle_variant
227                        }
228                    }
229                } else {
230                    let (lefts, rights): (Vec<_>, Vec<_>) = fields_patterns
231                        .into_iter()
232                        .map(|x| match x {
233                            EnumPatternField::One(..) => unreachable!("variant depends on variant of `deriving`, but it is constant in this arm"),
234                            EnumPatternField::Two(left, right) => (left, right),
235                        })
236                        .unzip();
237
238                    // match (self, other) {
239                    quote! {
240                        (
241                            Self::#variant_name { #(#members: #lefts,)* .. },
242                            Self::#variant_name { #(#members: #rights,)* .. }
243                        ) => {
244                            #handle_variant
245                        }
246                    }
247                }
248            });
249
250            deriving.handle_enum(handle_variants, &data.variants)
251        }
252        syn::Data::Union(_) => {
253            return Error::new(Span::call_site(), "this trait cannot be derived for unions")
254                .into_compile_error();
255        }
256    };
257
258    for type_param in input.generics.type_params_mut() {
259        type_param.bounds.push(parse_quote!( #deriving ));
260    }
261    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
262    let name = &input.ident;
263    let signature = deriving.signature();
264
265    quote! {
266        #[allow(non_snake_case, non_shorthand_field_patterns)]
267        #[automatically_derived]
268        impl #impl_generics #deriving for #name #ty_generics #where_clause {
269            #signature {
270                #body
271            }
272        }
273    }
274}
275
276/// The standard library trait that we are deriving
277///
278/// Each trait supports `#[ignore]` attribute on fields. The `#[ignore]` attribute
279/// receives a list of identifiers. If the `#[ignore]` attribute's list contains exactly
280/// the identifier of this trait, then the trait will skip this field.
281#[derive(Copy, Clone)]
282enum Deriving {
283    /// Deriving [`trait@PartialEq`]
284    PartialEq,
285    /// Deriving [`trait@PartialOrd`]
286    PartialOrd,
287    /// Deriving [`trait@Ord`]
288    Ord,
289    /// Deriving [`Debug`]
290    Debug,
291    /// Deriving [`Hash`]
292    Hash,
293}
294
295/// Path to the trait we are deriving
296impl ToTokens for Deriving {
297    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
298        let path = match self {
299            Deriving::PartialEq => quote! { ::core::cmp::PartialEq },
300            Deriving::PartialOrd => quote! { ::core::cmp::PartialOrd },
301            Deriving::Ord => quote! { ::core::cmp::Ord },
302            Deriving::Debug => quote! { ::core::fmt::Debug },
303            Deriving::Hash => quote! { ::core::hash::Hash },
304        };
305
306        tokens.extend(path);
307    }
308}
309
310impl Deriving {
311    /// Signature of the single function belonging to the trait we are deriving
312    pub fn signature(self) -> TokenStream {
313        match self {
314            Deriving::PartialEq => quote! {
315                fn eq(&self, other: &Self) -> bool
316            },
317            Deriving::PartialOrd => quote! {
318                fn partial_cmp(&self, other: &Self) -> ::core::option::Option<::core::cmp::Ordering>
319            },
320            Deriving::Ord => quote! {
321                fn cmp(&self, other: &Self) -> ::core::cmp::Ordering
322            },
323            Deriving::Debug => quote! {
324                fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result
325            },
326            Deriving::Hash => quote! {
327                fn hash<__H>(&self, state: &mut __H) where __H: ::core::hash::Hasher
328            },
329        }
330    }
331
332    /// Generate logic for a single field
333    pub fn handle_struct_field(self, member: Member) -> proc_macro2::TokenStream {
334        match self {
335            Deriving::PartialEq => quote! {
336                if self.#member != other.#member {
337                    return false;
338                }
339            },
340            Deriving::PartialOrd => quote! {
341                match #self::partial_cmp(&self.#member, &other.#member) {
342                    ::core::option::Option::Some(::core::cmp::Ordering::Equal) => {},
343                    cmp => return cmp,
344                }
345            },
346            Deriving::Ord => quote! {
347                match #self::cmp(&self.#member, &other.#member) {
348                    ::core::cmp::Ordering::Equal => {},
349                    cmp => return cmp,
350                }
351            },
352            Deriving::Debug => match &member {
353                Member::Named(ident) => {
354                    let name = ident.to_string();
355                    quote! { .field(#name, &self.#member) }
356                }
357                Member::Unnamed(_) => {
358                    quote! { .field(&self.#member) }
359                }
360            },
361            Deriving::Hash => quote! {
362                #self::hash(&self.#member, state);
363            },
364        }
365    }
366
367    /// Generate logic for the enum
368    pub fn handle_enum(
369        self,
370        handle_variants: impl Iterator<Item = TokenStream>,
371        variants: &Punctuated<Variant, Comma>,
372    ) -> proc_macro2::TokenStream {
373        match self {
374            Deriving::PartialEq => {
375                let arms_eq_discriminants = map_enum_discriminant_permutations(
376                    variants,
377                    |discriminant_left, variant_left, discriminant_right, variant_right| {
378                        let is_equal = discriminant_left == discriminant_right;
379
380                        quote! {
381                            (
382                                Self::#variant_left { .. },
383                                Self::#variant_right { .. }
384                            ) => #is_equal
385                        }
386                    },
387                );
388
389                quote! {
390                    let discriminant = match (self, other) {
391                        #(#arms_eq_discriminants),*
392                    };
393                    discriminant && match (self, other) {
394                        #(#handle_variants),*
395                        _ => true
396                    }
397                }
398            }
399            Deriving::PartialOrd | Deriving::Ord => {
400                let some = match self {
401                    Deriving::PartialOrd => Some(quote! { ::core::option::Option::Some }),
402                    Deriving::Ord => None,
403                    _ => unreachable!(),
404                };
405
406                let arms_cmp_discriminants = map_enum_discriminant_permutations(
407                    variants,
408                    |discriminant_left, variant_left, discriminant_right, variant_right| {
409                        let ordering = match discriminant_left.cmp(&discriminant_right) {
410                            Ordering::Less => quote!(Less),
411                            Ordering::Equal => quote!(Equal),
412                            Ordering::Greater => quote!(Greater),
413                        };
414
415                        quote! {
416                            (
417                                Self::#variant_left { .. },
418                                Self::#variant_right { .. }
419                            ) => { ::core::cmp::Ordering::#ordering }
420                        }
421                    },
422                );
423
424                quote! {
425                    match (self, other) {
426                        #(#handle_variants),*
427                        _ => {
428                            // if variants are not equal, then we just compare their discriminants
429                            #some(match (self, other) {
430                                #(#arms_cmp_discriminants)*
431                            })
432                        }
433                    }
434                }
435            }
436            Deriving::Debug => quote! {
437                match &self {
438                    #(#handle_variants,)*
439                }
440            },
441            Deriving::Hash => {
442                let arms = variants.iter().enumerate().map(|(discriminant, variant)| {
443                    let ident = &variant.ident;
444                    quote! { Self::#ident { .. } => #self::hash(&#discriminant, state) }
445                });
446                quote! {
447                    match self {
448                        #(#arms,)*
449                    }
450                    match &self {
451                        #(#handle_variants)*
452                    }
453                }
454            }
455        }
456    }
457
458    /// Generates logic for handling a single field of a variant
459    pub fn handle_variant_field(
460        self,
461        member: Member,
462    ) -> (EnumPatternField, proc_macro2::TokenStream) {
463        let member_str = member.to_token_stream().to_string();
464        match self {
465            Deriving::PartialEq => {
466                let left = format_ident!("__l_{}", member_str);
467                let right = format_ident!("__r_{}", member_str);
468
469                (
470                    EnumPatternField::Two(left.clone(), right.clone()),
471                    quote! {
472                        if #left != #right {
473                            return false;
474                        }
475                    },
476                )
477            }
478            Deriving::PartialOrd | Deriving::Ord => {
479                let (method, equal) = match self {
480                    Deriving::PartialOrd => (
481                        quote! { partial_cmp },
482                        quote! { ::core::option::Option::Some(::core::cmp::Ordering::Equal) },
483                    ),
484                    Deriving::Ord => (quote! { cmp }, quote! { ::core::cmp::Ordering::Equal }),
485                    _ => unreachable!(),
486                };
487
488                let left = format_ident!("__l_{member_str}");
489                let right = format_ident!("__r_{member_str}");
490
491                (
492                    EnumPatternField::Two(left.clone(), right.clone()),
493                    quote! {
494                        match #self::#method(&#left, &#right) {
495                            #equal => {},
496                            cmp => return cmp,
497                        }
498                    },
499                )
500            }
501            Deriving::Debug => {
502                let ident = format_ident!("__{member_str}");
503                match &member {
504                    Member::Named(ident) => {
505                        let name = ident.to_string();
506                        (
507                            EnumPatternField::One(ident.clone()),
508                            quote! { .field(#name, #ident) },
509                        )
510                    }
511                    Member::Unnamed(_) => (
512                        EnumPatternField::One(ident.clone()),
513                        quote! { .field(#ident) },
514                    ),
515                }
516            }
517            Deriving::Hash => {
518                let ident = format_ident!("__{member_str}");
519                (
520                    EnumPatternField::One(ident.clone()),
521                    quote! {
522                        #self::hash(&#ident, state);
523                    },
524                )
525            }
526        }
527    }
528
529    pub fn handle_struct(
530        self,
531        fields: impl Iterator<Item = TokenStream>,
532        name: &Ident,
533        is_tuple: bool,
534    ) -> TokenStream {
535        match self {
536            Deriving::PartialEq => quote! {
537                #(#fields)*
538                true
539            },
540            Deriving::PartialOrd => quote! {
541                #(#fields)*
542                ::core::option::Option::Some(::core::cmp::Ordering::Equal)
543            },
544            Deriving::Ord => quote! {
545                #(#fields)*
546                ::core::cmp::Ordering::Equal
547            },
548            Deriving::Debug => {
549                let name = name.to_string();
550                match is_tuple {
551                    true => quote! {
552                        f.debug_tuple(#name)
553                            #(#fields)*
554                            .finish()
555                    },
556                    false => quote! {
557                        f.debug_struct(#name)
558                            #(#fields)*
559                            .finish()
560                    },
561                }
562            }
563            Deriving::Hash => quote! {
564                #(#fields)*
565            },
566        }
567    }
568
569    /// If this derive ignores `field`
570    pub fn is_ignored_in(self, field: &Field, errors: &mut Vec<Error>) -> bool {
571        let mut partial_eq = false;
572        let mut partial_ord = false;
573        let mut ord = false;
574        let mut debug = false;
575        let mut hash = false;
576
577        for attr in &field.attrs {
578            if attr.path().is_ident("ignored") {
579                let result = attr.parse_nested_meta(|meta| {
580                    let ident = meta.path.require_ident()?;
581
582                    let value = match ident.to_string().as_str() {
583                        "PartialEq" => &mut partial_eq,
584                        "PartialOrd" => &mut partial_ord,
585                        "Ord" => &mut ord,
586                        "Debug" => &mut debug,
587                        "Hash" => &mut hash,
588                        _ => {
589                            return Err(Error::new(
590                                ident.span(),
591                                concat!(
592                                    "expected one of: `PartialEq`, `PartialOrd`, ",
593                                    "`Ord`, `Debug`, `Hash`"
594                                ),
595                            ));
596                        }
597                    };
598
599                    if *value {
600                        errors.push(Error::new(
601                            ident.span(),
602                            "this derive has already been ignored",
603                        ));
604                    }
605
606                    *value = true;
607
608                    Ok(())
609                });
610
611                if let Err(error) = result {
612                    errors.push(error);
613                }
614            }
615        }
616
617        match self {
618            Deriving::PartialEq => partial_eq,
619            Deriving::PartialOrd => partial_ord,
620            Deriving::Ord => ord,
621            Deriving::Debug => debug,
622            Deriving::Hash => hash,
623        }
624    }
625}
626
627/// Maps permutations of the enum `variants` with `f` into a `TokenStream`
628///
629/// Because Rust provides us no way of getting the discriminant value
630/// of an arbitrary enum, iterate through `n^2` permutations instead
631///
632/// For example, when deciding if enum `A`'s discriminant is greater than
633/// enum `B`'s discriminant (and both enums must be the same type), we
634/// `match` against every variant in `A`, and in each arm we again `match`
635/// against each variant in `B`. All sub-arms evaluate to `PartialOrd`.
636///
637/// This should optimize away at compile-time, but it could be done
638/// more nicely if we had a stable `core::intrinsics::discriminant_value`, for example
639/// the RFC <https://github.com/rust-lang/rfcs/pull/3607> would suffice.
640///
641/// # Correctness
642///
643/// This function is not always correct. For example, in this enum:
644///
645/// ```rust
646/// enum Foo {
647///     Foo = 3,
648///     Bar = 0,
649///     Baz
650/// }
651/// ```
652///
653/// `Foo::Foo`'s discriminant is greater than `Foo::Bar` or `Foo::Baz`,
654/// but our macro assigns `Foo::Foo` the smallest discriminant.
655///
656/// **There's no way to fix this**. Because value of the discriminant is
657/// only available after our macro generates code.
658///
659/// But we need to get value of the discriminant **inside** our proc macro
660/// in order to know what code to emit.
661///
662/// For the purposes of our macros, we don't care about **what** the actual discriminant
663/// value is - we only care that it's equal to, greater than or less than some
664/// other discriminant value of the same enum - which means it's sufficient
665/// to start discriminant at 0 for the first variant, and increment it for
666/// each variant.
667fn map_enum_discriminant_permutations(
668    variants: &Punctuated<Variant, Comma>,
669    f: fn(usize, &Ident, usize, &Ident) -> TokenStream,
670) -> impl Iterator<Item = TokenStream> + '_ {
671    variants
672        .iter()
673        .enumerate()
674        .flat_map(move |(discriminant_left, variant_left)| {
675            variants
676                .iter()
677                .enumerate()
678                .map(move |(discriminant_right, variant_right)| {
679                    f(
680                        discriminant_left,
681                        &variant_left.ident,
682                        discriminant_right,
683                        &variant_right.ident,
684                    )
685                })
686        })
687}
688
689/// We need to construct a pattern from a bunch of pieces, but this pattern
690/// can `match` either 1 enum or 2 enums.
691#[derive(std::fmt::Debug)]
692enum EnumPatternField {
693    /// For implementation of [`Debug`] and [`Hash`]
694    ///
695    /// ```ignore
696    /// match self {
697    ///     Self::One { 0: __0 } => { /* ... */ },
698    ///     // ...
699    /// }
700    /// ```
701    ///
702    /// `One` would hold `Ident(__0)`
703    One(Ident),
704    /// For implementation of [`trait@PartialOrd`], [`trait@Ord`] and [`trait@PartialEq`]
705    ///
706    /// ```ignore
707    /// match (self, other) {
708    ///     (Self::One { 0: __l_0 }, Self::One { 0: __r_0 }) => { /* ... */ },
709    ///     // ...
710    /// }
711    /// ```
712    ///
713    /// `Two` would hold `(Ident(__l_0), Ident(__r_0))`
714    Two(Ident, Ident),
715}
716
717impl ToTokens for EnumPatternField {
718    fn to_tokens(&self, tokens: &mut TokenStream) {
719        let new = match self {
720            EnumPatternField::One(ident) => quote! { #ident },
721            EnumPatternField::Two(left, right) => quote! { (#left, #right) },
722        };
723
724        tokens.extend(new);
725    }
726}
727
728macro_rules! create_derive {
729    { $Derive:ident $($tt:tt)* } => {
730        #[doc = concat!("Derives an `", stringify!($Derive), "` implementation.")]
731        ///
732        /// The only difference from the standard library's derive is that
733        #[doc = concat!("fields marked with `#[ignored(", stringify!($Derive), ")]`")]
734        /// will be ignored when implementing this trait
735        ///
736        /// See the [crate-level](crate) documentation for more info
737        #[proc_macro_derive($Derive, attributes(ignored))]
738        #[allow(non_snake_case)]
739        pub fn $Derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
740            let mut input = parse_macro_input!(input as DeriveInput);
741
742            let mut errors = Vec::new();
743            let output = generate(&mut input, Deriving::$Derive, &mut errors);
744            let errors = errors.into_iter().map(|error| error.into_compile_error());
745
746            quote! {
747                #output
748                #(#errors)*
749            }
750            .into()
751        }
752    };
753}
754use create_derive;