use proc_macro::TokenStream;
use quote::quote;
use syn::{
Ident, ImplItem, ImplItemFn, ItemImpl, Token, Type, bracketed,
parse::{Parse, ParseStream},
parse_macro_input,
punctuated::Punctuated,
};
struct NodeArgs {
active: Vec<Ident>,
passive: Vec<Ident>,
output: Option<(Ident, Type)>,
}
impl Parse for NodeArgs {
fn parse(input: ParseStream) -> syn::Result<Self> {
let mut active: Option<Vec<Ident>> = None;
let mut passive: Option<Vec<Ident>> = None;
let mut output: Option<(Ident, Type)> = None;
while !input.is_empty() {
let key: Ident = input.parse()?;
input.parse::<Token![=]>()?;
match key.to_string().as_str() {
"active" => {
if active.is_some() {
return Err(syn::Error::new(key.span(), "duplicate key `active`"));
}
let content;
bracketed!(content in input);
let list = Punctuated::<Ident, Token![,]>::parse_terminated(&content)?;
active = Some(list.into_iter().collect());
}
"passive" => {
if passive.is_some() {
return Err(syn::Error::new(key.span(), "duplicate key `passive`"));
}
let content;
bracketed!(content in input);
let list = Punctuated::<Ident, Token![,]>::parse_terminated(&content)?;
passive = Some(list.into_iter().collect());
}
"output" => {
if output.is_some() {
return Err(syn::Error::new(key.span(), "duplicate key `output`"));
}
let field: Ident = input.parse()?;
input.parse::<Token![:]>()?;
let ty: Type = input.parse()?;
output = Some((field, ty));
}
_ => {
return Err(syn::Error::new(
key.span(),
format!("unknown key `{key}`; expected `active`, `passive`, or `output`"),
));
}
}
if input.peek(Token![,]) {
input.parse::<Token![,]>()?;
}
}
Ok(NodeArgs {
active: active.unwrap_or_default(),
passive: passive.unwrap_or_default(),
output,
})
}
}
#[proc_macro_attribute]
pub fn node(attr: TokenStream, item: TokenStream) -> TokenStream {
let args = parse_macro_input!(attr as NodeArgs);
let mut impl_block = parse_macro_input!(item as ItemImpl);
let self_ty = impl_block.self_ty.clone();
let (impl_generics, _, where_clause) = impl_block.generics.split_for_impl();
if !args.active.is_empty() || !args.passive.is_empty() {
let active_fields = &args.active;
let passive_fields = &args.passive;
let upstreams_fn: ImplItemFn = syn::parse_quote! {
fn upstreams(&self) -> UpStreams {
let mut active: ::std::vec::Vec<::std::rc::Rc<dyn Node>> = ::std::vec::Vec::new();
let mut passive: ::std::vec::Vec<::std::rc::Rc<dyn Node>> = ::std::vec::Vec::new();
#(active.extend(AsUpstreamNodes::as_upstream_nodes(&self.#active_fields));)*
#(passive.extend(AsUpstreamNodes::as_upstream_nodes(&self.#passive_fields));)*
UpStreams::new(active, passive)
}
};
impl_block.items.push(ImplItem::Fn(upstreams_fn));
}
let peek_ref_impl = args.output.map(|(field, ty)| {
quote! {
impl #impl_generics StreamPeekRef<#ty> for #self_ty #where_clause {
fn peek_ref(&self) -> &#ty {
&self.#field
}
}
}
});
quote! {
#impl_block
#peek_ref_impl
}
.into()
}