use proc_macro2::Span;
use syn::parse::{Parse, ParseStream};
use syn::spanned::Spanned;
use syn::{Ident, LitInt, Token};
use crate::model::*;
impl Parse for BitfieldArgs {
fn parse(input: ParseStream) -> syn::Result<Self> {
let storage_ident: Ident = input.parse()?;
let storage_span = storage_ident.span();
let storage = StorageKind::from_str(&storage_ident.to_string()).ok_or_else(|| {
syn::Error::new(
storage_span,
format!(
"unsupported storage type `{}`, expected one of: u8, u16, u32, u64, u128",
storage_ident
),
)
})?;
let mut order: Option<(BitOrder, Span)> = None;
let mut width: Option<(u32, Span)> = None;
while !input.is_empty() {
input.parse::<Token![,]>()?;
if input.is_empty() {
break;
}
let key: Ident = input.parse()?;
input.parse::<Token![=]>()?;
match key.to_string().as_str() {
"order" => {
let val: Ident = input.parse()?;
let bit_order = match val.to_string().as_str() {
"msb0" => BitOrder::Msb0,
"lsb0" => BitOrder::Lsb0,
_ => {
return Err(syn::Error::new(
val.span(),
"expected `msb0` or `lsb0`",
))
}
};
order = Some((bit_order, key.span()));
}
"width" => {
let val: LitInt = input.parse()?;
let w: u32 = val.base10_parse()?;
width = Some((w, val.span()));
}
_ => {
return Err(syn::Error::new(
key.span(),
format!("unknown attribute `{}`", key),
))
}
}
}
let (order_val, order_span) = order.ok_or_else(|| {
syn::Error::new(Span::call_site(), "missing required `order = msb0|lsb0`")
})?;
Ok(BitfieldArgs {
storage,
storage_span,
order: order_val,
order_span,
width: width.map(|(w, _)| w),
width_span: width.map(|(_, s)| s),
})
}
}
struct BitsAttr {
range: BitRange,
readonly: bool,
aliases: Vec<String>,
overlay: Option<String>,
}
fn parse_bits_attr(input: ParseStream) -> syn::Result<BitsAttr> {
let mut readonly = false;
let mut aliases = Vec::new();
let mut overlay = None;
let range = if input.peek(LitInt) {
let start_lit: LitInt = input.parse()?;
let start: u32 = start_lit.base10_parse()?;
let span = start_lit.span();
if input.peek(Token![..]) {
let dots_span = input.parse::<Token![..]>()?.span();
let _ = dots_span;
let inclusive = input.peek(Token![=]);
if inclusive {
input.parse::<Token![=]>()?;
}
let end_lit: LitInt = input.parse()?;
let end_val: u32 = end_lit.base10_parse()?;
let end = if inclusive { end_val } else { end_val - 1 };
BitRange {
start,
end,
span,
}
} else {
BitRange {
start,
end: start,
span,
}
}
} else {
return Err(input.error("expected bit index or range"));
};
while !input.is_empty() {
input.parse::<Token![,]>()?;
if input.is_empty() {
break;
}
let key: Ident = input.parse()?;
match key.to_string().as_str() {
"readonly" => {
readonly = true;
}
"alias" => {
input.parse::<Token![=]>()?;
if input.peek(syn::token::Bracket) {
let content;
syn::bracketed!(content in input);
while !content.is_empty() {
let lit: syn::LitStr = content.parse()?;
aliases.push(lit.value());
if !content.is_empty() {
content.parse::<Token![,]>()?;
}
}
} else {
let lit: syn::LitStr = input.parse()?;
aliases.push(lit.value());
}
}
"overlay" => {
input.parse::<Token![=]>()?;
let lit: syn::LitStr = input.parse()?;
overlay = Some(lit.value());
}
_ => {
return Err(syn::Error::new(
key.span(),
format!("unknown bits attribute `{}`", key),
))
}
}
}
Ok(BitsAttr {
range,
readonly,
aliases,
overlay,
})
}
fn determine_field_type(ty: &syn::Type) -> FieldType {
if let syn::Type::Path(tp) = ty {
if let Some(seg) = tp.path.segments.last() {
let name = seg.ident.to_string();
if name == "bool" {
return FieldType::Bool;
}
if let Some(sk) = StorageKind::from_str(&name) {
return FieldType::Primitive(sk);
}
}
}
FieldType::Nested(ty.clone())
}
pub fn parse_struct(
args: &BitfieldArgs,
item: &syn::ItemStruct,
) -> syn::Result<BitfieldDef> {
let effective_width = args.width.unwrap_or_else(|| args.storage.bit_width());
let fields_named = match &item.fields {
syn::Fields::Named(f) => f,
_ => {
return Err(syn::Error::new_spanned(
item,
"bitfield requires a struct with named fields",
))
}
};
let mut field_defs = Vec::new();
for field in &fields_named.named {
let field_name = field.ident.as_ref().unwrap();
let bits_attr = field
.attrs
.iter()
.find(|a| a.path().is_ident("bits"))
.ok_or_else(|| {
syn::Error::new_spanned(field_name, "missing `#[bits(...)]` attribute")
})?;
let parsed: BitsAttr = bits_attr.parse_args_with(parse_bits_attr)?;
let name_str = field_name.to_string();
let (accessor_name, implicit_readonly) = if name_str.starts_with('_') {
(name_str[1..].to_string(), true)
} else {
(name_str.clone(), false)
};
let readonly = parsed.readonly || implicit_readonly;
let field_type = determine_field_type(&field.ty);
field_defs.push(FieldDef {
name: field_name.clone(),
accessor_name,
ty: field_type,
raw_ty: field.ty.clone(),
range: parsed.range,
readonly,
aliases: parsed.aliases,
overlay: parsed.overlay,
span: field_name.span(),
});
}
let user_attrs: Vec<syn::Attribute> = item
.attrs
.iter()
.filter(|a| !a.path().is_ident("bitfield"))
.cloned()
.collect();
Ok(BitfieldDef {
args: args.clone(),
effective_width,
fields: field_defs,
vis: item.vis.clone(),
name: item.ident.clone(),
user_attrs,
})
}