utility_macros_internals/derive/
container_attributes.rs1use 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 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}