From d10cafa804dd9b37563b7d6bf12405d3f4066730 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Thu, 20 Dec 2018 15:21:13 +0100 Subject: [PATCH] Make deriving the NetworkBehaviour more ergonomic (#782) --- Cargo.toml | 1 + core/src/swarm.rs | 9 +++++ examples/chat.rs | 14 ++++--- misc/core-derive/Cargo.toml | 1 + misc/core-derive/src/lib.rs | 25 ++---------- misc/core-derive/tests/test.rs | 74 ++++++++++++++++++++++++++-------- protocols/floodsub/src/lib.rs | 1 + 7 files changed, 82 insertions(+), 43 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 160be1b0..470fa4f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,7 @@ env_logger = "0.6.0" rand = "0.6" tokio = "0.1" tokio-stdin-stdout = "0.1" +void = "1.0" [workspace] members = [ diff --git a/core/src/swarm.rs b/core/src/swarm.rs index 145fd056..02c99b7e 100644 --- a/core/src/swarm.rs +++ b/core/src/swarm.rs @@ -336,6 +336,15 @@ pub trait NetworkBehaviour { fn poll(&mut self, topology: &mut PollParameters) -> Async::InEvent, Self::OutEvent>>; } +/// Used when deriving `NetworkBehaviour`. When deriving `NetworkBehaviour`, must be implemented +/// for all the possible event types generated by the various fields. +// TODO: document how the custom behaviour works and link this here +pub trait NetworkBehaviourEventProcess { + /// Called when one of the fields of the type you're deriving `NetworkBehaviour` on generates + /// an event. + fn inject_event(&mut self, event: TEvent); +} + /// Parameters passed to `poll()` that the `NetworkBehaviour` has access to. // TODO: #[derive(Debug)] pub struct PollParameters<'a, TTopology: 'a> { diff --git a/examples/chat.rs b/examples/chat.rs index dac5d6ec..34e34751 100644 --- a/examples/chat.rs +++ b/examples/chat.rs @@ -53,6 +53,7 @@ extern crate env_logger; extern crate futures; extern crate libp2p; extern crate tokio; +extern crate void; use futures::prelude::*; use libp2p::{ @@ -79,16 +80,19 @@ fn main() { // In the future, we want to improve libp2p to make this easier to do. #[derive(NetworkBehaviour)] struct MyBehaviour { - #[behaviour(handler = "on_floodsub")] floodsub: libp2p::floodsub::Floodsub, mdns: libp2p::mdns::Mdns, } - impl MyBehaviour { + impl libp2p::core::swarm::NetworkBehaviourEventProcess for MyBehaviour { + fn inject_event(&mut self, _ev: void::Void) { + void::unreachable(_ev) + } + } + + impl libp2p::core::swarm::NetworkBehaviourEventProcess for MyBehaviour { // Called when `floodsub` produces an event. - fn on_floodsub(&mut self, message: as libp2p::core::swarm::NetworkBehaviour>::OutEvent) - where TSubstream: libp2p::tokio_io::AsyncRead + libp2p::tokio_io::AsyncWrite - { + fn inject_event(&mut self, message: libp2p::floodsub::FloodsubMessage) { println!("Received: '{:?}' from {:?}", String::from_utf8_lossy(&message.data), message.source); } } diff --git a/misc/core-derive/Cargo.toml b/misc/core-derive/Cargo.toml index 5ed764d1..10c7533c 100644 --- a/misc/core-derive/Cargo.toml +++ b/misc/core-derive/Cargo.toml @@ -17,3 +17,4 @@ quote = "0.6" [dev-dependencies] libp2p = { version = "0.1.0", path = "../.." } +void = "1.0" diff --git a/misc/core-derive/src/lib.rs b/misc/core-derive/src/lib.rs index ed3ea742..1e420540 100644 --- a/misc/core-derive/src/lib.rs +++ b/misc/core-derive/src/lib.rs @@ -50,6 +50,7 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream { let name = &ast.ident; let (_, ty_generics, where_clause) = ast.generics.split_for_impl(); let trait_to_impl = quote!{::libp2p::core::swarm::NetworkBehaviour}; + let net_behv_event_proc = quote!{::libp2p::core::swarm::NetworkBehaviourEventProcess}; let either_ident = quote!{::libp2p::core::either::EitherOutput}; let network_behaviour_action = quote!{::libp2p::core::swarm::NetworkBehaviourAction}; let protocols_handler = quote!{::libp2p::core::protocols_handler::ProtocolsHandler}; @@ -97,6 +98,7 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream { let ty = &field.ty; vec![ quote!{#ty: #trait_to_impl<#topology_generic>}, + quote!{Self: #net_behv_event_proc<<#ty as #trait_to_impl<#topology_generic>>::OutEvent>}, quote!{<#ty as #trait_to_impl<#topology_generic>>::ProtocolsHandler: #protocols_handler}, // Note: this bound is required because of https://github.com/rust-lang/rust/issues/55697 quote!{<<#ty as #trait_to_impl<#topology_generic>>::ProtocolsHandler as #protocols_handler>::InboundProtocol: ::libp2p::core::InboundUpgrade<#substream_generic>}, @@ -276,27 +278,6 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream { None => quote!{ self.#field_n }, }; - let mut handler_fn: Option = None; - for meta_items in field.attrs.iter().filter_map(get_meta_items) { - for meta_item in meta_items { - match meta_item { - // Parse `#[behaviour(handler = "foo")]` - syn::NestedMeta::Meta(syn::Meta::NameValue(ref m)) if m.ident == "handler" => { - if let syn::Lit::Str(ref s) = m.lit { - handler_fn = Some(syn::parse_str(&s.value()).unwrap()); - } - } - _ => () - } - } - } - - let handling = if let Some(handler_fn) = handler_fn { - quote!{self.#handler_fn(event)} - } else { - quote!{} - }; - let mut wrapped_event = if enum_n != 0 { quote!{ #either_ident::Second(event) } } else { @@ -310,7 +291,7 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream { loop { match #field_name.poll(poll_params) { Async::Ready(#network_behaviour_action::GenerateEvent(event)) => { - #handling + #net_behv_event_proc::inject_event(self, event) } Async::Ready(#network_behaviour_action::DialAddress { address }) => { return Async::Ready(#network_behaviour_action::DialAddress { address }); diff --git a/misc/core-derive/tests/test.rs b/misc/core-derive/tests/test.rs index 576be04b..d2377e02 100644 --- a/misc/core-derive/tests/test.rs +++ b/misc/core-derive/tests/test.rs @@ -20,6 +20,9 @@ #[macro_use] extern crate libp2p; +extern crate void; + +use void::Void; /// Small utility to check that a type implements `NetworkBehaviour`. #[allow(dead_code)] @@ -41,6 +44,12 @@ fn one_field() { ping: libp2p::ping::PeriodicPing, } + impl libp2p::core::swarm::NetworkBehaviourEventProcess for Foo { + fn inject_event(&mut self, event: Void) { + void::unreachable(event) + } + } + fn foo() { require_net_behaviour::>(); } @@ -54,6 +63,16 @@ fn two_fields() { ping_dialer: libp2p::ping::PeriodicPing, ping_listener: libp2p::ping::PingListen, } + + impl libp2p::core::swarm::NetworkBehaviourEventProcess for Foo { + fn inject_event(&mut self, event: Void) { + void::unreachable(event) + } + } + + fn foo() { + require_net_behaviour::>(); + } } #[test] @@ -68,24 +87,14 @@ fn three_fields() { foo: String, } - fn foo() { - require_net_behaviour::>(); - } -} - -#[test] -fn event_handler() { - #[allow(dead_code)] - #[derive(NetworkBehaviour)] - struct Foo { - #[behaviour(handler = "foo")] - ping: libp2p::ping::PingListen, + impl libp2p::core::swarm::NetworkBehaviourEventProcess for Foo { + fn inject_event(&mut self, event: Void) { + void::unreachable(event) + } } - impl Foo { - // TODO: for some reason, the parameter cannot be the event type or we - // get a compilation error; figure out why or open an issue to Rust - fn foo(&mut self, ev: as libp2p::core::swarm::NetworkBehaviour>::OutEvent) { + impl libp2p::core::swarm::NetworkBehaviourEventProcess for Foo { + fn inject_event(&mut self, _: libp2p::identify::IdentifyEvent) { } } @@ -104,6 +113,17 @@ fn custom_polling() { identify: libp2p::identify::Identify, } + impl libp2p::core::swarm::NetworkBehaviourEventProcess for Foo { + fn inject_event(&mut self, event: Void) { + void::unreachable(event) + } + } + + impl libp2p::core::swarm::NetworkBehaviourEventProcess for Foo { + fn inject_event(&mut self, _: libp2p::identify::IdentifyEvent) { + } + } + impl Foo { fn foo(&mut self) -> libp2p::futures::Async> { libp2p::futures::Async::NotReady } } @@ -123,6 +143,17 @@ fn custom_event_no_polling() { identify: libp2p::identify::Identify, } + impl libp2p::core::swarm::NetworkBehaviourEventProcess for Foo { + fn inject_event(&mut self, event: Void) { + void::unreachable(event) + } + } + + impl libp2p::core::swarm::NetworkBehaviourEventProcess for Foo { + fn inject_event(&mut self, _: libp2p::identify::IdentifyEvent) { + } + } + fn foo() { require_net_behaviour::>(); } @@ -138,6 +169,17 @@ fn custom_event_and_polling() { identify: libp2p::identify::Identify, } + impl libp2p::core::swarm::NetworkBehaviourEventProcess for Foo { + fn inject_event(&mut self, event: Void) { + void::unreachable(event) + } + } + + impl libp2p::core::swarm::NetworkBehaviourEventProcess for Foo { + fn inject_event(&mut self, _: libp2p::identify::IdentifyEvent) { + } + } + impl Foo { fn foo(&mut self) -> libp2p::futures::Async> { libp2p::futures::Async::NotReady } } diff --git a/protocols/floodsub/src/lib.rs b/protocols/floodsub/src/lib.rs index 725de1b1..4405ccaf 100644 --- a/protocols/floodsub/src/lib.rs +++ b/protocols/floodsub/src/lib.rs @@ -42,4 +42,5 @@ mod rpc_proto; mod topic; pub use self::layer::Floodsub; +pub use self::protocol::{FloodsubMessage, FloodsubRpc}; pub use self::topic::{Topic, TopicBuilder, TopicHash};