bitstates 0.1.0

Reactive bit state management with event callbacks
Documentation
use proc_macro::TokenStream;
use quote::{format_ident, quote};
use syn::{Data, DeriveInput, parse_macro_input};

pub fn derive(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);

    let enum_name = &input.ident;

    let struct_name = format_ident!("{}States", enum_name);

    let enums = match &input.data {
        Data::Enum(data_enum) => &data_enum.variants,
        _ => panic!("Not an Enum"),
    };

    let enum_data: Vec<_> = enums
        .iter()
        .map(|v| {
            let variant_name = &v.ident;
            let variant_value = match &v.discriminant {
                Some((_, expr)) => expr,
                None => panic!("All variants/flags must have explicit values that are greater than Zero! (e.g., Ready = 1)"),
            };
            (variant_name, variant_value)
        })
        .collect();

    let branch_arms = enum_data.iter().map(|(vn, vv)| {
        quote! {
          #vv => Some(#enum_name::#vn),
        }
    });

    let max_branch_value = enum_data
        .iter()
        .filter_map(|x| {
            if let syn::Expr::Lit(el) = x.1 {
                if let syn::Lit::Int(lit_int) = &el.lit {
                    return lit_int.base10_parse::<u8>().ok();
                }
            }
            None
        })
        .max()
        .unwrap_or(0);

    let bit_state_type = match max_branch_value {
        0..=7 => {
            quote! { u8 }
        }
        8..=15 => {
            quote! { u16 }
        }
        16..=31 => {
            quote! { u32 }
        }
        32..=63 => {
            quote! { u64 }
        }
        64..=127 => {
            quote! { u128 }
        }
        n => {
            return syn::Error::new(
                proc_macro2::Span::call_site(),
                format!("Bit position {} is too large (max 127)", n),
            )
            .to_compile_error()
            .into();
        }
    };

    let expanded = quote! {

        impl #enum_name {
          pub fn from_flagbit(n: u8) ->  Option<Self>{
            match n {
              #(#branch_arms)*
              _ => None
            }
          }

          #[inline]
          pub const fn get_flagbit(&self) -> u8 {
            *self as u8
          }

          pub fn get_flagmask(&self) -> #bit_state_type {
            return 1 << self.get_flagbit()
          }

        }
        pub struct #struct_name<Fup, Fdown>
        where
          Fup: Fn(#enum_name),
          Fdown: Fn(#enum_name)
        {
          bit_state: #bit_state_type,
          up_event: Fup,
          down_event: Fdown
        }

        impl <Fup, Fdown> #struct_name<Fup, Fdown>
        where
          Fup: Fn(#enum_name),
          Fdown: Fn(#enum_name)
        {
          pub fn new(up_event: Fup, down_event: Fdown) -> Self {
            Self {
              bit_state: 0,
              up_event,
              down_event
            }
          }

          pub fn set(&mut self, new: #bit_state_type ) {

            let mut up_bits = (self.bit_state ^ new) & new;
            let mut down_bits = (self.bit_state ^ new) & (!new);

            if up_bits == 0 && down_bits == 0 {
              return;
            }

            while up_bits != 0 {
              let rightmost_set_bit = up_bits.trailing_zeros() as u8;
              if let Some(flag) = #enum_name::from_flagbit(rightmost_set_bit){
                (self.up_event)(flag);
              };
              up_bits &= up_bits - 1;
            }

            while down_bits != 0 {
              let rightmost_set_bit = down_bits.trailing_zeros() as u8;
              if let Some(flag) = #enum_name::from_flagbit(rightmost_set_bit){
                (self.down_event)(flag);
              };
              down_bits &= down_bits - 1;
            }
            self.bit_state = new;
          }

          pub fn set_flag(&mut self, flag: #enum_name) {
            let bit_mask = flag.get_flagmask();
            if !self.is_set(flag) {
              self.bit_state |= bit_mask;
              (self.up_event)(flag);
            }
          }

          pub fn reset_flag(&mut self, flag: #enum_name) {
            let bit_mask = flag.get_flagmask();
            if self.is_set(flag) {
              self.bit_state &= !bit_mask;
              (self.down_event)(flag);
            }
          }

          pub fn get(&self) -> #bit_state_type {
            self.bit_state
          }

          pub fn clear(&mut self) {
            self.bit_state = 0;
          }

          #[inline]
          pub fn is_set(&self, flag: #enum_name) -> bool {
            (self.bit_state & flag.get_flagmask()) != 0
          }

        }
    };
    TokenStream::from(expanded)
}