educe/trait_handlers/clone/models/
type_attribute.rs

1use quote::{quote, ToTokens};
2use syn::{
3    punctuated::Punctuated, token::Comma, Attribute, GenericParam, Lit, Meta, NestedMeta,
4    WherePredicate,
5};
6
7use super::super::super::{
8    create_where_predicates_from_generic_parameters, create_where_predicates_from_lit_str,
9};
10use crate::{panic, Trait};
11
12#[derive(Clone)]
13pub enum TypeAttributeBound {
14    None,
15    Auto,
16    Custom(Punctuated<WherePredicate, Comma>),
17}
18
19impl TypeAttributeBound {
20    pub fn into_punctuated_where_predicates_by_generic_parameters(
21        self,
22        params: &Punctuated<GenericParam, Comma>,
23    ) -> Punctuated<WherePredicate, Comma> {
24        match self {
25            TypeAttributeBound::None => Punctuated::new(),
26            TypeAttributeBound::Auto => create_where_predicates_from_generic_parameters(
27                params,
28                &syn::parse2(quote!(core::clone::Clone)).unwrap(),
29            ),
30            TypeAttributeBound::Custom(where_predicates) => where_predicates,
31        }
32    }
33
34    pub fn into_punctuated_where_predicates_by_generic_parameters_with_copy(
35        self,
36        params: &Punctuated<GenericParam, Comma>,
37    ) -> Punctuated<WherePredicate, Comma> {
38        match self {
39            TypeAttributeBound::None => Punctuated::new(),
40            TypeAttributeBound::Auto => create_where_predicates_from_generic_parameters(
41                params,
42                &syn::parse2(quote!(core::marker::Copy)).unwrap(),
43            ),
44            TypeAttributeBound::Custom(where_predicates) => where_predicates,
45        }
46    }
47}
48
49#[derive(Clone)]
50pub struct TypeAttribute {
51    pub flag:  bool,
52    pub bound: TypeAttributeBound,
53}
54
55#[derive(Debug, Clone)]
56pub struct TypeAttributeBuilder {
57    pub enable_flag:  bool,
58    pub enable_bound: bool,
59}
60
61impl TypeAttributeBuilder {
62    #[allow(clippy::wrong_self_convention)]
63    pub fn from_clone_meta(&self, meta: &Meta) -> TypeAttribute {
64        let mut flag = false;
65        let mut bound = TypeAttributeBound::None;
66
67        let correct_usage_for_clone_attribute = {
68            let mut usage = vec![];
69
70            if self.enable_flag {
71                usage.push(stringify!(#[educe(Clone)]));
72            }
73
74            usage
75        };
76
77        let correct_usage_for_bound = {
78            let usage = vec![
79                stringify!(#[educe(Clone(bound))]),
80                stringify!(#[educe(Clone(bound = "where_predicates"))]),
81                stringify!(#[educe(Clone(bound("where_predicates")))]),
82            ];
83
84            usage
85        };
86
87        match meta {
88            Meta::List(list) => {
89                let mut bound_is_set = false;
90
91                for p in list.nested.iter() {
92                    match p {
93                        NestedMeta::Meta(meta) => {
94                            let meta_name = meta.path().into_token_stream().to_string();
95
96                            match meta_name.as_str() {
97                                "bound" => {
98                                    if !self.enable_bound {
99                                        panic::unknown_parameter("Clone", meta_name.as_str());
100                                    }
101
102                                    match meta {
103                                        Meta::List(list) => {
104                                            for p in list.nested.iter() {
105                                                match p {
106                                                    NestedMeta::Lit(Lit::Str(s)) => {
107                                                        if bound_is_set {
108                                                            panic::reset_parameter(
109                                                                meta_name.as_str(),
110                                                            );
111                                                        }
112
113                                                        bound_is_set = true;
114
115                                                        let where_predicates =
116                                                            create_where_predicates_from_lit_str(s);
117
118                                                        bound = match where_predicates {
119                                                            Some(where_predicates) => {
120                                                                TypeAttributeBound::Custom(
121                                                                    where_predicates,
122                                                                )
123                                                            },
124                                                            None => panic::empty_parameter(
125                                                                meta_name.as_str(),
126                                                            ),
127                                                        };
128                                                    },
129                                                    _ => panic::parameter_incorrect_format(
130                                                        meta_name.as_str(),
131                                                        &correct_usage_for_bound,
132                                                    ),
133                                                }
134                                            }
135                                        },
136                                        Meta::NameValue(named_value) => {
137                                            let lit = &named_value.lit;
138
139                                            match lit {
140                                                Lit::Str(s) => {
141                                                    if bound_is_set {
142                                                        panic::reset_parameter(meta_name.as_str());
143                                                    }
144
145                                                    bound_is_set = true;
146
147                                                    let where_predicates =
148                                                        create_where_predicates_from_lit_str(s);
149
150                                                    bound = match where_predicates {
151                                                        Some(where_predicates) => {
152                                                            TypeAttributeBound::Custom(
153                                                                where_predicates,
154                                                            )
155                                                        },
156                                                        None => panic::empty_parameter(
157                                                            meta_name.as_str(),
158                                                        ),
159                                                    };
160                                                },
161                                                _ => panic::parameter_incorrect_format(
162                                                    meta_name.as_str(),
163                                                    &correct_usage_for_bound,
164                                                ),
165                                            }
166                                        },
167                                        Meta::Path(_) => {
168                                            if bound_is_set {
169                                                panic::reset_parameter(meta_name.as_str());
170                                            }
171
172                                            bound_is_set = true;
173
174                                            bound = TypeAttributeBound::Auto;
175                                        },
176                                    }
177                                },
178                                _ => panic::unknown_parameter("Clone", meta_name.as_str()),
179                            }
180                        },
181                        _ => panic::attribute_incorrect_format(
182                            "Clone",
183                            &correct_usage_for_clone_attribute,
184                        ),
185                    }
186                }
187            },
188            Meta::NameValue(_) => {
189                panic::attribute_incorrect_format("Clone", &correct_usage_for_clone_attribute)
190            },
191            Meta::Path(_) => {
192                if !self.enable_flag {
193                    panic::attribute_incorrect_format("Clone", &correct_usage_for_clone_attribute);
194                }
195
196                flag = true;
197            },
198        }
199
200        TypeAttribute {
201            flag,
202            bound,
203        }
204    }
205
206    #[allow(clippy::wrong_self_convention)]
207    pub fn from_attributes(self, attributes: &[Attribute], traits: &[Trait]) -> TypeAttribute {
208        let mut result = None;
209
210        for attribute in attributes.iter() {
211            if attribute.path.is_ident("educe") {
212                let meta = attribute.parse_meta().unwrap();
213
214                match meta {
215                    Meta::List(list) => {
216                        for p in list.nested.iter() {
217                            match p {
218                                NestedMeta::Meta(meta) => {
219                                    let meta_name = meta.path().into_token_stream().to_string();
220
221                                    let t = Trait::from_str(meta_name);
222
223                                    if traits.binary_search(&t).is_err() {
224                                        panic::trait_not_used(t);
225                                    }
226
227                                    if t == Trait::Clone {
228                                        if result.is_some() {
229                                            panic::reuse_a_trait(t);
230                                        }
231
232                                        result = Some(self.from_clone_meta(meta));
233                                    }
234                                },
235                                _ => panic::educe_format_incorrect(),
236                            }
237                        }
238                    },
239                    _ => panic::educe_format_incorrect(),
240                }
241            }
242        }
243
244        result.unwrap_or(TypeAttribute {
245            flag: false, bound: TypeAttributeBound::None
246        })
247    }
248}