use proc_macro::TokenStream;
use proc_macro2::Ident;
use quote::{quote, ToTokens};
use syn::{parse_macro_input, ImplItem, ItemEnum, ItemImpl};
#[proc_macro_attribute]
pub fn pyhash(_: TokenStream, item: TokenStream) -> TokenStream {
let mut ast = parse_macro_input!(item as ItemImpl);
let to_add = quote! {pub fn __hash__(&self) -> u64 {
solders_traits_core::PyHash::pyhash(self)
}};
ast.items.push(ImplItem::Verbatim(to_add));
TokenStream::from(ast.to_token_stream())
}
#[proc_macro_attribute]
pub fn richcmp_full(_: TokenStream, item: TokenStream) -> TokenStream {
let mut ast = parse_macro_input!(item as ItemImpl);
let to_add = quote! {pub fn __richcmp__(&self, other: &Self, op: pyo3::basic::CompareOp) -> bool {
solders_traits_core::RichcmpFull::richcmp(self, other, op)
}};
ast.items.push(ImplItem::Verbatim(to_add));
TokenStream::from(ast.to_token_stream())
}
#[proc_macro_attribute]
pub fn richcmp_eq_only(_: TokenStream, item: TokenStream) -> TokenStream {
let mut ast = parse_macro_input!(item as ItemImpl);
let to_add = quote! {pub fn __richcmp__(&self, other: &Self, op: pyo3::basic::CompareOp) -> pyo3::prelude::PyResult<bool> {
solders_traits_core::RichcmpEqualityOnly::richcmp(self, other, op)
}};
ast.items.push(ImplItem::Verbatim(to_add));
TokenStream::from(ast.to_token_stream())
}
#[proc_macro_attribute]
pub fn richcmp_signer(_: TokenStream, item: TokenStream) -> TokenStream {
let mut ast = parse_macro_input!(item as ItemImpl);
let to_add = quote! {pub fn __richcmp__(&self, other: crate::signer::Signer, op: pyo3::basic::CompareOp) -> pyo3::prelude::PyResult<bool> {
solders_traits::RichcmpSigner::richcmp(self, other, op)
}};
ast.items.push(ImplItem::Verbatim(to_add));
TokenStream::from(ast.to_token_stream())
}
fn add_core_methods(ast: &mut ItemImpl) {
let mut methods = vec![
ImplItem::Verbatim(
quote! {pub fn __bytes__<'a>(&self, py: pyo3::prelude::Python<'a>) -> &'a pyo3::types::PyBytes {
solders_traits_core::CommonMethodsCore::pybytes(self, py)
}},
),
ImplItem::Verbatim(quote! { pub fn __str__(&self) -> String {
solders_traits_core::CommonMethodsCore::pystr(self)
} }),
ImplItem::Verbatim(quote! { pub fn __repr__(&self) -> String {
solders_traits_core::CommonMethodsCore::pyrepr(self)
} }),
ImplItem::Verbatim(
quote! { pub fn __reduce__(&self) -> pyo3::prelude::PyResult<(pyo3::prelude::PyObject, pyo3::prelude::PyObject)> {
solders_traits_core::CommonMethodsCore::pyreduce(self)
} },
),
];
if !ast.items.iter().any(|item| match item {
ImplItem::Method(m) => m.sig.ident == "from_bytes",
_ => false,
}) {
let from_bytes = ImplItem::Verbatim(quote! {
#[staticmethod]
pub fn from_bytes(data: &[u8]) -> PyResult<Self> {
<Self as solders_traits_core::CommonMethodsCore>::py_from_bytes(data)
}
});
methods.push(from_bytes);
};
ast.items.extend_from_slice(&methods);
}
#[proc_macro_attribute]
pub fn common_methods_core(_: TokenStream, item: TokenStream) -> TokenStream {
let mut ast = parse_macro_input!(item as ItemImpl);
add_core_methods(&mut ast);
TokenStream::from(ast.to_token_stream())
}
#[proc_macro_attribute]
pub fn common_methods(_: TokenStream, item: TokenStream) -> TokenStream {
let mut ast = parse_macro_input!(item as ItemImpl);
add_core_methods(&mut ast);
let methods = vec![
ImplItem::Verbatim(quote! {
pub fn to_json(&self) -> String {
solders_traits_core::CommonMethods::py_to_json(self)
} }),
ImplItem::Verbatim(quote! {
#[staticmethod] pub fn from_json(raw: &str) -> PyResult<Self> {
<Self as solders_traits_core::CommonMethods>::py_from_json(raw)
} }),
];
ast.items.extend_from_slice(&methods);
TokenStream::from(ast.to_token_stream())
}
#[proc_macro_attribute]
pub fn common_methods_rpc_resp(_: TokenStream, item: TokenStream) -> TokenStream {
let mut ast = parse_macro_input!(item as ItemImpl);
let methods = vec![
ImplItem::Verbatim(
quote! {pub fn __bytes__<'a>(&self, py: pyo3::prelude::Python<'a>) -> &'a pyo3::types::PyBytes {
CommonMethodsRpcResp::pybytes(self, py)
}},
),
ImplItem::Verbatim(quote! { pub fn __str__(&self) -> String {
CommonMethodsRpcResp::pystr(self)
} }),
ImplItem::Verbatim(quote! { pub fn __repr__(&self) -> String {
CommonMethodsRpcResp::pyrepr(self)
} }),
ImplItem::Verbatim(
quote! { pub fn __reduce__(&self) -> pyo3::prelude::PyResult<(pyo3::prelude::PyObject, pyo3::prelude::PyObject)> {
CommonMethodsRpcResp::pyreduce(self)
} },
),
ImplItem::Verbatim(quote! {
pub fn to_json(&self) -> String {
CommonMethodsRpcResp::py_to_json(self)
} }),
ImplItem::Verbatim(quote! {
#[staticmethod]
pub fn from_json(raw: &str) -> PyResult<Resp<Self>> {
<Self as CommonMethodsRpcResp>::py_from_json(raw)
} }),
ImplItem::Verbatim(quote! {
#[staticmethod]
pub fn from_bytes(data: &[u8]) -> PyResult<Self> {
<Self as CommonMethodsRpcResp>::py_from_bytes(data)
}
}),
ImplItem::Verbatim(
quote! {pub fn __richcmp__(&self, other: &Self, op: pyo3::basic::CompareOp) -> pyo3::prelude::PyResult<bool> {
solders_traits_core::RichcmpEqualityOnly::richcmp(self, other, op)
}},
),
];
ast.items.extend_from_slice(&methods);
TokenStream::from(ast.to_token_stream())
}
#[proc_macro_attribute]
pub fn rpc_id_getter(_: TokenStream, item: TokenStream) -> TokenStream {
let mut ast = parse_macro_input!(item as ItemImpl);
let to_add = quote! {
#[getter]
pub fn id(&self) -> u64 {
self.base.id
}};
ast.items.push(ImplItem::Verbatim(to_add));
TokenStream::from(ast.to_token_stream())
}
#[proc_macro_attribute]
pub fn enum_original_mapping(original: TokenStream, item: TokenStream) -> TokenStream {
let mut new_stream = proc_macro2::TokenStream::from(item.clone());
let ast = parse_macro_input!(item as ItemEnum);
let enum_name = ast.ident;
let orig = parse_macro_input!(original as Ident);
let variant_names: Vec<Ident> = ast.variants.into_iter().map(|v| v.ident).collect();
let from_impl = quote! {
impl From<#orig> for #enum_name {
fn from(left: #orig) -> Self {
match left {
#(#orig::#variant_names => Self::#variant_names),*,
_ => panic!("Unrecognized variant: {:?}", left)
}
}
}
impl From<#enum_name> for #orig {
fn from(left: #enum_name) -> Self {
match left {
#(#enum_name::#variant_names => Self::#variant_names),*
}
}
}
};
new_stream.extend(from_impl);
TokenStream::from(new_stream)
}
#[proc_macro_derive(EnumIntoPy)]
pub fn enum_into_py(item: TokenStream) -> TokenStream {
let ast = parse_macro_input!(item as ItemEnum);
let enum_name = ast.ident;
let variant_names: Vec<Ident> = ast.variants.into_iter().map(|v| v.ident).collect();
let into_py_impl = quote! {
impl IntoPy<PyObject> for #enum_name {
fn into_py(self, py: Python<'_>) -> PyObject {
match self {
#(Self::#variant_names(x) => x.into_py(py)),*,
}
}
}
};
into_py_impl.to_token_stream().into()
}