use proc_macro2::TokenTree;
use quote::quote;
use syn::{parse::Parser, Attribute, Field, Fields, FieldsNamed, Ident, Meta, MetaList};
pub fn has_attr(attrs: &[Attribute], attr_name: &str) -> bool {
attrs
.iter()
.any(|attr| attr.path().segments[0].ident == attr_name)
}
pub fn get_attr(attrs: &[Attribute], attr_name: &str) -> Option<Attribute> {
attrs
.iter()
.filter(|attr| attr.path().segments[0].ident == attr_name)
.cloned()
.collect::<Vec<_>>()
.first()
.cloned()
}
pub fn remove_attr(attrs: &mut Vec<Attribute>, attr_name: &str) {
attrs.retain(|attr| attr.path().segments[0].ident != attr_name);
}
pub fn has_repr_c(attrs: &[Attribute]) -> bool {
let has = |meta: &Meta, ident: &str| meta.path().get_ident().map_or(false, |i| i == ident);
attrs
.iter()
.filter(|a| has(&a.meta, "repr"))
.filter_map(|a| match &a.meta {
Meta::List(MetaList { tokens, .. }) => {
Some(tokens.clone().into_iter().collect::<Vec<_>>())
}
_ => None,
})
.flat_map(IntoIterator::into_iter)
.map(|tree| match tree {
TokenTree::Ident(ident) => ident,
_ => todo!(),
})
.any(|n| n == "C")
}
pub fn add_repr_c(attrs: &mut Vec<Attribute>) {
if has_repr_c(attrs) {
return;
}
let mut repr_c = Attribute::parse_outer
.parse2(quote! { #[repr(C)] })
.expect("internal macro error with ill-formed #[repr(C)]");
attrs.append(&mut repr_c);
}
pub fn add_field(fields: &mut Fields, field_name: &Ident, field_ty_name: &Ident) {
match fields {
Fields::Named(FieldsNamed { named: fields, .. }) => {
if fields.first().map_or(true, |first_field| {
first_field
.ident
.as_ref()
.map_or(true, |ident| ident != field_name)
}) {
fields.insert(
0,
Field::parse_named
.parse2(quote! { pub #field_name: #field_ty_name })
.expect("Failed to create field in struct"),
);
}
}
_ => panic!("expected named fields"),
}
}