Skip to content

Commit 5db329c

Browse files
MrWad3rRexagon
authored andcommitted
feat: Add function handler support
1 parent 2285e39 commit 5db329c

File tree

4 files changed

+154
-48
lines changed

4 files changed

+154
-48
lines changed

abi-proc/src/common.rs

+79-18
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,96 @@
1-
use proc_macro2::Ident;
1+
use proc_macro2::{Ident, Span};
22
use quote::format_ident;
3-
use syn::{Attribute, LitStr, Path};
3+
use syn::spanned::Spanned;
4+
use syn::{Attribute, Error, LitStr, Path};
5+
6+
pub enum HandlerType {
7+
AbiType,
8+
IntoAbi,
9+
FromAbi,
10+
}
411

512
pub struct FieldAttributes {
613
pub custom_name: Option<Ident>,
7-
pub custom_handler: Option<Path>,
14+
pub mod_handler: Option<Path>,
15+
pub with_handlers: Vec<(HandlerType, Path)>,
16+
}
17+
18+
impl FieldAttributes {
19+
pub fn check(&self) -> Result<(), Error> {
20+
if !self.with_handlers.is_empty() && self.mod_handler.is_some() {
21+
return Err(Error::new(
22+
Span::call_site(),
23+
"`with` attribute should not be used with other handling attributes",
24+
));
25+
}
26+
Ok(())
27+
}
828
}
929

