ni_fpga_macros/
lib.rs

1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use quote::quote;
5
6#[proc_macro_derive(Cluster)]
7pub fn derive_cluster(item: TokenStream) -> TokenStream {
8    let input = syn::parse_macro_input!(item as syn::ItemStruct);
9
10    let field_names: Vec<_> = input.fields.iter().map(|field| &field.ident).collect();
11    let field_names_for_pack = &field_names;
12    let field_names_for_unpack = &field_names;
13    let field_types: Vec<_> = input.fields.iter().map(|field| &field.ty).collect();
14    let field_types_for_pack = &field_types;
15    let field_types_for_unpack = &field_types;
16    let struct_name = &input.ident;
17
18    let output = quote! {
19        impl ni_fpga::Datatype for #struct_name {
20            const SIZE_IN_BITS: usize = 0 #( + <#field_types as ni_fpga::Datatype>::SIZE_IN_BITS )*;
21
22            fn pack(fpga_bits: &mut ni_fpga::FpgaBits, data: &Self) -> Result<(), ni_fpga::Error> {
23                let mut remaining_bits = fpga_bits;
24                #(
25                {
26                    let (field_bits, other_remaining_bits) = remaining_bits.split_at_mut(
27                        <#field_types_for_pack as ni_fpga::Datatype>::SIZE_IN_BITS
28                    );
29                    remaining_bits = other_remaining_bits;
30                    ni_fpga::Datatype::pack(field_bits, &data.#field_names_for_pack)?;
31                }
32                )*
33                Ok(())
34            }
35
36            #[allow(clippy::eval_order_dependence)]
37            fn unpack(fpga_bits: &ni_fpga::FpgaBits) -> Result<Self, ni_fpga::Error> {
38                let mut remaining_bits = fpga_bits;
39                Ok(
40                    Self{
41                        #(
42                        #field_names_for_unpack: {
43                            let (field_bits, other_remaining_bits) = remaining_bits.split_at(
44                                <#field_types_for_unpack as ni_fpga::Datatype>::SIZE_IN_BITS
45                            );
46                            remaining_bits = other_remaining_bits;
47                            ni_fpga::Datatype::unpack(field_bits)?
48                        }
49                        ),*
50                    },
51                )
52            }
53        }
54    };
55
56    output.into()
57}
58
59#[proc_macro_derive(Enum)]
60pub fn derive_enum(item: TokenStream) -> TokenStream {
61    let input = syn::parse_macro_input!(item as syn::ItemEnum);
62
63    let backing_size = match (input.variants.len() as f64).log2().ceil().exp2() as usize {
64        x if x < 8 => 8,
65        x if x > 64 => return quote!{compile_error!("Enum can only be derived for enums with at most 2^64 variants!")}.into(),
66        x => x,
67    };
68    let backing_type = syn::Type::Verbatim(match backing_size {
69        8 => quote! {u8},
70        16 => quote! {u16},
71        32 => quote! {u32},
72        64 => quote! {u64},
73        _ => unreachable!(),
74    });
75    let discriminants: Vec<_> = (0..input.variants.len())
76        .map(|discriminant| {
77            syn::LitInt::new(
78                &discriminant.to_string(),
79                proc_macro::Span::call_site().into(),
80            )
81        })
82        .collect();
83    let discriminants_for_pack = &discriminants;
84    let discriminants_for_unpack = &discriminants;
85    let variants: Vec<_> = input.variants.iter().map(|v| &v.ident).collect();
86    let variants_for_pack = &variants;
87    let variants_for_unpack = &variants;
88    let enum_name = &input.ident;
89
90    let output = quote! {
91        impl ni_fpga::Datatype for #enum_name {
92            const SIZE_IN_BITS: usize = #backing_size;
93
94            fn pack(fpga_bits: &mut ni_fpga::FpgaBits, data: &Self) -> Result<(), ni_fpga::Error> {
95                match data {
96                    #(
97                    Self::#variants_for_pack => <#backing_type as ni_fpga::Datatype>::pack(fpga_bits, &#discriminants_for_pack)?
98                    ),*
99                };
100                Ok(())
101            }
102
103            fn unpack(fpga_bits: &ni_fpga::FpgaBits) -> Result<Self, ni_fpga::Error> {
104                match <#backing_type as ni_fpga::Datatype>::unpack(fpga_bits)? {
105                    #(
106                    #discriminants_for_unpack => Ok(Self::#variants_for_unpack)
107                    ),*,
108                    unknown => Err(ni_fpga::Error::InvalidEnumDiscriminant(unknown as u64)),
109                }
110            }
111        }
112    };
113
114    output.into()
115}