use quote::{quote, quote_spanned};
use syn::Generics;
use crate::{
newtypes::{BitLenExpr, StorageTypeExpr, TypeExpr},
MacroArgs, OptIn,
};
pub fn not_supported_err_span(what: &str, span: proc_macro2::Span) -> proc_macro::TokenStream {
quote_spanned! {
span => compile_error!(concat!(#what, " are not supported"));
}
.into()
}
pub fn not_supported_err(what: &str) -> proc_macro::TokenStream {
not_supported_err_span(what, proc_macro2::Span::call_site())
}
pub fn are_generics_empty(generics: &Generics) -> bool {
generics.lt_token.is_none()
&& generics.params.is_empty()
&& generics.gt_token.is_none()
&& generics.where_clause.is_none()
}
pub fn gen_explicit_bit_length_assertion(
explicit_bit_length: Option<usize>,
actual_bit_length: &BitLenExpr,
) -> proc_macro2::TokenStream {
match explicit_bit_length {
Some(explicit_bit_length) => quote! {
const _: () = if (#explicit_bit_length) != (#actual_bit_length) {
panic!("explicit bit length does not match actual bit length")
} else {
()
};
},
None => quote! {},
}
}
pub struct BitPieceGenImplParams<'a> {
pub type_ident: &'a syn::Ident,
pub type_vis: &'a syn::Visibility,
pub macro_args: &'a MacroArgs,
pub mut_type_ident: &'a syn::Ident,
pub bit_len: &'a BitLenExpr,
pub storage_type: &'a StorageTypeExpr,
pub fields_type: &'a TypeExpr,
pub to_fields_code: proc_macro2::TokenStream,
pub from_fields_code: proc_macro2::TokenStream,
pub to_bits_code: proc_macro2::TokenStream,
pub try_from_bits_code: proc_macro2::TokenStream,
pub zeroes: proc_macro2::TokenStream,
pub ones: proc_macro2::TokenStream,
pub min: proc_macro2::TokenStream,
pub max: proc_macro2::TokenStream,
}
pub fn bitpiece_gen_impl<'a>(params: BitPieceGenImplParams<'a>) -> proc_macro2::TokenStream {
let BitPieceGenImplParams {
type_ident,
type_vis,
macro_args,
mut_type_ident,
bit_len,
fields_type,
storage_type,
to_fields_code,
from_fields_code,
to_bits_code,
try_from_bits_code,
zeroes,
ones,
min,
max,
} = params;
let base_code = quote! {
#[automatically_derived]
impl ::bitpiece::BitPiece for #type_ident {
const BITS: usize = (#bit_len);
const ZEROES: Self = #zeroes;
const ONES: Self = #ones;
const MIN: Self = #min;
const MAX: Self = #max;
type Bits = #storage_type;
type Converter = Self;
fn try_from_bits(bits: Self::Bits) -> Option<Self> {
Self::try_from_bits(bits)
}
fn from_bits(bits: Self::Bits) -> Self {
Self::from_bits(bits)
}
fn to_bits(self) -> Self::Bits {
self.to_bits()
}
}
impl #type_ident {
pub const fn try_from_bits(bits: #storage_type) -> Option<Self> {
#try_from_bits_code
}
pub const fn from_bits(bits: #storage_type) -> Self {
Self::try_from_bits(bits).unwrap()
}
pub const fn to_bits(self) -> #storage_type {
#to_bits_code
}
}
};
let opt_mut_struct_code = macro_args.filter_opt_in_code(
OptIn::MutStruct,
quote! {
#[automatically_derived]
impl ::bitpiece::BitPieceHasMutRef for #type_ident {
type MutRef<'s> = #mut_type_ident<'s>;
}
::bitpiece::bitpiece_define_mut_ref_type! { #type_ident, #mut_type_ident, #type_vis }
},
);
let opt_fields_struct_code = macro_args.filter_opt_in_code(
OptIn::FieldsStruct,
quote! {
#[automatically_derived]
impl ::bitpiece::BitPieceHasFields for #type_ident {
type Fields = #fields_type;
fn from_fields(fields: Self::Fields) -> Self {
Self::from_fields(fields)
}
fn to_fields(self) -> Self::Fields {
self.to_fields()
}
}
impl #type_ident {
pub const fn from_fields(fields: #fields_type) -> Self {
#from_fields_code
}
pub const fn to_fields(self) -> #fields_type {
#to_fields_code
}
}
},
);
let opt_const_eq_code = macro_args.filter_opt_in_code(
OptIn::ConstEq,
quote! {
impl #type_ident {
pub const fn const_eq(a: Self, b: Self) -> bool {
a.to_bits() == b.to_bits()
}
}
},
);
quote! {
#base_code
#opt_mut_struct_code
#opt_fields_struct_code
#opt_const_eq_code
}
}