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}