educe/trait_handlers/partial_ord/
partial_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 PartialOrdStructHandler;
13
14impl TraitHandler for PartialOrdStructHandler {
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_partial_ord_meta(meta)?;
26
27 let mut partial_ord_types: Vec<&Type> = Vec::new();
28
29 let mut partial_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_partial_cmp: Path =
60 syn::parse2(quote!(::core::cmp::PartialOrd::partial_cmp)).unwrap();
61
62 for (index, field, field_attribute) in fields.values() {
63 let field_name = IdentOrIndex::from_ident_with_index(field.ident.as_ref(), *index);
64
65 let partial_cmp = field_attribute.method.as_ref().unwrap_or_else(|| {
66 partial_ord_types.push(&field.ty);
67
68 &built_in_partial_cmp
69 });
70
71 partial_cmp_token_stream.extend(quote! {
72 match #partial_cmp(&self.#field_name, &other.#field_name) {
73 Some(::core::cmp::Ordering::Equal) => (),
74 Some(::core::cmp::Ordering::Greater) => return Some(::core::cmp::Ordering::Greater),
75 Some(::core::cmp::Ordering::Less) => return Some(::core::cmp::Ordering::Less),
76 None => return None,
77 }
78 });
79 }
80 }
81
82 let ident = &ast.ident;
83
84 let bound = type_attribute.bound.into_where_predicates_by_generic_parameters_check_types(
85 &ast.generics.params,
86 &syn::parse2(quote!(::core::cmp::PartialOrd)).unwrap(),
87 &partial_ord_types,
88 &[quote! {::core::cmp::PartialEq}],
89 );
90
91 let mut generics = ast.generics.clone();
92 let where_clause = generics.make_where_clause();
93
94 for where_predicate in bound {
95 where_clause.predicates.push(where_predicate);
96 }
97
98 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
99
100 token_stream.extend(quote! {
101 impl #impl_generics ::core::cmp::PartialOrd for #ident #ty_generics #where_clause {
102 #[inline]
103 fn partial_cmp(&self, other: &Self) -> Option<::core::cmp::Ordering> {
104 #partial_cmp_token_stream
105
106 Some(::core::cmp::Ordering::Equal)
107 }
108 }
109 });
110
111 Ok(())
112 }
113}