utility_macros_internals/derive/
field_attributes.rs1use convert_case::{Case, Casing};
2use proc_macro2::TokenTree;
3use syn::{Field, Ident, Meta};
4
5pub struct FieldAttributesContext {
6 pub helper: &'static str,
7 pub rename_all: Option<Case>,
8}
9
10pub struct FieldAttributesData {
11 pub ident: Ident,
12 pub skip: bool,
13}
14
15pub fn field_attributes(
16 FieldAttributesContext { helper, rename_all }: &FieldAttributesContext,
17 field: &Field,
18) -> FieldAttributesData {
19 let mut field_ident = field.ident.clone().expect("Field must have an identifier");
20 let mut skip = false;
21
22 for attr in &field.attrs {
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 if ident == "name" {
43 let Some(TokenTree::Punct(punct)) = tokens.next() else {
44 panic!("Expected punct")
45 };
46
47 if punct.as_char() != '=' {
48 panic!("Expected '='")
49 }
50
51 let Some(TokenTree::Ident(ident)) = tokens.next() else {
52 panic!("Expected ident")
53 };
54
55 field_ident = ident;
56 } else if let Some(case) = rename_all {
57 let string = ident.to_string().to_case(case.clone());
58 field_ident = Ident::new(string.as_str(), ident.span());
59 }
60
61 if ident == "skip" {
62 skip = true;
63 }
64 }
65 }
66
67 FieldAttributesData {
68 ident: field_ident,
69 skip,
70 }
71}