register-macros 0.0.2

Procedural macros for the "registers" crate
Documentation
use darling::FromField;
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::{Ident, ItemFn, ItemImpl, ItemMod, ItemStruct, parse_quote};

use crate::{MacroArgs, field::Field, util::unsign};

pub struct Register {
    ident: Ident,
    fields: Vec<Field>,
    args: MacroArgs,
}

impl Register {
    pub fn new(item: ItemStruct, args: MacroArgs) -> Self {
        let ident = item.ident;
        let fields: Vec<_> = item
            .fields
            .iter()
            .map(|field| Field::from_field(field).unwrap())
            .collect();
        Self {
            ident,
            fields,
            args,
        }
    }

    fn mod_impl(&self) -> ItemMod {
        let mod_ident = self.mod_ident();
        let struct_impl = self.struct_impl();
        let get_impls = self.get_impls();
        let set_impls = self.set_impls();
        let ident = self.ident.clone();
        let new_impl = self.new_impl();
        let from_impl = self.from_impl();
        let into_impl = self.into_impl();
        let clear_impl = self.clear_impl();
        let eq_raw_impl = self.eq_raw_impl();
        let read_impl = self.read_impl();
        let write_impl = self.write_impl();
        let raw_impl = self.raw_impl();
        parse_quote! {
            mod #mod_ident {
                #struct_impl
                impl #ident {
                    #new_impl
                    #clear_impl
                    #( #get_impls )*
                    #( #set_impls )*
                    #raw_impl
                    #read_impl
                    #write_impl
                }
                #from_impl
                #into_impl
                #eq_raw_impl
            }
        }
    }

    fn new_impl(&self) -> ItemFn {
        parse_quote! {
            pub fn new() -> Self {
                Self { reg: 0 }
            }
        }
    }

    fn read_impl(&self) -> Option<ItemFn> {
        let inner_ty = self.inner_type();
        if self.args.read {
            Some(parse_quote! {
                pub unsafe fn read(&mut self, addr: *const #inner_ty) {
                    unsafe { self.reg = core::ptr::read_volatile(addr) }
                }
            })
        } else {
            None
        }
    }

    fn raw_impl(&self) -> ItemFn {
        let inner_ty = self.inner_type();
        parse_quote! {
            pub fn raw(&self) -> #inner_ty {
                self.reg
            }
        }
    }

    fn write_impl(&self) -> Option<ItemFn> {
        let inner_ty = self.inner_type();
        if self.args.write {
            Some(parse_quote! {
                pub unsafe fn write(&self, addr: *mut #inner_ty) {
                    unsafe { core::ptr::write_volatile(addr, self.reg) }
                }
            })
        } else {
            None
        }
    }

    fn get_impls(&self) -> Vec<ItemFn> {
        self.fields
            .iter()
            .filter_map(|f| {
                if f.read {
                    Some(f.get_impl(self.args.size))
                } else {
                    None
                }
            })
            .collect()
    }

    fn set_impls(&self) -> Vec<ItemFn> {
        self.fields
            .iter()
            .filter_map(|f| {
                if f.write {
                    Some(f.set_impl(self.args.size))
                } else {
                    None
                }
            })
            .collect()
    }

    fn struct_impl(&self) -> ItemStruct {
        let ident = self.ident.clone();
        let inner_ty = self.inner_type();
        parse_quote! {
            #[derive(Debug, Clone)]
            pub struct #ident {
                reg: #inner_ty,
            }
        }
    }

    fn clear_impl(&self) -> ItemFn {
        parse_quote! {
            pub fn clear(&mut self) {
                self.reg = 0;
            }
        }
    }
    #[allow(clippy::wrong_self_convention)]
    fn from_impl(&self) -> ItemImpl {
        let ident = self.ident.clone();
        let inner_ty = self.inner_type();
        parse_quote! {
            impl From<#inner_ty> for #ident {
                fn from(value: #inner_ty) -> Self {
                    Self { reg: value }
                }
            }
        }
    }

    #[allow(clippy::wrong_self_convention)]
    fn into_impl(&self) -> ItemImpl {
        let ident = self.ident.clone();
        let inner_ty = self.inner_type();
        parse_quote! {
            impl Into<#inner_ty> for #ident {
                fn into(self) -> #inner_ty {
                    self.reg
                }
            }
        }
    }

    fn eq_raw_impl(&self) -> ItemImpl {
        let ident = self.ident.clone();
        let inner_ty = self.inner_type();
        parse_quote! {
            impl PartialEq<#inner_ty> for #ident {
                fn eq(&self, other: &#inner_ty) -> bool {
                    self.reg == *other
                }
            }
        }
    }

    fn mod_ident(&self) -> Ident {
        format_ident!("_register_{}", self.ident.to_string().to_lowercase())
    }

    pub fn implement(&self) -> TokenStream {
        let mod_impl = self.mod_impl();
        let mod_ident = self.mod_ident();
        quote! {
            #mod_impl
            pub use #mod_ident::*;
        }
    }

    fn inner_type(&self) -> syn::Type {
        unsign(self.args.size)
    }
}