Make deriving the NetworkBehaviour more ergonomic (#782)

This commit is contained in:
Pierre Krieger 2018-12-20 15:21:13 +01:00 committed by GitHub
parent 6be3aca768
commit d10cafa804
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 82 additions and 43 deletions

View File

@ -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 = [

View File

@ -336,6 +336,15 @@ pub trait NetworkBehaviour<TTopology> {
fn poll(&mut self, topology: &mut PollParameters<TTopology>) -> Async<NetworkBehaviourAction<<Self::ProtocolsHandler as ProtocolsHandler>::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<TEvent> {
/// 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> {

View File

@ -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<TSubstream: libp2p::tokio_io::AsyncRead + libp2p::tokio_io::AsyncWrite> {
#[behaviour(handler = "on_floodsub")]
floodsub: libp2p::floodsub::Floodsub<TSubstream>,
mdns: libp2p::mdns::Mdns<TSubstream>,
}
impl<TSubstream: libp2p::tokio_io::AsyncRead + libp2p::tokio_io::AsyncWrite> MyBehaviour<TSubstream> {
impl<TSubstream: libp2p::tokio_io::AsyncRead + libp2p::tokio_io::AsyncWrite> libp2p::core::swarm::NetworkBehaviourEventProcess<void::Void> for MyBehaviour<TSubstream> {
fn inject_event(&mut self, _ev: void::Void) {
void::unreachable(_ev)
}
}
impl<TSubstream: libp2p::tokio_io::AsyncRead + libp2p::tokio_io::AsyncWrite> libp2p::core::swarm::NetworkBehaviourEventProcess<libp2p::floodsub::FloodsubMessage> for MyBehaviour<TSubstream> {
// Called when `floodsub` produces an event.
fn on_floodsub<TTopology>(&mut self, message: <libp2p::floodsub::Floodsub<TSubstream> as libp2p::core::swarm::NetworkBehaviour<TTopology>>::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);
}
}

View File

@ -17,3 +17,4 @@ quote = "0.6"
[dev-dependencies]
libp2p = { version = "0.1.0", path = "../.." }
void = "1.0"

View File

@ -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<Substream = #substream_generic>},
// 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<Ident> = 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 });

View File

@ -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<TSubstream>,
}
impl<TSubstream> libp2p::core::swarm::NetworkBehaviourEventProcess<Void> for Foo<TSubstream> {
fn inject_event(&mut self, event: Void) {
void::unreachable(event)
}
}
fn foo<TSubstream: libp2p::tokio_io::AsyncRead + libp2p::tokio_io::AsyncWrite>() {
require_net_behaviour::<Foo<TSubstream>>();
}
@ -54,6 +63,16 @@ fn two_fields() {
ping_dialer: libp2p::ping::PeriodicPing<TSubstream>,
ping_listener: libp2p::ping::PingListen<TSubstream>,
}
impl<TSubstream> libp2p::core::swarm::NetworkBehaviourEventProcess<Void> for Foo<TSubstream> {
fn inject_event(&mut self, event: Void) {
void::unreachable(event)
}
}
fn foo<TSubstream: libp2p::tokio_io::AsyncRead + libp2p::tokio_io::AsyncWrite>() {
require_net_behaviour::<Foo<TSubstream>>();
}
}
#[test]
@ -68,24 +87,14 @@ fn three_fields() {
foo: String,
}
fn foo<TSubstream: libp2p::tokio_io::AsyncRead + libp2p::tokio_io::AsyncWrite>() {
require_net_behaviour::<Foo<TSubstream>>();
impl<TSubstream> libp2p::core::swarm::NetworkBehaviourEventProcess<Void> for Foo<TSubstream> {
fn inject_event(&mut self, event: Void) {
void::unreachable(event)
}
}
#[test]
fn event_handler() {
#[allow(dead_code)]
#[derive(NetworkBehaviour)]
struct Foo<TSubstream: libp2p::tokio_io::AsyncRead + libp2p::tokio_io::AsyncWrite> {
#[behaviour(handler = "foo")]
ping: libp2p::ping::PingListen<TSubstream>,
}
impl<TSubstream: libp2p::tokio_io::AsyncRead + libp2p::tokio_io::AsyncWrite> Foo<TSubstream> {
// 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<TTopology>(&mut self, ev: <libp2p::ping::PingListen<TSubstream> as libp2p::core::swarm::NetworkBehaviour<TTopology>>::OutEvent) {
impl<TSubstream> libp2p::core::swarm::NetworkBehaviourEventProcess<libp2p::identify::IdentifyEvent> for Foo<TSubstream> {
fn inject_event(&mut self, _: libp2p::identify::IdentifyEvent) {
}
}
@ -104,6 +113,17 @@ fn custom_polling() {
identify: libp2p::identify::Identify<TSubstream>,
}
impl<TSubstream> libp2p::core::swarm::NetworkBehaviourEventProcess<Void> for Foo<TSubstream> {
fn inject_event(&mut self, event: Void) {
void::unreachable(event)
}
}
impl<TSubstream> libp2p::core::swarm::NetworkBehaviourEventProcess<libp2p::identify::IdentifyEvent> for Foo<TSubstream> {
fn inject_event(&mut self, _: libp2p::identify::IdentifyEvent) {
}
}
impl<TSubstream> Foo<TSubstream> {
fn foo<T>(&mut self) -> libp2p::futures::Async<libp2p::core::swarm::NetworkBehaviourAction<T, ()>> { libp2p::futures::Async::NotReady }
}
@ -123,6 +143,17 @@ fn custom_event_no_polling() {
identify: libp2p::identify::Identify<TSubstream>,
}
impl<TSubstream> libp2p::core::swarm::NetworkBehaviourEventProcess<Void> for Foo<TSubstream> {
fn inject_event(&mut self, event: Void) {
void::unreachable(event)
}
}
impl<TSubstream> libp2p::core::swarm::NetworkBehaviourEventProcess<libp2p::identify::IdentifyEvent> for Foo<TSubstream> {
fn inject_event(&mut self, _: libp2p::identify::IdentifyEvent) {
}
}
fn foo<TSubstream: libp2p::tokio_io::AsyncRead + libp2p::tokio_io::AsyncWrite>() {
require_net_behaviour::<Foo<TSubstream>>();
}
@ -138,6 +169,17 @@ fn custom_event_and_polling() {
identify: libp2p::identify::Identify<TSubstream>,
}
impl<TSubstream> libp2p::core::swarm::NetworkBehaviourEventProcess<Void> for Foo<TSubstream> {
fn inject_event(&mut self, event: Void) {
void::unreachable(event)
}
}
impl<TSubstream> libp2p::core::swarm::NetworkBehaviourEventProcess<libp2p::identify::IdentifyEvent> for Foo<TSubstream> {
fn inject_event(&mut self, _: libp2p::identify::IdentifyEvent) {
}
}
impl<TSubstream> Foo<TSubstream> {
fn foo<T>(&mut self) -> libp2p::futures::Async<libp2p::core::swarm::NetworkBehaviourAction<T, String>> { libp2p::futures::Async::NotReady }
}

View File

@ -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};