mirror of
https://github.com/fluencelabs/rust-libp2p
synced 2025-06-03 05:01:24 +00:00
parent
9f63a0a69e
commit
4f18d0413c
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -2785,7 +2785,7 @@ version = "0.32.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 1.0.109",
|
"syn 2.0.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -16,7 +16,7 @@ proc-macro = true
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
heck = "0.4"
|
heck = "0.4"
|
||||||
quote = "1.0"
|
quote = "1.0"
|
||||||
syn = { version = "1.0.109", default-features = false, features = ["clone-impls", "derive", "parsing", "printing", "proc-macro"] }
|
syn = { version = "2.0.2", default-features = false, features = ["clone-impls", "derive", "parsing", "printing", "proc-macro"] }
|
||||||
|
|
||||||
# Passing arguments to the docsrs builder in order to properly document cfg's.
|
# Passing arguments to the docsrs builder in order to properly document cfg's.
|
||||||
# More information: https://docs.rs/about/builds#cross-compiling
|
# More information: https://docs.rs/about/builds#cross-compiling
|
||||||
|
@ -24,8 +24,11 @@
|
|||||||
use heck::ToUpperCamelCase;
|
use heck::ToUpperCamelCase;
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::parse::Parse;
|
use syn::punctuated::Punctuated;
|
||||||
use syn::{parse_macro_input, Data, DataStruct, DeriveInput};
|
use syn::{
|
||||||
|
parse_macro_input, Data, DataStruct, DeriveInput, Expr, ExprLit, Lit, Meta, MetaNameValue,
|
||||||
|
Token,
|
||||||
|
};
|
||||||
|
|
||||||
/// Generates a delegating `NetworkBehaviour` implementation for the struct this is used for. See
|
/// Generates a delegating `NetworkBehaviour` implementation for the struct this is used for. See
|
||||||
/// the trait documentation for better description.
|
/// the trait documentation for better description.
|
||||||
@ -48,8 +51,13 @@ fn build(ast: &DeriveInput) -> TokenStream {
|
|||||||
fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream {
|
fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream {
|
||||||
let name = &ast.ident;
|
let name = &ast.ident;
|
||||||
let (_, ty_generics, where_clause) = ast.generics.split_for_impl();
|
let (_, ty_generics, where_clause) = ast.generics.split_for_impl();
|
||||||
let prelude_path = parse_attribute_value_by_key::<syn::Path>(ast, "prelude")
|
let BehaviourAttributes {
|
||||||
.unwrap_or_else(|| syn::parse_quote! { ::libp2p::swarm::derive_prelude });
|
prelude_path,
|
||||||
|
user_specified_out_event,
|
||||||
|
} = match parse_attributes(ast) {
|
||||||
|
Ok(attrs) => attrs,
|
||||||
|
Err(e) => return e,
|
||||||
|
};
|
||||||
|
|
||||||
let multiaddr = quote! { #prelude_path::Multiaddr };
|
let multiaddr = quote! { #prelude_path::Multiaddr };
|
||||||
let trait_to_impl = quote! { #prelude_path::NetworkBehaviour };
|
let trait_to_impl = quote! { #prelude_path::NetworkBehaviour };
|
||||||
@ -91,10 +99,7 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream {
|
|||||||
// If we find a `#[behaviour(out_event = "Foo")]` attribute on the
|
// If we find a `#[behaviour(out_event = "Foo")]` attribute on the
|
||||||
// struct, we set `Foo` as the out event. If not, the `OutEvent` is
|
// struct, we set `Foo` as the out event. If not, the `OutEvent` is
|
||||||
// generated.
|
// generated.
|
||||||
let user_provided_out_event_name =
|
match user_specified_out_event {
|
||||||
parse_attribute_value_by_key::<syn::Type>(ast, "out_event");
|
|
||||||
|
|
||||||
match user_provided_out_event_name {
|
|
||||||
// User provided `OutEvent`.
|
// User provided `OutEvent`.
|
||||||
Some(name) => {
|
Some(name) => {
|
||||||
let definition = None;
|
let definition = None;
|
||||||
@ -111,7 +116,8 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream {
|
|||||||
// User did not provide `OutEvent`. Generate it.
|
// User did not provide `OutEvent`. Generate it.
|
||||||
None => {
|
None => {
|
||||||
let enum_name_str = ast.ident.to_string() + "Event";
|
let enum_name_str = ast.ident.to_string() + "Event";
|
||||||
let enum_name: syn::Type = syn::parse_str(&enum_name_str).unwrap();
|
let enum_name: syn::Type =
|
||||||
|
syn::parse_str(&enum_name_str).expect("ident + `Event` is a valid type");
|
||||||
let definition = {
|
let definition = {
|
||||||
let fields = data_struct.fields.iter().map(|field| {
|
let fields = data_struct.fields.iter().map(|field| {
|
||||||
let variant: syn::Variant = syn::parse_str(
|
let variant: syn::Variant = syn::parse_str(
|
||||||
@ -122,7 +128,7 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream {
|
|||||||
.to_string()
|
.to_string()
|
||||||
.to_upper_camel_case(),
|
.to_upper_camel_case(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.expect("uppercased field name to be a valid enum variant");
|
||||||
let ty = &field.ty;
|
let ty = &field.ty;
|
||||||
(variant, ty)
|
(variant, ty)
|
||||||
});
|
});
|
||||||
@ -680,7 +686,7 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream {
|
|||||||
&field
|
&field
|
||||||
.to_string()
|
.to_string()
|
||||||
.to_upper_camel_case()
|
.to_upper_camel_case()
|
||||||
).unwrap();
|
).expect("uppercased field name to be a valid enum variant name");
|
||||||
quote! { #out_event_name::#event_variant(event) }
|
quote! { #out_event_name::#event_variant(event) }
|
||||||
} else {
|
} else {
|
||||||
quote! { event.into() }
|
quote! { event.into() }
|
||||||
@ -842,41 +848,83 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream {
|
|||||||
final_quote.into()
|
final_quote.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses the `value` of a key=value pair in the `#[behaviour]` attribute into the requested type.
|
struct BehaviourAttributes {
|
||||||
///
|
prelude_path: syn::Path,
|
||||||
/// Only `String` values are supported, e.g. `#[behaviour(foo="bar")]`.
|
user_specified_out_event: Option<syn::Type>,
|
||||||
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>> {
|
/// Parses the `value` of a key=value pair in the `#[behaviour]` attribute into the requested type.
|
||||||
if attr.path.segments.len() == 1 && attr.path.segments[0].ident == "behaviour" {
|
fn parse_attributes(ast: &DeriveInput) -> Result<BehaviourAttributes, TokenStream> {
|
||||||
match attr.parse_meta() {
|
let mut attributes = BehaviourAttributes {
|
||||||
Ok(syn::Meta::List(ref meta)) => Some(meta.nested.iter().cloned().collect()),
|
prelude_path: syn::parse_quote! { ::libp2p::swarm::derive_prelude },
|
||||||
Ok(_) => None,
|
user_specified_out_event: None,
|
||||||
Err(e) => {
|
};
|
||||||
eprintln!("error parsing attribute metadata: {e}");
|
|
||||||
None
|
for attr in ast
|
||||||
|
.attrs
|
||||||
|
.iter()
|
||||||
|
.filter(|attr| attr.path().is_ident("behaviour"))
|
||||||
|
{
|
||||||
|
let nested = attr
|
||||||
|
.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)
|
||||||
|
.expect("`parse_args_with` never fails when parsing nested meta");
|
||||||
|
|
||||||
|
for meta in nested {
|
||||||
|
if meta.path().is_ident("prelude") {
|
||||||
|
match meta {
|
||||||
|
Meta::Path(_) => unimplemented!(),
|
||||||
|
Meta::List(_) => unimplemented!(),
|
||||||
|
Meta::NameValue(MetaNameValue {
|
||||||
|
value:
|
||||||
|
Expr::Lit(ExprLit {
|
||||||
|
lit: Lit::Str(s), ..
|
||||||
|
}),
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
attributes.prelude_path = syn::parse_str(&s.value()).unwrap();
|
||||||
|
}
|
||||||
|
Meta::NameValue(name_value) => {
|
||||||
|
return Err(syn::Error::new_spanned(
|
||||||
|
name_value.value,
|
||||||
|
"`prelude` value must be a quoted path",
|
||||||
|
)
|
||||||
|
.to_compile_error()
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if meta.path().is_ident("out_event") {
|
||||||
|
match meta {
|
||||||
|
Meta::Path(_) => unimplemented!(),
|
||||||
|
Meta::List(_) => unimplemented!(),
|
||||||
|
|
||||||
|
Meta::NameValue(MetaNameValue {
|
||||||
|
value:
|
||||||
|
Expr::Lit(ExprLit {
|
||||||
|
lit: Lit::Str(s), ..
|
||||||
|
}),
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
attributes.user_specified_out_event =
|
||||||
|
Some(syn::parse_str(&s.value()).unwrap());
|
||||||
|
}
|
||||||
|
Meta::NameValue(name_value) => {
|
||||||
|
return Err(syn::Error::new_spanned(
|
||||||
|
name_value.value,
|
||||||
|
"`out_event` value must be a quoted type",
|
||||||
|
)
|
||||||
|
.to_compile_error()
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(attributes)
|
||||||
}
|
}
|
||||||
|
@ -433,6 +433,27 @@ fn generated_out_event_derive_debug() {
|
|||||||
require_debug::<Foo>();
|
require_debug::<Foo>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiple_behaviour_attributes() {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(NetworkBehaviour)]
|
||||||
|
#[behaviour(out_event = "FooEvent")]
|
||||||
|
#[behaviour(prelude = "libp2p_swarm::derive_prelude")]
|
||||||
|
struct Foo {
|
||||||
|
ping: ping::Behaviour,
|
||||||
|
}
|
||||||
|
|
||||||
|
require_net_behaviour::<Foo>();
|
||||||
|
|
||||||
|
struct FooEvent;
|
||||||
|
|
||||||
|
impl From<ping::Event> for FooEvent {
|
||||||
|
fn from(_: ping::Event) -> Self {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn custom_out_event_no_type_parameters() {
|
fn custom_out_event_no_type_parameters() {
|
||||||
use libp2p_identity::PeerId;
|
use libp2p_identity::PeerId;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user