mirror of
https://github.com/fluencelabs/rust-libp2p
synced 2025-06-13 10:01:25 +00:00
swarm-derive/: Generate OutEvent if not provided (#2792)
Generate `NetworkBehaviour::OutEvent` if not provided through `#[behaviour(out_event = "MyOutEvent")]` and event processing is disabled (default).
This commit is contained in:
@ -20,6 +20,7 @@
|
||||
|
||||
#![recursion_limit = "256"]
|
||||
|
||||
use heck::ToUpperCamelCase;
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, Data, DataStruct, DeriveInput, Ident};
|
||||
@ -99,49 +100,106 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream {
|
||||
.filter(|f| !is_ignored(f))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// The final out event.
|
||||
// If we find a `#[behaviour(out_event = "Foo")]` attribute on the struct, we set `Foo` as
|
||||
// the out event. Otherwise we use `()`.
|
||||
let out_event = {
|
||||
let mut out = quote! {()};
|
||||
for meta_items in ast.attrs.iter().filter_map(get_meta_items) {
|
||||
for meta_item in meta_items {
|
||||
match meta_item {
|
||||
syn::NestedMeta::Meta(syn::Meta::NameValue(ref m))
|
||||
if m.path.is_ident("out_event") =>
|
||||
{
|
||||
let (out_event_name, out_event_definition, out_event_from_clauses) = {
|
||||
// If we find a `#[behaviour(out_event = "Foo")]` attribute on the
|
||||
// struct, we set `Foo` as the out event. If not, the `OutEvent` is
|
||||
// generated.
|
||||
let user_provided_out_event_name: Option<syn::Type> = ast
|
||||
.attrs
|
||||
.iter()
|
||||
.filter_map(get_meta_items)
|
||||
.flatten()
|
||||
.filter_map(|meta_item| {
|
||||
if let syn::NestedMeta::Meta(syn::Meta::NameValue(ref m)) = meta_item {
|
||||
if m.path.is_ident("out_event") {
|
||||
if let syn::Lit::Str(ref s) = m.lit {
|
||||
let ident: syn::Type = syn::parse_str(&s.value()).unwrap();
|
||||
out = quote! {#ident};
|
||||
return Some(syn::parse_str(&s.value()).unwrap());
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
None
|
||||
})
|
||||
.next();
|
||||
|
||||
match (user_provided_out_event_name, event_process) {
|
||||
// User provided `OutEvent`.
|
||||
(Some(name), false) => {
|
||||
let definition = None;
|
||||
let from_clauses = data_struct_fields
|
||||
.iter()
|
||||
.map(|field| {
|
||||
let ty = &field.ty;
|
||||
quote! {#name #ty_generics: From< <#ty as #trait_to_impl>::OutEvent >}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
(name, definition, from_clauses)
|
||||
}
|
||||
// User did not provide `OutEvent`. Generate it.
|
||||
(None, false) => {
|
||||
let name: syn::Type = syn::parse_str(&(ast.ident.to_string() + "Event")).unwrap();
|
||||
let definition = {
|
||||
let fields = data_struct_fields
|
||||
.iter()
|
||||
.map(|field| {
|
||||
let variant: syn::Variant = syn::parse_str(
|
||||
&field
|
||||
.ident
|
||||
.clone()
|
||||
.expect(
|
||||
"Fields of NetworkBehaviour implementation to be named.",
|
||||
)
|
||||
.to_string()
|
||||
.to_upper_camel_case(),
|
||||
)
|
||||
.unwrap();
|
||||
let ty = &field.ty;
|
||||
quote! {#variant(<#ty as NetworkBehaviour>::OutEvent)}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let visibility = &ast.vis;
|
||||
|
||||
Some(quote! {
|
||||
#visibility enum #name #impl_generics {
|
||||
#(#fields),*
|
||||
}
|
||||
})
|
||||
};
|
||||
let from_clauses = vec![];
|
||||
(name, definition, from_clauses)
|
||||
}
|
||||
// User uses `NetworkBehaviourEventProcess`.
|
||||
(name, true) => {
|
||||
let definition = None;
|
||||
let from_clauses = data_struct_fields
|
||||
.iter()
|
||||
.map(|field| {
|
||||
let ty = &field.ty;
|
||||
quote! {Self: #net_behv_event_proc<<#ty as #trait_to_impl>::OutEvent>}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
(
|
||||
name.unwrap_or_else(|| syn::parse_str("()").unwrap()),
|
||||
definition,
|
||||
from_clauses,
|
||||
)
|
||||
}
|
||||
}
|
||||
out
|
||||
};
|
||||
|
||||
// Build the `where ...` clause of the trait implementation.
|
||||
let where_clause = {
|
||||
let additional = data_struct_fields
|
||||
.iter()
|
||||
.flat_map(|field| {
|
||||
.map(|field| {
|
||||
let ty = &field.ty;
|
||||
vec![
|
||||
quote! {#ty: #trait_to_impl},
|
||||
if event_process {
|
||||
quote! {Self: #net_behv_event_proc<<#ty as #trait_to_impl>::OutEvent>}
|
||||
} else {
|
||||
quote! {#out_event: From< <#ty as #trait_to_impl>::OutEvent >}
|
||||
},
|
||||
]
|
||||
quote! {#ty: #trait_to_impl}
|
||||
})
|
||||
.chain(out_event_from_clauses)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if let Some(where_clause) = where_clause {
|
||||
if where_clause.predicates.trailing_punct() {
|
||||
Some(quote! {#where_clause #(#additional),*})
|
||||
Some(quote! {#where_clause #(#additional),* })
|
||||
} else {
|
||||
Some(quote! {#where_clause, #(#additional),*})
|
||||
}
|
||||
@ -437,18 +495,18 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream {
|
||||
// List of statements to put in `poll()`.
|
||||
//
|
||||
// We poll each child one by one and wrap around the output.
|
||||
let poll_stmts = data_struct_fields.iter().enumerate().enumerate().map(|(enum_n, (field_n, field))| {
|
||||
let field_name = match field.ident {
|
||||
Some(ref i) => quote!{ self.#i },
|
||||
None => quote!{ self.#field_n },
|
||||
};
|
||||
let poll_stmts = data_struct_fields.iter().enumerate().map(|(field_n, field)| {
|
||||
let field = field
|
||||
.ident
|
||||
.clone()
|
||||
.expect("Fields of NetworkBehaviour implementation to be named.");
|
||||
|
||||
let mut wrapped_event = if enum_n != 0 {
|
||||
let mut wrapped_event = if field_n != 0 {
|
||||
quote!{ #either_ident::Second(event) }
|
||||
} else {
|
||||
quote!{ event }
|
||||
};
|
||||
for _ in 0 .. data_struct_fields.len() - 1 - enum_n {
|
||||
for _ in 0 .. data_struct_fields.len() - 1 - field_n {
|
||||
wrapped_event = quote!{ #either_ident::First(#wrapped_event) };
|
||||
}
|
||||
|
||||
@ -460,7 +518,6 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream {
|
||||
let mut out_handler = None;
|
||||
|
||||
for (f_n, f) in data_struct_fields.iter().enumerate() {
|
||||
|
||||
let f_name = match f.ident {
|
||||
Some(ref i) => quote! { self.#i },
|
||||
None => quote! { self.#f_n },
|
||||
@ -492,16 +549,31 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If the `NetworkBehaviour`'s `OutEvent` is generated by the derive macro, wrap the sub
|
||||
// `NetworkBehaviour` `OutEvent` in the variant of the generated `OutEvent`. If the
|
||||
// `NetworkBehaviour`'s `OutEvent` is provided by the user, use the corresponding `From`
|
||||
// implementation.
|
||||
let into_out_event = if out_event_definition.is_some() {
|
||||
let event_variant: syn::Variant = syn::parse_str(
|
||||
&field
|
||||
.to_string()
|
||||
.to_upper_camel_case()
|
||||
).unwrap();
|
||||
quote! { #out_event_name::#event_variant(event) }
|
||||
} else {
|
||||
quote! { event.into() }
|
||||
};
|
||||
|
||||
quote! {
|
||||
std::task::Poll::Ready(#network_behaviour_action::GenerateEvent(event)) => {
|
||||
return std::task::Poll::Ready(#network_behaviour_action::GenerateEvent(event.into()))
|
||||
return std::task::Poll::Ready(#network_behaviour_action::GenerateEvent(#into_out_event))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Some(quote!{
|
||||
loop {
|
||||
match #trait_to_impl::poll(&mut #field_name, cx, poll_params) {
|
||||
match #trait_to_impl::poll(&mut self.#field, cx, poll_params) {
|
||||
#generate_event_match_arm
|
||||
std::task::Poll::Ready(#network_behaviour_action::Dial { opts, handler: provided_handler }) => {
|
||||
return std::task::Poll::Ready(#network_behaviour_action::Dial { opts, handler: #provided_handler_and_new_handlers });
|
||||
@ -527,11 +599,13 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream {
|
||||
|
||||
// Now the magic happens.
|
||||
let final_quote = quote! {
|
||||
#out_event_definition
|
||||
|
||||
impl #impl_generics #trait_to_impl for #name #ty_generics
|
||||
#where_clause
|
||||
{
|
||||
type ConnectionHandler = #connection_handler_ty;
|
||||
type OutEvent = #out_event;
|
||||
type OutEvent = #out_event_name #ty_generics;
|
||||
|
||||
fn new_handler(&mut self) -> Self::ConnectionHandler {
|
||||
use #into_connection_handler;
|
||||
|
Reference in New Issue
Block a user