educe/trait_handlers/hash/models/
type_attribute.rs
1use syn::{punctuated::Punctuated, Attribute, Meta, Token};
2
3use crate::{
4 common::{bound::Bound, unsafe_punctuated_meta::UnsafePunctuatedMeta},
5 panic, Trait,
6};
7
8pub(crate) struct TypeAttribute {
9 pub(crate) has_unsafe: bool,
10 pub(crate) bound: Bound,
11}
12
13#[derive(Debug)]
14pub(crate) struct TypeAttributeBuilder {
15 pub(crate) enable_flag: bool,
16 pub(crate) enable_unsafe: bool,
17 pub(crate) enable_bound: bool,
18}
19
20impl TypeAttributeBuilder {
21 pub(crate) fn build_from_hash_meta(&self, meta: &Meta) -> syn::Result<TypeAttribute> {
22 debug_assert!(meta.path().is_ident("Hash"));
23
24 let mut has_unsafe = false;
25 let mut bound = Bound::Auto;
26
27 let correct_usage_for_hash_attribute = {
28 let mut usage = vec![];
29
30 if self.enable_flag {
31 usage.push(stringify!(#[educe(Hash)]));
32 }
33
34 if self.enable_bound {
35 usage.push(stringify!(#[educe(Hash(bound(where_predicates)))]));
36 usage.push(stringify!(#[educe(Hash(bound = false))]));
37 }
38
39 usage
40 };
41
42 match meta {
43 Meta::Path(_) => {
44 if !self.enable_flag {
45 return Err(panic::attribute_incorrect_format(
46 meta.path().get_ident().unwrap(),
47 &correct_usage_for_hash_attribute,
48 ));
49 }
50 },
51 Meta::NameValue(_) => {
52 return Err(panic::attribute_incorrect_format(
53 meta.path().get_ident().unwrap(),
54 &correct_usage_for_hash_attribute,
55 ));
56 },
57 Meta::List(list) => {
58 let result = if self.enable_unsafe {
59 let result: UnsafePunctuatedMeta = list.parse_args()?;
60
61 has_unsafe = result.has_unsafe;
62
63 result.list
64 } else {
65 list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?
66 };
67
68 let mut bound_is_set = false;
69
70 let mut handler = |meta: Meta| -> syn::Result<bool> {
71 if let Some(ident) = meta.path().get_ident() {
72 if ident == "bound" {
73 if !self.enable_bound {
74 return Ok(false);
75 }
76
77 let v = Bound::from_meta(&meta)?;
78
79 if bound_is_set {
80 return Err(panic::parameter_reset(ident));
81 }
82
83 bound_is_set = true;
84
85 bound = v;
86
87 return Ok(true);
88 }
89 }
90
91 Ok(false)
92 };
93
94 for p in result {
95 if !handler(p)? {
96 return Err(panic::attribute_incorrect_format(
97 meta.path().get_ident().unwrap(),
98 &correct_usage_for_hash_attribute,
99 ));
100 }
101 }
102 },
103 }
104
105 Ok(TypeAttribute {
106 has_unsafe,
107 bound,
108 })
109 }
110
111 pub(crate) fn build_from_attributes(
112 &self,
113 attributes: &[Attribute],
114 traits: &[Trait],
115 ) -> syn::Result<TypeAttribute> {
116 let mut output = None;
117
118 for attribute in attributes.iter() {
119 let path = attribute.path();
120
121 if path.is_ident("educe") {
122 if let Meta::List(list) = &attribute.meta {
123 let result =
124 list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
125
126 for meta in result {
127 let path = meta.path();
128
129 let t = match Trait::from_path(path) {
130 Some(t) => t,
131 None => return Err(panic::unsupported_trait(meta.path())),
132 };
133
134 if !traits.contains(&t) {
135 return Err(panic::trait_not_used(path.get_ident().unwrap()));
136 }
137
138 if t == Trait::Hash {
139 if output.is_some() {
140 return Err(panic::reuse_a_trait(path.get_ident().unwrap()));
141 }
142
143 output = Some(self.build_from_hash_meta(&meta)?);
144 }
145 }
146 }
147 }
148 }
149
150 Ok(output.unwrap_or(TypeAttribute {
151 has_unsafe: false, bound: Bound::Auto
152 }))
153 }
154}