allegro_motor_derive/
lib.rs

1//! A derive macro to implement `allegro_motor_drivers::regs::AllegroRegister` trait.
2
3extern crate proc_macro;
4
5use proc_macro::TokenStream;
6use quote::quote;
7use syn::{parse_macro_input, DeriveInput};
8
9/// Derive macro to implement the `AllegroRegister` trait.
10///
11/// See the [original trait][trait] for examples and complete documentation.
12///
13/// [trait]: ../allegro_motor_drivers/regs/trait.AllegroRegister.html
14///
15/// Please note that a compile failure will occur if derive macros are defined in the wrong order,
16/// or if the bitsize derive macro is not used at all. This occurs because derive macros are parsed
17/// from the outside in, and each macro depends on implementations defined by other macros.
18///
19/// ```compile_fail
20/// # #[macro_use] extern crate allegro_motor_derive;
21/// # use bilge::prelude::*;
22/// #[derive(AllegroRegister)]
23/// struct WithoutBitsize {
24///     field_1: bool,
25/// }
26/// ```
27///
28/// ```compile_fail
29/// # #[macro_use] extern crate allegro_motor_derive;
30/// # use bilge::prelude::*;
31/// #[bitsize(1)]
32/// #[derive(AllegroRegister)]
33/// struct WrongMacroOrder {
34///     field_1: bool,
35/// }
36/// ```
37///
38/// # Panics
39/// Panics on a Lex Error if parsing fails.
40///
41#[proc_macro_derive(AllegroRegister)]
42pub fn allegro_derive(item: TokenStream) -> TokenStream {
43    let DeriveInput { ident, attrs, .. } = parse_macro_input!(item as DeriveInput);
44
45    let bitsize_assignment_function: proc_macro2::TokenStream = match analyze_bitsize(&attrs) {
46        Ok(bitsize) => format!(
47            "bilge::arbitrary_int::u{}::new_unchecked(value)",
48            proc_macro2::Literal::u16_unsuffixed(bitsize)
49        )
50        .parse()
51        .unwrap(),
52        Err(error_msg) => return error_msg.into_compile_error().into(),
53    };
54
55    quote! {
56        impl crate::regs::AllegroRegister for #ident {
57            fn value(&self) -> u16 {
58                self.value.into()
59            }
60
61            fn set_value(&mut self, value: u16) {
62                unsafe { self.value = #bitsize_assignment_function };
63            }
64        }
65    }
66    .into()
67}
68
69fn analyze_bitsize(attrs: &[syn::Attribute]) -> Result<u16, syn::Error> {
70    let mut bitsizes = attrs.iter().filter_map(|attr| {
71        if attr.path().is_ident("bitsize") {
72            let a: syn::LitInt = attr.parse_args().unwrap();
73            Some(a.base10_parse::<u16>().unwrap())
74        } else {
75            None
76        }
77    });
78
79    match bitsizes.next() {
80        Some(bitsize) => Ok(bitsize),
81        None => Err(syn::Error::new_spanned(
82            attrs.iter().next(),
83            "'bitsize' not found in object attributes.",
84        )),
85    }
86}
87
88/// Derive macro to implement the `Parity` trait.
89///
90/// See the [original trait][trait] for examples and complete documentation.
91///
92/// [trait]: ../allegro_motor_drivers/regs/trait.AllegroRegister.html
93
94#[proc_macro_derive(Parity)]
95pub fn parity_derive(item: TokenStream) -> TokenStream {
96    let DeriveInput {
97        ident, generics, ..
98    } = parse_macro_input!(item as DeriveInput);
99    let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
100
101    quote! {
102        impl #impl_generics crate::io::Parity for #ident #type_generics #where_clause {
103            fn set_odd_parity(&mut self) {
104                let calculated_parity = crate::io::parity(self.value);
105                let next_parity = !(self.parity() ^ calculated_parity);
106                self.set_parity(next_parity);
107            }
108        }
109    }
110    .into()
111}