diff --git a/CHANGELOG.md b/CHANGELOG.md index d3eaaf10..ee75100c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ - [`libp2p-yamux` CHANGELOG](muxers/yamux/CHANGELOG.md) - [`multistream-select` CHANGELOG](misc/multistream-select/CHANGELOG.md) - [`parity-multiaddr` CHANGELOG](misc/multiaddr/CHANGELOG.md) +- [`libp2p-core-derive` CHANGELOG](misc/core-derive/CHANGELOG.md) # Version 0.21.1 (2020-07-02) diff --git a/misc/core-derive/CHANGELOG.md b/misc/core-derive/CHANGELOG.md new file mode 100644 index 00000000..15f337b5 --- /dev/null +++ b/misc/core-derive/CHANGELOG.md @@ -0,0 +1,6 @@ +# 0.20.1 [2020-07-08] + +- Allow users to opt out of the `NetworkBehaviourEventProcess` +mechanism through `#[behaviour(event_process = false)]`. This is +useful if users want to process all events while polling the +swarm through `SwarmEvent::Behaviour`. diff --git a/misc/core-derive/Cargo.toml b/misc/core-derive/Cargo.toml index 5552eac0..a3ed9aad 100644 --- a/misc/core-derive/Cargo.toml +++ b/misc/core-derive/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-core-derive" edition = "2018" description = "Procedural macros of libp2p-core" -version = "0.20.0" +version = "0.20.1" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" diff --git a/misc/core-derive/src/lib.rs b/misc/core-derive/src/lib.rs index 7ce859fe..c100b516 100644 --- a/misc/core-derive/src/lib.rs +++ b/misc/core-derive/src/lib.rs @@ -70,28 +70,24 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream { quote!{<#(#lf,)* #(#tp,)* #(#cst,)*>} }; - // Build the `where ...` clause of the trait implementation. - let where_clause = { - let additional = data_struct.fields.iter() - .filter(|x| !is_ignored(x)) - .flat_map(|field| { - let ty = &field.ty; - vec![ - quote!{#ty: #trait_to_impl}, - quote!{Self: #net_behv_event_proc<<#ty as #trait_to_impl>::OutEvent>}, - ] - }) - .collect::>(); + // Whether or not we require the `NetworkBehaviourEventProcess` trait to be implemented. + let event_process = { + let mut event_process = true; // Default to true for backwards compatibility - if let Some(where_clause) = where_clause { - if where_clause.predicates.trailing_punct() { - Some(quote!{#where_clause #(#additional),*}) - } else { - Some(quote!{#where_clause, #(#additional),*}) + 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("event_process") => { + if let syn::Lit::Bool(ref b) = m.lit { + event_process = b.value + } + } + _ => () + } } - } else { - Some(quote!{where #(#additional),*}) } + + event_process }; // The final out event. @@ -115,6 +111,34 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream { out }; + // Build the `where ...` clause of the trait implementation. + let where_clause = { + let additional = data_struct.fields.iter() + .filter(|x| !is_ignored(x)) + .flat_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 >} + } + ] + }) + .collect::>(); + + if let Some(where_clause) = where_clause { + if where_clause.predicates.trailing_punct() { + Some(quote!{#where_clause #(#additional),*}) + } else { + Some(quote!{#where_clause, #(#additional),*}) + } + } else { + Some(quote!{where #(#additional),*}) + } + }; + // Build the list of statements to put in the body of `addresses_of_peer()`. let addresses_of_peer_stmts = { data_struct.fields.iter().enumerate().filter_map(move |(field_n, field)| { @@ -395,12 +419,24 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream { wrapped_event = quote!{ #either_ident::First(#wrapped_event) }; } + let generate_event_match_arm = if event_process { + quote! { + std::task::Poll::Ready(#network_behaviour_action::GenerateEvent(event)) => { + #net_behv_event_proc::inject_event(self, event) + } + } + } else { + quote! { + std::task::Poll::Ready(#network_behaviour_action::GenerateEvent(event)) => { + return std::task::Poll::Ready(#network_behaviour_action::GenerateEvent(event.into())) + } + } + }; + Some(quote!{ loop { match #field_name.poll(cx, poll_params) { - std::task::Poll::Ready(#network_behaviour_action::GenerateEvent(event)) => { - #net_behv_event_proc::inject_event(self, event) - } + #generate_event_match_arm std::task::Poll::Ready(#network_behaviour_action::DialAddress { address }) => { return std::task::Poll::Ready(#network_behaviour_action::DialAddress { address }); } diff --git a/misc/core-derive/tests/test.rs b/misc/core-derive/tests/test.rs index 6181de8e..1dfcd847 100644 --- a/misc/core-derive/tests/test.rs +++ b/misc/core-derive/tests/test.rs @@ -263,3 +263,46 @@ fn nested_derives_with_import() { require_net_behaviour::(); } } + +#[test] +fn event_process_false() { + enum BehaviourOutEvent { + Ping(libp2p::ping::PingEvent), + Identify(libp2p::identify::IdentifyEvent) + } + + impl From for BehaviourOutEvent { + fn from(event: libp2p::ping::PingEvent) -> Self { + BehaviourOutEvent::Ping(event) + } + } + + impl From for BehaviourOutEvent { + fn from(event: libp2p::identify::IdentifyEvent) -> Self { + BehaviourOutEvent::Identify(event) + } + } + + #[allow(dead_code)] + #[derive(NetworkBehaviour)] + #[behaviour(out_event = "BehaviourOutEvent", event_process = false)] + struct Foo { + ping: libp2p::ping::Ping, + identify: libp2p::identify::Identify, + } + + #[allow(dead_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 { + match swarm.next().await { + BehaviourOutEvent::Ping(_) => {}, + BehaviourOutEvent::Identify(_) => {}, + } + }; + } +} diff --git a/swarm/CHANGELOG.md b/swarm/CHANGELOG.md index 66333f6e..8a01d483 100644 --- a/swarm/CHANGELOG.md +++ b/swarm/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.20.1 [2020-07-08] + +- Documentation updates. + # 0.20.0 [2020-07-01] - Updated the `libp2p-core` dependency. diff --git a/swarm/Cargo.toml b/swarm/Cargo.toml index 37727f52..99848420 100644 --- a/swarm/Cargo.toml +++ b/swarm/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-swarm" edition = "2018" description = "The libp2p swarm" -version = "0.20.0" +version = "0.20.1" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" diff --git a/swarm/src/behaviour.rs b/swarm/src/behaviour.rs index bce5fbe2..f75a31ab 100644 --- a/swarm/src/behaviour.rs +++ b/swarm/src/behaviour.rs @@ -31,18 +31,27 @@ use std::{error, task::Context, task::Poll}; /// /// Crate users can implement this trait by using the the `#[derive(NetworkBehaviour)]` /// proc macro re-exported by the `libp2p` crate. The macro generates a delegating `trait` -/// implementation for the `struct`, which delegates method calls to all trait members. Any events -/// generated by struct members are delegated to [`NetworkBehaviourEventProcess`] implementations -/// which are expected to be provided by the user. +/// implementation for the `struct`, which delegates method calls to all trait members. /// -/// Optionally one can implement a custom `poll` function, which needs to be tagged with the -/// `#[behaviour(poll_method = "poll")]` attribute, and would be called last with no parameters. -/// -/// By default the derive sets the `NetworkBehaviour::OutEvent` as `()` but this can be overriden +/// By default the derive sets the [`NetworkBehaviour::OutEvent`] as `()` but this can be overridden /// with `#[behaviour(out_event = "AnotherType")]`. /// -/// `#[behaviour(ignore)]` can be added on a struct field to disable generation of delegation to -/// the fields which do not implement `NetworkBehaviour`. +/// Struct members that don't implement [`NetworkBehaviour`] must be annotated with `#[behaviour(ignore)]`. +/// +/// By default, events generated by the remaining members are delegated to [`NetworkBehaviourEventProcess`] +/// implementations. Those must be provided by the user on the type that [`NetworkBehaviour`] is +/// derived on. +/// +/// Alternatively, users can specify `#[behaviour(event_process = false)]`. In this case, users +/// should provide a custom `out_event` and implement [`From`] for each of the event types generated +/// by the struct members. +/// Not processing events within the derived [`NetworkBehaviour`] will cause them to be emitted as +/// part of polling the swarm in [`SwarmEvent::Behaviour`](crate::SwarmEvent::Behaviour). +/// +/// 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. pub trait NetworkBehaviour: Send + 'static { /// Handler for all the protocols the network behaviour supports. type ProtocolsHandler: IntoProtocolsHandler; @@ -193,8 +202,11 @@ pub trait PollParameters { fn local_peer_id(&self) -> &PeerId; } -/// When deriving [`NetworkBehaviour`] this trait must be implemented for all the possible event types -/// generated by the inner behaviours. +/// When deriving [`NetworkBehaviour`] this trait must by default be implemented for all the +/// possible event types generated by the inner behaviours. +/// +/// You can opt out of this behaviour through `#[behaviour(event_process = false)]`. See the +/// documentation of [`NetworkBehaviour`] for details. pub trait NetworkBehaviourEventProcess { /// Called when one of the fields of the type you're deriving `NetworkBehaviour` on generates /// an event.