1use darling::FromAttributes;
2use proc_macro::TokenStream;
3use quote::quote;
4use syn::{parse_macro_input, DataEnum, DataStruct, DeriveInput};
5
6#[derive(Debug, FromAttributes)]
7#[darling(attributes(sb), forward_attrs(allow, doc, cfg))]
8struct FieldAttr {
9 #[darling(default)]
10 skip: bool,
11}
12
13#[proc_macro_derive(SwapBytes, attributes(sb))]
14pub fn derive_swap_bytes(input: TokenStream) -> TokenStream {
15 let input: DeriveInput = parse_macro_input!(input);
16 match &input.data {
17 syn::Data::Struct(data) => impl_struct(&input, data),
18 syn::Data::Enum(data) => impl_enum(&input, data),
19 _ => panic!("SwapBytesMut only supported on structs and enums"),
20 }
21}
22
23fn impl_struct(input: &DeriveInput, data: &DataStruct) -> TokenStream {
25 let ident = &input.ident;
26 let generics = &input.generics;
27 let where_clause = generics.where_clause.as_ref();
28
29 let swap_bytes_impls = data
31 .fields
32 .iter()
33 .filter(|value| {
34 let attr = FieldAttr::from_attributes(&value.attrs).expect("Failed to parse attrs");
35 !attr.skip
36 })
37 .map(|value| {
38 let ident = value.ident.as_ref().expect("Struct field missing ident");
39 quote! {
40 swapbytes::SwapBytes::swap_bytes_mut(&mut self.#ident);
41 }
42 });
43
44 quote! {
45 impl #generics swapbytes::SwapBytes for #ident #generics #where_clause {
46 fn swap_bytes_mut(&mut self) {
47 #(#swap_bytes_impls)*
48 }
49 }
50 }
51 .into()
52}
53
54fn impl_enum(input: &DeriveInput, _data: &DataEnum) -> TokenStream {
56 let repr = input
58 .attrs
59 .iter()
60 .find_map(|attr| match &attr.meta {
61 syn::Meta::List(list) if list.path.is_ident("repr") => Some(&list.tokens),
62 _ => None,
63 })
64 .expect("Enum missing repr type");
65
66 let ident = &input.ident;
67
68 quote! {
70 impl swapbytes::SwapBytes for #ident {
71 fn swap_bytes_mut(&mut self) {
72 let mut value: #repr = *self as #repr;
73 value.swap_bytes_mut();
74 *self = unsafe { std::mem::transmute(value) };
75 }
76 }
77 }
78 .into()
79}