Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(swarm-derive): allow mixing of ignored fields #2570

Merged
merged 4 commits into from Mar 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions swarm-derive/CHANGELOG.md
@@ -1,3 +1,9 @@
# 0.27.1

- Allow mixing of ignored fields. See [PR 2570].

[PR 2570]: https://github.com/libp2p/rust-libp2p/pull/2570

# 0.27.0 [2022-02-22]

- Adjust to latest changes in `libp2p-swarm`.
Expand Down
2 changes: 1 addition & 1 deletion swarm-derive/Cargo.toml
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.27.0"
version = "0.27.1"
authors = ["Parity Technologies <admin@parity.io>"]
license = "MIT"
repository = "https://github.com/libp2p/rust-libp2p"
Expand Down
183 changes: 56 additions & 127 deletions swarm-derive/src/lib.rs
Expand Up @@ -92,6 +92,13 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream {
event_process
};

// The fields of the struct we are interested in (no ignored fields).
let data_struct_fields = data_struct
.fields
.iter()
.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 `()`.
Expand All @@ -117,10 +124,8 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream {

// Build the `where ...` clause of the trait implementation.
let where_clause = {
let additional = data_struct
.fields
let additional = data_struct_fields
.iter()
.filter(|x| !is_ignored(x))
.flat_map(|field| {
let ty = &field.ty;
vec![
Expand All @@ -147,57 +152,42 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream {

// Build the list of statements to put in the body of `addresses_of_peer()`.
let addresses_of_peer_stmts = {
data_struct
.fields
data_struct_fields
.iter()
.enumerate()
.filter_map(move |(field_n, field)| {
if is_ignored(field) {
return None;
}

Some(match field.ident {
Some(ref i) => quote! { out.extend(self.#i.addresses_of_peer(peer_id)); },
None => quote! { out.extend(self.#field_n.addresses_of_peer(peer_id)); },
})
.map(move |(field_n, field)| match field.ident {
Some(ref i) => quote! { out.extend(self.#i.addresses_of_peer(peer_id)); },
None => quote! { out.extend(self.#field_n.addresses_of_peer(peer_id)); },
})
};

// Build the list of statements to put in the body of `inject_connection_established()`.
let inject_connection_established_stmts = {
data_struct.fields.iter().enumerate().filter_map(move |(field_n, field)| {
if is_ignored(field) {
return None;
}
Some(match field.ident {
data_struct_fields.iter().enumerate().map(move |(field_n, field)| {
match field.ident {
Some(ref i) => quote!{ self.#i.inject_connection_established(peer_id, connection_id, endpoint, errors, other_established); },
None => quote!{ self.#field_n.inject_connection_established(peer_id, connection_id, endpoint, errors, other_established); },
})
}
})
};

// Build the list of statements to put in the body of `inject_address_change()`.
let inject_address_change_stmts = {
data_struct.fields.iter().enumerate().filter_map(move |(field_n, field)| {
if is_ignored(field) {
return None;
}
Some(match field.ident {
data_struct_fields.iter().enumerate().map(move |(field_n, field)| {
match field.ident {
Some(ref i) => quote!{ self.#i.inject_address_change(peer_id, connection_id, old, new); },
None => quote!{ self.#field_n.inject_address_change(peer_id, connection_id, old, new); },
})
}
})
};

// Build the list of statements to put in the body of `inject_connection_closed()`.
let inject_connection_closed_stmts = {
data_struct
.fields
data_struct_fields
.iter()
.enumerate()
// The outmost handler belongs to the last behaviour.
.rev()
.filter(|f| !is_ignored(f.1))
.enumerate()
.map(move |(enum_n, (field_n, field))| {
let handler = if field_n == 0 {
Expand All @@ -222,13 +212,11 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream {

// Build the list of statements to put in the body of `inject_dial_failure()`.
let inject_dial_failure_stmts = {
data_struct
.fields
data_struct_fields
.iter()
.enumerate()
// The outmost handler belongs to the last behaviour.
.rev()
.filter(|f| !is_ignored(f.1))
.enumerate()
.map(move |(enum_n, (field_n, field))| {
let handler = if field_n == 0 {
Expand Down Expand Up @@ -258,12 +246,10 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream {

// Build the list of statements to put in the body of `inject_listen_failure()`.
let inject_listen_failure_stmts = {
data_struct
.fields
data_struct_fields
.iter()
.enumerate()
.rev()
.filter(|f| !is_ignored(f.1))
.enumerate()
.map(move |(enum_n, (field_n, field))| {
let handler = if field_n == 0 {
Expand All @@ -288,140 +274,93 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream {

// Build the list of statements to put in the body of `inject_new_listener()`.
let inject_new_listener_stmts = {
data_struct
.fields
data_struct_fields
.iter()
.enumerate()
.filter_map(move |(field_n, field)| {
if is_ignored(field) {
return None;
}

Some(match field.ident {
Some(ref i) => quote! { self.#i.inject_new_listener(id); },
None => quote! { self.#field_n.inject_new_listener(id); },
})
.map(move |(field_n, field)| match field.ident {
Some(ref i) => quote! { self.#i.inject_new_listener(id); },
None => quote! { self.#field_n.inject_new_listener(id); },
})
};

// Build the list of statements to put in the body of `inject_new_listen_addr()`.
let inject_new_listen_addr_stmts = {
data_struct
.fields
data_struct_fields
.iter()
.enumerate()
.filter_map(move |(field_n, field)| {
if is_ignored(field) {
return None;
}

Some(match field.ident {
Some(ref i) => quote! { self.#i.inject_new_listen_addr(id, addr); },
None => quote! { self.#field_n.inject_new_listen_addr(id, addr); },
})
.map(move |(field_n, field)| match field.ident {
Some(ref i) => quote! { self.#i.inject_new_listen_addr(id, addr); },
None => quote! { self.#field_n.inject_new_listen_addr(id, addr); },
})
};

// Build the list of statements to put in the body of `inject_expired_listen_addr()`.
let inject_expired_listen_addr_stmts = {
data_struct
.fields
data_struct_fields
.iter()
.enumerate()
.filter_map(move |(field_n, field)| {
if is_ignored(field) {
return None;
}

Some(match field.ident {
Some(ref i) => quote! { self.#i.inject_expired_listen_addr(id, addr); },
None => quote! { self.#field_n.inject_expired_listen_addr(id, addr); },
})
.map(move |(field_n, field)| match field.ident {
Some(ref i) => quote! { self.#i.inject_expired_listen_addr(id, addr); },
None => quote! { self.#field_n.inject_expired_listen_addr(id, addr); },
})
};

// Build the list of statements to put in the body of `inject_new_external_addr()`.
let inject_new_external_addr_stmts = {
data_struct
.fields
data_struct_fields
.iter()
.enumerate()
.filter_map(move |(field_n, field)| {
if is_ignored(field) {
return None;
}

Some(match field.ident {
Some(ref i) => quote! { self.#i.inject_new_external_addr(addr); },
None => quote! { self.#field_n.inject_new_external_addr(addr); },
})
.map(move |(field_n, field)| match field.ident {
Some(ref i) => quote! { self.#i.inject_new_external_addr(addr); },
None => quote! { self.#field_n.inject_new_external_addr(addr); },
})
};

// Build the list of statements to put in the body of `inject_expired_external_addr()`.
let inject_expired_external_addr_stmts = {
data_struct
.fields
data_struct_fields
.iter()
.enumerate()
.filter_map(move |(field_n, field)| {
if is_ignored(field) {
return None;
}

Some(match field.ident {
Some(ref i) => quote! { self.#i.inject_expired_external_addr(addr); },
None => quote! { self.#field_n.inject_expired_external_addr(addr); },
})
.map(move |(field_n, field)| match field.ident {
Some(ref i) => quote! { self.#i.inject_expired_external_addr(addr); },
None => quote! { self.#field_n.inject_expired_external_addr(addr); },
})
};

// Build the list of statements to put in the body of `inject_listener_error()`.
let inject_listener_error_stmts = {
data_struct
.fields
data_struct_fields
.iter()
.enumerate()
.filter_map(move |(field_n, field)| {
if is_ignored(field) {
return None;
}
Some(match field.ident {
Some(ref i) => quote!(self.#i.inject_listener_error(id, err);),
None => quote!(self.#field_n.inject_listener_error(id, err);),
})
.map(move |(field_n, field)| match field.ident {
Some(ref i) => quote!(self.#i.inject_listener_error(id, err);),
None => quote!(self.#field_n.inject_listener_error(id, err);),
})
};

// Build the list of statements to put in the body of `inject_listener_closed()`.
let inject_listener_closed_stmts = {
data_struct
.fields
data_struct_fields
.iter()
.enumerate()
.filter_map(move |(field_n, field)| {
if is_ignored(field) {
return None;
}
Some(match field.ident {
Some(ref i) => quote!(self.#i.inject_listener_closed(id, reason);),
None => quote!(self.#field_n.inject_listener_closed(id, reason);),
})
.map(move |(field_n, field)| match field.ident {
Some(ref i) => quote!(self.#i.inject_listener_closed(id, reason);),
None => quote!(self.#field_n.inject_listener_closed(id, reason);),
})
};

// Build the list of variants to put in the body of `inject_event()`.
//
// The event type is a construction of nested `#either_ident`s of the events of the children.
// We call `inject_event` on the corresponding child.
let inject_node_event_stmts = data_struct.fields.iter().enumerate().filter(|f| !is_ignored(f.1)).enumerate().map(|(enum_n, (field_n, field))| {
let inject_node_event_stmts = data_struct_fields.iter().enumerate().enumerate().map(|(enum_n, (field_n, field))| {
let mut elem = if enum_n != 0 {
quote!{ #either_ident::Second(ev) }
} else {
quote!{ ev }
};

for _ in 0 .. data_struct.fields.iter().filter(|f| !is_ignored(f)).count() - 1 - enum_n {
for _ in 0 .. data_struct_fields.len() - 1 - enum_n {
elem = quote!{ #either_ident::First(#elem) };
}

Expand All @@ -434,10 +373,7 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream {
// The [`ConnectionHandler`] associated type.
let protocols_handler_ty = {
let mut ph_ty = None;
for field in data_struct.fields.iter() {
if is_ignored(field) {
continue;
}
for field in data_struct_fields.iter() {
let ty = &field.ty;
let field_info = quote! { <#ty as #trait_to_impl>::ConnectionHandler };
match ph_ty {
Expand All @@ -454,11 +390,7 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream {
let new_handler = {
let mut out_handler = None;

for (field_n, field) in data_struct.fields.iter().enumerate() {
if is_ignored(field) {
continue;
}

for (field_n, field) in data_struct_fields.iter().enumerate() {
let field_name = match field.ident {
Some(ref i) => quote! { self.#i },
None => quote! { self.#field_n },
Expand Down Expand Up @@ -505,7 +437,7 @@ 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().filter(|f| !is_ignored(f.1)).enumerate().map(|(enum_n, (field_n, field))| {
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 },
Expand All @@ -516,7 +448,7 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream {
} else {
quote!{ event }
};
for _ in 0 .. data_struct.fields.iter().filter(|f| !is_ignored(f)).count() - 1 - enum_n {
for _ in 0 .. data_struct_fields.len() - 1 - enum_n {
wrapped_event = quote!{ #either_ident::First(#wrapped_event) };
}

Expand All @@ -527,10 +459,7 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream {
let provided_handler_and_new_handlers = {
let mut out_handler = None;

for (f_n, f) in data_struct.fields.iter().enumerate() {
if is_ignored(f) {
continue;
}
for (f_n, f) in data_struct_fields.iter().enumerate() {

let f_name = match f.ident {
Some(ref i) => quote! { self.#i },
Expand Down
27 changes: 27 additions & 0 deletions swarm-derive/tests/test.rs
Expand Up @@ -419,3 +419,30 @@ fn no_event_with_either() {
require_net_behaviour::<Foo>();
}
}

#[test]
fn mixed_field_order() {
struct Foo {}

#[derive(NetworkBehaviour)]
#[behaviour(event_process = true)]
pub struct Behaviour {
#[behaviour(ignore)]
_foo: Foo,
_ping: libp2p::ping::Ping,
#[behaviour(ignore)]
_foo2: Foo,
_identify: libp2p::identify::Identify,
#[behaviour(ignore)]
_foo3: Foo,
}

impl<T> libp2p::swarm::NetworkBehaviourEventProcess<T> for Behaviour {
fn inject_event(&mut self, _evt: T) {}
}

#[allow(dead_code)]
fn behaviour() {
require_net_behaviour::<Behaviour>();
}
}