10-
pub fn extract_field_attributes(attrs: &[Attribute]) -> FieldAttributes {
30+
const ABI: &str = "abi";
31+
const NAME: &str = "name";
32+
const WITH: &str = "with";
33+
const ABI_TYPE_WITH: &str = "abi_type_with";
34+
const INTO_ABI_WITH: &str = "into_abi_with";
35+
const FROM_ABI_WITH: &str = "from_abi_with";
36+
37+
pub fn extract_field_attributes(attrs: &[Attribute]) -> Result<FieldAttributes, Error> {
1138
let mut attributes = FieldAttributes {
1239
custom_name: None,
13-
custom_handler: None,
40+
mod_handler: None,
41+
with_handlers: Vec::new(),
1442
};
43+
44+
fn parse_path(meta: syn::meta::ParseNestedMeta) -> Result<Path, Error> {
45+
let value = meta.value()?;
46+
let path_str: syn::LitStr = value.parse()?;
47+
let path = syn::parse_str::<Path>(&path_str.value())?;
48+
Ok(path)
49+
}
50+
1551
for attr in attrs {
16-
if !attr.path().is_ident("abi") {
52+
if !attr.path().is_ident(ABI) {
1753
continue;
1854
}
19-
let _ = attr.parse_nested_meta(|meta| {
20-
if meta.path.is_ident("name") {
21-
let value = meta.value()?;
22-
let lit: LitStr = value.parse()?;
23-
attributes.custom_name = Some(format_ident!("{}", lit.value(), span = lit.span()));
24-
} else if meta.path.is_ident("with") {
25-
let value = meta.value()?;
26-
let path_str: syn::LitStr = value.parse()?;
27-
let path = syn::parse_str::<Path>(&path_str.value())?;
28-
attributes.custom_handler = Some(path);
55+
attr.parse_nested_meta(|meta| {
56+
match &meta.path {
57+
path if path.is_ident(NAME) => {
58+
let value = meta.value()?;
59+
let lit: LitStr = value.parse()?;
60+
attributes.custom_name =
61+
Some(format_ident!("{}", lit.value(), span = lit.span()));
62+
}
63+
path if path.is_ident(WITH) => {
64+
let value = meta.value()?;
65+
let path_str: syn::LitStr = value.parse()?;
66+
let path = syn::parse_str::<Path>(&path_str.value())?;
67+
attributes.mod_handler = Some(path);
68+
}
69+
path if path.is_ident(ABI_TYPE_WITH) => {
70+
let path = parse_path(meta)?;
71+
attributes.with_handlers.push((HandlerType::AbiType, path));
72+
}
73+
path if path.is_ident(INTO_ABI_WITH) => {
74+
let path = parse_path(meta)?;
75+
attributes.with_handlers.push((HandlerType::IntoAbi, path));
76+
}
77+
path if path.is_ident(FROM_ABI_WITH) => {
78+
let path = parse_path(meta)?;
79+
attributes.with_handlers.push((HandlerType::FromAbi, path));
80+
}
81+
82+
path => {
83+
let path = path.get_ident().map(|x| x.to_string()).unwrap_or_default();
84+
return Err(Error::new(
85+
attr.span(),
86+
format!("Parameter {path} is not supported"),
87+
));
88+
}
2989
}
90+
3091
Ok(())
31-
});
92+
})?;
3293
}
3394

34-
attributes
95+
Ok(attributes)
3596
}

abi-proc/src/derive_from_abi.rs

+29-12
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
use crate::common;
2-
use crate::common::FieldAttributes;
2+
use crate::common::{FieldAttributes, HandlerType};
33
use quote::quote;
4+
use syn::Error;
45

5-
pub fn impl_derive(input: syn::DeriveInput) -> Result<proc_macro2::TokenStream, syn::Error> {
6+
pub fn impl_derive(input: syn::DeriveInput) -> Result<proc_macro2::TokenStream, Error> {
67
let data = match &input.data {
78
syn::Data::Struct(data_struct) => data_struct,
89
syn::Data::Enum(_) => {
9-
return Err(syn::Error::new_spanned(
10+
return Err(Error::new_spanned(
1011
&input,
1112
"FromAbi is not supported for enum",
1213
))
1314
}
1415
syn::Data::Union(_) => {
15-
return Err(syn::Error::new_spanned(
16+
return Err(Error::new_spanned(
1617
&input,
1718
"FromAbi is not supported for unions",
1819
))
@@ -28,8 +29,8 @@ pub fn impl_derive(input: syn::DeriveInput) -> Result<proc_macro2::TokenStream,
2829
continue;
2930
};
3031

31-
let attributes = common::extract_field_attributes(i.attrs.as_slice());
32-
let token = construct_from_abi(name, &attributes);
32+
let attributes = common::extract_field_attributes(i.attrs.as_slice())?;
33+
let token = construct_from_abi(name, &attributes)?;
3334
struct_fields.push(token);
3435
}
3536

@@ -51,19 +52,35 @@ pub fn impl_derive(input: syn::DeriveInput) -> Result<proc_macro2::TokenStream,
5152
pub fn construct_from_abi(
5253
field_name: &syn::Ident,
5354
attrs: &FieldAttributes,
54-
) -> proc_macro2::TokenStream {
55-
match &attrs.custom_handler {
56-
Some(handler) => {
57-
quote!(#field_name: #handler::from_abi(
55+
) -> Result<proc_macro2::TokenStream, Error> {
56+
attrs.check()?;
57+
58+
for (ty, path) in &attrs.with_handlers {
59+
if !matches!(ty, HandlerType::FromAbi) {
60+
continue;
61+
}
62+
63+
return Ok(quote! {
64+
#field_name: #path(
5865
iter.next()
5966
.ok_or(anyhow::anyhow!("unable to get field from abi"))?.value.clone())?
60-
)
67+
});
68+
}
69+
70+
//fallback to mod handler if present
71+
Ok(match &attrs.mod_handler {
72+
Some(path) => {
73+
quote! {
74+
#field_name: #path::from_abi(
75+
iter.next()
76+
.ok_or(anyhow::anyhow!("unable to get field from abi"))?.value.clone())?
77+
}
6178
}
6279
None => {
6380
quote!(#field_name: <_>::from_abi(
6481
iter.next()
6582
.ok_or(anyhow::anyhow!("unable to get field from abi"))?.value.clone())?
6683
)
6784
}
68-
}
85+
})
6986
}

abi-proc/src/derive_into_abi.rs

+23-9
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
use crate::common;
2-
use crate::common::FieldAttributes;
2+
use crate::common::{FieldAttributes, HandlerType};
33
use quote::quote;
4+
use syn::Error;
45

5-
pub fn impl_derive(input: syn::DeriveInput) -> Result<proc_macro2::TokenStream, syn::Error> {
6+
pub fn impl_derive(input: syn::DeriveInput) -> Result<proc_macro2::TokenStream, Error> {
67
let data = match input.data {
78
syn::Data::Struct(data_struct) => data_struct,
89
syn::Data::Enum(_) => {
9-
return Err(syn::Error::new_spanned(
10+
return Err(Error::new_spanned(
1011
&input,
1112
"IntoAbi is not supported for enum",
1213
))
1314
}
1415
syn::Data::Union(_) => {
15-
return Err(syn::Error::new_spanned(
16+
return Err(Error::new_spanned(
1617
&input,
1718
"IntoAbi is not supported for unions",
1819
))
@@ -28,8 +29,8 @@ pub fn impl_derive(input: syn::DeriveInput) -> Result<proc_macro2::TokenStream,
2829
continue;
2930
};
3031

31-
let attributes = common::extract_field_attributes(i.attrs.as_slice());
32-
let token = construct_named_abi_value(name, &attributes);
32+
let attributes = common::extract_field_attributes(i.attrs.as_slice())?;
33+
let token = construct_named_abi_value(name, &attributes)?;
3334

3435
tuple.push(token);
3536
}
@@ -61,15 +62,28 @@ pub fn impl_derive(input: syn::DeriveInput) -> Result<proc_macro2::TokenStream,
6162
pub fn construct_named_abi_value(
6263
field_name: &syn::Ident,
6364
attrs: &FieldAttributes,
64-
) -> proc_macro2::TokenStream {
65+
) -> Result<proc_macro2::TokenStream, Error> {
66+
attrs.check()?;
67+
6568
let to_change = match &attrs.custom_name {
6669
Some(custom) => custom.to_string(),
6770
None => field_name.to_string(),
6871
};
6972
let custom_name = to_change.to_string();
7073

71-
match &attrs.custom_handler {
74+
for (ty, path) in &attrs.with_handlers {
75+
if !matches!(ty, HandlerType::IntoAbi) {
76+
continue;
77+
}
78+
79+
return Ok(quote! {
80+
quote!(#path(self.#field_name.clone()).named(#custom_name))
81+
});
82+
}
83+
84+
//fallback to mod handler if present
85+
Ok(match &attrs.mod_handler {
7286
Some(handler) => quote!(#handler::into_abi(self.#field_name.clone()).named(#custom_name)),
7387
None => quote!(self.#field_name.clone().into_abi().named(#custom_name)),
74-
}
88+
})
7589
}

abi-proc/src/derive_with_abi_type.rs

+23-9
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
use crate::common;
2-
use crate::common::FieldAttributes;
2+
use crate::common::{FieldAttributes, HandlerType};
33
use quote::quote;
4+
use syn::Error;
45

5-
pub fn impl_derive(input: syn::DeriveInput) -> Result<proc_macro2::TokenStream, syn::Error> {
6+
pub fn impl_derive(input: syn::DeriveInput) -> Result<proc_macro2::TokenStream, Error> {
67
let data = match input.data {
78
syn::Data::Struct(data_struct) => data_struct,
89
syn::Data::Enum(_) => {
9-
return Err(syn::Error::new_spanned(
10+
return Err(Error::new_spanned(
1011
&input,
1112
"WithAbiType is not supported for enum",
1213
))
1314
}
1415
syn::Data::Union(_) => {
15-
return Err(syn::Error::new_spanned(
16+
return Err(Error::new_spanned(
1617
&input,
1718
"WithAbiType is not supported for unions",
1819
))
@@ -28,8 +29,8 @@ pub fn impl_derive(input: syn::DeriveInput) -> Result<proc_macro2::TokenStream,
2829
continue;
2930
};
3031

31-
let attributes = common::extract_field_attributes(i.attrs.as_slice());
32-
let token = construct_with_abi_type(name, &i.ty, attributes);
32+
let attributes = common::extract_field_attributes(i.attrs.as_slice())?;
33+
let token = construct_with_abi_type(name, &i.ty, attributes)?;
3334

3435
tuple.push(token);
3536
}
@@ -53,15 +54,28 @@ pub fn construct_with_abi_type(
5354
field_name: &syn::Ident,
5455
ty: &syn::Type,
5556
attrs: FieldAttributes,
56-
) -> proc_macro2::TokenStream {
57+
) -> Result<proc_macro2::TokenStream, Error> {
58+
attrs.check()?;
59+
5760
let to_change = match &attrs.custom_name {
5861
Some(custom) => custom.to_string(),
5962
None => field_name.to_string(),
6063
};
6164
let custom_name = to_change.to_string();
6265

63-
match &attrs.custom_handler {
66+
for (ty, path) in &attrs.with_handlers {
67+
if !matches!(ty, HandlerType::AbiType) {
68+
continue;
69+
}
70+
71+
return Ok(quote! {
72+
#path().named(#custom_name)
73+
});
74+
}
75+
76+
//fallback to mod handler if present
77+
Ok(match &attrs.mod_handler {
6478
Some(handler) => quote!(#handler::abi_type().named(#custom_name)),
6579
None => quote!(<#ty>::abi_type().named(#custom_name)),
66-
}
80+
})
6781
}

0 commit comments

Comments
 (0)