Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@

- Update to [`libp2p-dcutr` `v0.5.0`](protocols/dcutr/CHANGELOG.md#050).

- Update to [`libp2p-derive` `v0.29.0`](swarm-derive/CHANGELOG.md#0290).

- Update to [`libp2p-rendezvous` `v0.8.0`](protocols/rendezvous/CHANGELOG.md#080).

- Update to [`libp2p-ping` `v0.38.0`](protocols/ping/CHANGELOG.md#0380).
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ libp2p-relay = { version = "0.11.0", path = "protocols/relay", optional = true }
libp2p-rendezvous = { version = "0.8.0", path = "protocols/rendezvous", optional = true }
libp2p-request-response = { version = "0.20.0", path = "protocols/request-response", optional = true }
libp2p-swarm = { version = "0.38.0", path = "swarm" }
libp2p-swarm-derive = { version = "0.28.0", path = "swarm-derive" }
libp2p-swarm-derive = { version = "0.29.0", path = "swarm-derive" }
libp2p-uds = { version = "0.34.0", path = "transports/uds", optional = true }
libp2p-wasm-ext = { version = "0.35.0", path = "transports/wasm-ext", default-features = false, optional = true }
libp2p-yamux = { version = "0.39.0", path = "muxers/yamux", optional = true }
Expand Down
5 changes: 5 additions & 0 deletions swarm-derive/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# 0.29.0 - [unreleased]

- Generate `NetworkBehaviour::OutEvent` if not provided through `#[behaviour(out_event =
"MyOutEvent")]` and event processing is disabled (default).

# 0.28.0

- Import `ListenerId` from `libp2p::core::transport`. See [PR 2652].
Expand Down
5 changes: 3 additions & 2 deletions swarm-derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name = "libp2p-swarm-derive"
edition = "2021"
rust-version = "1.56.1"
description = "Procedural macros of libp2p-core"
version = "0.28.0"
version = "0.29.0"
authors = ["Parity Technologies <admin@parity.io>"]
license = "MIT"
repository = "https://github.com/libp2p/rust-libp2p"
Expand All @@ -14,8 +14,9 @@ categories = ["network-programming", "asynchronous"]
proc-macro = true

[dependencies]
syn = { version = "1.0.8", default-features = false, features = ["clone-impls", "derive", "parsing", "printing", "proc-macro"] }
heck = "0.4"
quote = "1.0"
syn = { version = "1.0.8", default-features = false, features = ["clone-impls", "derive", "parsing", "printing", "proc-macro"] }

[dev-dependencies]
libp2p = { path = "../", default-features = false, features = ["ping", "identify", "kad"] }
Expand Down
146 changes: 110 additions & 36 deletions swarm-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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),*})
}
Expand Down Expand Up @@ -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) };
}

Expand All @@ -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 },
Expand Down Expand Up @@ -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 });
Expand All @@ -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;
Expand Down
Loading