From 8cee072a629b23ebe2015a49ad3d583b64c0f1f8 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Tue, 2 Aug 2022 21:25:04 +0900 Subject: [PATCH 1/4] swarm-derive/: Generate OutEvent if not provided Generate `NetworkBehaviour::OutEvent` if not provided through `#[behaviour(out_event = "MyOutEvent")]` and event processing is disabled (default). --- CHANGELOG.md | 2 + Cargo.toml | 2 +- swarm-derive/CHANGELOG.md | 5 + swarm-derive/Cargo.toml | 5 +- swarm-derive/src/lib.rs | 146 +++++++++++++++++++------ swarm-derive/tests/test.rs | 219 ++++++++++++++++++------------------- swarm/CHANGELOG.md | 9 ++ swarm/src/behaviour.rs | 23 +--- 8 files changed, 244 insertions(+), 167 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4496d987a0..ca42a4e26eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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). diff --git a/Cargo.toml b/Cargo.toml index f716d822552..75867ddbf4f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 } diff --git a/swarm-derive/CHANGELOG.md b/swarm-derive/CHANGELOG.md index 4d467c5e76a..b8b1e4530e0 100644 --- a/swarm-derive/CHANGELOG.md +++ b/swarm-derive/CHANGELOG.md @@ -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]. diff --git a/swarm-derive/Cargo.toml b/swarm-derive/Cargo.toml index 97a205674d9..05ed063257d 100644 --- a/swarm-derive/Cargo.toml +++ b/swarm-derive/Cargo.toml @@ -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 "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -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"] } diff --git a/swarm-derive/src/lib.rs b/swarm-derive/src/lib.rs index 1216add96c0..07aae641e0f 100644 --- a/swarm-derive/src/lib.rs +++ b/swarm-derive/src/lib.rs @@ -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::>(); - // 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 = 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::>(); + (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::>(); + 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::>(); + ( + name.unwrap_or(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::>(); 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.clone() + .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; diff --git a/swarm-derive/tests/test.rs b/swarm-derive/tests/test.rs index 4961806d357..9a53ac7e1c5 100644 --- a/swarm-derive/tests/test.rs +++ b/swarm-derive/tests/test.rs @@ -38,15 +38,10 @@ fn empty() { fn one_field() { #[allow(dead_code)] #[derive(NetworkBehaviour)] - #[behaviour(event_process = true)] struct Foo { ping: libp2p::ping::Ping, } - impl libp2p::swarm::NetworkBehaviourEventProcess for Foo { - fn inject_event(&mut self, _: libp2p::ping::PingEvent) {} - } - #[allow(dead_code)] fn foo() { require_net_behaviour::(); @@ -57,20 +52,11 @@ fn one_field() { fn two_fields() { #[allow(dead_code)] #[derive(NetworkBehaviour)] - #[behaviour(event_process = true)] struct Foo { ping: libp2p::ping::Ping, identify: libp2p::identify::Identify, } - impl libp2p::swarm::NetworkBehaviourEventProcess for Foo { - fn inject_event(&mut self, _: libp2p::identify::IdentifyEvent) {} - } - - impl libp2p::swarm::NetworkBehaviourEventProcess for Foo { - fn inject_event(&mut self, _: libp2p::ping::PingEvent) {} - } - #[allow(dead_code)] fn foo() { require_net_behaviour::(); @@ -81,7 +67,6 @@ fn two_fields() { fn three_fields() { #[allow(dead_code)] #[derive(NetworkBehaviour)] - #[behaviour(event_process = true)] struct Foo { ping: libp2p::ping::Ping, identify: libp2p::identify::Identify, @@ -90,18 +75,6 @@ fn three_fields() { foo: String, } - impl libp2p::swarm::NetworkBehaviourEventProcess for Foo { - fn inject_event(&mut self, _: libp2p::ping::PingEvent) {} - } - - impl libp2p::swarm::NetworkBehaviourEventProcess for Foo { - fn inject_event(&mut self, _: libp2p::identify::IdentifyEvent) {} - } - - impl libp2p::swarm::NetworkBehaviourEventProcess for Foo { - fn inject_event(&mut self, _: libp2p::kad::KademliaEvent) {} - } - #[allow(dead_code)] fn foo() { require_net_behaviour::(); @@ -112,7 +85,6 @@ fn three_fields() { fn three_fields_non_last_ignored() { #[allow(dead_code)] #[derive(NetworkBehaviour)] - #[behaviour(event_process = true)] struct Foo { ping: libp2p::ping::Ping, #[behaviour(ignore)] @@ -120,14 +92,6 @@ fn three_fields_non_last_ignored() { kad: libp2p::kad::Kademlia, } - impl libp2p::swarm::NetworkBehaviourEventProcess for Foo { - fn inject_event(&mut self, _: libp2p::ping::PingEvent) {} - } - - impl libp2p::swarm::NetworkBehaviourEventProcess for Foo { - fn inject_event(&mut self, _: libp2p::kad::KademliaEvent) {} - } - #[allow(dead_code)] fn foo() { require_net_behaviour::(); @@ -138,20 +102,12 @@ fn three_fields_non_last_ignored() { fn custom_polling() { #[allow(dead_code)] #[derive(NetworkBehaviour)] - #[behaviour(poll_method = "foo", event_process = true)] + #[behaviour(poll_method = "foo")] struct Foo { ping: libp2p::ping::Ping, identify: libp2p::identify::Identify, } - impl libp2p::swarm::NetworkBehaviourEventProcess for Foo { - fn inject_event(&mut self, _: libp2p::ping::PingEvent) {} - } - - impl libp2p::swarm::NetworkBehaviourEventProcess for Foo { - fn inject_event(&mut self, _: libp2p::identify::IdentifyEvent) {} - } - impl Foo { fn foo( &mut self, @@ -177,18 +133,27 @@ fn custom_polling() { fn custom_event_no_polling() { #[allow(dead_code)] #[derive(NetworkBehaviour)] - #[behaviour(out_event = "Vec", event_process = true)] + #[behaviour(out_event = "MyEvent")] struct Foo { ping: libp2p::ping::Ping, identify: libp2p::identify::Identify, } - impl libp2p::swarm::NetworkBehaviourEventProcess for Foo { - fn inject_event(&mut self, _: libp2p::ping::PingEvent) {} + enum MyEvent { + Ping(libp2p::ping::PingEvent), + Identify(libp2p::identify::IdentifyEvent), } - impl libp2p::swarm::NetworkBehaviourEventProcess for Foo { - fn inject_event(&mut self, _: libp2p::identify::IdentifyEvent) {} + impl From for MyEvent { + fn from(event: libp2p::ping::PingEvent) -> Self { + MyEvent::Ping(event) + } + } + + impl From for MyEvent { + fn from(event: libp2p::identify::IdentifyEvent) -> Self { + MyEvent::Identify(event) + } } #[allow(dead_code)] @@ -201,18 +166,27 @@ fn custom_event_no_polling() { fn custom_event_and_polling() { #[allow(dead_code)] #[derive(NetworkBehaviour)] - #[behaviour(poll_method = "foo", out_event = "String", event_process = true)] + #[behaviour(poll_method = "foo", out_event = "MyEvent")] struct Foo { ping: libp2p::ping::Ping, identify: libp2p::identify::Identify, } - impl libp2p::swarm::NetworkBehaviourEventProcess for Foo { - fn inject_event(&mut self, _: libp2p::ping::PingEvent) {} + enum MyEvent { + Ping(libp2p::ping::PingEvent), + Identify(libp2p::identify::IdentifyEvent), } - impl libp2p::swarm::NetworkBehaviourEventProcess for Foo { - fn inject_event(&mut self, _: libp2p::identify::IdentifyEvent) {} + impl From for MyEvent { + fn from(event: libp2p::ping::PingEvent) -> Self { + MyEvent::Ping(event) + } + } + + impl From for MyEvent { + fn from(event: libp2p::identify::IdentifyEvent) -> Self { + MyEvent::Identify(event) + } } impl Foo { @@ -236,12 +210,44 @@ fn custom_event_and_polling() { } } +#[test] +fn custom_event_mismatching_field_names() { + #[allow(dead_code)] + #[derive(NetworkBehaviour)] + #[behaviour(out_event = "MyEvent")] + struct Foo { + a: libp2p::ping::Ping, + b: libp2p::identify::Identify, + } + + enum MyEvent { + Ping(libp2p::ping::PingEvent), + Identify(libp2p::identify::IdentifyEvent), + } + + impl From for MyEvent { + fn from(event: libp2p::ping::PingEvent) -> Self { + MyEvent::Ping(event) + } + } + + impl From for MyEvent { + fn from(event: libp2p::identify::IdentifyEvent) -> Self { + MyEvent::Identify(event) + } + } + + #[allow(dead_code)] + fn foo() { + require_net_behaviour::(); + } +} + #[test] fn where_clause() { #[allow(dead_code)] #[derive(NetworkBehaviour)] - #[behaviour(event_process = true)] - struct Foo { + struct Foo { ping: libp2p::ping::Ping, bar: T, } @@ -249,30 +255,18 @@ fn where_clause() { #[test] fn nested_derives_with_import() { - use libp2p::swarm::NetworkBehaviourEventProcess; - #[allow(dead_code)] #[derive(NetworkBehaviour)] - #[behaviour(event_process = true)] struct Foo { ping: libp2p::ping::Ping, } #[allow(dead_code)] #[derive(NetworkBehaviour)] - #[behaviour(event_process = true)] struct Bar { foo: Foo, } - impl NetworkBehaviourEventProcess for Foo { - fn inject_event(&mut self, _: libp2p::ping::PingEvent) {} - } - - impl NetworkBehaviourEventProcess<()> for Bar { - fn inject_event(&mut self, _: ()) {} - } - #[allow(dead_code)] fn bar() { require_net_behaviour::(); @@ -280,7 +274,7 @@ fn nested_derives_with_import() { } #[test] -fn event_process_false() { +fn custom_event_emit_event_through_poll() { enum BehaviourOutEvent { Ping(libp2p::ping::PingEvent), Identify(libp2p::identify::IdentifyEvent), @@ -331,20 +325,11 @@ fn with_toggle() { #[allow(dead_code)] #[derive(NetworkBehaviour)] - #[behaviour(event_process = true)] struct Foo { identify: libp2p::identify::Identify, ping: Toggle, } - impl libp2p::swarm::NetworkBehaviourEventProcess for Foo { - fn inject_event(&mut self, _: libp2p::identify::IdentifyEvent) {} - } - - impl libp2p::swarm::NetworkBehaviourEventProcess for Foo { - fn inject_event(&mut self, _: libp2p::ping::PingEvent) {} - } - #[allow(dead_code)] fn foo() { require_net_behaviour::(); @@ -357,28 +342,11 @@ fn with_either() { #[allow(dead_code)] #[derive(NetworkBehaviour)] - #[behaviour(event_process = true)] struct Foo { kad: libp2p::kad::Kademlia, ping_or_identify: Either, } - impl libp2p::swarm::NetworkBehaviourEventProcess for Foo { - fn inject_event(&mut self, _: libp2p::kad::KademliaEvent) {} - } - - impl - libp2p::swarm::NetworkBehaviourEventProcess< - Either, - > for Foo - { - fn inject_event( - &mut self, - _: Either, - ) { - } - } - #[allow(dead_code)] fn foo() { require_net_behaviour::(); @@ -386,7 +354,7 @@ fn with_either() { } #[test] -fn no_event_with_either() { +fn custom_event_with_either() { use either::Either; enum BehaviourOutEvent { @@ -394,14 +362,6 @@ fn no_event_with_either() { PingOrIdentify(Either), } - #[allow(dead_code)] - #[derive(NetworkBehaviour)] - #[behaviour(out_event = "BehaviourOutEvent", event_process = false)] - struct Foo { - kad: libp2p::kad::Kademlia, - ping_or_identify: Either, - } - impl From for BehaviourOutEvent { fn from(event: libp2p::kad::KademliaEvent) -> Self { BehaviourOutEvent::Kad(event) @@ -414,6 +374,14 @@ fn no_event_with_either() { } } + #[allow(dead_code)] + #[derive(NetworkBehaviour)] + #[behaviour(out_event = "BehaviourOutEvent")] + struct Foo { + kad: libp2p::kad::Kademlia, + ping_or_identify: Either, + } + #[allow(dead_code)] fn foo() { require_net_behaviour::(); @@ -425,7 +393,6 @@ fn mixed_field_order() { struct Foo {} #[derive(NetworkBehaviour)] - #[behaviour(event_process = true)] pub struct Behaviour { #[behaviour(ignore)] _foo: Foo, @@ -437,12 +404,44 @@ fn mixed_field_order() { _foo3: Foo, } - impl libp2p::swarm::NetworkBehaviourEventProcess for Behaviour { - fn inject_event(&mut self, _evt: T) {} - } - #[allow(dead_code)] fn behaviour() { require_net_behaviour::(); } } + +#[test] +fn event_process() { + #[allow(dead_code)] + #[derive(NetworkBehaviour)] + #[behaviour(event_process = true)] + struct Foo { + ping: libp2p::ping::Ping, + identify: libp2p::identify::Identify, + } + + impl libp2p::swarm::NetworkBehaviourEventProcess for Foo { + fn inject_event(&mut self, _: libp2p::identify::IdentifyEvent) {} + } + + impl libp2p::swarm::NetworkBehaviourEventProcess for Foo { + fn inject_event(&mut self, _: libp2p::ping::PingEvent) {} + } + + #[allow(dead_code, unreachable_code)] + fn bar() { + require_net_behaviour::(); + + let mut _swarm: libp2p::Swarm = unimplemented!(); + + // check that the event is bubbled up all the way to swarm + let _ = async { + loop { + match _swarm.select_next_some().await { + SwarmEvent::Behaviour(()) => break, + _ => {} + } + } + }; + } +} diff --git a/swarm/CHANGELOG.md b/swarm/CHANGELOG.md index 02edf9beef4..62de6afa063 100644 --- a/swarm/CHANGELOG.md +++ b/swarm/CHANGELOG.md @@ -4,6 +4,15 @@ - Update to `libp2p-core` `v0.35.0`. +- When deriving `NetworkBehaviour` on a custom `struct` where the user does not specify their own + `OutEvent` via `#[behaviour(out_event = "MyBehaviourEvent")]` and where the user does not enable + `#[behaviour(event_process = true)]`, then the derive macro generates an `OutEvent` definition for + the user. + + See [`NetworkBehaviour` + documentation](https://docs.rs/libp2p/latest/libp2p/swarm/trait.NetworkBehaviour.html) for + details. + [PR 2741]: https://github.com/libp2p/rust-libp2p/pull/2741/ # 0.37.0 diff --git a/swarm/src/behaviour.rs b/swarm/src/behaviour.rs index 3dd6ddf9588..7ab54c73c05 100644 --- a/swarm/src/behaviour.rs +++ b/swarm/src/behaviour.rs @@ -79,16 +79,11 @@ pub(crate) type THandlerOutEvent = /// it will delegate to each `struct` member and return a concatenated array of all addresses /// returned by the struct members. /// -/// When creating a custom [`NetworkBehaviour`], you must choose one of two methods to respond to -/// incoming events: -/// * One option is setting a custom `out_event` with `#[behaviour(out_event = "AnotherType")]`. -/// In this case, events generated by the custom [`NetworkBehaviour`] struct members will be -/// converted to your custom `out_event` for you to handle after polling the swarm. -/// * Alternatively, users that need access to the root [`NetworkBehaviour`] implementation while -/// processing emitted events, can specify `#[behaviour(event_process = true)]` (default is false). -/// Events generated by the behaviour's struct members are delegated to [`NetworkBehaviourEventProcess`] -/// trait implementations. Those must be provided by the user on the type that [`NetworkBehaviour`] -/// is derived on. +/// Events ([`NetworkBehaviour::OutEvent`]) returned by each `struct` member are wrapped in a new +/// `enum` event, with an `enum` variant for each `struct` member. Users can define this event +/// `enum` themselves and provide the name to the derive macro via `#[behaviour(out_event = +/// "MyCustomOutEvent")]`. If the user does not specify an `out_event`, the derive macro generates +/// the event definition itself, naming it `Event`. /// /// When setting a custom `out_event`, the aforementioned conversion of each of the event types /// generated by the struct members to the custom `out_event` is handled by [`From`] @@ -123,14 +118,6 @@ pub(crate) type THandlerOutEvent = /// } /// ``` /// -/// When using `event_process = true` the [`NetworkBehaviourEventProcess`] trait implementations -/// are granted exclusive access to the [`NetworkBehaviour`], therefore -/// [blocking code](https://ryhl.io/blog/async-what-is-blocking/) in these implementations will -/// block the entire [`Swarm`](crate::Swarm) from processing new events, since the swarm cannot progress -/// without also having exclusive access to the [`NetworkBehaviour`]. A better alternative is to execute -/// blocking or asynchronous logic on a separate task, perhaps with the help of a bounded channel to -/// maintain backpressure. The sender for the channel could be included in the NetworkBehaviours constructor. -/// /// Optionally one can provide a custom `poll` function through the `#[behaviour(poll_method = /// "poll")]` attribute. This function must have the same signature as the [`NetworkBehaviour#poll`] /// function and will be called last within the generated [`NetworkBehaviour`] implementation. From 3833545c448cf4c7247de563091dd34357685bc4 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Tue, 2 Aug 2022 21:40:55 +0900 Subject: [PATCH 2/4] swarm-derive/: Fix clippy warning --- swarm-derive/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/swarm-derive/src/lib.rs b/swarm-derive/src/lib.rs index 07aae641e0f..8a42220246a 100644 --- a/swarm-derive/src/lib.rs +++ b/swarm-derive/src/lib.rs @@ -178,7 +178,7 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream { }) .collect::>(); ( - name.unwrap_or(syn::parse_str("()").unwrap()), + name.unwrap_or_else(|| syn::parse_str("()").unwrap()), definition, from_clauses, ) @@ -555,7 +555,7 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream { // implementation. let into_out_event = if out_event_definition.is_some() { let event_variant: syn::Variant = syn::parse_str( - &field.clone() + &field .to_string() .to_upper_camel_case() ).unwrap(); From b4e32ed7a908aded7678f8e6a9d30ab29ee34ec1 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Thu, 4 Aug 2022 18:25:10 +0900 Subject: [PATCH 3/4] swarm-derive/tests: Match on generated event --- swarm-derive/tests/test.rs | 55 +++++++++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/swarm-derive/tests/test.rs b/swarm-derive/tests/test.rs index 9a53ac7e1c5..67b34c75590 100644 --- a/swarm-derive/tests/test.rs +++ b/swarm-derive/tests/test.rs @@ -43,8 +43,14 @@ fn one_field() { } #[allow(dead_code)] + #[allow(unreachable_code)] fn foo() { - require_net_behaviour::(); + let _out_event: ::OutEvent = unimplemented!(); + match _out_event { + FooEvent::Ping(event) => { + let _: libp2p::ping::Event = event; + } + } } } @@ -58,8 +64,17 @@ fn two_fields() { } #[allow(dead_code)] + #[allow(unreachable_code)] fn foo() { - require_net_behaviour::(); + let _out_event: ::OutEvent = unimplemented!(); + match _out_event { + FooEvent::Ping(event) => { + let _: libp2p::ping::Event = event; + } + FooEvent::Identify(event) => { + let _: libp2p::identify::IdentifyEvent = event; + } + } } } @@ -76,8 +91,20 @@ fn three_fields() { } #[allow(dead_code)] + #[allow(unreachable_code)] fn foo() { - require_net_behaviour::(); + let _out_event: ::OutEvent = unimplemented!(); + match _out_event { + FooEvent::Ping(event) => { + let _: libp2p::ping::Event = event; + } + FooEvent::Identify(event) => { + let _: libp2p::identify::IdentifyEvent = event; + } + FooEvent::Kad(event) => { + let _: libp2p::kad::KademliaEvent = event; + } + } } } @@ -93,8 +120,17 @@ fn three_fields_non_last_ignored() { } #[allow(dead_code)] + #[allow(unreachable_code)] fn foo() { - require_net_behaviour::(); + let _out_event: ::OutEvent = unimplemented!(); + match _out_event { + FooEvent::Ping(event) => { + let _: libp2p::ping::Event = event; + } + FooEvent::Kad(event) => { + let _: libp2p::kad::KademliaEvent = event; + } + } } } @@ -268,8 +304,14 @@ fn nested_derives_with_import() { } #[allow(dead_code)] - fn bar() { - require_net_behaviour::(); + #[allow(unreachable_code)] + fn foo() { + let _out_event: ::OutEvent = unimplemented!(); + match _out_event { + BarEvent::Foo(FooEvent::Ping(event)) => { + let _: libp2p::ping::Event = event; + } + } } } @@ -434,7 +476,6 @@ fn event_process() { let mut _swarm: libp2p::Swarm = unimplemented!(); - // check that the event is bubbled up all the way to swarm let _ = async { loop { match _swarm.select_next_some().await { From ff636810156a54fd9eb63566ed1093352ef650bb Mon Sep 17 00:00:00 2001 From: Max Inden Date: Thu, 4 Aug 2022 22:08:05 +0900 Subject: [PATCH 4/4] swarm-derive/tests: Extend pattern match on ping::Event --- swarm-derive/tests/test.rs | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/swarm-derive/tests/test.rs b/swarm-derive/tests/test.rs index 67b34c75590..09048e5e801 100644 --- a/swarm-derive/tests/test.rs +++ b/swarm-derive/tests/test.rs @@ -47,9 +47,7 @@ fn one_field() { fn foo() { let _out_event: ::OutEvent = unimplemented!(); match _out_event { - FooEvent::Ping(event) => { - let _: libp2p::ping::Event = event; - } + FooEvent::Ping(libp2p::ping::Event { .. }) => {} } } } @@ -68,9 +66,7 @@ fn two_fields() { fn foo() { let _out_event: ::OutEvent = unimplemented!(); match _out_event { - FooEvent::Ping(event) => { - let _: libp2p::ping::Event = event; - } + FooEvent::Ping(libp2p::ping::Event { .. }) => {} FooEvent::Identify(event) => { let _: libp2p::identify::IdentifyEvent = event; } @@ -95,9 +91,7 @@ fn three_fields() { fn foo() { let _out_event: ::OutEvent = unimplemented!(); match _out_event { - FooEvent::Ping(event) => { - let _: libp2p::ping::Event = event; - } + FooEvent::Ping(libp2p::ping::Event { .. }) => {} FooEvent::Identify(event) => { let _: libp2p::identify::IdentifyEvent = event; } @@ -124,9 +118,7 @@ fn three_fields_non_last_ignored() { fn foo() { let _out_event: ::OutEvent = unimplemented!(); match _out_event { - FooEvent::Ping(event) => { - let _: libp2p::ping::Event = event; - } + FooEvent::Ping(libp2p::ping::Event { .. }) => {} FooEvent::Kad(event) => { let _: libp2p::kad::KademliaEvent = event; } @@ -308,9 +300,7 @@ fn nested_derives_with_import() { fn foo() { let _out_event: ::OutEvent = unimplemented!(); match _out_event { - BarEvent::Foo(FooEvent::Ping(event)) => { - let _: libp2p::ping::Event = event; - } + BarEvent::Foo(FooEvent::Ping(libp2p::ping::Event { .. })) => {} } } }