mirror of
https://github.com/fluencelabs/rust-libp2p
synced 2025-07-24 13:41:56 +00:00
swarm-derive: Add prelude
configuration option to NetworkBehaviour
macro (#3055)
Currently, our `NetworkBehaviour` derive macro depends on the `libp2p` crate to be in scope. This prevents standalone usage which forces us to depend on `libp2p` in all our tests where we want to derive a `NetworkBehaviour`. This PR introduces a `prelude` option that - by default - points to `libp2p::swarm::derive_prelude`, a new module added to `libp2p_swarm`. With this config option, users of `libp2p_swarm` can now refer to the macro without depending on `libp2p`, breaking the circular dependency in our workspace. For consistency with the ecosystem, the macro is now also re-exported by `libp2p_swarm` instead of `libp2p` at the same position as the trait that it implements. Lastly, we introduce an off-by-default `macros` feature flag that shrinks the dependency tree for users that don't need the derive macro.
This commit is contained in:
@@ -24,6 +24,7 @@
|
||||
use heck::ToUpperCamelCase;
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::parse::Parse;
|
||||
use syn::{parse_macro_input, Data, DataStruct, DeriveInput};
|
||||
|
||||
/// Generates a delegating `NetworkBehaviour` implementation for the struct this is used for. See
|
||||
@@ -47,21 +48,24 @@ fn build(ast: &DeriveInput) -> TokenStream {
|
||||
fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream {
|
||||
let name = &ast.ident;
|
||||
let (_, ty_generics, where_clause) = ast.generics.split_for_impl();
|
||||
let multiaddr = quote! {::libp2p::core::Multiaddr};
|
||||
let trait_to_impl = quote! {::libp2p::swarm::NetworkBehaviour};
|
||||
let either_ident = quote! {::libp2p::core::either::EitherOutput};
|
||||
let network_behaviour_action = quote! {::libp2p::swarm::NetworkBehaviourAction};
|
||||
let into_connection_handler = quote! {::libp2p::swarm::IntoConnectionHandler};
|
||||
let connection_handler = quote! {::libp2p::swarm::ConnectionHandler};
|
||||
let into_proto_select_ident = quote! {::libp2p::swarm::IntoConnectionHandlerSelect};
|
||||
let peer_id = quote! {::libp2p::core::PeerId};
|
||||
let connection_id = quote! {::libp2p::core::connection::ConnectionId};
|
||||
let dial_errors = quote! {Option<&Vec<::libp2p::core::Multiaddr>>};
|
||||
let connected_point = quote! {::libp2p::core::ConnectedPoint};
|
||||
let listener_id = quote! {::libp2p::core::transport::ListenerId};
|
||||
let dial_error = quote! {::libp2p::swarm::DialError};
|
||||
|
||||
let poll_parameters = quote! {::libp2p::swarm::PollParameters};
|
||||
let prelude_path = parse_attribute_value_by_key::<syn::Path>(ast, "prelude")
|
||||
.unwrap_or_else(|| syn::parse_quote! { ::libp2p::swarm::derive_prelude });
|
||||
|
||||
let multiaddr = quote! { #prelude_path::Multiaddr };
|
||||
let trait_to_impl = quote! { #prelude_path::NetworkBehaviour };
|
||||
let either_ident = quote! { #prelude_path::EitherOutput };
|
||||
let network_behaviour_action = quote! { #prelude_path::NetworkBehaviourAction };
|
||||
let into_connection_handler = quote! { #prelude_path::IntoConnectionHandler };
|
||||
let connection_handler = quote! { #prelude_path::ConnectionHandler };
|
||||
let into_proto_select_ident = quote! { #prelude_path::IntoConnectionHandlerSelect };
|
||||
let peer_id = quote! { #prelude_path::PeerId };
|
||||
let connection_id = quote! { #prelude_path::ConnectionId };
|
||||
let dial_errors = quote! {Option<&Vec<#prelude_path::Multiaddr>> };
|
||||
let connected_point = quote! { #prelude_path::ConnectedPoint };
|
||||
let listener_id = quote! { #prelude_path::ListenerId };
|
||||
let dial_error = quote! { #prelude_path::DialError };
|
||||
let poll_parameters = quote! { #prelude_path::PollParameters };
|
||||
|
||||
// Build the generics.
|
||||
let impl_generics = {
|
||||
@@ -75,22 +79,8 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream {
|
||||
// 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<syn::Type> = 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 {
|
||||
return Some(syn::parse_str(&s.value()).unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
.next();
|
||||
let user_provided_out_event_name =
|
||||
parse_attribute_value_by_key::<syn::Type>(ast, "out_event");
|
||||
|
||||
match user_provided_out_event_name {
|
||||
// User provided `OutEvent`.
|
||||
@@ -625,7 +615,7 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream {
|
||||
}
|
||||
|
||||
fn poll(&mut self, cx: &mut std::task::Context, poll_params: &mut impl #poll_parameters) -> std::task::Poll<#network_behaviour_action<Self::OutEvent, Self::ConnectionHandler>> {
|
||||
use libp2p::futures::prelude::*;
|
||||
use #prelude_path::futures::*;
|
||||
#(#poll_stmts)*
|
||||
std::task::Poll::Pending
|
||||
}
|
||||
@@ -635,6 +625,30 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream {
|
||||
final_quote.into()
|
||||
}
|
||||
|
||||
/// Parses the `value` of a key=value pair in the `#[behaviour]` attribute into the requested type.
|
||||
///
|
||||
/// Only `String` values are supported, e.g. `#[behaviour(foo="bar")]`.
|
||||
fn parse_attribute_value_by_key<T>(ast: &DeriveInput, key: &str) -> Option<T>
|
||||
where
|
||||
T: Parse,
|
||||
{
|
||||
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(key) {
|
||||
if let syn::Lit::Str(ref s) = m.lit {
|
||||
return Some(syn::parse_str(&s.value()).unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
.next()
|
||||
}
|
||||
|
||||
fn get_meta_items(attr: &syn::Attribute) -> Option<Vec<syn::NestedMeta>> {
|
||||
if attr.path.segments.len() == 1 && attr.path.segments[0].ident == "behaviour" {
|
||||
match attr.parse_meta() {
|
||||
|
Reference in New Issue
Block a user