derive_attr_parser/internals/
parse.rs1use std::collections::HashMap;
2
3use proc_macro2::Span;
4use syn::meta::ParseNestedMeta;
5use syn::punctuated::Punctuated;
6use syn::{token, Attribute, DeriveInput, Error, Token};
7
8use crate::internals::ast::{Container, Data, Field, Style, Symbol, Val, Variant};
9use crate::internals::ctxt::Ctxt;
10
11pub fn from_ast<'a>(
27 cx: &Ctxt,
28 input: &'a DeriveInput,
29 root: Symbol,
30) -> Result<Container<'a>, Error> {
31 container_from_ast(cx, input, root)
32}
33
34fn container_from_ast<'a>(
35 cx: &Ctxt,
36 input: &'a DeriveInput,
37 root: Symbol,
38) -> Result<Container<'a>, Error> {
39 let attrs = parse_attrs(cx, &input.attrs, root)?;
40 let res = data_from_ast(cx, &input, root);
41 if let Some(data) = res {
42 let item = Container {
44 ident: input.ident.clone(),
45 attrs,
46 data,
47 generics: &input.generics,
48 original: &input,
49 };
50 Ok(item)
51 } else {
52 Err(Error::new(
53 Span::call_site(),
54 "Data is none#container_from_ast",
55 ))
56 }
57}
58
59fn parse_sub_attrs(cx: &Ctxt, meta: &ParseNestedMeta) -> syn::Result<HashMap<String, Val>> {
60 let lookahead = meta.input.lookahead1();
61 let mut attrs = HashMap::new();
62 if let Some(ident) = meta.path.get_ident() {
63 let key = ident.to_string();
64 if lookahead.peek(Token![=]) {
66 attrs.insert(key, get_val_str(&meta)?);
67 } else if lookahead.peek(token::Paren) {
68 let mut all_sub_attrs = HashMap::new();
70 if let Err(err) = meta.parse_nested_meta(|m| {
71 merge_map(cx, parse_sub_attrs(cx, &m)?, &mut all_sub_attrs);
72 Ok(())
73 }) {
74 cx.syn_error(err);
75 }
76 attrs.insert(key, Val::Map(all_sub_attrs));
77 } else if lookahead.peek(Token![:]) {
78 attrs.insert(key, get_val_str(&meta)?);
79 } else {
80 attrs.insert(key, Val::Empty);
81 }
82 } else {
83 let msg = format!("no ident found #parse_sub_attrs");
84 let err = Error::new(Span::call_site(), msg);
85 cx.syn_error(err);
86 }
87
88 Ok(attrs)
89}
90
91fn get_val_str(meta: &ParseNestedMeta) -> syn::Result<Val> {
92 if let Err(eq) = meta.input.parse::<Token![=]>() {
93 if let Err(_ec) = meta.input.parse::<Token![:]>() {
94 let ident = meta.path.get_ident();
95 let msg = format!("expect either '=' or ':' after ident {ident:?} #get_val_str");
96 let err = Error::new(eq.span(), msg);
97 return Err(err);
98 }
99 }
100 let expr: syn::Expr = meta.input.parse()?;
101 let mut value = &expr;
102 while let syn::Expr::Group(e) = value {
103 value = &e.expr;
104 }
105 if let syn::Expr::Lit(syn::ExprLit {
106 lit: syn::Lit::Str(lit),
107 ..
108 }) = value
109 {
110 let suffix = lit.suffix();
111 if !suffix.is_empty() {}
112 Ok(Val::Str(lit.clone().value()))
113 } else {
114 Ok(Val::Str("".to_string()))
115 }
116}
117
118fn data_from_ast<'a>(cx: &Ctxt, input: &'a DeriveInput, root: Symbol) -> Option<Data<'a>> {
119 let data = match &input.data {
120 syn::Data::Enum(data) => Data::Enum(enum_from_ast(cx, &data.variants, root)),
121 syn::Data::Struct(data) => {
122 let (style, fields) = struct_from_ast(cx, &data.fields, root);
123 Data::Struct(style, fields)
124 }
125 syn::Data::Union(_) => {
126 let msg = format!("Does not support derive for unions#data_from_ast");
127 cx.error_spanned_by(input, &msg);
128 return None;
129 }
130 };
131
132 Some(data)
133}
134
135fn struct_from_ast<'a>(
136 cx: &Ctxt,
137 fields: &'a syn::Fields,
138 root: Symbol,
139) -> (Style, Vec<Field<'a>>) {
140 match fields {
141 syn::Fields::Named(fields) => (Style::Struct, fields_from_ast(cx, &fields.named, root)),
142 syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
143 (Style::Newtype, fields_from_ast(cx, &fields.unnamed, root))
144 }
145 syn::Fields::Unnamed(fields) => (Style::Tuple, fields_from_ast(cx, &fields.unnamed, root)),
146 syn::Fields::Unit => (Style::Unit, Vec::new()),
147 }
148}
149
150fn fields_from_ast<'a>(
151 cx: &Ctxt,
152 fields: &'a Punctuated<syn::Field, Token![,]>,
153 root: Symbol,
154) -> Vec<Field<'a>> {
155 fields
156 .iter()
157 .enumerate()
158 .map(|(i, field)| Field {
159 member: match &field.ident {
160 Some(ident) => syn::Member::Named(ident.clone()),
161 None => syn::Member::Unnamed(i.into()),
162 },
163 attrs: filed_from_ast(cx, i, field, root),
164 ty: &field.ty,
165 original: field,
166 })
167 .collect()
168}
169
170fn parse_attrs(
171 cx: &Ctxt,
172 attrs: &Vec<Attribute>,
173 root: Symbol,
174) -> syn::Result<HashMap<String, Val>> {
175 let mut all = HashMap::new();
176 for attr in attrs {
177 if attr.path() != root {
178 continue;
179 }
180 if let syn::Meta::List(meta) = &attr.meta {
181 if meta.tokens.is_empty() {
182 continue;
183 }
184 }
185 let mut attrs = HashMap::new();
186 if let Err(err) = attr.parse_nested_meta(|meta| {
187 let sub_attrs = parse_sub_attrs(cx, &meta);
189 merge_map(cx, sub_attrs?, &mut attrs);
190 Ok(())
191 }) {
192 cx.syn_error(err);
193 }
194 merge_map(cx, attrs, &mut all)
195 }
196
197 Ok(all)
199}
200
201fn filed_from_ast(
202 cx: &Ctxt,
203 _index: usize,
204 field: &syn::Field,
205 root: Symbol,
206) -> HashMap<String, Val> {
207 match parse_attrs(cx, &field.attrs, root) {
208 Ok(m) => m,
209 Err(e) => {
210 cx.error_spanned_by(field, e);
211 HashMap::new()
212 }
213 }
214}
215
216fn variant_from_ast(cx: &Ctxt, variant: &syn::Variant, root: Symbol) -> HashMap<String, Val> {
217 match parse_attrs(cx, &variant.attrs, root) {
218 Ok(map) => map,
219 Err(e) => {
220 cx.syn_error(e);
221 HashMap::new()
222 }
223 }
224}
225
226fn enum_from_ast<'a>(
227 cx: &Ctxt,
228 variants: &'a Punctuated<syn::Variant, Token![,]>,
229 root: Symbol,
230) -> Vec<Variant<'a>> {
231 let variants: Vec<Variant> = variants
232 .iter()
233 .map(|variant| {
234 let attrs = variant_from_ast(cx, variant, root);
235 let (style, fields) = struct_from_ast(cx, &variant.fields, root);
236 Variant {
237 ident: variant.ident.clone(),
238 attrs,
239 style,
240 fields,
241 original: variant,
242 }
243 })
244 .collect();
245 variants
246}
247
248fn merge_map(cx: &Ctxt, from: HashMap<String, Val>, to: &mut HashMap<String, Val>) {
249 for (k, v) in from {
250 if to.get(&k).is_some() {
251 let msg = format!("duplicated key {{{k}}}#merge_map");
252 cx.syn_error(Error::new(Span::call_site(), msg));
253 } else {
254 to.insert(k, v);
255 }
256 }
257}