educe/trait_handlers/ord/
ord_struct.rs1use 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}