Skip to main content

mcu_comms_macros/
lib.rs

1use proc_macro::TokenStream;
2use proc_macro2::TokenStream as TokenStream2;
3use quote::quote;
4use syn::{Data, DeriveInput, Fields};
5
6#[proc_macro_attribute]
7pub fn payload(_attr: TokenStream, item: TokenStream) -> TokenStream {
8    let input: DeriveInput = syn::parse(item).unwrap();
9    let name = &input.ident;
10
11    let field_sizes = field_sizes(&input.data);
12
13    let expanded = quote! {
14        #[derive(
15            ::mcu_comms::serde::Serialize,
16            ::mcu_comms::serde::Deserialize,
17        )]
18        #[serde(crate = "::mcu_comms::serde")]
19        #input
20
21        impl ::mcu_comms::payload_size::MaxSize for #name {
22            const MAX_SIZE: usize = 0 #(+ #field_sizes)*;
23        }
24        impl ::mcu_comms::payload_size::MaxPayloadSize for #name {
25            const FRAME_SIZE: usize = <#name as ::mcu_comms::payload_size::MaxSize>::MAX_SIZE
26                + ::mcu_comms::aesccm::HEADER_SIZE
27                + ::mcu_comms::aesccm::TAG_SIZE;
28            type FrameBuf = [u8; Self::FRAME_SIZE];
29            fn new_buf() -> Self::FrameBuf {
30                [0_u8; Self::FRAME_SIZE]
31            }
32        }
33        impl ::mcu_comms::payload_size::Payload for #name {}
34    };
35    expanded.into()
36}
37
38fn field_sizes(data: &syn::Data) -> Vec<TokenStream2> {
39    let field_sizes = match data {
40        Data::Struct(data) => match &data.fields {
41            Fields::Named(fields) => fields
42                .named
43                .iter()
44                .map(|f| {
45                    let ty = &f.ty;
46                    quote! { <#ty as ::mcu_comms::payload_size::MaxSize>::MAX_SIZE }
47                })
48                .collect(),
49            Fields::Unnamed(fields) => fields
50                .unnamed
51                .iter()
52                .map(|f| {
53                    let ty = &f.ty;
54                    quote! {<#ty as ::mcu_comms::payload_size::MaxSize>::MAX_SIZE}
55                })
56                .collect(),
57            Fields::Unit => vec![],
58        },
59        Data::Enum(data) => {
60            let variant_sizes: Vec<TokenStream2> = data
61                .variants
62                .iter()
63                .map(|v| {
64                    let field_sizes: Vec<TokenStream2> = v
65                        .fields
66                        .iter()
67                        .map(|f| {
68                            let ty = &f.ty;
69                            quote! { <#ty as ::mcu_comms::payload_size::MaxSize>::MAX_SIZE }
70                        })
71                        .collect();
72                    quote! { (0 #(+ #field_sizes)*) }
73                })
74                .collect();
75
76            vec![quote! {{
77                const fn max(a: usize, b: usize) -> usize {
78                    if a > b { a } else { b }
79                }
80                let mut m = 0;
81                #( m = max(m, #variant_sizes); )*
82                m + 5
83            }}]
84        }
85        _ => panic!("Payload does not support this type"),
86    };
87    field_sizes
88}