use roxygen::roxygen;
use crate::{
codegen::{cfg_expr::CfgExpr, CfgExprRules},
SourceLocation,
};
#[roxygen]
pub(crate) fn process_item_features(
item: &mut syn::Item,
rules: &CfgExprRules,
source_location: &SourceLocation,
) -> bool {
let attrs = match item {
syn::Item::Fn(f) => {
process_fn_inputs(&mut f.sig.inputs, rules, source_location);
&mut f.attrs
}
syn::Item::Struct(s) => {
process_struct_fields(&mut s.fields, rules, source_location);
&mut s.attrs
}
syn::Item::Enum(e) => {
process_enum_variants(&mut e.variants, rules, source_location);
&mut e.attrs
}
syn::Item::Union(u) => {
process_union_fields(&mut u.fields, rules, source_location);
&mut u.attrs
}
syn::Item::Type(t) => &mut t.attrs,
syn::Item::Const(c) => &mut c.attrs,
syn::Item::Static(s) => &mut s.attrs,
syn::Item::Mod(m) => &mut m.attrs,
syn::Item::Use(u) => &mut u.attrs,
syn::Item::Impl(i) => &mut i.attrs,
syn::Item::Trait(t) => &mut t.attrs,
_ => return true, };
process_attributes(attrs, rules, source_location)
}
fn process_struct_fields(
fields: &mut syn::Fields,
rules: &CfgExprRules,
source_location: &SourceLocation,
) {
match fields {
syn::Fields::Named(fields_named) => {
let mut new_fields = syn::punctuated::Punctuated::new();
for field in fields_named.named.pairs() {
let mut field = field.into_value().clone();
if process_attributes(&mut field.attrs, rules, source_location) {
new_fields.push(field);
}
}
fields_named.named = new_fields;
}
syn::Fields::Unnamed(fields_unnamed) => {
let mut new_fields = syn::punctuated::Punctuated::new();
for field in fields_unnamed.unnamed.pairs() {
let mut field = field.into_value().clone();
if process_attributes(&mut field.attrs, rules, source_location) {
new_fields.push(field);
}
}
fields_unnamed.unnamed = new_fields;
}
syn::Fields::Unit => {
}
}
}
fn process_fn_inputs(
inputs: &mut syn::punctuated::Punctuated<syn::FnArg, syn::Token![,]>,
rules: &CfgExprRules,
source_location: &SourceLocation,
) {
let mut new_inputs = syn::punctuated::Punctuated::new();
for input_pair in inputs.pairs() {
let mut input = input_pair.into_value().clone();
let keep = match &mut input {
syn::FnArg::Typed(pt) => process_attributes(&mut pt.attrs, rules, source_location),
syn::FnArg::Receiver(r) => process_attributes(&mut r.attrs, rules, source_location),
};
if keep {
new_inputs.push(input);
}
}
*inputs = new_inputs;
}
fn process_enum_variants(
variants: &mut syn::punctuated::Punctuated<syn::Variant, syn::Token![,]>,
rules: &CfgExprRules,
source_location: &SourceLocation,
) {
let mut new_variants = syn::punctuated::Punctuated::new();
for variant_pair in variants.pairs() {
let mut variant = variant_pair.into_value().clone();
let keep_variant = process_attributes(&mut variant.attrs, rules, source_location);
if keep_variant {
process_struct_fields(&mut variant.fields, rules, source_location);
new_variants.push(variant);
}
}
*variants = new_variants;
}
fn process_union_fields(
fields: &mut syn::FieldsNamed,
rules: &CfgExprRules,
source_location: &SourceLocation,
) {
let mut new_fields = syn::punctuated::Punctuated::new();
for field_pair in fields.named.pairs() {
let mut field = field_pair.into_value().clone();
if process_attributes(&mut field.attrs, rules, source_location) {
new_fields.push(field);
}
}
fields.named = new_fields;
}
fn process_attributes(
attrs: &mut Vec<syn::Attribute>,
rules: &CfgExprRules,
source_location: &SourceLocation,
) -> bool {
let mut keep_item = true;
let mut remove_attrs = Vec::new();
for (i, attr) in attrs.iter_mut().enumerate() {
if attr.path().is_ident("cfg") {
if let syn::Meta::List(meta_list) = &attr.meta {
match CfgExpr::parse_from_tokens(&meta_list.tokens) {
Ok(cfg_expr) => {
match cfg_expr.apply_rules(rules, source_location) {
Some(processed_expr) => {
if matches!(processed_expr, CfgExpr::False) {
keep_item = false;
break;
} else {
let new_tokens = processed_expr.to_tokens();
let new_meta = syn::parse_quote! {
cfg(#new_tokens)
};
attr.meta = new_meta;
}
}
None => {
remove_attrs.push(i);
}
}
}
Err(_) => {
}
}
}
}
}
for &i in remove_attrs.iter().rev() {
attrs.remove(i);
}
keep_item
}
#[cfg(test)]
mod tests;