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