1use proc_macro::TokenStream;
44use quote::quote;
45use syn::Data;
46use syn::DeriveInput;
47use syn::Fields;
48
49#[proc_macro_derive(Pack, attributes(pack))]
50pub fn derive_pack(input: TokenStream) -> TokenStream {
51 let input = syn::parse_macro_input!(input as DeriveInput);
52 match derive_pack_impl(&input) {
53 Ok(ts) => ts.into(),
54 Err(e) => e.to_compile_error().into(),
55 }
56}
57
58#[proc_macro_derive(Unpack, attributes(pack))]
59pub fn derive_unpack(input: TokenStream) -> TokenStream {
60 let input = syn::parse_macro_input!(input as DeriveInput);
61 match derive_unpack_impl(&input) {
62 Ok(ts) => ts.into(),
63 Err(e) => e.to_compile_error().into(),
64 }
65}
66
67fn has_bytes_attr(attrs: &[syn::Attribute]) -> bool {
68 attrs.iter().any(|a| {
69 if !a.path().is_ident("pack") { return false; }
70 let mut found = false;
71 let _ = a.parse_nested_meta(|meta| {
72 if meta.path.is_ident("bytes") { found = true; }
73 Ok(())
74 });
75 found
76 })
77}
78
79fn derive_pack_impl(input: &DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
82 let name = &input.ident;
83 let (impl_g, ty_g, where_g) = input.generics.split_for_impl();
84
85 let body = match &input.data {
86 Data::Struct(s) => pack_struct(&s.fields)?,
87 Data::Enum(e) => pack_enum(e)?,
88 Data::Union(_) => return Err(syn::Error::new_spanned(name, "unions are not supported")),
89 };
90
91 Ok(quote! {
92 impl #impl_g neopack::Pack for #name #ty_g #where_g {
93 fn pack(&self, enc: &mut neopack::Encoder) -> neopack::Result<()> {
94 #body
95 }
96 }
97 })
98}
99
100fn pack_struct(fields: &Fields) -> syn::Result<proc_macro2::TokenStream> {
101 match fields {
102 Fields::Named(f) => {
103 let stmts = f.named.iter().map(|field| {
104 let ident = field.ident.as_ref().unwrap();
105 pack_field_expr(quote!(self.#ident), &field.attrs)
106 }).collect::<Vec<_>>();
107 Ok(quote! {
108 enc.list_begin()?;
109 #(#stmts)*
110 enc.list_end()
111 })
112 }
113 Fields::Unnamed(f) if f.unnamed.len() == 1 => {
114 let stmt = pack_field_expr(quote!(self.0), &f.unnamed[0].attrs);
115 Ok(quote! { #stmt Ok(()) })
116 }
117 Fields::Unnamed(f) => {
118 let stmts = f.unnamed.iter().enumerate().map(|(i, field)| {
119 let idx = syn::Index::from(i);
120 pack_field_expr(quote!(self.#idx), &field.attrs)
121 }).collect::<Vec<_>>();
122 Ok(quote! {
123 enc.list_begin()?;
124 #(#stmts)*
125 enc.list_end()
126 })
127 }
128 Fields::Unit => {
129 Ok(quote! { enc.unit() })
130 }
131 }
132}
133
134fn pack_enum(e: &syn::DataEnum) -> syn::Result<proc_macro2::TokenStream> {
135 let arms = e.variants.iter().map(|v| {
136 let vname = &v.ident;
137 let vstr = vname.to_string();
138 match &v.fields {
139 Fields::Unit => {
140 quote! {
141 Self::#vname => {
142 enc.variant_begin(#vstr)?;
143 enc.unit()?;
144 enc.variant_end()
145 }
146 }
147 }
148 Fields::Unnamed(f) if f.unnamed.len() == 1 => {
149 let stmt = pack_field_expr(quote!(v0), &f.unnamed[0].attrs);
150 quote! {
151 Self::#vname(v0) => {
152 enc.variant_begin(#vstr)?;
153 #stmt
154 enc.variant_end()
155 }
156 }
157 }
158 Fields::Unnamed(f) => {
159 let bindings: Vec<_> = (0..f.unnamed.len())
160 .map(|i| syn::Ident::new(&format!("v{i}"), proc_macro2::Span::call_site()))
161 .collect();
162 let stmts: Vec<_> = f.unnamed.iter().enumerate().map(|(i, field)| {
163 let b = &bindings[i];
164 pack_field_expr(quote!(#b), &field.attrs)
165 }).collect();
166 quote! {
167 Self::#vname(#(#bindings),*) => {
168 enc.variant_begin(#vstr)?;
169 enc.list_begin()?;
170 #(#stmts)*
171 enc.list_end()?;
172 enc.variant_end()
173 }
174 }
175 }
176 Fields::Named(f) => {
177 let field_idents: Vec<_> = f.named.iter()
178 .map(|field| field.ident.as_ref().unwrap())
179 .collect();
180 let stmts: Vec<_> = f.named.iter().map(|field| {
181 let ident = field.ident.as_ref().unwrap();
182 pack_field_expr(quote!(#ident), &field.attrs)
183 }).collect();
184 quote! {
185 Self::#vname { #(#field_idents),* } => {
186 enc.variant_begin(#vstr)?;
187 enc.list_begin()?;
188 #(#stmts)*
189 enc.list_end()?;
190 enc.variant_end()
191 }
192 }
193 }
194 }
195 }).collect::<Vec<_>>();
196
197 Ok(quote! {
198 match self {
199 #(#arms)*
200 }
201 })
202}
203
204fn pack_field_expr(expr: proc_macro2::TokenStream, attrs: &[syn::Attribute]) -> proc_macro2::TokenStream {
205 if has_bytes_attr(attrs) {
206 quote! { enc.bytes(&#expr)?; }
207 } else {
208 quote! { neopack::Pack::pack(&#expr, enc)?; }
209 }
210}
211
212fn derive_unpack_impl(input: &DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
215 let name = &input.ident;
216 let (impl_g, ty_g, where_g) = input.generics.split_for_impl();
217
218 let body = match &input.data {
219 Data::Struct(s) => unpack_struct(name, &s.fields)?,
220 Data::Enum(e) => unpack_enum(name, e)?,
221 Data::Union(_) => return Err(syn::Error::new_spanned(name, "unions are not supported")),
222 };
223
224 Ok(quote! {
225 impl #impl_g neopack::Unpack for #name #ty_g #where_g {
226 fn unpack(dec: &mut neopack::Decoder<'_>) -> neopack::Result<Self> {
227 #body
228 }
229 }
230 })
231}
232
233fn unpack_struct(name: &syn::Ident, fields: &Fields) -> syn::Result<proc_macro2::TokenStream> {
234 match fields {
235 Fields::Named(f) => {
236 let field_decodes: Vec<_> = f.named.iter().map(|field| {
237 let ident = field.ident.as_ref().unwrap();
238 let ty = &field.ty;
239 let decode = unpack_field_expr(ty, &field.attrs);
240 quote! { let #ident = { let mut d = list.next().ok_or(neopack::Error::UnexpectedEnd)?; #decode }; }
241 }).collect();
242 let field_names: Vec<_> = f.named.iter()
243 .map(|field| field.ident.as_ref().unwrap())
244 .collect();
245 Ok(quote! {
246 let mut list = dec.list()?;
247 #(#field_decodes)*
248 Ok(#name { #(#field_names),* })
249 })
250 }
251 Fields::Unnamed(f) if f.unnamed.len() == 1 => {
252 let ty = &f.unnamed[0].ty;
253 let decode = unpack_field_expr(ty, &f.unnamed[0].attrs);
254 Ok(quote! {
255 let v0 = { let mut d = &mut *dec; #decode };
256 Ok(#name(v0))
257 })
258 }
259 Fields::Unnamed(f) => {
260 let field_decodes: Vec<_> = f.unnamed.iter().enumerate().map(|(i, field)| {
261 let var = syn::Ident::new(&format!("v{i}"), proc_macro2::Span::call_site());
262 let ty = &field.ty;
263 let decode = unpack_field_expr(ty, &field.attrs);
264 quote! { let #var = { let mut d = list.next().ok_or(neopack::Error::UnexpectedEnd)?; #decode }; }
265 }).collect();
266 let vars: Vec<_> = (0..f.unnamed.len())
267 .map(|i| syn::Ident::new(&format!("v{i}"), proc_macro2::Span::call_site()))
268 .collect();
269 Ok(quote! {
270 let mut list = dec.list()?;
271 #(#field_decodes)*
272 Ok(#name(#(#vars),*))
273 })
274 }
275 Fields::Unit => {
276 Ok(quote! { dec.unit()?; Ok(#name) })
277 }
278 }
279}
280
281fn unpack_enum(name: &syn::Ident, e: &syn::DataEnum) -> syn::Result<proc_macro2::TokenStream> {
282 let arms = e.variants.iter().map(|v| {
283 let vname = &v.ident;
284 let vstr = vname.to_string();
285 match &v.fields {
286 Fields::Unit => {
287 quote! { #vstr => { inner.unit()?; Ok(#name::#vname) } }
288 }
289 Fields::Unnamed(f) if f.unnamed.len() == 1 => {
290 let ty = &f.unnamed[0].ty;
291 let decode = unpack_field_expr(ty, &f.unnamed[0].attrs);
292 quote! { #vstr => { let mut d = &mut inner; let v0 = #decode; Ok(#name::#vname(v0)) } }
293 }
294 Fields::Unnamed(f) => {
295 let field_decodes: Vec<_> = f.unnamed.iter().enumerate().map(|(i, field)| {
296 let var = syn::Ident::new(&format!("v{i}"), proc_macro2::Span::call_site());
297 let ty = &field.ty;
298 let decode = unpack_field_expr(ty, &field.attrs);
299 quote! { let #var = { let mut d = list.next().ok_or(neopack::Error::UnexpectedEnd)?; #decode }; }
300 }).collect();
301 let vars: Vec<_> = (0..f.unnamed.len())
302 .map(|i| syn::Ident::new(&format!("v{i}"), proc_macro2::Span::call_site()))
303 .collect();
304 quote! {
305 #vstr => {
306 let mut list = inner.list()?;
307 #(#field_decodes)*
308 Ok(#name::#vname(#(#vars),*))
309 }
310 }
311 }
312 Fields::Named(f) => {
313 let field_decodes: Vec<_> = f.named.iter().map(|field| {
314 let ident = field.ident.as_ref().unwrap();
315 let ty = &field.ty;
316 let decode = unpack_field_expr(ty, &field.attrs);
317 quote! { let #ident = { let mut d = list.next().ok_or(neopack::Error::UnexpectedEnd)?; #decode }; }
318 }).collect();
319 let field_names: Vec<_> = f.named.iter()
320 .map(|field| field.ident.as_ref().unwrap())
321 .collect();
322 quote! {
323 #vstr => {
324 let mut list = inner.list()?;
325 #(#field_decodes)*
326 Ok(#name::#vname { #(#field_names),* })
327 }
328 }
329 }
330 }
331 }).collect::<Vec<_>>();
332
333 Ok(quote! {
334 let (variant_name, mut inner) = dec.variant()?;
335 match variant_name {
336 #(#arms)*
337 _ => Err(neopack::Error::InvalidTag(0)),
338 }
339 })
340}
341
342fn unpack_field_expr(ty: &syn::Type, attrs: &[syn::Attribute]) -> proc_macro2::TokenStream {
343 if has_bytes_attr(attrs) {
344 quote! { d.bytes()?.to_vec() }
345 } else {
346 quote! { <#ty as neopack::Unpack>::unpack(&mut d)? }
347 }
348}