swapbytes_derive/
lib.rs

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
23/// Implements [`SwapBytes`] for a struct
24fn 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    // Generate `swap_bytes_mut` calls for all the non-skipped fields
30    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
54/// Implements [`SwapBytes`] for a repr enum
55fn impl_enum(input: &DeriveInput, _data: &DataEnum) -> TokenStream {
56    // Extract the repr type from the enum attributes
57    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    // Impl casts the enum to its repr type, swaps its order then transmutes back
69    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}