use std::borrow::Borrow;
use itertools::Itertools;
use proc_macro2::TokenStream;
use quote::{
format_ident,
quote,
};
use syn::{
Attribute,
FnArg,
Ident,
Lit,
Meta,
NestedMeta,
Pat,
PatType,
Type,
};
use crate::types::AttributeArgs;
#[macro_export]
macro_rules! format_err_spanned {
($tokens:expr, $($msg:tt)*) => {
::syn::Error::new_spanned(
&$tokens,
format_args!($($msg)*)
)
}
}
pub fn into_u16<T: ToString>(ident: T) -> u16 {
let mut output = [0; 32];
blake2b_256(ident.to_string().as_bytes(), &mut output);
u16::from_be_bytes([output[0], output[1]])
}
pub fn into_u32<T: ToString>(ident: T) -> u32 {
let mut output = [0; 32];
blake2b_256(ident.to_string().as_bytes(), &mut output);
u32::from_be_bytes([output[0], output[1], output[2], output[3]])
}
pub fn blake2b_256(input: &[u8], output: &mut [u8; 32]) {
use ::blake2::digest::{
consts::U32,
Digest as _,
};
type Blake2b256 = blake2::Blake2b<U32>;
let mut blake2 = Blake2b256::new();
blake2.update(input);
let result = blake2.finalize();
output.copy_from_slice(&result);
}
pub trait AttributeParser<A> {
fn split_attrs(self) -> Result<(Vec<NestedMeta>, Vec<A>), syn::Error>;
}
impl<A, I> AttributeParser<A> for I
where
A: Borrow<Attribute>,
I: IntoIterator<Item = A>,
{
fn split_attrs(self) -> Result<(Vec<NestedMeta>, Vec<A>), syn::Error> {
let (obce_attrs, other_attrs): (Vec<_>, Vec<_>) =
self.into_iter().partition(|attr| attr.borrow().path.is_ident("obce"));
let meta = obce_attrs
.into_iter()
.map(|attr| Attribute::parse_args(attr.borrow()))
.map_ok(AttributeArgs::into_iter)
.flatten_ok()
.try_collect()?;
Ok((meta, other_attrs))
}
}
pub enum LitOrPath<'a> {
Lit(&'a Lit),
Path,
}
pub trait MetaUtils<'a> {
fn find_by_name(self, name: &str) -> Option<(LitOrPath<'a>, &'a Ident)>;
}
impl<'a, I> MetaUtils<'a> for I
where
I: IntoIterator<Item = &'a NestedMeta>,
{
fn find_by_name(self, name: &str) -> Option<(LitOrPath<'a>, &'a Ident)> {
self.into_iter().find_map(|attr| {
match attr.borrow() {
NestedMeta::Meta(Meta::NameValue(value)) => {
if let Some(ident) = value.path.get_ident() {
(ident == name).then_some((LitOrPath::Lit(&value.lit), ident))
} else {
None
}
}
NestedMeta::Meta(Meta::Path(path)) => {
if let Some(ident) = path.get_ident() {
(ident == name).then_some((LitOrPath::Path, ident))
} else {
None
}
}
_ => None,
}
})
}
}
pub struct InputBindings<'a> {
bindings: Vec<&'a PatType>,
}
impl<'a> InputBindings<'a> {
pub fn iter_call_params(&self) -> impl Iterator<Item = Ident> + ExactSizeIterator + '_ {
self.bindings
.iter()
.enumerate()
.map(|(n, _)| format_ident!("__ink_binding_{}", n))
}
pub fn iter_raw_call_params(&self) -> impl Iterator<Item = &Pat> + ExactSizeIterator + '_ {
self.bindings.iter().map(|pat| &*pat.pat)
}
pub fn lhs_pat(&self, ty: Option<Type>) -> TokenStream {
let bindings = self.iter_call_params();
let ty = ty.map(|val| {
quote! {
: #val
}
});
match bindings.len() {
0 => quote! { _ : () },
1 => quote! { #( #bindings ),* #ty },
_ => quote! { ( #( #bindings ),* ) #ty },
}
}
pub fn raw_special_mapping(&self) -> TokenStream {
let lhs = self.bindings.iter().map(|val| &val.pat);
let rhs = self.iter_call_params();
quote! {
let (#(#lhs,)*) = (#(&#rhs,)*);
}
}
}
impl<'a> FromIterator<&'a FnArg> for InputBindings<'a> {
fn from_iter<T: IntoIterator<Item = &'a FnArg>>(iter: T) -> Self {
let bindings = iter
.into_iter()
.filter_map(|fn_arg| {
if let FnArg::Typed(pat) = fn_arg {
Some(pat)
} else {
None
}
})
.collect();
Self { bindings }
}
}
#[cfg(test)]
mod tests {
use std::iter;
use quote::{
format_ident,
quote,
};
use syn::{
parse::Parser,
parse2,
parse_quote,
punctuated::Punctuated,
FnArg,
Stmt,
Token,
};
use super::InputBindings;
#[test]
fn special_bindings_conversion() {
let parser = Punctuated::<FnArg, Token![,]>::parse_terminated;
let fn_args = parser
.parse2(quote! {
one: u32, two: u64, three: &'a str
})
.unwrap();
let input_bindings = InputBindings::from_iter(&fn_args);
assert_eq!(
input_bindings.iter_call_params().collect::<Vec<_>>(),
vec![
format_ident!("__ink_binding_0"),
format_ident!("__ink_binding_1"),
format_ident!("__ink_binding_2")
]
);
}
#[test]
fn raw_special_mapping_empty() {
let input_bindings = InputBindings::from_iter(iter::empty());
assert_eq!(
parse2::<Stmt>(input_bindings.raw_special_mapping()).unwrap(),
parse_quote! {
let () = ();
}
);
}
#[test]
fn raw_special_mapping_one() {
let parser = Punctuated::<FnArg, Token![,]>::parse_terminated;
let fn_args = parser
.parse2(quote! {
one: u32
})
.unwrap();
let input_bindings = InputBindings::from_iter(&fn_args);
assert_eq!(
parse2::<Stmt>(input_bindings.raw_special_mapping()).unwrap(),
parse_quote! {
let (one,) = (&__ink_binding_0,);
}
);
}
#[test]
fn raw_special_mapping_multiple() {
let parser = Punctuated::<FnArg, Token![,]>::parse_terminated;
let fn_args = parser
.parse2(quote! {
one: u32, two: u64, three: &'a str
})
.unwrap();
let input_bindings = InputBindings::from_iter(&fn_args);
assert_eq!(
parse2::<Stmt>(input_bindings.raw_special_mapping()).unwrap(),
parse_quote! {
let (one, two, three,) = (&__ink_binding_0, &__ink_binding_1, &__ink_binding_2,);
}
);
}
}