educe/trait_handlers/clone/models/
field_attribute.rs

1use syn::{punctuated::Punctuated, Attribute, Meta, Path, Token};
2
3use crate::{common::path::meta_2_path, panic, supported_traits::Trait};
4
5pub(crate) struct FieldAttribute {
6    pub(crate) method: Option<Path>,
7}
8
9pub(crate) struct FieldAttributeBuilder {
10    pub(crate) enable_method: bool,
11}
12
13impl FieldAttributeBuilder {
14    pub(crate) fn build_from_clone_meta(&self, meta: &Meta) -> syn::Result<FieldAttribute> {
15        debug_assert!(meta.path().is_ident("Clone"));
16
17        let mut method = None;
18
19        let correct_usage_for_clone_attribute = {
20            let mut usage = vec![];
21
22            if self.enable_method {
23                usage.push(stringify!(#[educe(Clone(method(path_to_method)))]));
24            }
25
26            usage
27        };
28
29        match meta {
30            Meta::Path(_) | Meta::NameValue(_) => {
31                return Err(panic::attribute_incorrect_format(
32                    meta.path().get_ident().unwrap(),
33                    &correct_usage_for_clone_attribute,
34                ));
35            },
36            Meta::List(list) => {
37                let result =
38                    list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
39
40                let mut method_is_set = false;
41
42                let mut handler = |meta: Meta| -> syn::Result<bool> {
43                    if let Some(ident) = meta.path().get_ident() {
44                        if ident == "method" {
45                            if !self.enable_method {
46                                return Ok(false);
47                            }
48
49                            let v = meta_2_path(&meta)?;
50
51                            if method_is_set {
52                                return Err(panic::parameter_reset(ident));
53                            }
54
55                            method_is_set = true;
56
57                            method = Some(v);
58
59                            return Ok(true);
60                        }
61                    }
62
63                    Ok(false)
64                };
65
66                for p in result {
67                    if !handler(p)? {
68                        return Err(panic::attribute_incorrect_format(
69                            meta.path().get_ident().unwrap(),
70                            &correct_usage_for_clone_attribute,
71                        ));
72                    }
73                }
74            },
75        }
76
77        Ok(FieldAttribute {
78            method,
79        })
80    }
81
82    pub(crate) fn build_from_attributes(
83        &self,
84        attributes: &[Attribute],
85        traits: &[Trait],
86    ) -> syn::Result<FieldAttribute> {
87        let mut output = None;
88
89        for attribute in attributes.iter() {
90            let path = attribute.path();
91
92            if path.is_ident("educe") {
93                if let Meta::List(list) = &attribute.meta {
94                    let result =
95                        list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
96
97                    for meta in result {
98                        let path = meta.path();
99
100                        let t = match Trait::from_path(path) {
101                            Some(t) => t,
102                            None => return Err(panic::unsupported_trait(meta.path())),
103                        };
104
105                        if !traits.contains(&t) {
106                            return Err(panic::trait_not_used(path.get_ident().unwrap()));
107                        }
108
109                        if t == Trait::Clone {
110                            if output.is_some() {
111                                return Err(panic::reuse_a_trait(path.get_ident().unwrap()));
112                            }
113
114                            output = Some(self.build_from_clone_meta(&meta)?);
115                        }
116                    }
117                }
118            }
119        }
120
121        Ok(output.unwrap_or(FieldAttribute {
122            method: None
123        }))
124    }
125}