use proc_macro2::{Ident, TokenStream};
use proc_macro_error2::abort_call_site;
use quote::quote;
use syn::{Data, DeriveInput, Fields, Type};
use crate::shared::{self, fallback::Fallback, unreachable, BitSize};
pub(crate) fn default_bits(item: TokenStream) -> TokenStream {
let derive_input = parse(item);
let (derive_data, _, name, ..) = analyze(&derive_input);
match derive_data {
Data::Struct(data) => generate_struct_default_impl(name, &data.fields),
Data::Enum(_) => abort_call_site!("use derive(Default) for enums"),
_ => unreachable(()),
}
}
fn generate_struct_default_impl(struct_name: &Ident, fields: &Fields) -> TokenStream {
let default_value = fields
.iter()
.map(|field| generate_default_inner(&field.ty))
.reduce(|acc, next| quote!(#acc | #next));
quote! {
impl ::core::default::Default for #struct_name {
fn default() -> Self {
let mut offset = 0;
let value = #default_value;
let value = <#struct_name as Bitsized>::ArbitraryInt::new(value);
Self { value }
}
}
}
}
fn generate_default_inner(ty: &Type) -> TokenStream {
use Type::*;
match ty {
Array(array) => {
let len_expr = &array.len;
let elem_ty = &*array.elem;
let value_shifted = generate_default_inner(elem_ty);
quote! {{
let mut acc = 0;
let mut i = 0;
while i < #len_expr {
let value_shifted = #value_shifted;
acc |= value_shifted;
i += 1;
}
acc
}}
}
Path(path) => {
let field_size = shared::generate_type_bitsize(ty);
quote! {{
let as_int = <#path as Bitsized>::ArbitraryInt::from(<#path as ::core::default::Default>::default()).value();
let as_base_int = as_int as <<Self as Bitsized>::ArbitraryInt as Integer>::UnderlyingType;
let shifted = as_base_int << offset;
offset += #field_size;
shifted
}}
}
Tuple(tuple) => {
tuple
.elems
.iter()
.map(generate_default_inner)
.reduce(|acc, next| quote!(#acc | #next))
.unwrap_or_else(|| quote!(0))
}
_ => unreachable(()),
}
}
fn parse(item: TokenStream) -> DeriveInput {
shared::parse_derive(item)
}
fn analyze(derive_input: &DeriveInput) -> (&Data, TokenStream, &Ident, BitSize, Option<Fallback>) {
shared::analyze_derive(derive_input, false)
}