use proc_macro2::Ident;
use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote};
use syn::Expr;
use crate::parser::*;
mod bit_helpers;
mod bit_pattern;
mod enum_gen;
mod pack;
mod packed_struct;
mod register_traits;
mod unpack;
pub use packed_struct::*;
pub use register_traits::*;
pub fn generate_interface_objects(interface_def: &InterfaceObjectsDefinition) -> syn::Result<TokenStream2> {
let mut generated = TokenStream2::new();
for definition in &interface_def.definitions {
match definition {
Definition::Register(register) => {
let register_code = generate_register(interface_def, register)?;
generated.extend(register_code);
}
Definition::Struct(struct_def) => {
let struct_code = generate_struct(interface_def, struct_def)?;
generated.extend(struct_code);
}
Definition::Enum(enum_def) => {
let enum_code = enum_gen::generate_enum(enum_def)?;
generated.extend(enum_code);
}
}
}
Ok(generated)
}
fn generate_register(
interface_def: &InterfaceObjectsDefinition,
register: &RegisterDefinition,
) -> syn::Result<TokenStream2> {
let register_name = ®ister.name;
let unpacked_name = format_ident!("{}Unpacked", register_name);
let attrs = interface_def.get_effective_register_attrs(register)?;
let (addr, mode, size) = extract_register_attrs(®ister.name, &attrs)?;
let (codec_error, i2c_codec, spi_codec) = extract_codec_attrs(®ister.name, &attrs)?;
let packed_structs = generate_packed_struct_pair(
interface_def,
register_name,
&unpacked_name,
®ister.fields,
®ister.attributes,
size,
)?;
let trait_impls = generate_register_trait_implementations(
interface_def,
register_name,
&unpacked_name,
&addr,
&mode,
size,
&codec_error,
&i2c_codec,
&spi_codec,
)?;
Ok(quote! {
#packed_structs
#trait_impls
})
}
fn generate_struct(
interface_def: &InterfaceObjectsDefinition,
struct_def: &StructDefinition,
) -> syn::Result<TokenStream2> {
let struct_name = &struct_def.name;
let unpacked_name = format_ident!("{}Unpacked", struct_name);
let attrs = interface_def.get_effective_struct_attrs(struct_def)?;
let size = extract_struct_size(&struct_def.name, &attrs)?;
let packed_structs = generate_packed_struct_pair(
interface_def,
struct_name,
&unpacked_name,
&struct_def.fields,
&struct_def.attributes,
size,
)?;
Ok(packed_structs)
}
fn extract_register_attrs(register_name: &Ident, attrs: &[Attr]) -> syn::Result<(TokenStream2, String, usize)> {
let mut addr = None;
let mut mode = None;
let mut size = None;
for attr in attrs {
match attr.name.to_string().as_str() {
"addr" => {
let attr_value = &attr.value;
addr = Some(quote! { #attr_value });
}
"mode" => {
mode = Some(extract_mode_string(&attr.value)?);
}
"size" => {
size = Some(extract_size_integer(&attr.value)?);
}
_ => {}
}
}
let addr = addr.ok_or_else(|| syn::Error::new_spanned(register_name, "Missing required 'addr' attribute"))?;
let mode = mode.ok_or_else(|| syn::Error::new_spanned(register_name, "Missing required 'mode' attribute"))?;
let size = size.ok_or_else(|| syn::Error::new_spanned(register_name, "Missing required 'size' attribute"))?;
Ok((addr, mode, size))
}
fn extract_struct_size(struct_name: &Ident, attrs: &[Attr]) -> syn::Result<usize> {
for attr in attrs {
if attr.name.to_string().as_str() == "size" {
return extract_size_integer(&attr.value);
}
}
Err(syn::Error::new_spanned(
struct_name,
"Missing required 'size' attribute",
))
}
fn extract_codec_attrs(
register_name: &Ident,
attrs: &[Attr],
) -> syn::Result<(TokenStream2, TokenStream2, TokenStream2)> {
let mut codec_error = None;
let mut i2c_codec = None;
let mut spi_codec = None;
for attr in attrs {
let attr_value = &attr.value;
match attr.name.to_string().as_str() {
"codec_error" => {
codec_error = Some(quote! { #attr_value });
}
"i2c_codec" => {
i2c_codec = Some(quote! { #attr_value });
}
"spi_codec" => {
spi_codec = Some(quote! { #attr_value });
}
_ => {}
}
}
let codec_error = codec_error
.ok_or_else(|| syn::Error::new_spanned(register_name, "Missing required 'codec_error' attribute"))?;
let i2c_codec =
i2c_codec.ok_or_else(|| syn::Error::new_spanned(register_name, "Missing required 'i2c_codec' attribute"))?;
let spi_codec =
spi_codec.ok_or_else(|| syn::Error::new_spanned(register_name, "Missing required 'spi_codec' attribute"))?;
Ok((codec_error, i2c_codec, spi_codec))
}
fn extract_mode_string(expr: &Expr) -> syn::Result<String> {
match expr {
Expr::Lit(syn::ExprLit {
lit: syn::Lit::Str(lit_str),
..
}) => Ok(lit_str.value()),
Expr::Path(path) => {
if let Some(ident) = path.path.get_ident() {
Ok(ident.to_string())
} else {
Err(syn::Error::new_spanned(
expr,
"Mode must be a string literal or identifier (e.g., \"rw\", r, rw)",
))
}
}
_ => Err(syn::Error::new_spanned(
expr,
"Mode must be a string literal or identifier (e.g., \"rw\", r, rw)",
)),
}
}
fn extract_size_integer(expr: &Expr) -> syn::Result<usize> {
match expr {
Expr::Lit(syn::ExprLit {
lit: syn::Lit::Int(lit_int),
..
}) => lit_int.base10_parse::<usize>(),
_ => Err(syn::Error::new_spanned(expr, "Size must be an integer literal")),
}
}