ral-macro 0.2.0

The ral procedural macro implementations
Documentation
use convert_case::{Case, Casing};
use proc_macro2::TokenStream;
use quote::{format_ident, quote, quote_spanned};
use syn::parse::Result;
use syn::spanned::Spanned;
use syn::{Ident, LitInt, LitStr};

use crate::field::{_Field, _Fields};
use crate::field_type::_FieldType;
use crate::register::{_Register, _RegisterWithUses};
use crate::spanned::_Spanned;
use crate::uses::_Uses;

pub(super) fn render_register_with_uses(
    register_with_uses: _RegisterWithUses,
) -> Result<TokenStream> {
    let _RegisterWithUses(uses, register) = register_with_uses;
    let uses = render_uses(uses);
    let definition = render_register(register)?;
    Ok(quote! {
        #uses

        #definition
    })
}

pub(super) fn render_uses(uses: _Uses) -> TokenStream {
    let _Uses(uses) = uses;
    quote! {
        use core::sync::atomic::AtomicPtr;

        use core::convert::TryFrom;

        use ral::{borrow_register, init_register, return_register, value_read, value_write, R, Register, ReadableRegister, VolatileCell, WritableRegister};

        #(#uses)*
    }
}

pub(super) fn render_register(register: _Register) -> Result<TokenStream> {
    let name = format_ident!(
        "{}",
        register.name.to_string().to_case(Case::UpperCamel),
        span = register.name.span()
    );
    let method_name = register.name;
    let description = render_description(&register.description)?;
    let offset = register.offset;
    let value_size = register.value_size;
    let value_type = format_ident!("u{}", value_size.value, span = value_size.span());
    let reset_mask = register.reset_mask;
    let value_reset = register.reset_value;
    let access = register.access.as_ref();
    let register_impl = render_impl(
        register.fields,
        &name,
        &value_type,
        value_size.value,
        access,
    )?;
    let access = render_access(&name, access)?;
    Ok(quote! {
        const REGISTER: AtomicPtr<VolatileCell<<#name as Register>::ValueType>> =
            init_register!(super::BASE_ADDRESS + #offset, #name);

        #description
        pub fn #method_name() -> Option<#name> {
            borrow_register(&REGISTER).map(#name)
        }

        pub struct #name(R<#value_type, #name>);

        impl Drop for #name {
            fn drop(&mut self) {
                let #name(register) = self;
                return_register(&REGISTER, register);
            }
        }

        impl Register for #name {
            type RegisterType = Self;

            type ValueType = #value_type;

            const RESET_MASK: Self::ValueType = #reset_mask;

            const RESET_VALUE: Self::ValueType = #value_reset;
        }

        #access

        #register_impl
    })
}

fn render_description(description: &Option<LitStr>) -> Result<TokenStream> {
    if let Some(description) = description {
        Ok(quote! {
            #[doc = #description]
        })
    } else {
        Ok(TokenStream::new())
    }
}

fn render_access(name: &Ident, access: Option<&LitStr>) -> Result<TokenStream> {
    let access = access
        .map(|lit_str| lit_str.value())
        .unwrap_or(String::from("read-write"));
    match access.as_str() {
        "write-only" | "writeOnce" => render_writable(name),
        "read-only" => render_readable(name),
        _ => {
            let readable = render_readable(name)?;
            let writable = render_writable(name)?;
            Ok(quote! {
                #readable

                #writable
            })
        }
    }
}

fn render_readable(name: &Ident) -> Result<TokenStream> {
    Ok(quote! {
        impl ReadableRegister for #name {
            fn get_bits(&self) -> Self::ValueType {
                self.0.get_bits()
            }

            fn read(&mut self) -> &mut Self::RegisterType {
                self.0.read();
                self
            }
        }
    })
}

fn render_writable(name: &Ident) -> Result<TokenStream> {
    Ok(quote! {
        impl WritableRegister for #name {
            fn set_bits(&mut self, bits: Self::ValueType) -> &mut Self::RegisterType {
                self.0.set_bits(bits);
                self
            }

            fn reset(&mut self) -> &mut Self::RegisterType {
                self.set_bits(Self::RESET_VALUE)
            }

            fn write(&mut self) -> &mut Self::RegisterType {
                self.0.write();
                self
            }
        }
    })
}

fn render_impl(
    fields: _Fields,
    name: &Ident,
    value_type: &Ident,
    value_size: u32,
    access: Option<&LitStr>,
) -> Result<TokenStream> {
    let mut methods = Vec::<TokenStream>::new();
    let methods_iter = fields
        .into_iter()
        .map(|field| render_field(field, &value_type, value_size, access));
    for method in methods_iter {
        methods.push(method?);
    }
    Ok(if methods.is_empty() {
        TokenStream::new()
    } else {
        quote! {
            impl #name {
                #(#methods)*
            }
        }
    })
}

