use crate::tokens::BitstructFieldTargetType;
use super::*;
pub(crate) fn gen_bitstruct_field_enum_types_def(
cache: &mut Cache,
bitstruct: &Bitstruct,
) -> Result<TokenStream2> {
let mut enum_types_def = Vec::new();
for field in bitstruct.fields.iter() {
if let Some(field_target_type) = &field.target_type {
if let BitstructFieldTargetType::InlineEnum(field_enum_type) = field_target_type {
let field_bit_width = field.bit_width()?;
check_bitstruct_enum_type_def(field_enum_type, field_bit_width)?;
enum_types_def.push(unsafe {
unchecked_gen_bitstruct_field_enum_type_def(
cache,
field_enum_type,
field_bit_width,
false,
)?
});
}
}
}
Ok(quote! {
#(#enum_types_def)*
})
}
pub fn check_bitstruct_enum_type_def(
field_enum_type: &BitstructFieldEnumType,
field_bit_width: u32,
) -> Result<()> {
if field_bit_width > BitstructFieldEnumType::SUPPORT_MAX_BIT_WIDTH {
return Err(syn::Error::new(
field_enum_type.span(),
format!(
"field bit size must be less than or equal to {0}, biger than {0} is not supported",
BitstructFieldEnumType::SUPPORT_MAX_BIT_WIDTH
),
));
}
let two_power_of_field_bit_width = 2_usize.pow(field_bit_width);
if field_enum_type.variants.len() > 0 {
let variants = &field_enum_type.variants;
let mut variant_count = 0;
let mut variant_max_value = core::usize::MIN;
for variant in variants.iter() {
variant_count += 1;
let value = variant.value.base10_parse::<i128>()?;
if value < 0 {
return Err(syn::Error::new(
variant.value.span(),
"enum variant value must be non-negative in bitstruct field",
));
}
if value > variant_max_value as i128 {
variant_max_value = value as usize;
}
}
if variant_count > two_power_of_field_bit_width {
return Err(syn::Error::new(
field_enum_type.span(),
format!(
"enum type variant count must be less than or equal to 2^{}",
field_bit_width
),
));
}
if variant_max_value >= two_power_of_field_bit_width {
return Err(syn::Error::new(
field_enum_type.span(),
format!(
"enum type variant value must be less than 2^{}",
field_bit_width
),
));
}
} else {
return Err(syn::Error::new(
field_enum_type.span(),
"enum type must have variants",
));
}
Ok(())
}
pub(super) unsafe fn unchecked_gen_bitstruct_field_enum_type_def(
cache: &mut Cache,
field_enum_type: &BitstructFieldEnumType,
field_bit_width: u32,
impl_trait: bool,
) -> Result<TokenStream2> {
let enum_ident = &field_enum_type.ident;
let enum_variants = &field_enum_type.variants;
let mut variants_value_set = std::collections::HashSet::new();
let mut enum_variants_def = vec![];
let mut enum_value2variant_arm = vec![];
let mut enum_variant2value_arm = vec![];
for variant in enum_variants.iter() {
let variant_attrs = &variant.attrs;
let variant_ident = &variant.ident;
let variant_value = &variant.value;
enum_variants_def.push(quote! {
#(#variant_attrs)*
#variant_ident = #variant_value,
});
enum_value2variant_arm.push(quote! {
#variant_value => #enum_ident::#variant_ident,
});
enum_variant2value_arm.push(quote! {
#enum_ident::#variant_ident => #variant_value,
});
variants_value_set.insert(variant_value.base10_parse::<usize>()?);
}
for i in 0..(2_usize.pow(field_bit_width as u32)) {
if !variants_value_set.contains(&i) {
let ident = syn::Ident::new(&format!("__Reserved{}", i), field_enum_type.span());
let value = syn::LitInt::new(&format!("{}", i), field_enum_type.span());
enum_variants_def.push(quote! {
#ident = #value,
});
enum_value2variant_arm.push(quote! {
#value => #enum_ident::#ident,
});
enum_variant2value_arm.push(quote! {
#enum_ident::#ident => #value,
});
}
}
let enum_attrs = &field_enum_type.attrs;
let enum_vis = &field_enum_type.vis;
let sotre_type = tokens::BitstructField::infer_store_type_by_field_bit_width(field_bit_width)?;
if impl_trait {
Ok(quote! {
#(#enum_attrs)*
#enum_vis enum #enum_ident {
#(#enum_variants_def)*
}
unsafe impl bitstructs::BitstructFromValue for #enum_ident {
const BIT_WIDTH: u32 = #field_bit_width;
type StoreType = #sotre_type;
unsafe fn from_value(value: Self::StoreType) -> Self {
match value {
#(#enum_value2variant_arm)*
_ => unreachable!(),
}
}
}
unsafe impl bitstructs::BitstructToValue for #enum_ident {
const BIT_WIDTH: u32 = #field_bit_width;
type StoreType = #sotre_type;
unsafe fn to_value(self) -> Self::StoreType {
match self {
#(#enum_variant2value_arm)*
}
}
}
})
} else {
cache.field_enum_type_match_arms.insert(
field_enum_type.ident.clone(),
(enum_value2variant_arm, enum_variant2value_arm),
);
Ok(quote! {
#(#enum_attrs)*
#enum_vis enum #enum_ident {
#(#enum_variants_def)*
}
})
}
}