educe/trait_handlers/ord/
ord_struct.rs

1use std::collections::BTreeMap;
2
3use quote::quote;
4use syn::{spanned::Spanned, Data, DeriveInput, Field, Meta, Path, Type};
5
6use super::{
7    models::{FieldAttribute, FieldAttributeBuilder, TypeAttributeBuilder},
8    TraitHandler,
9};
10use crate::{common::ident_index::IdentOrIndex, Trait};
11
12pub(crate) struct OrdStructHandler;
13
14impl TraitHandler for OrdStructHandler {
15    #[inline]
16    fn trait_meta_handler(
17        ast: &DeriveInput,
18        token_stream: &mut proc_macro2::TokenStream,
19        traits: &[Trait],
20        meta: &Meta,
21    ) -> syn::Result<()> {
22        let type_attribute = TypeAttributeBuilder {
23            enable_flag: true, enable_bound: true
24        }
25        .build_from_ord_meta(meta)?;
26
27        let mut ord_types: Vec<&Type> = Vec::new();
28
29        let mut cmp_token_stream = proc_macro2::TokenStream::new();
30
31        if let Data::Struct(data) = &ast.data {
32            let mut fields: BTreeMap<isize, (usize, &Field, FieldAttribute)> = BTreeMap::new();
33
34            for (index, field) in data.fields.iter().enumerate() {
35                let field_attribute = FieldAttributeBuilder {
36                    enable_ignore: true,
37                    enable_method: true,
38                    enable_rank:   true,
39                    rank:          isize::MIN + index as isize,
40                }
41                .build_from_attributes(&field.attrs, traits)?;
42
43                if field_attribute.ignore {
44                    continue;
45                }
46
47                let rank = field_attribute.rank;
48
49                if fields.contains_key(&rank) {
50                    return Err(super::panic::reuse_a_rank(
51                        field_attribute.rank_span.unwrap_or_else(|| field.span()),
52                        rank,
53                    ));
54                }
55
56                fields.insert(rank, (index, field, field_attribute));
57            }
58
59            let built_in_cmp: Path = syn::parse2(quote!(::core::cmp::Ord::cmp)).unwrap();
60
61            for (index, field, field_attribute) in fields.values() {
62                let field_name = IdentOrIndex::from_ident_with_index(field.ident.as_ref(), *index);
63
64                let cmp = field_attribute.method.as_ref().unwrap_or_else(|| {
65                    ord_types.push(&field.ty);
66
67                    &built_in_cmp
68                });
69
70                cmp_token_stream.extend(quote! {
71                    match #cmp(&self.#field_name, &other.#field_name) {
72                        ::core::cmp::Ordering::Equal => (),
73                        ::core::cmp::Ordering::Greater => return ::core::cmp::Ordering::Greater,
74                        ::core::cmp::Ordering::Less => return ::core::cmp::Ordering::Less,
75                    }
76                });
77            }
78        }
79
80        let ident = &ast.ident;
81
82        let bound = type_attribute.bound.into_where_predicates_by_generic_parameters_check_types(
83            &ast.generics.params,
84            &syn::parse2(quote!(::core::cmp::Ord)).unwrap(),
85            &ord_types,
86            &crate::trait_handlers::ord::supertraits(traits),
87        );
88
89        let mut generics = ast.generics.clone();
90        let where_clause = generics.make_where_clause();
91
92        for where_predicate in bound {
93            where_clause.predicates.push(where_predicate);
94        }
95
96        let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
97
98        token_stream.extend(quote! {
99            impl #impl_generics ::core::cmp::Ord for #ident #ty_generics #where_clause {
100                #[inline]
101                fn cmp(&self, other: &Self) -> ::core::cmp::Ordering {
102                    #cmp_token_stream
103
104                    ::core::cmp::Ordering::Equal
105                }
106            }
107        });
108
109        #[cfg(feature = "PartialOrd")]
110        if traits.contains(&Trait::PartialOrd) {
111            token_stream.extend(quote! {
112                impl #impl_generics ::core::cmp::PartialOrd for #ident #ty_generics #where_clause {
113                    #[inline]
114                    fn partial_cmp(&self, other: &Self) -> Option<::core::cmp::Ordering> {
115                        Some(::core::cmp::Ord::cmp(self, other))
116                    }
117                }
118            });
119        }
120
121        Ok(())
122    }
123}