1use std::{
2 collections::{HashMap, HashSet},
3 convert::TryFrom,
4};
5
6use crate::{EndpointAttribute, Error};
7use syn::{
8 spanned::Spanned, Attribute, Field, Ident, LitStr, Meta, MetaNameValue, NestedMeta, Type,
9};
10
11pub(crate) fn attr_list(attr: &Meta) -> Result<Vec<Meta>, Error> {
20 let mut result = Vec::<Meta>::new();
21 if let Meta::List(list) = &attr {
22 if list.nested.is_empty() {
23 return Err(Error::new(attr.span(), "Attribute cannot be empty"));
24 }
25
26 for nested in list.nested.iter() {
27 if let NestedMeta::Meta(nested_meta) = nested {
28 result.push(nested_meta.clone())
29 } else {
30 return Err(Error::new(
31 nested.span(),
32 "Attribute cannot contain any literals",
33 ));
34 }
35 }
36
37 Ok(result)
38 } else {
39 Err(Error::new(attr.span(), "Cannot parse attribute as list"))
40 }
41}
42
43pub(crate) fn attr_kv(attr: &Meta) -> Result<Vec<MetaNameValue>, Error> {
53 let meta_list = attr_list(attr)?;
54 let mut result = Vec::<MetaNameValue>::new();
55 for meta in meta_list.iter() {
56 if let syn::Meta::NameValue(nv_meta) = meta {
57 result.push(nv_meta.clone());
58 } else {
59 return Err(Error::new(
60 attr.span(),
61 "Cannot parse attribute as a key/value list",
62 ));
63 }
64 }
65 Ok(result)
66}
67
68pub(crate) fn to_map(values: &[MetaNameValue]) -> Result<HashMap<Ident, LitStr>, Error> {
78 let mut map = HashMap::<Ident, LitStr>::new();
79 for value in values.iter() {
80 let id = value.path.get_ident().unwrap().clone();
81 if let syn::Lit::Str(lit) = &value.lit {
82 map.insert(id, lit.clone());
83 } else {
84 return Err(Error::new(
85 value.span(),
86 "Values must be in string literal form",
87 ));
88 }
89 }
90
91 Ok(map)
92}
93
94pub(crate) fn attributes(attrs: &[Attribute], name: &str) -> Result<Vec<Meta>, Error> {
96 let mut result = Vec::<Meta>::new();
97 for attr in attrs.iter() {
98 let meta = attr.parse_meta().map_err(Error::from)?;
99
100 if meta.path().is_ident(name) {
101 result.push(meta);
102 }
103 }
104
105 Ok(result)
106}
107
108pub(crate) fn field_attributes(
114 data: &syn::Data,
115) -> Result<HashMap<EndpointAttribute, Vec<Field>>, Error> {
116 let mut result = HashMap::<EndpointAttribute, Vec<Field>>::new();
117 if let syn::Data::Struct(data) = data {
118 for field in data.fields.iter() {
119 let attrs = attributes(&field.attrs, crate::ATTR_NAME)?;
121
122 if attrs.is_empty() {
124 match result.get_mut(&EndpointAttribute::Untagged) {
125 Some(r) => {
126 r.push(field.clone());
127 }
128 None => {
129 result.insert(EndpointAttribute::Untagged, vec![field.clone()]);
130 }
131 }
132 }
133
134 let attrs = attrs
136 .iter()
137 .map(attr_list)
138 .collect::<Result<Vec<Vec<Meta>>, Error>>()?;
139
140 let attrs = attrs.into_iter().flatten().collect::<HashSet<Meta>>();
142
143 for attr in attrs.iter() {
145 let attr_ty = EndpointAttribute::try_from(attr)?;
146 match result.get_mut(&attr_ty) {
147 Some(r) => {
148 r.push(field.clone());
149 }
150 None => {
151 result.insert(attr_ty, vec![field.clone()]);
152 }
153 }
154 }
155 }
156 }
157
158 Ok(result)
159}
160
161pub(crate) fn fields_to_struct(fields: &[Field], attrs: &[Meta]) -> proc_macro2::TokenStream {
175 let def = fields
177 .iter()
178 .map(|f| {
179 let id = f.ident.clone().unwrap();
180 let ty = &f.ty;
181
182 let mut attrs = Vec::<&Attribute>::new();
184 if !f.attrs.is_empty() {
185 for attr in &f.attrs {
186 if attr.path.is_ident("serde") {
187 attrs.push(attr);
188 }
189 }
190 }
191
192 if is_std_option(ty) {
194 quote! {
195 #(#attrs)*
196 #[serde(skip_serializing_if = "Option::is_none")]
197 #id: &'a #ty,
198 }
199 } else {
200 quote! {
201 #(#attrs)*
202 #id: &'a #ty,
203 }
204 }
205 })
206 .collect::<Vec<proc_macro2::TokenStream>>();
207 let attrs = attrs
208 .iter()
209 .map(|m| quote! { #[#m]})
210 .collect::<Vec<proc_macro2::TokenStream>>();
211
212 let inst = fields
214 .iter()
215 .map(|f| {
216 let id = f.ident.clone().unwrap();
217 quote! { #id: &self.#id, }
218 })
219 .collect::<Vec<proc_macro2::TokenStream>>();
220
221 quote! {
222 #[derive(Serialize)]
223 #(#attrs)*
224 struct __Temp<'a> {
225 #(#def)*
226 }
227
228 let __temp = __Temp {
229 #(#inst)*
230 };
231 }
232}
233
234pub(crate) fn is_std_option(ty: &Type) -> bool {
236 if let Type::Path(tp) = ty {
237 let path = &tp.path;
238 (path.leading_colon.is_none()
239 && path.segments.len() == 1
240 && path.segments[0].ident == "Option")
241 || (path.segments.len() == 3
242 && (path.segments[0].ident == "std" || path.segments[0].ident == "core")
243 && path.segments[1].ident == "option"
244 && path.segments[2].ident == "Option")
245 } else {
246 false
247 }
248}