mirror of
https://github.com/fluencelabs/rust-libp2p
synced 2025-06-15 02:51:25 +00:00
swarm-derive/: Generate OutEvent if not provided (#2792)
Generate `NetworkBehaviour::OutEvent` if not provided through `#[behaviour(out_event = "MyOutEvent")]` and event processing is disabled (default).
This commit is contained in:
@ -47,6 +47,8 @@
|
|||||||
|
|
||||||
- Update to [`libp2p-dcutr` `v0.5.0`](protocols/dcutr/CHANGELOG.md#050).
|
- 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-rendezvous` `v0.8.0`](protocols/rendezvous/CHANGELOG.md#080).
|
||||||
|
|
||||||
- Update to [`libp2p-ping` `v0.38.0`](protocols/ping/CHANGELOG.md#0380).
|
- Update to [`libp2p-ping` `v0.38.0`](protocols/ping/CHANGELOG.md#0380).
|
||||||
|
@ -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-rendezvous = { version = "0.8.0", path = "protocols/rendezvous", optional = true }
|
||||||
libp2p-request-response = { version = "0.20.0", path = "protocols/request-response", 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 = { 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-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-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 }
|
libp2p-yamux = { version = "0.39.0", path = "muxers/yamux", optional = true }
|
||||||
|
@ -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
|
# 0.28.0
|
||||||
|
|
||||||
- Import `ListenerId` from `libp2p::core::transport`. See [PR 2652].
|
- Import `ListenerId` from `libp2p::core::transport`. See [PR 2652].
|
||||||
|
@ -3,7 +3,7 @@ name = "libp2p-swarm-derive"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.56.1"
|
rust-version = "1.56.1"
|
||||||
description = "Procedural macros of libp2p-core"
|
description = "Procedural macros of libp2p-core"
|
||||||
version = "0.28.0"
|
version = "0.29.0"
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/libp2p/rust-libp2p"
|
repository = "https://github.com/libp2p/rust-libp2p"
|
||||||
@ -14,8 +14,9 @@ categories = ["network-programming", "asynchronous"]
|
|||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
syn = { version = "1.0.8", default-features = false, features = ["clone-impls", "derive", "parsing", "printing", "proc-macro"] }
|
heck = "0.4"
|
||||||
quote = "1.0"
|
quote = "1.0"
|
||||||
|
syn = { version = "1.0.8", default-features = false, features = ["clone-impls", "derive", "parsing", "printing", "proc-macro"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
libp2p = { path = "../", default-features = false, features = ["ping", "identify", "kad"] }
|
libp2p = { path = "../", default-features = false, features = ["ping", "identify", "kad"] }
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#![recursion_limit = "256"]
|
#![recursion_limit = "256"]
|
||||||
|
|
||||||
|
use heck::ToUpperCamelCase;
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::{parse_macro_input, Data, DataStruct, DeriveInput, Ident};
|
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))
|
.filter(|f| !is_ignored(f))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
// The final 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
|
// If we find a `#[behaviour(out_event = "Foo")]` attribute on the
|
||||||
// the out event. Otherwise we use `()`.
|
// struct, we set `Foo` as the out event. If not, the `OutEvent` is
|
||||||
let out_event = {
|
// generated.
|
||||||
let mut out = quote! {()};
|
let user_provided_out_event_name: Option<syn::Type> = ast
|
||||||
for meta_items in ast.attrs.iter().filter_map(get_meta_items) {
|
.attrs
|
||||||
for meta_item in meta_items {
|
.iter()
|
||||||
match meta_item {
|
.filter_map(get_meta_items)
|
||||||
syn::NestedMeta::Meta(syn::Meta::NameValue(ref m))
|
.flatten()
|
||||||
if m.path.is_ident("out_event") =>
|
.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 {
|
if let syn::Lit::Str(ref s) = m.lit {
|
||||||
let ident: syn::Type = syn::parse_str(&s.value()).unwrap();
|
return Some(syn::parse_str(&s.value()).unwrap());
|
||||||
out = quote! {#ident};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out
|
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::<Vec<_>>();
|
||||||
|
(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::<Vec<_>>();
|
||||||
|
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::<Vec<_>>();
|
||||||
|
(
|
||||||
|
name.unwrap_or_else(|| syn::parse_str("()").unwrap()),
|
||||||
|
definition,
|
||||||
|
from_clauses,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Build the `where ...` clause of the trait implementation.
|
// Build the `where ...` clause of the trait implementation.
|
||||||
let where_clause = {
|
let where_clause = {
|
||||||
let additional = data_struct_fields
|
let additional = data_struct_fields
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|field| {
|
.map(|field| {
|
||||||
let ty = &field.ty;
|
let ty = &field.ty;
|
||||||
vec![
|
quote! {#ty: #trait_to_impl}
|
||||||
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 >}
|
|
||||||
},
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
|
.chain(out_event_from_clauses)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
if let Some(where_clause) = where_clause {
|
if let Some(where_clause) = where_clause {
|
||||||
if where_clause.predicates.trailing_punct() {
|
if where_clause.predicates.trailing_punct() {
|
||||||
Some(quote! {#where_clause #(#additional),*})
|
Some(quote! {#where_clause #(#additional),* })
|
||||||
} else {
|
} else {
|
||||||
Some(quote! {#where_clause, #(#additional),*})
|
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()`.
|
// List of statements to put in `poll()`.
|
||||||
//
|
//
|
||||||
// We poll each child one by one and wrap around the output.
|
// 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 poll_stmts = data_struct_fields.iter().enumerate().map(|(field_n, field)| {
|
||||||
let field_name = match field.ident {
|
let field = field
|
||||||
Some(ref i) => quote!{ self.#i },
|
.ident
|
||||||
None => quote!{ self.#field_n },
|
.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) }
|
quote!{ #either_ident::Second(event) }
|
||||||
} else {
|
} else {
|
||||||
quote!{ event }
|
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) };
|
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;
|
let mut out_handler = None;
|
||||||
|
|
||||||
for (f_n, f) in data_struct_fields.iter().enumerate() {
|
for (f_n, f) in data_struct_fields.iter().enumerate() {
|
||||||
|
|
||||||
let f_name = match f.ident {
|
let f_name = match f.ident {
|
||||||
Some(ref i) => quote! { self.#i },
|
Some(ref i) => quote! { self.#i },
|
||||||
None => quote! { self.#f_n },
|
None => quote! { self.#f_n },
|
||||||
@ -492,16 +549,31 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} 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
|
||||||
|
.to_string()
|
||||||
|
.to_upper_camel_case()
|
||||||
|
).unwrap();
|
||||||
|
quote! { #out_event_name::#event_variant(event) }
|
||||||
|
} else {
|
||||||
|
quote! { event.into() }
|
||||||
|
};
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
std::task::Poll::Ready(#network_behaviour_action::GenerateEvent(event)) => {
|
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!{
|
Some(quote!{
|
||||||
loop {
|
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
|
#generate_event_match_arm
|
||||||
std::task::Poll::Ready(#network_behaviour_action::Dial { opts, handler: provided_handler }) => {
|
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 });
|
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.
|
// Now the magic happens.
|
||||||
let final_quote = quote! {
|
let final_quote = quote! {
|
||||||
|
#out_event_definition
|
||||||
|
|
||||||
impl #impl_generics #trait_to_impl for #name #ty_generics
|
impl #impl_generics #trait_to_impl for #name #ty_generics
|
||||||
#where_clause
|
#where_clause
|
||||||
{
|
{
|
||||||
type ConnectionHandler = #connection_handler_ty;
|
type ConnectionHandler = #connection_handler_ty;
|
||||||
type OutEvent = #out_event;
|
type OutEvent = #out_event_name #ty_generics;
|
||||||
|
|
||||||
fn new_handler(&mut self) -> Self::ConnectionHandler {
|
fn new_handler(&mut self) -> Self::ConnectionHandler {
|
||||||
use #into_connection_handler;
|
use #into_connection_handler;
|
||||||
|
@ -38,18 +38,17 @@ fn empty() {
|
|||||||
fn one_field() {
|
fn one_field() {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(NetworkBehaviour)]
|
#[derive(NetworkBehaviour)]
|
||||||
#[behaviour(event_process = true)]
|
|
||||||
struct Foo {
|
struct Foo {
|
||||||
ping: libp2p::ping::Ping,
|
ping: libp2p::ping::Ping,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl libp2p::swarm::NetworkBehaviourEventProcess<libp2p::ping::PingEvent> for Foo {
|
|
||||||
fn inject_event(&mut self, _: libp2p::ping::PingEvent) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
#[allow(unreachable_code)]
|
||||||
fn foo() {
|
fn foo() {
|
||||||
require_net_behaviour::<Foo>();
|
let _out_event: <Foo as NetworkBehaviour>::OutEvent = unimplemented!();
|
||||||
|
match _out_event {
|
||||||
|
FooEvent::Ping(libp2p::ping::Event { .. }) => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,23 +56,21 @@ fn one_field() {
|
|||||||
fn two_fields() {
|
fn two_fields() {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(NetworkBehaviour)]
|
#[derive(NetworkBehaviour)]
|
||||||
#[behaviour(event_process = true)]
|
|
||||||
struct Foo {
|
struct Foo {
|
||||||
ping: libp2p::ping::Ping,
|
ping: libp2p::ping::Ping,
|
||||||
identify: libp2p::identify::Identify,
|
identify: libp2p::identify::Identify,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl libp2p::swarm::NetworkBehaviourEventProcess<libp2p::identify::IdentifyEvent> for Foo {
|
|
||||||
fn inject_event(&mut self, _: libp2p::identify::IdentifyEvent) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl libp2p::swarm::NetworkBehaviourEventProcess<libp2p::ping::PingEvent> for Foo {
|
|
||||||
fn inject_event(&mut self, _: libp2p::ping::PingEvent) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
#[allow(unreachable_code)]
|
||||||
fn foo() {
|
fn foo() {
|
||||||
require_net_behaviour::<Foo>();
|
let _out_event: <Foo as NetworkBehaviour>::OutEvent = unimplemented!();
|
||||||
|
match _out_event {
|
||||||
|
FooEvent::Ping(libp2p::ping::Event { .. }) => {}
|
||||||
|
FooEvent::Identify(event) => {
|
||||||
|
let _: libp2p::identify::IdentifyEvent = event;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,7 +78,6 @@ fn two_fields() {
|
|||||||
fn three_fields() {
|
fn three_fields() {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(NetworkBehaviour)]
|
#[derive(NetworkBehaviour)]
|
||||||
#[behaviour(event_process = true)]
|
|
||||||
struct Foo {
|
struct Foo {
|
||||||
ping: libp2p::ping::Ping,
|
ping: libp2p::ping::Ping,
|
||||||
identify: libp2p::identify::Identify,
|
identify: libp2p::identify::Identify,
|
||||||
@ -90,21 +86,19 @@ fn three_fields() {
|
|||||||
foo: String,
|
foo: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl libp2p::swarm::NetworkBehaviourEventProcess<libp2p::ping::PingEvent> for Foo {
|
|
||||||
fn inject_event(&mut self, _: libp2p::ping::PingEvent) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl libp2p::swarm::NetworkBehaviourEventProcess<libp2p::identify::IdentifyEvent> for Foo {
|
|
||||||
fn inject_event(&mut self, _: libp2p::identify::IdentifyEvent) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl libp2p::swarm::NetworkBehaviourEventProcess<libp2p::kad::KademliaEvent> for Foo {
|
|
||||||
fn inject_event(&mut self, _: libp2p::kad::KademliaEvent) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
#[allow(unreachable_code)]
|
||||||
fn foo() {
|
fn foo() {
|
||||||
require_net_behaviour::<Foo>();
|
let _out_event: <Foo as NetworkBehaviour>::OutEvent = unimplemented!();
|
||||||
|
match _out_event {
|
||||||
|
FooEvent::Ping(libp2p::ping::Event { .. }) => {}
|
||||||
|
FooEvent::Identify(event) => {
|
||||||
|
let _: libp2p::identify::IdentifyEvent = event;
|
||||||
|
}
|
||||||
|
FooEvent::Kad(event) => {
|
||||||
|
let _: libp2p::kad::KademliaEvent = event;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,7 +106,6 @@ fn three_fields() {
|
|||||||
fn three_fields_non_last_ignored() {
|
fn three_fields_non_last_ignored() {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(NetworkBehaviour)]
|
#[derive(NetworkBehaviour)]
|
||||||
#[behaviour(event_process = true)]
|
|
||||||
struct Foo {
|
struct Foo {
|
||||||
ping: libp2p::ping::Ping,
|
ping: libp2p::ping::Ping,
|
||||||
#[behaviour(ignore)]
|
#[behaviour(ignore)]
|
||||||
@ -120,17 +113,16 @@ fn three_fields_non_last_ignored() {
|
|||||||
kad: libp2p::kad::Kademlia<libp2p::kad::record::store::MemoryStore>,
|
kad: libp2p::kad::Kademlia<libp2p::kad::record::store::MemoryStore>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl libp2p::swarm::NetworkBehaviourEventProcess<libp2p::ping::PingEvent> for Foo {
|
|
||||||
fn inject_event(&mut self, _: libp2p::ping::PingEvent) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl libp2p::swarm::NetworkBehaviourEventProcess<libp2p::kad::KademliaEvent> for Foo {
|
|
||||||
fn inject_event(&mut self, _: libp2p::kad::KademliaEvent) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
#[allow(unreachable_code)]
|
||||||
fn foo() {
|
fn foo() {
|
||||||
require_net_behaviour::<Foo>();
|
let _out_event: <Foo as NetworkBehaviour>::OutEvent = unimplemented!();
|
||||||
|
match _out_event {
|
||||||
|
FooEvent::Ping(libp2p::ping::Event { .. }) => {}
|
||||||
|
FooEvent::Kad(event) => {
|
||||||
|
let _: libp2p::kad::KademliaEvent = event;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,20 +130,12 @@ fn three_fields_non_last_ignored() {
|
|||||||
fn custom_polling() {
|
fn custom_polling() {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(NetworkBehaviour)]
|
#[derive(NetworkBehaviour)]
|
||||||
#[behaviour(poll_method = "foo", event_process = true)]
|
#[behaviour(poll_method = "foo")]
|
||||||
struct Foo {
|
struct Foo {
|
||||||
ping: libp2p::ping::Ping,
|
ping: libp2p::ping::Ping,
|
||||||
identify: libp2p::identify::Identify,
|
identify: libp2p::identify::Identify,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl libp2p::swarm::NetworkBehaviourEventProcess<libp2p::ping::PingEvent> for Foo {
|
|
||||||
fn inject_event(&mut self, _: libp2p::ping::PingEvent) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl libp2p::swarm::NetworkBehaviourEventProcess<libp2p::identify::IdentifyEvent> for Foo {
|
|
||||||
fn inject_event(&mut self, _: libp2p::identify::IdentifyEvent) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Foo {
|
impl Foo {
|
||||||
fn foo(
|
fn foo(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -177,18 +161,27 @@ fn custom_polling() {
|
|||||||
fn custom_event_no_polling() {
|
fn custom_event_no_polling() {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(NetworkBehaviour)]
|
#[derive(NetworkBehaviour)]
|
||||||
#[behaviour(out_event = "Vec<String>", event_process = true)]
|
#[behaviour(out_event = "MyEvent")]
|
||||||
struct Foo {
|
struct Foo {
|
||||||
ping: libp2p::ping::Ping,
|
ping: libp2p::ping::Ping,
|
||||||
identify: libp2p::identify::Identify,
|
identify: libp2p::identify::Identify,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl libp2p::swarm::NetworkBehaviourEventProcess<libp2p::ping::PingEvent> for Foo {
|
enum MyEvent {
|
||||||
fn inject_event(&mut self, _: libp2p::ping::PingEvent) {}
|
Ping(libp2p::ping::PingEvent),
|
||||||
|
Identify(libp2p::identify::IdentifyEvent),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl libp2p::swarm::NetworkBehaviourEventProcess<libp2p::identify::IdentifyEvent> for Foo {
|
impl From<libp2p::ping::PingEvent> for MyEvent {
|
||||||
fn inject_event(&mut self, _: libp2p::identify::IdentifyEvent) {}
|
fn from(event: libp2p::ping::PingEvent) -> Self {
|
||||||
|
MyEvent::Ping(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<libp2p::identify::IdentifyEvent> for MyEvent {
|
||||||
|
fn from(event: libp2p::identify::IdentifyEvent) -> Self {
|
||||||
|
MyEvent::Identify(event)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -201,18 +194,27 @@ fn custom_event_no_polling() {
|
|||||||
fn custom_event_and_polling() {
|
fn custom_event_and_polling() {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(NetworkBehaviour)]
|
#[derive(NetworkBehaviour)]
|
||||||
#[behaviour(poll_method = "foo", out_event = "String", event_process = true)]
|
#[behaviour(poll_method = "foo", out_event = "MyEvent")]
|
||||||
struct Foo {
|
struct Foo {
|
||||||
ping: libp2p::ping::Ping,
|
ping: libp2p::ping::Ping,
|
||||||
identify: libp2p::identify::Identify,
|
identify: libp2p::identify::Identify,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl libp2p::swarm::NetworkBehaviourEventProcess<libp2p::ping::PingEvent> for Foo {
|
enum MyEvent {
|
||||||
fn inject_event(&mut self, _: libp2p::ping::PingEvent) {}
|
Ping(libp2p::ping::PingEvent),
|
||||||
|
Identify(libp2p::identify::IdentifyEvent),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl libp2p::swarm::NetworkBehaviourEventProcess<libp2p::identify::IdentifyEvent> for Foo {
|
impl From<libp2p::ping::PingEvent> for MyEvent {
|
||||||
fn inject_event(&mut self, _: libp2p::identify::IdentifyEvent) {}
|
fn from(event: libp2p::ping::PingEvent) -> Self {
|
||||||
|
MyEvent::Ping(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<libp2p::identify::IdentifyEvent> for MyEvent {
|
||||||
|
fn from(event: libp2p::identify::IdentifyEvent) -> Self {
|
||||||
|
MyEvent::Identify(event)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Foo {
|
impl Foo {
|
||||||
@ -236,12 +238,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<libp2p::ping::PingEvent> for MyEvent {
|
||||||
|
fn from(event: libp2p::ping::PingEvent) -> Self {
|
||||||
|
MyEvent::Ping(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<libp2p::identify::IdentifyEvent> for MyEvent {
|
||||||
|
fn from(event: libp2p::identify::IdentifyEvent) -> Self {
|
||||||
|
MyEvent::Identify(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn foo() {
|
||||||
|
require_net_behaviour::<Foo>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn where_clause() {
|
fn where_clause() {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(NetworkBehaviour)]
|
#[derive(NetworkBehaviour)]
|
||||||
#[behaviour(event_process = true)]
|
struct Foo<T: Copy + NetworkBehaviour> {
|
||||||
struct Foo<T: Copy> {
|
|
||||||
ping: libp2p::ping::Ping,
|
ping: libp2p::ping::Ping,
|
||||||
bar: T,
|
bar: T,
|
||||||
}
|
}
|
||||||
@ -249,38 +283,30 @@ fn where_clause() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn nested_derives_with_import() {
|
fn nested_derives_with_import() {
|
||||||
use libp2p::swarm::NetworkBehaviourEventProcess;
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(NetworkBehaviour)]
|
#[derive(NetworkBehaviour)]
|
||||||
#[behaviour(event_process = true)]
|
|
||||||
struct Foo {
|
struct Foo {
|
||||||
ping: libp2p::ping::Ping,
|
ping: libp2p::ping::Ping,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(NetworkBehaviour)]
|
#[derive(NetworkBehaviour)]
|
||||||
#[behaviour(event_process = true)]
|
|
||||||
struct Bar {
|
struct Bar {
|
||||||
foo: Foo,
|
foo: Foo,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NetworkBehaviourEventProcess<libp2p::ping::PingEvent> for Foo {
|
|
||||||
fn inject_event(&mut self, _: libp2p::ping::PingEvent) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NetworkBehaviourEventProcess<()> for Bar {
|
|
||||||
fn inject_event(&mut self, _: ()) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn bar() {
|
#[allow(unreachable_code)]
|
||||||
require_net_behaviour::<Bar>();
|
fn foo() {
|
||||||
|
let _out_event: <Bar as NetworkBehaviour>::OutEvent = unimplemented!();
|
||||||
|
match _out_event {
|
||||||
|
BarEvent::Foo(FooEvent::Ping(libp2p::ping::Event { .. })) => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn event_process_false() {
|
fn custom_event_emit_event_through_poll() {
|
||||||
enum BehaviourOutEvent {
|
enum BehaviourOutEvent {
|
||||||
Ping(libp2p::ping::PingEvent),
|
Ping(libp2p::ping::PingEvent),
|
||||||
Identify(libp2p::identify::IdentifyEvent),
|
Identify(libp2p::identify::IdentifyEvent),
|
||||||
@ -331,20 +357,11 @@ fn with_toggle() {
|
|||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(NetworkBehaviour)]
|
#[derive(NetworkBehaviour)]
|
||||||
#[behaviour(event_process = true)]
|
|
||||||
struct Foo {
|
struct Foo {
|
||||||
identify: libp2p::identify::Identify,
|
identify: libp2p::identify::Identify,
|
||||||
ping: Toggle<libp2p::ping::Ping>,
|
ping: Toggle<libp2p::ping::Ping>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl libp2p::swarm::NetworkBehaviourEventProcess<libp2p::identify::IdentifyEvent> for Foo {
|
|
||||||
fn inject_event(&mut self, _: libp2p::identify::IdentifyEvent) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl libp2p::swarm::NetworkBehaviourEventProcess<libp2p::ping::PingEvent> for Foo {
|
|
||||||
fn inject_event(&mut self, _: libp2p::ping::PingEvent) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn foo() {
|
fn foo() {
|
||||||
require_net_behaviour::<Foo>();
|
require_net_behaviour::<Foo>();
|
||||||
@ -357,28 +374,11 @@ fn with_either() {
|
|||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(NetworkBehaviour)]
|
#[derive(NetworkBehaviour)]
|
||||||
#[behaviour(event_process = true)]
|
|
||||||
struct Foo {
|
struct Foo {
|
||||||
kad: libp2p::kad::Kademlia<libp2p::kad::record::store::MemoryStore>,
|
kad: libp2p::kad::Kademlia<libp2p::kad::record::store::MemoryStore>,
|
||||||
ping_or_identify: Either<libp2p::ping::Ping, libp2p::identify::Identify>,
|
ping_or_identify: Either<libp2p::ping::Ping, libp2p::identify::Identify>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl libp2p::swarm::NetworkBehaviourEventProcess<libp2p::kad::KademliaEvent> for Foo {
|
|
||||||
fn inject_event(&mut self, _: libp2p::kad::KademliaEvent) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl
|
|
||||||
libp2p::swarm::NetworkBehaviourEventProcess<
|
|
||||||
Either<libp2p::ping::PingEvent, libp2p::identify::IdentifyEvent>,
|
|
||||||
> for Foo
|
|
||||||
{
|
|
||||||
fn inject_event(
|
|
||||||
&mut self,
|
|
||||||
_: Either<libp2p::ping::PingEvent, libp2p::identify::IdentifyEvent>,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn foo() {
|
fn foo() {
|
||||||
require_net_behaviour::<Foo>();
|
require_net_behaviour::<Foo>();
|
||||||
@ -386,7 +386,7 @@ fn with_either() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn no_event_with_either() {
|
fn custom_event_with_either() {
|
||||||
use either::Either;
|
use either::Either;
|
||||||
|
|
||||||
enum BehaviourOutEvent {
|
enum BehaviourOutEvent {
|
||||||
@ -394,14 +394,6 @@ fn no_event_with_either() {
|
|||||||
PingOrIdentify(Either<libp2p::ping::PingEvent, libp2p::identify::IdentifyEvent>),
|
PingOrIdentify(Either<libp2p::ping::PingEvent, libp2p::identify::IdentifyEvent>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(NetworkBehaviour)]
|
|
||||||
#[behaviour(out_event = "BehaviourOutEvent", event_process = false)]
|
|
||||||
struct Foo {
|
|
||||||
kad: libp2p::kad::Kademlia<libp2p::kad::record::store::MemoryStore>,
|
|
||||||
ping_or_identify: Either<libp2p::ping::Ping, libp2p::identify::Identify>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<libp2p::kad::KademliaEvent> for BehaviourOutEvent {
|
impl From<libp2p::kad::KademliaEvent> for BehaviourOutEvent {
|
||||||
fn from(event: libp2p::kad::KademliaEvent) -> Self {
|
fn from(event: libp2p::kad::KademliaEvent) -> Self {
|
||||||
BehaviourOutEvent::Kad(event)
|
BehaviourOutEvent::Kad(event)
|
||||||
@ -414,6 +406,14 @@ fn no_event_with_either() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(NetworkBehaviour)]
|
||||||
|
#[behaviour(out_event = "BehaviourOutEvent")]
|
||||||
|
struct Foo {
|
||||||
|
kad: libp2p::kad::Kademlia<libp2p::kad::record::store::MemoryStore>,
|
||||||
|
ping_or_identify: Either<libp2p::ping::Ping, libp2p::identify::Identify>,
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn foo() {
|
fn foo() {
|
||||||
require_net_behaviour::<Foo>();
|
require_net_behaviour::<Foo>();
|
||||||
@ -425,7 +425,6 @@ fn mixed_field_order() {
|
|||||||
struct Foo {}
|
struct Foo {}
|
||||||
|
|
||||||
#[derive(NetworkBehaviour)]
|
#[derive(NetworkBehaviour)]
|
||||||
#[behaviour(event_process = true)]
|
|
||||||
pub struct Behaviour {
|
pub struct Behaviour {
|
||||||
#[behaviour(ignore)]
|
#[behaviour(ignore)]
|
||||||
_foo: Foo,
|
_foo: Foo,
|
||||||
@ -437,12 +436,43 @@ fn mixed_field_order() {
|
|||||||
_foo3: Foo,
|
_foo3: Foo,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> libp2p::swarm::NetworkBehaviourEventProcess<T> for Behaviour {
|
|
||||||
fn inject_event(&mut self, _evt: T) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn behaviour() {
|
fn behaviour() {
|
||||||
require_net_behaviour::<Behaviour>();
|
require_net_behaviour::<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<libp2p::identify::IdentifyEvent> for Foo {
|
||||||
|
fn inject_event(&mut self, _: libp2p::identify::IdentifyEvent) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl libp2p::swarm::NetworkBehaviourEventProcess<libp2p::ping::PingEvent> for Foo {
|
||||||
|
fn inject_event(&mut self, _: libp2p::ping::PingEvent) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code, unreachable_code)]
|
||||||
|
fn bar() {
|
||||||
|
require_net_behaviour::<Foo>();
|
||||||
|
|
||||||
|
let mut _swarm: libp2p::Swarm<Foo> = unimplemented!();
|
||||||
|
|
||||||
|
let _ = async {
|
||||||
|
loop {
|
||||||
|
match _swarm.select_next_some().await {
|
||||||
|
SwarmEvent::Behaviour(()) => break,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -4,6 +4,15 @@
|
|||||||
|
|
||||||
- Update to `libp2p-core` `v0.35.0`.
|
- 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/
|
[PR 2741]: https://github.com/libp2p/rust-libp2p/pull/2741/
|
||||||
|
|
||||||
# 0.37.0
|
# 0.37.0
|
||||||
|
@ -79,16 +79,11 @@ pub(crate) type THandlerOutEvent<THandler> =
|
|||||||
/// it will delegate to each `struct` member and return a concatenated array of all addresses
|
/// it will delegate to each `struct` member and return a concatenated array of all addresses
|
||||||
/// returned by the struct members.
|
/// returned by the struct members.
|
||||||
///
|
///
|
||||||
/// When creating a custom [`NetworkBehaviour`], you must choose one of two methods to respond to
|
/// Events ([`NetworkBehaviour::OutEvent`]) returned by each `struct` member are wrapped in a new
|
||||||
/// incoming events:
|
/// `enum` event, with an `enum` variant for each `struct` member. Users can define this event
|
||||||
/// * One option is setting a custom `out_event` with `#[behaviour(out_event = "AnotherType")]`.
|
/// `enum` themselves and provide the name to the derive macro via `#[behaviour(out_event =
|
||||||
/// In this case, events generated by the custom [`NetworkBehaviour`] struct members will be
|
/// "MyCustomOutEvent")]`. If the user does not specify an `out_event`, the derive macro generates
|
||||||
/// converted to your custom `out_event` for you to handle after polling the swarm.
|
/// the event definition itself, naming it `<STRUCT_NAME>Event`.
|
||||||
/// * 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.
|
|
||||||
///
|
///
|
||||||
/// When setting a custom `out_event`, the aforementioned conversion of each of the event types
|
/// 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`]
|
/// generated by the struct members to the custom `out_event` is handled by [`From`]
|
||||||
@ -123,14 +118,6 @@ pub(crate) type THandlerOutEvent<THandler> =
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// 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 =
|
/// 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`]
|
/// "poll")]` attribute. This function must have the same signature as the [`NetworkBehaviour#poll`]
|
||||||
/// function and will be called last within the generated [`NetworkBehaviour`] implementation.
|
/// function and will be called last within the generated [`NetworkBehaviour`] implementation.
|
||||||
|
Reference in New Issue
Block a user