utility_macros_internals/derive/
container_attributes.rs

1use convert_case::Case;
2use proc_macro2::TokenTree;
3use syn::{Attribute, Ident, Meta};
4
5use crate::case::parse_case;
6
7pub struct ContainerAttributesData {
8    pub ident: Ident,
9    pub derives: Vec<Ident>,
10    pub rename_all: Option<Case>,
11}
12
13pub fn container_attributes(
14    helper: &'static str,
15    attributes: Vec<Attribute>,
16    or_ident: Ident,
17) -> ContainerAttributesData {
18    let mut field_ident = or_ident;
19    let mut derives = Vec::new();
20    let mut rename_all = None;
21
22    for attr in attributes {
23        let Meta::List(meta) = &attr.meta else {
24            continue;
25        };
26
27        if !meta
28            .path
29            .segments
30            .first()
31            .map_or(false, |seg| seg.ident == helper)
32        {
33            continue;
34        }
35
36        let mut tokens = meta.clone().tokens.into_iter().peekable();
37        while let Some(token) = tokens.next() {
38            let TokenTree::Ident(ident) = token else {
39                continue;
40            };
41
42            match ident.to_string().as_str() {
43                "name" => {
44                    let Some(TokenTree::Punct(punct)) = tokens.next() else {
45                        panic!("Expected punct")
46                    };
47
48                    if punct.as_char() != '=' {
49                        panic!("Expected '='")
50                    }
51
52                    let Some(TokenTree::Ident(ident)) = tokens.next() else {
53                        panic!("Expected ident")
54                    };
55
56                    field_ident = ident;
57                }
58                "derive" => {
59                    // ignore equals sign
60                    if let Some(TokenTree::Punct(punct)) = tokens.peek() {
61                        if punct.as_char() != '=' {
62                            panic!("Expected '=' ident or group of identifiers")
63                        }
64
65                        tokens.next();
66                    }
67
68                    let Some(TokenTree::Group(group)) = tokens.next() else {
69                        panic!("Expected group")
70                    };
71
72                    let mut tokens = group.stream().into_iter().peekable();
73                    while let Some(token) = tokens.next() {
74                        let TokenTree::Ident(ident) = token else {
75                            panic!("Expected ident")
76                        };
77
78                        derives.push(ident);
79
80                        if let Some(TokenTree::Punct(_)) = tokens.peek() {
81                            tokens.next();
82                        }
83                    }
84                }
85                "rename_all" => {
86                    let Some(TokenTree::Punct(punct)) = tokens.next() else {
87                        panic!("Expected '='")
88                    };
89
90                    if punct.as_char() != '=' {
91                        panic!("Expected '='")
92                    }
93
94                    let Some(TokenTree::Literal(literal)) = tokens.next() else {
95                        panic!("Expected literal")
96                    };
97
98                    rename_all = Some(
99                        parse_case(literal.to_string().as_str())
100                            .unwrap_or_else(|_| panic!("Invalid case")),
101                    );
102                }
103                _ => {}
104            }
105        }
106    }
107
108    ContainerAttributesData {
109        ident: field_ident,
110        derives,
111        rename_all,
112    }
113}