fn render_field(
    field: _Field,
    value_type: &Ident,
    value_size: u32,
    default_access: Option<&LitStr>,
) -> Result<TokenStream> {
    let access = field
        .access
        .as_ref()
        .or(default_access)
        .map(|lit_str| lit_str.value())
        .unwrap_or(String::from("read-write"));
    match access.as_str() {
        "write-only" | "writeOnce" => render_write(&field, value_type, value_size),
        "read-only" => render_read(&field, value_type, value_size),
        _ => {
            let read = render_read(&field, value_type, value_size)?;
            let write = render_write(&field, value_type, value_size)?;
            Ok(quote! {
                #read

                #write
            })
        }
    }
}

fn render_read(field: &_Field, value_type: &Ident, value_size: u32) -> Result<TokenStream> {
    let description = render_description(&field.description)?;
    let offset = &field.offset;
    let mask = build_mask(&field.width, value_size);
    match &field.ty {
        _FieldType::Bool(ty) => {
            let method_name = format_ident!("is_{}_set", field.name);
            Ok(quote! {
                #description
                #[inline]
                pub fn #method_name(&self) -> #ty {
                    value_read!(self, #mask, #offset) == 1
                }
            })
        }
        _FieldType::Primitive(ty) => {
            let method_name = format_ident!("get_{}", field.name);
            Ok(quote! {
                #description
                #[inline]
                pub fn #method_name(&self) -> #ty {
                    value_read!(self, #mask, #offset) as #ty
                }
            })
        }
        _FieldType::Custom(ty) => {
            let ty_span = ty.span();
            let _ = quote_spanned! {ty_span=>
                struct _AssertTryFrom where #ty: core::convert::TryFrom<#value_type>;
            };
            let method_name = format_ident!("get_{}", field.name);
            Ok(quote! {
                #description
                #[inline]
                pub fn #method_name(&self) -> Result<#ty, <#ty as TryFrom<<Self as Register>::ValueType>>::Error> {
                    <#ty as TryFrom<<Self as Register>::ValueType>>::try_from(value_read!(self, #mask, #offset))
                }
            })
        }
    }
}

fn render_write(field: &_Field, value_type: &Ident, value_size: u32) -> Result<TokenStream> {
    let description = render_description(&field.description)?;
    let offset = &field.offset;
    let mask = build_mask(&field.width, value_size);
    match &field.ty {
        _FieldType::Bool(ty) => {
            let method_name = format_ident!("set_{}_value", field.name);
            let method_name_set = format_ident!("set_{}", field.name);
            let method_name_unset = format_ident!("unset_{}", field.name);
            Ok(quote! {
                #description
                #[inline]
                pub fn #method_name(&mut self, value: #ty) -> &mut Self {
                    value_write!(self, #mask, #offset, value as <Self as Register>::ValueType);
                    self
                }

                #description
                #[inline]
                pub fn #method_name_set(&mut self) -> &mut Self {
                    self.#method_name(true)
                }

                #description
                #[inline]
                pub fn #method_name_unset(&mut self) -> &mut Self {
                    self.#method_name(false)
                }
            })
        }
        _FieldType::Primitive(ty) => {
            let method_name = format_ident!("set_{}", field.name);
            Ok(quote! {
                #description
                #[inline]
                pub fn #method_name(&mut self, value: #ty) -> &mut Self {
                    value_write!(self, #mask, #offset, value as <Self as Register>::ValueType);
                    self
                }
            })
        }
        _FieldType::Custom(ty) => {
            let ty_span = ty.span();
            let _ = quote_spanned! {ty_span=>
                struct _AssertTryInto where #value_type: core::convert::TryFrom<#ty>;
            };
            let method_name = format_ident!("set_{}", field.name);
            Ok(quote! {
                #description
                #[inline]
                pub fn #method_name(&mut self, value: #ty) -> Result<&mut Self, <<Self as Register>::ValueType as TryFrom<#ty>>::Error> {
                    value_write!(self, #mask, #offset, <<Self as Register>::ValueType as TryFrom<#ty>>::try_from(value)?);
                    Ok(self)
                }
            })
        }
    }
}

fn build_mask(width: &_Spanned<u32>, value_size: u32) -> LitInt {
    let mask = format!(
        "{mask:#0width$X}u{bits}",
        mask = 2u128.pow(width.value) - 1,
        width = ((value_size / 4) + 2) as usize,
        bits = value_size
    );
    LitInt::new(mask.as_str(), width.span())
}