1use proc_macro2::TokenStream;
20use quote::quote;
21use syn::{Attribute, Item, Meta, Token, punctuated::Punctuated};
22
23pub(crate) fn select(attr: &TokenStream, item: &Item) -> syn::Result<Option<TokenStream>> {
26 for member in 0..member_count(item) {
27 let Some(attrs) = member_attrs(item, member) else {
28 continue;
29 };
30 let Some(position) = attrs
31 .iter()
32 .position(|gate| gate.path().is_ident("cfg") || gate.path().is_ident("cfg_attr"))
33 else {
34 continue;
35 };
36 let gate = &attrs[position];
37 let mut enabled = item.clone();
38 let mut disabled = item.clone();
39 let enabled_attrs = member_attrs_mut(&mut enabled, member);
40 enabled_attrs.remove(position);
41 let predicate = if gate.path().is_ident("cfg") {
42 remove_member(&mut disabled, member);
43 gate.parse_args::<Meta>()?
44 } else {
45 let mut args = gate
46 .parse_args_with(Punctuated::<Meta, ::syn::token::CommaToken![,]>::parse_terminated)?
47 .into_iter();
48 let predicate = args
49 .next()
50 .ok_or_else(|| syn::Error::new_spanned(gate, "expected a cfg_attr predicate"))?;
51 enabled_attrs.splice(
52 position..position,
53 args.map(|meta| -> Attribute { ::syn::__private::parse_quote({
let mut _s = ::quote::__private::TokenStream::new();
::quote::__private::push_pound(&mut _s);
::quote::__private::push_group(&mut _s,
::quote::__private::Delimiter::Bracket,
{
let mut _s = ::quote::__private::TokenStream::new();
::quote::ToTokens::to_tokens(&meta, &mut _s);
_s
});
_s
})syn::parse_quote!(#[#meta]) }),
54 );
55 member_attrs_mut(&mut disabled, member).remove(position);
56 predicate
57 };
58 return Ok(Some({
let mut _s = ::quote::__private::TokenStream::new();
::quote::__private::push_pound(&mut _s);
::quote::__private::push_group(&mut _s,
::quote::__private::Delimiter::Bracket,
{
let mut _s = ::quote::__private::TokenStream::new();
::quote::__private::push_ident(&mut _s, "cfg");
::quote::__private::push_group(&mut _s,
::quote::__private::Delimiter::Parenthesis,
{
let mut _s = ::quote::__private::TokenStream::new();
::quote::ToTokens::to_tokens(&predicate, &mut _s);
_s
});
_s
});
::quote::__private::push_pound(&mut _s);
::quote::__private::push_group(&mut _s,
::quote::__private::Delimiter::Bracket,
{
let mut _s = ::quote::__private::TokenStream::new();
::quote::__private::push_colon2(&mut _s);
::quote::__private::push_ident(&mut _s, "miniextendr_api");
::quote::__private::push_colon2(&mut _s);
::quote::__private::push_ident(&mut _s, "miniextendr");
::quote::__private::push_group(&mut _s,
::quote::__private::Delimiter::Parenthesis,
{
let mut _s = ::quote::__private::TokenStream::new();
::quote::ToTokens::to_tokens(&attr, &mut _s);
_s
});
_s
});
::quote::ToTokens::to_tokens(&enabled, &mut _s);
::quote::__private::push_pound(&mut _s);
::quote::__private::push_group(&mut _s,
::quote::__private::Delimiter::Bracket,
{
let mut _s = ::quote::__private::TokenStream::new();
::quote::__private::push_ident(&mut _s, "cfg");
::quote::__private::push_group(&mut _s,
::quote::__private::Delimiter::Parenthesis,
{
let mut _s = ::quote::__private::TokenStream::new();
::quote::__private::push_ident(&mut _s, "not");
::quote::__private::push_group(&mut _s,
::quote::__private::Delimiter::Parenthesis,
{
let mut _s = ::quote::__private::TokenStream::new();
::quote::ToTokens::to_tokens(&predicate, &mut _s);
_s
});
_s
});
_s
});
::quote::__private::push_pound(&mut _s);
::quote::__private::push_group(&mut _s,
::quote::__private::Delimiter::Bracket,
{
let mut _s = ::quote::__private::TokenStream::new();
::quote::__private::push_colon2(&mut _s);
::quote::__private::push_ident(&mut _s, "miniextendr_api");
::quote::__private::push_colon2(&mut _s);
::quote::__private::push_ident(&mut _s, "miniextendr");
::quote::__private::push_group(&mut _s,
::quote::__private::Delimiter::Parenthesis,
{
let mut _s = ::quote::__private::TokenStream::new();
::quote::ToTokens::to_tokens(&attr, &mut _s);
_s
});
_s
});
::quote::ToTokens::to_tokens(&disabled, &mut _s);
_s
}quote! {
59 #[cfg(#predicate)]
60 #[::miniextendr_api::miniextendr(#attr)]
61 #enabled
62
63 #[cfg(not(#predicate))]
64 #[::miniextendr_api::miniextendr(#attr)]
65 #disabled
66 }));
67 }
68 Ok(None)
69}
70
71fn member_count(item: &Item) -> usize {
74 match item {
75 Item::Fn(item) => item.sig.inputs.len(),
76 Item::Impl(item) => item.items.len(),
77 Item::Trait(item) => item.items.len(),
78 Item::Enum(item) => item.variants.len(),
79 Item::Struct(item) => item.fields.len(),
80 _ => 0,
81 }
82}
83
84fn member_attrs(item: &Item, member: usize) -> Option<&Vec<Attribute>> {
85 match item {
86 Item::Fn(item) => Some(match &item.sig.inputs[member] {
87 syn::FnArg::Receiver(receiver) => &receiver.attrs,
88 syn::FnArg::Typed(typed) => &typed.attrs,
89 }),
90 Item::Impl(item) => match &item.items[member] {
91 syn::ImplItem::Fn(member) => Some(&member.attrs),
92 syn::ImplItem::Const(member) => Some(&member.attrs),
93 syn::ImplItem::Type(member) => Some(&member.attrs),
94 syn::ImplItem::Macro(member) => Some(&member.attrs),
95 _ => None,
96 },
97 Item::Trait(item) => match &item.items[member] {
98 syn::TraitItem::Fn(member) => Some(&member.attrs),
99 syn::TraitItem::Const(member) => Some(&member.attrs),
100 syn::TraitItem::Type(member) => Some(&member.attrs),
101 syn::TraitItem::Macro(member) => Some(&member.attrs),
102 _ => None,
103 },
104 Item::Enum(item) => Some(&item.variants[member].attrs),
105 Item::Struct(item) => item.fields.iter().nth(member).map(|field| &field.attrs),
106 _ => None,
107 }
108}
109
110fn member_attrs_mut(item: &mut Item, member: usize) -> &mut Vec<Attribute> {
112 match item {
113 Item::Fn(item) => match &mut item.sig.inputs[member] {
114 syn::FnArg::Receiver(receiver) => &mut receiver.attrs,
115 syn::FnArg::Typed(typed) => &mut typed.attrs,
116 },
117 Item::Impl(item) => match &mut item.items[member] {
118 syn::ImplItem::Fn(member) => &mut member.attrs,
119 syn::ImplItem::Const(member) => &mut member.attrs,
120 syn::ImplItem::Type(member) => &mut member.attrs,
121 syn::ImplItem::Macro(member) => &mut member.attrs,
122 _ => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
123 },
124 Item::Trait(item) => match &mut item.items[member] {
125 syn::TraitItem::Fn(member) => &mut member.attrs,
126 syn::TraitItem::Const(member) => &mut member.attrs,
127 syn::TraitItem::Type(member) => &mut member.attrs,
128 syn::TraitItem::Macro(member) => &mut member.attrs,
129 _ => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
130 },
131 Item::Enum(item) => &mut item.variants[member].attrs,
132 Item::Struct(item) => &mut item.fields.iter_mut().nth(member).unwrap().attrs,
133 _ => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
134 }
135}
136
137fn remove_member(item: &mut Item, member: usize) {
138 match item {
139 Item::Fn(item) => remove_at(&mut item.sig.inputs, member),
140 Item::Impl(item) => {
141 item.items.remove(member);
142 }
143 Item::Trait(item) => {
144 item.items.remove(member);
145 }
146 Item::Enum(item) => remove_at(&mut item.variants, member),
147 Item::Struct(item) => match &mut item.fields {
148 syn::Fields::Named(fields) => remove_at(&mut fields.named, member),
149 syn::Fields::Unnamed(fields) => remove_at(&mut fields.unnamed, member),
150 syn::Fields::Unit => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
151 },
152 _ => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
153 }
154}
155
156fn remove_at<T, P: Default>(list: &mut Punctuated<T, P>, index: usize) {
157 *list = std::mem::take(list)
158 .into_iter()
159 .enumerate()
160 .filter_map(|(position, member)| (position != index).then_some(member))
161 .collect();
162}