use quote::quote;
use proc_macro2::Span;
use proc_macro2::TokenStream as TokenStream2;
use syn::parse::{Parse, ParseStream};
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::{parenthesized, parse2, Attribute, Error, Field, Fields, Ident, Index, Result, Token};
pub fn has_valid_repr(attrs: &[Attribute], predicate: impl Fn(&Ident) -> bool + Copy) -> bool {
attrs
.iter()
.filter(|a| a.path.get_ident().map(|a| a == "repr").unwrap_or(false))
.any(|a| {
parse2::<IdentListAttribute>(a.tokens.clone())
.ok()
.and_then(|s| s.idents.iter().find(|s| predicate(s)).map(|_| ()))
.is_some()
})
}
struct IdentListAttribute {
idents: Punctuated<Ident, Token![,]>,
}
impl Parse for IdentListAttribute {
fn parse(input: ParseStream) -> Result<Self> {
let content;
let _paren = parenthesized!(content in input);
Ok(IdentListAttribute {
idents: content.parse_terminated(Ident::parse)?,
})
}
}
pub fn wrap_field_inits(streams: &[TokenStream2], fields: &Fields) -> TokenStream2 {
match *fields {
Fields::Named(_) => quote!( { #(#streams),* } ),
Fields::Unnamed(_) => quote!( ( #(#streams),* ) ),
Fields::Unit => {
unreachable!("#[make_(var)ule] should have already checked that there are fields")
}
}
}
pub fn semi_for(f: &Fields) -> TokenStream2 {
if let Fields::Unnamed(..) = *f {
quote!(;)
} else {
quote!()
}
}
pub fn repr_for(f: &Fields) -> TokenStream2 {
if f.len() == 1 {
quote!(transparent)
} else {
quote!(packed)
}
}
fn suffixed_ident(name: &str, suffix: usize, s: Span) -> Ident {
Ident::new(&format!("{name}_{suffix}"), s)
}
pub(crate) fn generate_per_field_offsets<'a>(
fields: &[FieldInfo<'a>],
fields_are_asule: bool,
mut per_field_code: impl FnMut(&FieldInfo<'a>, &Ident, &Ident) -> TokenStream2, ) -> (TokenStream2, syn::Ident) {
let mut prev_offset_ident = Ident::new("ZERO", Span::call_site());
let mut code = quote!(
const ZERO: usize = 0;
);
for (i, field_info) in fields.iter().enumerate() {
let field = &field_info.field;
let ty = &field.ty;
let ty = if fields_are_asule {
quote!(<#ty as zerovec::ule::AsULE>::ULE)
} else {
quote!(#ty)
};
let new_offset_ident = suffixed_ident("OFFSET", i, field.span());
let size_ident = suffixed_ident("SIZE", i, field.span());
let pf_code = per_field_code(field_info, &prev_offset_ident, &size_ident);
code = quote! {
#code;
const #size_ident: usize = ::core::mem::size_of::<#ty>();
const #new_offset_ident: usize = #prev_offset_ident + #size_ident;
#pf_code;
};
prev_offset_ident = new_offset_ident;
}
(code, prev_offset_ident)
}
#[derive(Clone, Debug)]
pub(crate) struct FieldInfo<'a> {
pub accessor: TokenStream2,
pub field: &'a Field,
pub index: usize,
}
impl<'a> FieldInfo<'a> {
pub fn make_list(iter: impl Iterator<Item = &'a Field>) -> Vec<Self> {
iter.enumerate()
.map(|(i, field)| Self::new_for_field(field, i))
.collect()
}
pub fn new_for_field(f: &'a Field, index: usize) -> Self {
if let Some(ref i) = f.ident {
FieldInfo {
accessor: quote!(#i),
field: f,
index,
}
} else {
let idx = Index::from(index);
FieldInfo {
accessor: quote!(#idx),
field: f,
index,
}
}
}
pub fn setter(&self) -> TokenStream2 {
if let Some(ref i) = self.field.ident {
quote!(#i: )
} else {
quote!()
}
}
}
pub fn extract_parenthetical_zerovec_attrs(
attrs: &mut Vec<Attribute>,
name: &str,
) -> Result<Vec<Ident>> {
let mut ret = vec![];
let mut error = None;
attrs.retain(|a| {
let second_segment = a.path.segments.iter().nth(1);
if let Some(second) = second_segment {
if second.ident == name {
let list = match parse2::<IdentListAttribute>(a.tokens.clone()) {
Ok(l) => l,
Err(_) => {
error = Some(Error::new(
a.span(),
"#[zerovec::name(..)] takes in a comma separated list of identifiers",
));
return false;
}
};
ret.extend(list.idents.iter().cloned());
return false;
}
}
true
});
if let Some(error) = error {
return Err(error);
}
Ok(ret)
}
pub fn extract_zerovec_attributes(attrs: &mut Vec<Attribute>) -> Vec<Attribute> {
let mut ret = vec![];
attrs.retain(|a| {
if a.path.segments.len() == 2 && a.path.segments[0].ident == "zerovec" {
ret.push(a.clone());
return false;
}
true
});
ret
}
#[derive(Default, Copy, Clone)]
pub struct ZeroVecAttrs {
pub skip_kv: bool,
pub skip_ord: bool,
pub serialize: bool,
pub deserialize: bool,
pub debug: bool,
}
pub fn extract_attributes_common(
attrs: &mut Vec<Attribute>,
span: Span,
is_var: bool,
) -> Result<ZeroVecAttrs> {
let mut zerovec_attrs = extract_zerovec_attributes(attrs);
let derive = extract_parenthetical_zerovec_attrs(&mut zerovec_attrs, "derive")?;
let skip = extract_parenthetical_zerovec_attrs(&mut zerovec_attrs, "skip_derive")?;
let name = if is_var { "make_varule" } else { "make_ule" };
if let Some(attr) = zerovec_attrs.get(0) {
return Err(Error::new(
attr.span(),
format!("Found unknown or duplicate attribute for #[{name}]"),
));
}
let mut attrs = ZeroVecAttrs::default();
for ident in derive {
if ident == "Serialize" {
attrs.serialize = true;
} else if ident == "Deserialize" {
attrs.deserialize = true;
} else if ident == "Debug" {
attrs.debug = true;
} else {
return Err(Error::new(
ident.span(),
format!(
"Found unknown derive attribute for #[{name}]: #[zerovec::derive({ident})]"
),
));
}
}
for ident in skip {
if ident == "ZeroMapKV" {
attrs.skip_kv = true;
} else if ident == "Ord" {
attrs.skip_ord = true;
} else {
return Err(Error::new(
ident.span(),
format!("Found unknown derive attribute for #[{name}]: #[zerovec::skip_derive({ident})]"),
));
}
}
if (attrs.serialize || attrs.deserialize) && !is_var {
return Err(Error::new(
span,
"#[make_ule] does not support #[zerovec::derive(Serialize, Deserialize)]",
));
}
Ok(attrs)
}