Skip to main content

msgpacker_derive/
lib.rs

1#![crate_type = "proc-macro"]
2extern crate proc_macro;
3
4use proc_macro::TokenStream;
5use proc_macro2::TokenStream as TokenStream2;
6use quote::{format_ident, quote};
7use syn::{
8    parse_macro_input, Data, DataEnum, DataStruct, DeriveInput, Field, Fields, FieldsNamed,
9    FieldsUnnamed, GenericArgument, Generics, Ident, Meta, PathArguments, Type,
10};
11
12fn has_attr(field: &Field, name: &str) -> bool {
13    field.attrs.iter().any(|attr| {
14        let Meta::List(list) = &attr.meta else {
15            return false;
16        };
17        list.path.is_ident("msgpacker")
18            && list
19                .tokens
20                .clone()
21                .into_iter()
22                .any(|t| t.to_string() == name)
23    })
24}
25
26fn is_slice_of_u8(ty: &Type) -> bool {
27    let Type::Reference(r) = ty else { return false };
28    let Type::Slice(s) = r.elem.as_ref() else {
29        return false;
30    };
31    let Type::Path(p) = s.elem.as_ref() else {
32        return false;
33    };
34    p.path.segments.last().is_some_and(|s| s.ident == "u8")
35}
36
37fn is_vec_of_u8(ty: &Type) -> bool {
38    let Type::Path(p) = ty else { return false };
39    let Some(last) = p.path.segments.last() else {
40        return false;
41    };
42    if last.ident != "Vec" {
43        return false;
44    }
45    let PathArguments::AngleBracketed(args) = &last.arguments else {
46        return false;
47    };
48    if args.args.len() != 1 {
49        return false;
50    }
51    let Some(GenericArgument::Type(Type::Path(inner))) = args.args.first() else {
52        return false;
53    };
54    inner.path.segments.last().is_some_and(|s| s.ident == "u8")
55}
56
57fn is_option_type(ty: &Type) -> bool {
58    let Type::Path(p) = ty else { return false };
59    let Some(last) = p.path.segments.last() else {
60        return false;
61    };
62    last.ident == "Option"
63        && matches!(&last.arguments, PathArguments::AngleBracketed(args) if args.args.len() == 1)
64}
65
66fn is_vec_of_non_u8(ty: &Type) -> bool {
67    if is_vec_of_u8(ty) {
68        return false;
69    }
70    let Type::Path(p) = ty else { return false };
71    let Some(last) = p.path.segments.last() else {
72        return false;
73    };
74    if last.ident != "Vec" {
75        return false;
76    }
77    let PathArguments::AngleBracketed(args) = &last.arguments else {
78        return false;
79    };
80    args.args.len() == 1
81}
82
83// ── Binary annotation helpers ────────────────────────────────────────────────
84
85// Returns (pack, unpack_ofs, unpack_iter) for a named struct field annotated #[msgpacker(binary)].
86// When the field type is Option<T>, uses the nil-aware option helpers; otherwise uses direct binary.
87fn binary_named_stmts(ident: &Ident, ty: &Type) -> (TokenStream2, TokenStream2, TokenStream2) {
88    if is_option_type(ty) {
89        (
90            quote! {
91                n += ::msgpacker::pack_bytes_option(
92                    buf,
93                    self.#ident.as_ref().map(::core::convert::AsRef::as_ref),
94                );
95            },
96            quote! {
97                let #ident = ::msgpacker::unpack_bytes_option(buf).map(|(nv, t)| {
98                    n += nv;
99                    buf = &buf[nv..];
100                    t.map(::core::convert::From::from)
101                })?;
102            },
103            quote! {
104                let #ident = ::msgpacker::unpack_bytes_option_iter(bytes.by_ref()).map(|(nv, t)| {
105                    n += nv;
106                    t.map(::core::convert::From::from)
107                })?;
108            },
109        )
110    } else {
111        (
112            quote! {
113                n += ::msgpacker::pack_bytes(buf, ::core::convert::AsRef::<[u8]>::as_ref(&self.#ident));
114            },
115            quote! {
116                let #ident = ::msgpacker::unpack_bytes(buf).map(|(nv, t)| {
117                    n += nv;
118                    buf = &buf[nv..];
119                    ::core::convert::From::from(<[u8]>::to_vec(t))
120                })?;
121            },
122            quote! {
123                let #ident = ::msgpacker::unpack_bytes_iter(bytes.by_ref()).map(|(nv, t)| {
124                    n += nv;
125                    ::core::convert::From::from(t)
126                })?;
127            },
128        )
129    }
130}
131
132// Returns the unpack statement for a named field in a borrowed derive.
133fn binary_named_unpack_borrowed(ident: &Ident, ty: &Type) -> TokenStream2 {
134    if is_option_type(ty) {
135        quote! {
136            let #ident = ::msgpacker::unpack_bytes_option_ref(buf).map(|(nv, t)| {
137                n += nv;
138                buf = &buf[nv..];
139                t.map(::core::convert::From::from)
140            })?;
141        }
142    } else {
143        quote! {
144            let #ident = ::msgpacker::unpack_bytes(buf).map(|(nv, t)| {
145                n += nv;
146                buf = &buf[nv..];
147                ::core::convert::From::from(t)
148            })?;
149        }
150    }
151}
152
153// ── MsgPacker (Packable + Unpackable) ────────────────────────────────────────
154
155fn emit_struct_impls(
156    name: &Ident,
157    generics: &Generics,
158    pack_stmts: &[TokenStream2],
159    unpack_stmts: &[TokenStream2],
160    unpack_iter_stmts: &[TokenStream2],
161    construct: TokenStream2,
162) -> TokenStream2 {
163    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
164    quote! {
165        impl #impl_generics ::msgpacker::Packable for #name #ty_generics #where_clause {
166            fn pack<T>(&self, buf: &mut T) -> usize
167            where
168                T: ::msgpacker::BufMut,
169            {
170                use ::msgpacker::Packable as _;
171                let mut n = 0usize;
172                #(#pack_stmts)*
173                n
174            }
175        }
176
177        impl #impl_generics ::msgpacker::Unpackable for #name #ty_generics #where_clause {
178            type Error = ::msgpacker::Error;
179
180            fn unpack_with_ofs(mut buf: &[u8]) -> Result<(usize, Self), Self::Error> {
181                let mut n = 0usize;
182                #(#unpack_stmts)*
183                Ok((n, #construct))
184            }
185
186            fn unpack_iter<I>(bytes: I) -> Result<(usize, Self), Self::Error>
187            where
188                I: IntoIterator<Item = u8>,
189            {
190                let mut bytes = bytes.into_iter();
191                let mut n = 0usize;
192                #(#unpack_iter_stmts)*
193                Ok((n, #construct))
194            }
195        }
196    }
197}
198
199fn named_field_stmts(ident: &Ident, field: &Field) -> (TokenStream2, TokenStream2, TokenStream2) {
200    let ty = &field.ty;
201
202    if has_attr(field, "binary") {
203        return binary_named_stmts(ident, ty);
204    }
205
206    if has_attr(field, "map") {
207        return (
208            quote! { n += ::msgpacker::pack_map(buf, &self.#ident); },
209            quote! {
210                let #ident = ::msgpacker::unpack_map(buf).map(|(nv, t)| {
211                    n += nv;
212                    buf = &buf[nv..];
213                    t
214                })?;
215            },
216            quote! {
217                let #ident = ::msgpacker::unpack_map_iter(bytes.by_ref()).map(|(nv, t)| {
218                    n += nv;
219                    t
220                })?;
221            },
222        );
223    }
224
225    if has_attr(field, "array") || is_vec_of_non_u8(ty) {
226        return (
227            quote! { n += ::msgpacker::pack_array(buf, &self.#ident); },
228            quote! {
229                let #ident = ::msgpacker::unpack_array(buf).map(|(nv, t)| {
230                    n += nv;
231                    buf = &buf[nv..];
232                    t
233                })?;
234            },
235            quote! {
236                let #ident = ::msgpacker::unpack_array_iter(bytes.by_ref()).map(|(nv, t)| {
237                    n += nv;
238                    t
239                })?;
240            },
241        );
242    }
243
244    if is_slice_of_u8(ty) {
245        return (
246            quote! { n += ::msgpacker::pack_bytes(buf, self.#ident); },
247            quote! {
248                let #ident = ::msgpacker::unpack_bytes(buf).map(|(nv, t)| {
249                    n += nv;
250                    buf = &buf[nv..];
251                    t
252                })?;
253            },
254            quote! {
255                let #ident = ::msgpacker::unpack_bytes_iter(bytes.by_ref()).map(|(nv, t)| {
256                    n += nv;
257                    t
258                })?;
259            },
260        );
261    }
262
263    if is_vec_of_u8(ty) {
264        return (
265            quote! { n += ::msgpacker::pack_bytes(buf, self.#ident.as_slice()); },
266            quote! {
267                let #ident = ::msgpacker::unpack_bytes(buf).map(|(nv, t)| {
268                    n += nv;
269                    buf = &buf[nv..];
270                    <[u8]>::to_vec(t)
271                })?;
272            },
273            quote! {
274                let #ident = ::msgpacker::unpack_bytes_iter(bytes.by_ref()).map(|(nv, t)| {
275                    n += nv;
276                    t
277                })?;
278            },
279        );
280    }
281
282    (
283        quote! { n += self.#ident.pack(buf); },
284        quote! {
285            let #ident = ::msgpacker::Unpackable::unpack_with_ofs(buf).map(|(nv, t)| {
286                n += nv;
287                buf = &buf[nv..];
288                t
289            })?;
290        },
291        quote! {
292            let #ident = ::msgpacker::Unpackable::unpack_iter(bytes.by_ref()).map(|(nv, t)| {
293                n += nv;
294                t
295            })?;
296        },
297    )
298}
299
300fn impl_struct_named(name: &Ident, f: FieldsNamed, generics: &Generics) -> TokenStream2 {
301    let mut pack_stmts = Vec::new();
302    let mut unpack_stmts = Vec::new();
303    let mut unpack_iter_stmts = Vec::new();
304    let mut field_names = Vec::new();
305
306    for field in f.named {
307        let ident = field.ident.as_ref().cloned().unwrap();
308        let (pack, unpack, unpack_iter) = named_field_stmts(&ident, &field);
309        pack_stmts.push(pack);
310        unpack_stmts.push(unpack);
311        unpack_iter_stmts.push(unpack_iter);
312        field_names.push(ident);
313    }
314
315    emit_struct_impls(
316        name,
317        generics,
318        &pack_stmts,
319        &unpack_stmts,
320        &unpack_iter_stmts,
321        quote! { Self { #(#field_names),* } },
322    )
323}
324
325fn impl_struct_unnamed(name: &Ident, f: FieldsUnnamed, generics: &Generics) -> TokenStream2 {
326    let mut pack_stmts = Vec::new();
327    let mut unpack_stmts = Vec::new();
328    let mut unpack_iter_stmts = Vec::new();
329    let mut var_names = Vec::new();
330
331    for (i, field) in f.unnamed.into_iter().enumerate() {
332        if has_attr(&field, "map") {
333            todo!("unnamed map is not implemented for derive macro; implement the traits manually");
334        }
335        if has_attr(&field, "array") {
336            todo!(
337                "unnamed array is not implemented for derive macro; implement the traits manually"
338            );
339        }
340
341        let var = format_ident!("v{}", i);
342        let index = syn::Index::from(i);
343
344        if has_attr(&field, "binary") {
345            if is_option_type(&field.ty) {
346                pack_stmts.push(quote! {
347                    n += ::msgpacker::pack_bytes_option(
348                        buf,
349                        self.#index.as_ref().map(::core::convert::AsRef::as_ref),
350                    );
351                });
352                unpack_stmts.push(quote! {
353                    let #var = ::msgpacker::unpack_bytes_option(buf).map(|(nv, t)| {
354                        n += nv;
355                        buf = &buf[nv..];
356                        t.map(::core::convert::From::from)
357                    })?;
358                });
359                unpack_iter_stmts.push(quote! {
360                    let #var = ::msgpacker::unpack_bytes_option_iter(bytes.by_ref()).map(|(nv, t)| {
361                        n += nv;
362                        t.map(::core::convert::From::from)
363                    })?;
364                });
365            } else {
366                pack_stmts.push(quote! {
367                    n += ::msgpacker::pack_bytes(buf, ::core::convert::AsRef::<[u8]>::as_ref(&self.#index));
368                });
369                unpack_stmts.push(quote! {
370                    let #var = ::msgpacker::unpack_bytes(buf).map(|(nv, t)| {
371                        n += nv;
372                        buf = &buf[nv..];
373                        ::core::convert::From::from(<[u8]>::to_vec(t))
374                    })?;
375                });
376                unpack_iter_stmts.push(quote! {
377                    let #var = ::msgpacker::unpack_bytes_iter(bytes.by_ref()).map(|(nv, t)| {
378                        n += nv;
379                        ::core::convert::From::from(t)
380                    })?;
381                });
382            }
383            var_names.push(var);
384            continue;
385        }
386
387        if is_vec_of_u8(&field.ty) {
388            pack_stmts.push(quote! { n += ::msgpacker::pack_bytes(buf, self.#index.as_slice()); });
389            unpack_stmts.push(quote! {
390                let #var = ::msgpacker::unpack_bytes(buf).map(|(nv, t)| {
391                    n += nv;
392                    buf = &buf[nv..];
393                    <[u8]>::to_vec(t)
394                })?;
395            });
396            unpack_iter_stmts.push(quote! {
397                let #var = ::msgpacker::unpack_bytes_iter(bytes.by_ref()).map(|(nv, t)| {
398                    n += nv;
399                    t
400                })?;
401            });
402        } else {
403            pack_stmts.push(quote! { n += self.#index.pack(buf); });
404            unpack_stmts.push(quote! {
405                let #var = ::msgpacker::Unpackable::unpack_with_ofs(buf).map(|(nv, t)| {
406                    n += nv;
407                    buf = &buf[nv..];
408                    t
409                })?;
410            });
411            unpack_iter_stmts.push(quote! {
412                let #var = ::msgpacker::Unpackable::unpack_iter(bytes.by_ref()).map(|(nv, t)| {
413                    n += nv;
414                    t
415                })?;
416            });
417        }
418        var_names.push(var);
419    }
420
421    emit_struct_impls(
422        name,
423        generics,
424        &pack_stmts,
425        &unpack_stmts,
426        &unpack_iter_stmts,
427        quote! { Self(#(#var_names),*) },
428    )
429}
430
431fn impl_struct_unit(name: &Ident, generics: &Generics) -> TokenStream2 {
432    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
433    quote! {
434        impl #impl_generics ::msgpacker::Packable for #name #ty_generics #where_clause {
435            fn pack<T>(&self, _buf: &mut T) -> usize
436            where
437                T: ::msgpacker::BufMut,
438            {
439                0
440            }
441        }
442
443        impl #impl_generics ::msgpacker::Unpackable for #name #ty_generics #where_clause {
444            type Error = ::msgpacker::Error;
445
446            fn unpack_with_ofs(_buf: &[u8]) -> Result<(usize, Self), Self::Error> {
447                Ok((0, Self))
448            }
449
450            fn unpack_iter<I>(_bytes: I) -> Result<(usize, Self), Self::Error>
451            where
452                I: IntoIterator<Item = u8>,
453            {
454                Ok((0, Self))
455            }
456        }
457    }
458}
459
460fn impl_enum(name: &Ident, data: DataEnum, generics: &Generics) -> TokenStream2 {
461    if data.variants.is_empty() {
462        todo!("empty enum is not implemented for derive macro; implement the traits manually");
463    }
464
465    let mut pack_arms = Vec::new();
466    let mut unpack_arms = Vec::new();
467    let mut unpack_iter_arms = Vec::new();
468
469    for (i, variant) in data.variants.into_iter().enumerate() {
470        let discriminant = variant
471            .discriminant
472            .map(|(_, d)| d)
473            .unwrap_or_else(|| syn::parse_str(&i.to_string()).unwrap());
474        let variant_ident = &variant.ident;
475
476        match variant.fields {
477            Fields::Named(f) => {
478                let mut field_names = Vec::new();
479                let mut pack_field_stmts = Vec::new();
480                let mut unpack_field_stmts = Vec::new();
481                let mut unpack_iter_field_stmts = Vec::new();
482
483                for field in &f.named {
484                    let ident = field.ident.as_ref().unwrap();
485                    field_names.push(ident.clone());
486                    if has_attr(field, "binary") {
487                        if is_option_type(&field.ty) {
488                            pack_field_stmts.push(quote! {
489                                n += ::msgpacker::pack_bytes_option(
490                                    buf,
491                                    #ident.as_ref().map(::core::convert::AsRef::as_ref),
492                                );
493                            });
494                            unpack_field_stmts.push(quote! {
495                                let #ident = ::msgpacker::unpack_bytes_option(buf).map(|(nv, t)| {
496                                    n += nv;
497                                    buf = &buf[nv..];
498                                    t.map(::core::convert::From::from)
499                                })?;
500                            });
501                            unpack_iter_field_stmts.push(quote! {
502                                let #ident = ::msgpacker::unpack_bytes_option_iter(bytes.by_ref()).map(|(nv, t)| {
503                                    n += nv;
504                                    t.map(::core::convert::From::from)
505                                })?;
506                            });
507                        } else {
508                            pack_field_stmts.push(quote! {
509                                n += ::msgpacker::pack_bytes(buf, ::core::convert::AsRef::<[u8]>::as_ref(#ident));
510                            });
511                            unpack_field_stmts.push(quote! {
512                                let #ident = ::msgpacker::unpack_bytes(buf).map(|(nv, t)| {
513                                    n += nv;
514                                    buf = &buf[nv..];
515                                    ::core::convert::From::from(<[u8]>::to_vec(t))
516                                })?;
517                            });
518                            unpack_iter_field_stmts.push(quote! {
519                                let #ident = ::msgpacker::unpack_bytes_iter(bytes.by_ref()).map(|(nv, t)| {
520                                    n += nv;
521                                    ::core::convert::From::from(t)
522                                })?;
523                            });
524                        }
525                    } else if is_vec_of_u8(&field.ty) {
526                        pack_field_stmts
527                            .push(quote! { n += ::msgpacker::pack_bytes(buf, #ident.as_slice()); });
528                        unpack_field_stmts.push(quote! {
529                            let #ident = ::msgpacker::unpack_bytes(buf).map(|(nv, t)| {
530                                n += nv;
531                                buf = &buf[nv..];
532                                <[u8]>::to_vec(t)
533                            })?;
534                        });
535                        unpack_iter_field_stmts.push(quote! {
536                            let #ident = ::msgpacker::unpack_bytes_iter(bytes.by_ref()).map(|(nv, t)| {
537                                n += nv;
538                                t
539                            })?;
540                        });
541                    } else {
542                        pack_field_stmts.push(quote! { n += #ident.pack(buf); });
543                        unpack_field_stmts.push(quote! {
544                            let #ident = ::msgpacker::Unpackable::unpack_with_ofs(buf).map(|(nv, t)| {
545                                n += nv;
546                                buf = &buf[nv..];
547                                t
548                            })?;
549                        });
550                        unpack_iter_field_stmts.push(quote! {
551                            let #ident = ::msgpacker::Unpackable::unpack_iter(bytes.by_ref()).map(|(nv, t)| {
552                                n += nv;
553                                t
554                            })?;
555                        });
556                    }
557                }
558
559                pack_arms.push(quote! {
560                    #name::#variant_ident { #(#field_names),* } => {
561                        n += <u32 as ::msgpacker::Packable>::pack(&(#discriminant as u32), buf);
562                        #(#pack_field_stmts)*
563                    }
564                });
565                unpack_arms.push(quote! {
566                    #discriminant => {
567                        #(#unpack_field_stmts)*
568                        slf = #name::#variant_ident { #(#field_names),* };
569                    }
570                });
571                unpack_iter_arms.push(quote! {
572                    #discriminant => {
573                        #(#unpack_iter_field_stmts)*
574                        slf = #name::#variant_ident { #(#field_names),* };
575                    }
576                });
577            }
578
579            Fields::Unnamed(f) => {
580                let vars: Vec<Ident> = (0..f.unnamed.len())
581                    .map(|j| format_ident!("t{}", j))
582                    .collect();
583                let mut pack_field_stmts = Vec::new();
584                let mut unpack_field_stmts = Vec::new();
585                let mut unpack_iter_field_stmts = Vec::new();
586
587                for (var, field) in vars.iter().zip(f.unnamed.iter()) {
588                    if has_attr(field, "binary") {
589                        if is_option_type(&field.ty) {
590                            pack_field_stmts.push(quote! {
591                                n += ::msgpacker::pack_bytes_option(
592                                    buf,
593                                    #var.as_ref().map(::core::convert::AsRef::as_ref),
594                                );
595                            });
596                            unpack_field_stmts.push(quote! {
597                                let #var = ::msgpacker::unpack_bytes_option(buf).map(|(nv, t)| {
598                                    n += nv;
599                                    buf = &buf[nv..];
600                                    t.map(::core::convert::From::from)
601                                })?;
602                            });
603                            unpack_iter_field_stmts.push(quote! {
604                                let #var = ::msgpacker::unpack_bytes_option_iter(bytes.by_ref()).map(|(nv, t)| {
605                                    n += nv;
606                                    t.map(::core::convert::From::from)
607                                })?;
608                            });
609                        } else {
610                            pack_field_stmts.push(quote! {
611                                n += ::msgpacker::pack_bytes(buf, ::core::convert::AsRef::<[u8]>::as_ref(#var));
612                            });
613                            unpack_field_stmts.push(quote! {
614                                let #var = ::msgpacker::unpack_bytes(buf).map(|(nv, t)| {
615                                    n += nv;
616                                    buf = &buf[nv..];
617                                    ::core::convert::From::from(<[u8]>::to_vec(t))
618                                })?;
619                            });
620                            unpack_iter_field_stmts.push(quote! {
621                                let #var = ::msgpacker::unpack_bytes_iter(bytes.by_ref()).map(|(nv, t)| {
622                                    n += nv;
623                                    ::core::convert::From::from(t)
624                                })?;
625                            });
626                        }
627                    } else if is_vec_of_u8(&field.ty) {
628                        pack_field_stmts
629                            .push(quote! { n += ::msgpacker::pack_bytes(buf, #var.as_slice()); });
630                        unpack_field_stmts.push(quote! {
631                            let #var = ::msgpacker::unpack_bytes(buf).map(|(nv, t)| {
632                                n += nv;
633                                buf = &buf[nv..];
634                                <[u8]>::to_vec(t)
635                            })?;
636                        });
637                        unpack_iter_field_stmts.push(quote! {
638                            let #var = ::msgpacker::unpack_bytes_iter(bytes.by_ref()).map(|(nv, t)| {
639                                n += nv;
640                                t
641                            })?;
642                        });
643                    } else {
644                        pack_field_stmts.push(quote! { n += #var.pack(buf); });
645                        unpack_field_stmts.push(quote! {
646                            let #var = ::msgpacker::Unpackable::unpack_with_ofs(buf).map(|(nv, t)| {
647                                n += nv;
648                                buf = &buf[nv..];
649                                t
650                            })?;
651                        });
652                        unpack_iter_field_stmts.push(quote! {
653                            let #var = ::msgpacker::Unpackable::unpack_iter(bytes.by_ref()).map(|(nv, t)| {
654                                n += nv;
655                                t
656                            })?;
657                        });
658                    }
659                }
660
661                pack_arms.push(quote! {
662                    #name::#variant_ident(#(#vars),*) => {
663                        n += <u32 as ::msgpacker::Packable>::pack(&(#discriminant as u32), buf);
664                        #(#pack_field_stmts)*
665                    }
666                });
667                unpack_arms.push(quote! {
668                    #discriminant => {
669                        #(#unpack_field_stmts)*
670                        slf = #name::#variant_ident(#(#vars),*);
671                    }
672                });
673                unpack_iter_arms.push(quote! {
674                    #discriminant => {
675                        #(#unpack_iter_field_stmts)*
676                        slf = #name::#variant_ident(#(#vars),*);
677                    }
678                });
679            }
680
681            Fields::Unit => {
682                pack_arms.push(quote! {
683                    #name::#variant_ident => {
684                        n += <u32 as ::msgpacker::Packable>::pack(&(#discriminant as u32), buf);
685                    }
686                });
687                unpack_arms.push(quote! {
688                    #discriminant => slf = #name::#variant_ident,
689                });
690                unpack_iter_arms.push(quote! {
691                    #discriminant => slf = #name::#variant_ident,
692                });
693            }
694        }
695    }
696
697    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
698    quote! {
699        impl #impl_generics ::msgpacker::Packable for #name #ty_generics #where_clause {
700            fn pack<T>(&self, buf: &mut T) -> usize
701            where
702                T: ::msgpacker::BufMut,
703            {
704                use ::msgpacker::Packable as _;
705                let mut n = 0usize;
706                match self {
707                    #(#pack_arms)*
708                }
709                n
710            }
711        }
712
713        impl #impl_generics ::msgpacker::Unpackable for #name #ty_generics #where_clause {
714            type Error = ::msgpacker::Error;
715
716            #[allow(unused_mut)]
717            fn unpack_with_ofs(mut buf: &[u8]) -> Result<(usize, Self), Self::Error> {
718                let (mut n, discriminant) =
719                    <u32 as ::msgpacker::Unpackable>::unpack_with_ofs(buf)?;
720                buf = &buf[n..];
721                let slf;
722                match discriminant {
723                    #(#unpack_arms)*
724                    _ => return Err(::msgpacker::Error::InvalidEnumVariant),
725                }
726                Ok((n, slf))
727            }
728
729            fn unpack_iter<I>(bytes: I) -> Result<(usize, Self), Self::Error>
730            where
731                I: IntoIterator<Item = u8>,
732            {
733                let mut bytes = bytes.into_iter();
734                let (mut n, discriminant) =
735                    <u32 as ::msgpacker::Unpackable>::unpack_iter(bytes.by_ref())?;
736                let slf;
737                match discriminant {
738                    #(#unpack_iter_arms)*
739                    _ => return Err(::msgpacker::Error::InvalidEnumVariant),
740                }
741                Ok((n, slf))
742            }
743        }
744    }
745}
746
747#[proc_macro_derive(MsgPacker, attributes(msgpacker))]
748pub fn msg_packer(input: TokenStream) -> TokenStream {
749    let input = parse_macro_input!(input as DeriveInput);
750    let name = &input.ident;
751    let generics = &input.generics;
752
753    let tokens = match input.data {
754        Data::Struct(DataStruct {
755            fields: Fields::Named(f),
756            ..
757        }) => impl_struct_named(name, f, generics),
758
759        Data::Struct(DataStruct {
760            fields: Fields::Unnamed(f),
761            ..
762        }) => impl_struct_unnamed(name, f, generics),
763
764        Data::Struct(DataStruct {
765            fields: Fields::Unit,
766            ..
767        }) => impl_struct_unit(name, generics),
768
769        Data::Enum(data) => impl_enum(name, data, generics),
770
771        Data::Union(_) => {
772            todo!(
773                "union support is not implemented for derive macro; implement the traits manually"
774            )
775        }
776    };
777
778    tokens.into()
779}
780
781// ── MsgUnpackBorrowed (Packable + UnpackableBorrowed) ────────────────────────
782
783fn resolve_borrow_lifetime(generics: &Generics) -> (syn::Lifetime, Option<syn::Generics>) {
784    match generics.lifetimes().next() {
785        Some(lt_def) => (lt_def.lifetime.clone(), None),
786        None => {
787            let lt: syn::Lifetime = syn::parse_quote!('__a);
788            let mut modified = generics.clone();
789            modified.params.insert(
790                0,
791                syn::GenericParam::Lifetime(syn::LifetimeParam::new(lt.clone())),
792            );
793            (lt, Some(modified))
794        }
795    }
796}
797
798fn emit_borrowed_struct_impl(
799    name: &Ident,
800    generics: &Generics,
801    borrow_lt: &syn::Lifetime,
802    impl_gen_src: &Generics,
803    pack_stmts: Option<&[TokenStream2]>,
804    unpack_stmts: &[TokenStream2],
805    construct: TokenStream2,
806) -> TokenStream2 {
807    let (pack_impl_generics, ty_generics, pack_where_clause) = generics.split_for_impl();
808    let (impl_generics, _, where_clause) = impl_gen_src.split_for_impl();
809
810    let packable_impl = pack_stmts.map(|stmts| quote! {
811        impl #pack_impl_generics ::msgpacker::Packable for #name #ty_generics #pack_where_clause {
812            fn pack<T>(&self, buf: &mut T) -> usize
813            where
814                T: ::msgpacker::BufMut,
815            {
816                use ::msgpacker::Packable as _;
817                let mut n = 0usize;
818                #(#stmts)*
819                n
820            }
821        }
822    });
823
824    quote! {
825        #packable_impl
826
827        impl #impl_generics ::msgpacker::UnpackableBorrowed<#borrow_lt> for #name #ty_generics #where_clause {
828            type Error = ::msgpacker::Error;
829
830            fn unpack_with_ofs(mut buf: &#borrow_lt [u8]) -> Result<(usize, Self), Self::Error> {
831                let mut n = 0usize;
832                #(#unpack_stmts)*
833                Ok((n, #construct))
834            }
835        }
836    }
837}
838
839fn named_field_borrowed_stmt(
840    ident: &Ident,
841    field: &Field,
842    borrow_lt: &syn::Lifetime,
843) -> TokenStream2 {
844    let ty = &field.ty;
845
846    if has_attr(field, "binary") {
847        return binary_named_unpack_borrowed(ident, ty);
848    }
849
850    if has_attr(field, "map") {
851        return quote! {
852            let #ident = ::msgpacker::unpack_map(buf).map(|(nv, t)| {
853                n += nv;
854                buf = &buf[nv..];
855                t
856            })?;
857        };
858    }
859
860    if is_slice_of_u8(ty) || is_vec_of_u8(ty) {
861        return quote! {
862            let #ident = ::msgpacker::unpack_bytes(buf).map(|(nv, t)| {
863                n += nv;
864                buf = &buf[nv..];
865                t.into()
866            })?;
867        };
868    }
869
870    if has_attr(field, "array") || is_vec_of_non_u8(ty) {
871        return quote! {
872            let #ident = ::msgpacker::unpack_array_borrowed::<_, _>(buf).map(|(nv, t)| {
873                n += nv;
874                buf = &buf[nv..];
875                t
876            })?;
877        };
878    }
879
880    quote! {
881        let #ident = <_ as ::msgpacker::UnpackableBorrowed<#borrow_lt>>::unpack_with_ofs(buf).map(|(nv, t)| {
882            n += nv;
883            buf = &buf[nv..];
884            t
885        })?;
886    }
887}
888
889fn impl_struct_named_borrowed(
890    name: &Ident,
891    f: FieldsNamed,
892    generics: &Generics,
893    with_packable: bool,
894) -> TokenStream2 {
895    let (borrow_lt, maybe_modified) = resolve_borrow_lifetime(generics);
896    let impl_gen_src = maybe_modified.as_ref().unwrap_or(generics);
897
898    let mut pack_stmts = Vec::new();
899    let mut unpack_stmts = Vec::new();
900    let mut field_names = Vec::new();
901
902    for field in f.named {
903        let ident = field.ident.as_ref().cloned().unwrap();
904        if with_packable {
905            pack_stmts.push(named_field_stmts(&ident, &field).0);
906        }
907        unpack_stmts.push(named_field_borrowed_stmt(&ident, &field, &borrow_lt));
908        field_names.push(ident);
909    }
910
911    emit_borrowed_struct_impl(
912        name,
913        generics,
914        &borrow_lt,
915        impl_gen_src,
916        with_packable.then_some(&pack_stmts[..]),
917        &unpack_stmts,
918        quote! { Self { #(#field_names),* } },
919    )
920}
921
922fn impl_struct_unnamed_borrowed(
923    name: &Ident,
924    f: FieldsUnnamed,
925    generics: &Generics,
926    with_packable: bool,
927) -> TokenStream2 {
928    let (borrow_lt, maybe_modified) = resolve_borrow_lifetime(generics);
929    let impl_gen_src = maybe_modified.as_ref().unwrap_or(generics);
930
931    let mut pack_stmts = Vec::new();
932    let mut unpack_stmts = Vec::new();
933    let mut var_names = Vec::new();
934
935    for (i, field) in f.unnamed.into_iter().enumerate() {
936        if has_attr(&field, "map") {
937            todo!("unnamed map is not implemented for derive macro; implement the traits manually");
938        }
939        if has_attr(&field, "array") {
940            todo!(
941                "unnamed array is not implemented for derive macro; implement the traits manually"
942            );
943        }
944
945        let var = format_ident!("v{}", i);
946        let index = syn::Index::from(i);
947
948        if has_attr(&field, "binary") {
949            if with_packable {
950                if is_option_type(&field.ty) {
951                    pack_stmts.push(quote! {
952                        n += ::msgpacker::pack_bytes_option(
953                            buf,
954                            self.#index.as_ref().map(::core::convert::AsRef::as_ref),
955                        );
956                    });
957                } else {
958                    pack_stmts.push(quote! {
959                        n += ::msgpacker::pack_bytes(buf, ::core::convert::AsRef::<[u8]>::as_ref(&self.#index));
960                    });
961                }
962            }
963            if is_option_type(&field.ty) {
964                unpack_stmts.push(quote! {
965                    let #var = ::msgpacker::unpack_bytes_option_ref(buf).map(|(nv, t)| {
966                        n += nv;
967                        buf = &buf[nv..];
968                        t.map(::core::convert::From::from)
969                    })?;
970                });
971            } else {
972                unpack_stmts.push(quote! {
973                    let #var = ::msgpacker::unpack_bytes(buf).map(|(nv, t)| {
974                        n += nv;
975                        buf = &buf[nv..];
976                        ::core::convert::From::from(t)
977                    })?;
978                });
979            }
980            var_names.push(var);
981            continue;
982        }
983
984        if with_packable {
985            pack_stmts.push(if is_vec_of_u8(&field.ty) {
986                quote! { n += ::msgpacker::pack_bytes(buf, self.#index.as_slice()); }
987            } else if is_slice_of_u8(&field.ty) {
988                quote! { n += ::msgpacker::pack_bytes(buf, self.#index); }
989            } else {
990                quote! { n += self.#index.pack(buf); }
991            });
992        }
993        unpack_stmts.push(quote! {
994            let #var = <_ as ::msgpacker::UnpackableBorrowed<#borrow_lt>>::unpack_with_ofs(buf).map(|(nv, t)| {
995                n += nv;
996                buf = &buf[nv..];
997                t
998            })?;
999        });
1000        var_names.push(var);
1001    }
1002
1003    emit_borrowed_struct_impl(
1004        name,
1005        generics,
1006        &borrow_lt,
1007        impl_gen_src,
1008        with_packable.then_some(&pack_stmts[..]),
1009        &unpack_stmts,
1010        quote! { Self(#(#var_names),*) },
1011    )
1012}
1013
1014fn impl_struct_unit_borrowed(
1015    name: &Ident,
1016    generics: &Generics,
1017    with_packable: bool,
1018) -> TokenStream2 {
1019    let (borrow_lt, maybe_modified) = resolve_borrow_lifetime(generics);
1020    let impl_gen_src = maybe_modified.as_ref().unwrap_or(generics);
1021    let (pack_impl_generics, ty_generics, pack_where_clause) = generics.split_for_impl();
1022    let (impl_generics, _, where_clause) = impl_gen_src.split_for_impl();
1023
1024    let packable_impl = with_packable.then(|| quote! {
1025        impl #pack_impl_generics ::msgpacker::Packable for #name #ty_generics #pack_where_clause {
1026            fn pack<T>(&self, _buf: &mut T) -> usize
1027            where
1028                T: ::msgpacker::BufMut,
1029            {
1030                0
1031            }
1032        }
1033    });
1034
1035    quote! {
1036        #packable_impl
1037
1038        impl #impl_generics ::msgpacker::UnpackableBorrowed<#borrow_lt> for #name #ty_generics #where_clause {
1039            type Error = ::msgpacker::Error;
1040
1041            fn unpack_with_ofs(_buf: &#borrow_lt [u8]) -> Result<(usize, Self), Self::Error> {
1042                Ok((0, Self))
1043            }
1044        }
1045    }
1046}
1047
1048fn impl_enum_borrowed(
1049    name: &Ident,
1050    data: DataEnum,
1051    generics: &Generics,
1052    with_packable: bool,
1053) -> TokenStream2 {
1054    if data.variants.is_empty() {
1055        todo!("empty enum is not implemented for derive macro; implement the traits manually");
1056    }
1057
1058    let (borrow_lt, maybe_modified) = resolve_borrow_lifetime(generics);
1059    let impl_gen_src = maybe_modified.as_ref().unwrap_or(generics);
1060    let (pack_impl_generics, ty_generics, pack_where_clause) = generics.split_for_impl();
1061    let (impl_generics, _, where_clause) = impl_gen_src.split_for_impl();
1062
1063    let mut pack_arms = Vec::new();
1064    let mut unpack_arms = Vec::new();
1065
1066    for (i, variant) in data.variants.into_iter().enumerate() {
1067        let discriminant = variant
1068            .discriminant
1069            .map(|(_, d)| d)
1070            .unwrap_or_else(|| syn::parse_str(&i.to_string()).unwrap());
1071        let variant_ident = &variant.ident;
1072
1073        match variant.fields {
1074            Fields::Named(f) => {
1075                let mut field_names = Vec::new();
1076                let mut pack_field_stmts = Vec::new();
1077                let mut unpack_field_stmts = Vec::new();
1078
1079                for field in &f.named {
1080                    let ident = field.ident.as_ref().unwrap();
1081                    field_names.push(ident.clone());
1082                    if has_attr(field, "binary") {
1083                        if with_packable {
1084                            if is_option_type(&field.ty) {
1085                                pack_field_stmts.push(quote! {
1086                                    n += ::msgpacker::pack_bytes_option(
1087                                        buf,
1088                                        #ident.as_ref().map(::core::convert::AsRef::as_ref),
1089                                    );
1090                                });
1091                            } else {
1092                                pack_field_stmts.push(quote! {
1093                                    n += ::msgpacker::pack_bytes(buf, ::core::convert::AsRef::<[u8]>::as_ref(#ident));
1094                                });
1095                            }
1096                        }
1097                        if is_option_type(&field.ty) {
1098                            unpack_field_stmts.push(quote! {
1099                                let #ident = ::msgpacker::unpack_bytes_option_ref(buf).map(|(nv, t)| {
1100                                    n += nv;
1101                                    buf = &buf[nv..];
1102                                    t.map(::core::convert::From::from)
1103                                })?;
1104                            });
1105                        } else {
1106                            unpack_field_stmts.push(quote! {
1107                                let #ident = ::msgpacker::unpack_bytes(buf).map(|(nv, t)| {
1108                                    n += nv;
1109                                    buf = &buf[nv..];
1110                                    ::core::convert::From::from(t)
1111                                })?;
1112                            });
1113                        }
1114                    } else {
1115                        if with_packable {
1116                            pack_field_stmts.push(if is_vec_of_u8(&field.ty) {
1117                                quote! { n += ::msgpacker::pack_bytes(buf, #ident.as_slice()); }
1118                            } else if is_slice_of_u8(&field.ty) {
1119                                quote! { n += ::msgpacker::pack_bytes(buf, #ident); }
1120                            } else {
1121                                quote! { n += #ident.pack(buf); }
1122                            });
1123                        }
1124                        unpack_field_stmts.push(quote! {
1125                            let #ident = <_ as ::msgpacker::UnpackableBorrowed<#borrow_lt>>::unpack_with_ofs(buf).map(|(nv, t)| {
1126                                n += nv;
1127                                buf = &buf[nv..];
1128                                t
1129                            })?;
1130                        });
1131                    }
1132                }
1133
1134                if with_packable {
1135                    pack_arms.push(quote! {
1136                        #name::#variant_ident { #(#field_names),* } => {
1137                            n += <u32 as ::msgpacker::Packable>::pack(&(#discriminant as u32), buf);
1138                            #(#pack_field_stmts)*
1139                        }
1140                    });
1141                }
1142                unpack_arms.push(quote! {
1143                    #discriminant => {
1144                        #(#unpack_field_stmts)*
1145                        slf = #name::#variant_ident { #(#field_names),* };
1146                    }
1147                });
1148            }
1149
1150            Fields::Unnamed(f) => {
1151                let vars: Vec<Ident> = (0..f.unnamed.len())
1152                    .map(|j| format_ident!("t{}", j))
1153                    .collect();
1154                let mut pack_field_stmts = Vec::new();
1155                let mut unpack_field_stmts = Vec::new();
1156
1157                for (var, field) in vars.iter().zip(f.unnamed.iter()) {
1158                    if has_attr(field, "binary") {
1159                        if with_packable {
1160                            if is_option_type(&field.ty) {
1161                                pack_field_stmts.push(quote! {
1162                                    n += ::msgpacker::pack_bytes_option(
1163                                        buf,
1164                                        #var.as_ref().map(::core::convert::AsRef::as_ref),
1165                                    );
1166                                });
1167                            } else {
1168                                pack_field_stmts.push(quote! {
1169                                    n += ::msgpacker::pack_bytes(buf, ::core::convert::AsRef::<[u8]>::as_ref(#var));
1170                                });
1171                            }
1172                        }
1173                        if is_option_type(&field.ty) {
1174                            unpack_field_stmts.push(quote! {
1175                                let #var = ::msgpacker::unpack_bytes_option_ref(buf).map(|(nv, t)| {
1176                                    n += nv;
1177                                    buf = &buf[nv..];
1178                                    t.map(::core::convert::From::from)
1179                                })?;
1180                            });
1181                        } else {
1182                            unpack_field_stmts.push(quote! {
1183                                let #var = ::msgpacker::unpack_bytes(buf).map(|(nv, t)| {
1184                                    n += nv;
1185                                    buf = &buf[nv..];
1186                                    ::core::convert::From::from(t)
1187                                })?;
1188                            });
1189                        }
1190                    } else {
1191                        if with_packable {
1192                            pack_field_stmts.push(if is_vec_of_u8(&field.ty) {
1193                                quote! { n += ::msgpacker::pack_bytes(buf, #var.as_slice()); }
1194                            } else if is_slice_of_u8(&field.ty) {
1195                                quote! { n += ::msgpacker::pack_bytes(buf, #var); }
1196                            } else {
1197                                quote! { n += #var.pack(buf); }
1198                            });
1199                        }
1200                        unpack_field_stmts.push(quote! {
1201                            let #var = <_ as ::msgpacker::UnpackableBorrowed<#borrow_lt>>::unpack_with_ofs(buf).map(|(nv, t)| {
1202                                n += nv;
1203                                buf = &buf[nv..];
1204                                t
1205                            })?;
1206                        });
1207                    }
1208                }
1209
1210                if with_packable {
1211                    pack_arms.push(quote! {
1212                        #name::#variant_ident(#(#vars),*) => {
1213                            n += <u32 as ::msgpacker::Packable>::pack(&(#discriminant as u32), buf);
1214                            #(#pack_field_stmts)*
1215                        }
1216                    });
1217                }
1218                unpack_arms.push(quote! {
1219                    #discriminant => {
1220                        #(#unpack_field_stmts)*
1221                        slf = #name::#variant_ident(#(#vars),*);
1222                    }
1223                });
1224            }
1225
1226            Fields::Unit => {
1227                if with_packable {
1228                    pack_arms.push(quote! {
1229                        #name::#variant_ident => {
1230                            n += <u32 as ::msgpacker::Packable>::pack(&(#discriminant as u32), buf);
1231                        }
1232                    });
1233                }
1234                unpack_arms.push(quote! {
1235                    #discriminant => slf = #name::#variant_ident,
1236                });
1237            }
1238        }
1239    }
1240
1241    let packable_impl = with_packable.then(|| quote! {
1242        impl #pack_impl_generics ::msgpacker::Packable for #name #ty_generics #pack_where_clause {
1243            fn pack<T>(&self, buf: &mut T) -> usize
1244            where
1245                T: ::msgpacker::BufMut,
1246            {
1247                use ::msgpacker::Packable as _;
1248                let mut n = 0usize;
1249                match self {
1250                    #(#pack_arms)*
1251                }
1252                n
1253            }
1254        }
1255    });
1256
1257    quote! {
1258        #packable_impl
1259
1260        impl #impl_generics ::msgpacker::UnpackableBorrowed<#borrow_lt> for #name #ty_generics #where_clause {
1261            type Error = ::msgpacker::Error;
1262
1263            #[allow(unused_mut)]
1264            fn unpack_with_ofs(mut buf: &#borrow_lt [u8]) -> Result<(usize, Self), Self::Error> {
1265                let (mut n, discriminant) =
1266                    <u32 as ::msgpacker::Unpackable>::unpack_with_ofs(buf)?;
1267                buf = &buf[n..];
1268                let slf;
1269                match discriminant {
1270                    #(#unpack_arms)*
1271                    _ => return Err(::msgpacker::Error::InvalidEnumVariant),
1272                }
1273                Ok((n, slf))
1274            }
1275        }
1276    }
1277}
1278
1279fn dispatch_borrowed(input: DeriveInput, with_packable: bool) -> TokenStream2 {
1280    let name = &input.ident;
1281    let generics = &input.generics;
1282
1283    match input.data {
1284        Data::Struct(DataStruct {
1285            fields: Fields::Named(f),
1286            ..
1287        }) => impl_struct_named_borrowed(name, f, generics, with_packable),
1288
1289        Data::Struct(DataStruct {
1290            fields: Fields::Unnamed(f),
1291            ..
1292        }) => impl_struct_unnamed_borrowed(name, f, generics, with_packable),
1293
1294        Data::Struct(DataStruct {
1295            fields: Fields::Unit,
1296            ..
1297        }) => impl_struct_unit_borrowed(name, generics, with_packable),
1298
1299        Data::Enum(data) => impl_enum_borrowed(name, data, generics, with_packable),
1300
1301        Data::Union(_) => {
1302            todo!(
1303                "union support is not implemented for derive macro; implement the traits manually"
1304            )
1305        }
1306    }
1307}
1308
1309#[proc_macro_derive(MsgPackerBorrowed, attributes(msgpacker))]
1310pub fn msg_packer_borrowed(input: TokenStream) -> TokenStream {
1311    dispatch_borrowed(parse_macro_input!(input as DeriveInput), true).into()
1312}
1313
1314#[proc_macro_derive(MsgUnpackerBorrowed, attributes(msgpacker))]
1315pub fn msg_unpacker_borrowed(input: TokenStream) -> TokenStream {
1316    dispatch_borrowed(parse_macro_input!(input as DeriveInput), false).into()
1317}