use proc_macro::TokenStream;
use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::quote;
use syn::parse::{Parse, ParseStream, Result as ParseResult};
use syn::{Data, DeriveInput, Field, Ident, LitInt, LitStr, Token, Type};
#[derive(Debug)]
struct BitField<'a> {
parent: Option<&'a Field>,
getter: Option<Ident>,
setter: Option<Ident>,
msb: LitInt,
lsb: LitInt,
as_type: Option<Type>,
doc: Option<LitStr>,
}
impl<'a> BitField<'a> {
fn codegen(&self, ident: &Ident) -> TokenStream2 {
let empty_str = LitStr::new("", Span::call_site());
let getter = &self.getter;
let setter = &self.setter;
let msb = &self.msb;
let lsb = &self.lsb;
let as_type = &self.as_type;
let field = self.parent.unwrap();
let field_name = &field.ident;
let value_type = &field.ty;
let vis = &field.vis;
let (getter_doc, setter_doc) = match self.doc {
Some(ref x) => {
let sa = format!(
"See also: [`{}`]({}::{})",
quote!(#getter),
quote!(#ident),
quote!(#getter)
);
let gd = x.clone();
let sd = if getter.is_none() {
x.clone()
} else {
LitStr::new(&sa, Span::call_site())
};
(gd, sd)
}
None => (empty_str.clone(), empty_str),
};
let getter_tokens = if getter.is_some() {
if self.is_as_bool() {
quote! {
#[doc = #getter_doc]
#[inline]
#vis fn #getter(&self) -> bool {
let mask = ((1 << (#msb - #lsb + 1)) - 1) << #lsb;
((self.#field_name & mask) >> #lsb) != 0
}
}
} else if as_type.is_some() {
quote! {
#[doc = #getter_doc]
#[inline]
#vis fn #getter(&self) -> #as_type {
let one: #value_type = 1;
let (mask, over) = one.overflowing_shl(#msb - #lsb + 1);
let mask = if over {
#value_type::MAX
} else {
(mask - 1) << #lsb
};
((self.#field_name & mask) >> #lsb) as #as_type
}
}
} else {
quote! {
#[doc = #getter_doc]
#[inline]
#vis fn #getter(&self) -> #value_type {
let one: #value_type = 1;
let (mask, over) = one.overflowing_shl(#msb - #lsb + 1);
let mask = if over {
#value_type::MAX
} else {
(mask - 1) << #lsb
};
(self.#field_name & mask) >> #lsb
}
}
}
} else {
quote! {}
};
let setter_tokens = if setter.is_some() {
if self.is_as_bool() {
quote! {
#[doc = #setter_doc]
#[inline]
#vis fn #setter(&mut self, value: bool) {
if value {
self.#field_name |= 1 << #lsb;
} else {
self.#field_name &= !(1 << #lsb);
}
}
}
} else if as_type.is_some() {
quote! {
#[doc = #setter_doc]
#[inline]
#vis fn #setter(&mut self, value: #as_type) {
let one: #value_type = 1;
let (mask, over) = one.overflowing_shl(#msb - #lsb + 1);
let mask = if over {
#value_type::MAX
} else {
(mask - 1) << #lsb
};
self.#field_name &= !mask;
self.#field_name |= ((value as #value_type) << #lsb) & mask;
}
}
} else {
quote! {
#[doc = #setter_doc]
#[inline]
#vis fn #setter(&mut self, value: #value_type) {
let one: #value_type = 1;
let (mask, over) = one.overflowing_shl(#msb - #lsb + 1);
let mask = if over {
#value_type::MAX
} else {
(mask - 1) << #lsb
};
self.#field_name &= !mask;
self.#field_name |= (value << #lsb) & mask;
}
}
}
} else {
quote! {}
};
quote! {
#getter_tokens
#setter_tokens
}
}
fn is_as_bool(&self) -> bool {
self.as_type.as_ref().map_or(false, |x| match x {
Type::Path(p) => p.path.is_ident("bool"),
_ => false,
})
}
}
impl<'a> Parse for BitField<'a> {
fn parse(input: ParseStream<'_>) -> ParseResult<Self> {
let getter = input
.parse::<Token![_]>()
.ok()
.map_or(input.parse::<Ident>().ok(), |_| None);
let setter = input
.parse::<Token![,]>()
.ok()
.map_or(setter_for_name(&getter), |_| {
input
.parse::<Token![_]>()
.ok()
.map_or(input.parse::<Ident>().ok(), |_| None)
});
let _at_token: Token![@] = input.parse()?;
let range = input.parse::<LitStr>()?;
let range_str = range.value();
let range_vec: Vec<&str> = range_str.as_str().split(':').collect();
let msb = LitInt::new(range_vec.get(0).unwrap(), Span::call_site());
let lsb = LitInt::new(
range_vec.get(1).or_else(|| range_vec.get(0)).unwrap(),
Span::call_site(),
);
let as_type = input
.parse::<Token![as]>()
.ok()
.and_then(|_| input.parse::<Type>().ok());
let doc: Option<LitStr> = input.parse()?;
Ok(Self {
parent: None,
getter,
setter,
msb,
lsb,
as_type,
doc,
})
}
}
#[proc_macro_derive(BitFields, attributes(bitfield))]
pub fn derive_bitfields(input: TokenStream) -> TokenStream {
let ast: DeriveInput = syn::parse(input).unwrap();
let ident = &ast.ident;
let generics = &ast.generics;
let bitfields = parse_bitfields(&ast);
let bitfields_impls: Vec<TokenStream2> = bitfields.iter().map(|x| x.codegen(ident)).collect();
let tokens = quote! {
#[automatically_derived]
impl #generics #ident #generics {
#(#bitfields_impls)*
}
};
tokens.into()
}
fn parse_bitfields(ast: &DeriveInput) -> Vec<BitField> {
let mut bitfields: Vec<BitField> = vec![];
if let Data::Struct(s) = &ast.data {
for f in &s.fields {
for a in &f.attrs {
if a.path.is_ident("bitfield") {
let mut bitfield: BitField = a.parse_args().unwrap();
bitfield.parent = Some(f);
bitfields.push(bitfield);
}
}
}
}
bitfields
}
fn setter_for_name(name: &Option<Ident>) -> Option<Ident> {
name.as_ref()
.map(|x| Ident::new(&format!("set_{}", x), Span::call_site()))
}