educe/trait_handlers/clone/models/
field_attribute.rs

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