bitstructs_macro 0.2.1

Procedural macro for bitstructs.
Documentation
use crate::tokens::BitstructRepr;

use super::*;
use quote::{ToTokens, quote};
use syn::Attribute;

pub fn gen_struct_def(bitstruct: &Bitstruct) -> Result<TokenStream2> {
    let attrs = filter_bitstruct_attr(&bitstruct.attrs);
    let vis = &bitstruct.vis;
    let ident = &bitstruct.ident;
    let size = get_struct_aligned_byte_size(bitstruct)?;
    let size = syn::Index::from(size as usize);
    Ok(quote! {
        #(#attrs)*
        #[repr(transparent)]
        #vis struct #ident {
            inner: [u8; #size],
        }
        #[allow(dead_code)]
        impl #ident {
            pub const fn new() -> Self {
                Self {
                    inner: [0; #size],
                }
            }
            pub const fn bytes_len() -> usize {
                #size
            }
            pub fn from_bytes(bytes: &[u8]) -> Result<Self, &'static str> {
                if bytes.len() < #size {
                    return Err("bytes length is too short");
                }
                let mut inner = [0; #size];
                inner.copy_from_slice(&bytes[..#size]);
                Ok(Self { inner })
            }
            pub const fn as_bytes(&self) -> &[u8] {
                &self.inner
            }
            pub const fn as_bytes_mut(&mut self) -> &mut [u8] {
                &mut self.inner
            }
        }
    })
}

pub fn gen_struct_def_with_cow(bitstruct: &Bitstruct) -> Result<TokenStream2> {
    let attrs = filter_bitstruct_attr(&bitstruct.attrs);
    let vis = &bitstruct.vis;
    let ident = &bitstruct.ident;
    let size = get_struct_aligned_byte_size(bitstruct)?;
    let size = syn::Index::from(size as usize);
    Ok(quote! {
        #(#attrs)*
        #[repr(transparent)]
        #vis struct #ident<'a> {
            inner: std::borrow::Cow<'a, [u8]>,
        }
        #[allow(dead_code)]
        impl<'a> #ident<'a> {
            pub fn new() -> Self {
                Self {
                    inner: std::borrow::Cow::Owned(vec![0; #size]),
                }
            }
            pub const fn bytes_len() -> usize {
                #size
            }
            pub fn from_bytes(bytes: &'a [u8]) -> Result<Self,&'static str> {
                if bytes.len() < #size {
                    return Err("bytes length is too short");
                }
                Ok(Self {
                    inner: std::borrow::Cow::Borrowed(&bytes[..#size]),
                })
            }
            pub fn to_bytes(&self) -> &[u8] {
                &self.inner
            }
            pub fn to_bytes_mut(&mut self) -> &mut [u8] {
                self.inner.to_mut()
            }
        }
    })
}

pub fn gen_struct_def_for_small(
    cache: &mut Cache,
    options: &BistructGenOptions,
    bitstruct: &Bitstruct,
) -> Result<TokenStream2> {
    let bitstruct_store_type = match &options.given_store_type {
        Some(store_type) => {
            let given_store_type_bit_width = BitstructRepr::sotre_type_bit_width(store_type)?;
            if given_store_type_bit_width < bitstruct.bit_width()? {
                return Err(syn::Error::new(
                    store_type.span(),
                    "store_type bit size is too small",
                ));
            }
            cache.bitstruct_store_type = Some(store_type.into_token_stream());

            quote! {
                #store_type
            }
        }
        None => {
            let store_type = bitstruct.store_type()?;
            quote! {
                #store_type
            }
        }
    };

    let attrs = filter_bitstruct_attr(&bitstruct.attrs);
    let vis = &bitstruct.vis;
    let ident = &bitstruct.ident;
    Ok(quote! {
        #(#attrs)*
        #[repr(transparent)]
        #vis struct #ident {
            inner: #bitstruct_store_type,
        }
        #[allow(dead_code)]
        impl #ident {
            pub fn new() -> Self {
                Self {
                    inner: 0,
                }
            }
            pub fn from_value(value: #bitstruct_store_type) -> Self {
                Self { inner: value }
            }
            /// # Safety
            /// 1. The pointer must be valid and properly aligned.
            /// 2. Other raw pointer rules.
            /// ```
            /// #   use bitstructs::bitstruct_small;
            /// #   bitstruct_small! {
            /// #       #[derive(Debug, PartialEq, Eq)]
            /// #       pub struct Foo {
            /// #           a: 4
            /// #       }
            /// #   }
            /// // Recommended usage
            /// let foo = Foo::new();
            /// let ptr = foo.to_ptr();
            /// // ...
            /// // other operations
            /// // ...
            /// let foo2 = unsafe { Foo::from_ptr(ptr) };
            ///
            /// // Not recommended usage
            /// let a = 0u8; // assume a is a valid value for Foo
            /// let ptr = &a as *const u8;
            /// let foo = unsafe { Foo::from_ptr(ptr) };
            /// ```
            pub unsafe fn from_ptr(ptr: *const #bitstruct_store_type) -> *const Self {
                ptr as *const Self
            }
            /// # Safety
            /// 1. The pointer must be valid and properly aligned.
            /// 2. Other raw pointer rules.
            /// ```
            /// #   use bitstructs::bitstruct_small;
            /// #   bitstruct_small! {
            /// #       #[derive(Debug, PartialEq, Eq)]
            /// #       pub struct Foo {
            /// #           a: 4
            /// #       }
            /// #   }
            /// // Recommended usage
            /// let mut foo = Foo::new();
            /// let ptr = foo.to_mut_ptr();
            /// // ...
            /// // other operations
            /// // ...
            /// let foo2 = unsafe { Foo::from_mut_ptr(ptr) };
            ///
            /// // Not recommended usage
            /// let mut a = 0u8; // assume a is a valid value for Foo
            /// let ptr = &mut a as *mut u8;
            /// let foo = unsafe { Foo::from_mut_ptr(ptr) };
            /// ```
            pub unsafe fn from_mut_ptr(ptr: *mut #bitstruct_store_type) -> *mut Self {
                ptr as *mut Self
            }
            pub fn to_ptr(&self) -> *const #bitstruct_store_type {
                self as *const Self as *const _
            }
            pub fn to_mut_ptr(&mut self) -> *mut #bitstruct_store_type {
                self as *mut Self as *mut _
            }
            pub fn to_value(self) -> #bitstruct_store_type {
                self.inner
            }
            pub fn as_ref(&self) -> &#bitstruct_store_type {
                &self.inner
            }
            pub fn as_mut(&mut self) -> &mut #bitstruct_store_type {
                &mut self.inner
            }
        }
    })
}

/// 8-bit aligned size of the struct
fn get_struct_aligned_byte_size(bitstruct: &Bitstruct) -> Result<u32> {
    let size = bitstruct.bit_width()?;
    Ok((size + 7) / 8)
}

fn filter_bitstruct_attr(attrs: &[Attribute]) -> Vec<&Attribute> {
    attrs
        .iter()
        .filter(|attr| !attr.path().is_ident(BITSTRUCT_REPR))
        .collect()
}