1use std::borrow::Borrow;
23
24use itertools::Itertools;
25use proc_macro2::TokenStream;
26use quote::{
27 format_ident,
28 quote,
29};
30use syn::{
31 Attribute,
32 FnArg,
33 Ident,
34 Lit,
35 Meta,
36 NestedMeta,
37 Pat,
38 PatType,
39 Type,
40};
41
42use crate::types::AttributeArgs;
43
44#[macro_export]
45macro_rules! format_err_spanned {
46 ($tokens:expr, $($msg:tt)*) => {
47 ::syn::Error::new_spanned(
48 &$tokens,
49 format_args!($($msg)*)
50 )
51 }
52}
53
54pub fn into_u16<T: ToString>(ident: T) -> u16 {
55 let mut output = [0; 32];
56 blake2b_256(ident.to_string().as_bytes(), &mut output);
57 u16::from_be_bytes([output[0], output[1]])
58}
59
60pub fn into_u32<T: ToString>(ident: T) -> u32 {
61 let mut output = [0; 32];
62 blake2b_256(ident.to_string().as_bytes(), &mut output);
63 u32::from_be_bytes([output[0], output[1], output[2], output[3]])
64}
65
66pub fn blake2b_256(input: &[u8], output: &mut [u8; 32]) {
67 use ::blake2::digest::{
68 consts::U32,
69 Digest as _,
70 };
71
72 type Blake2b256 = blake2::Blake2b<U32>;
73
74 let mut blake2 = Blake2b256::new();
75 blake2.update(input);
76 let result = blake2.finalize();
77 output.copy_from_slice(&result);
78}
79
80pub trait AttributeParser<A> {
81 fn split_attrs(self) -> Result<(Vec<NestedMeta>, Vec<A>), syn::Error>;
82}
83
84impl<A, I> AttributeParser<A> for I
85where
86 A: Borrow<Attribute>,
87 I: IntoIterator<Item = A>,
88{
89 fn split_attrs(self) -> Result<(Vec<NestedMeta>, Vec<A>), syn::Error> {
90 let (obce_attrs, other_attrs): (Vec<_>, Vec<_>) =
91 self.into_iter().partition(|attr| attr.borrow().path.is_ident("obce"));
92
93 let meta = obce_attrs
94 .into_iter()
95 .map(|attr| Attribute::parse_args(attr.borrow()))
96 .map_ok(AttributeArgs::into_iter)
97 .flatten_ok()
98 .try_collect()?;
99
100 Ok((meta, other_attrs))
101 }
102}
103
104pub enum LitOrPath<'a> {
105 Lit(&'a Lit),
106 Path,
107}
108
109pub trait MetaUtils<'a> {
110 fn find_by_name(self, name: &str) -> Option<(LitOrPath<'a>, &'a Ident)>;
111}
112
113impl<'a, I> MetaUtils<'a> for I
114where
115 I: IntoIterator<Item = &'a NestedMeta>,
116{
117 fn find_by_name(self, name: &str) -> Option<(LitOrPath<'a>, &'a Ident)> {
118 self.into_iter().find_map(|attr| {
119 match attr.borrow() {
120 NestedMeta::Meta(Meta::NameValue(value)) => {
121 if let Some(ident) = value.path.get_ident() {
122 (ident == name).then_some((LitOrPath::Lit(&value.lit), ident))
123 } else {
124 None
125 }
126 }
127 NestedMeta::Meta(Meta::Path(path)) => {
128 if let Some(ident) = path.get_ident() {
129 (ident == name).then_some((LitOrPath::Path, ident))
130 } else {
131 None
132 }
133 }
134 _ => None,
135 }
136 })
137 }
138}
139
140pub struct InputBindings<'a> {
141 bindings: Vec<&'a PatType>,
142}
143
144impl<'a> InputBindings<'a> {
145 pub fn iter_call_params(&self) -> impl Iterator<Item = Ident> + ExactSizeIterator + '_ {
149 self.bindings
150 .iter()
151 .enumerate()
152 .map(|(n, _)| format_ident!("__ink_binding_{}", n))
153 }
154
155 pub fn iter_raw_call_params(&self) -> impl Iterator<Item = &Pat> + ExactSizeIterator + '_ {
159 self.bindings.iter().map(|pat| &*pat.pat)
160 }
161
162 pub fn lhs_pat(&self, ty: Option<Type>) -> TokenStream {
170 let bindings = self.iter_call_params();
171 let ty = ty.map(|val| {
172 quote! {
173 : #val
174 }
175 });
176
177 match bindings.len() {
178 0 => quote! { _ : () },
179 1 => quote! { #( #bindings ),* #ty },
180 _ => quote! { ( #( #bindings ),* ) #ty },
181 }
182 }
183
184 pub fn raw_special_mapping(&self) -> TokenStream {
186 let lhs = self.bindings.iter().map(|val| &val.pat);
187
188 let rhs = self.iter_call_params();
189
190 quote! {
191 let (#(#lhs,)*) = (#(&#rhs,)*);
192 }
193 }
194}
195
196impl<'a> FromIterator<&'a FnArg> for InputBindings<'a> {
197 fn from_iter<T: IntoIterator<Item = &'a FnArg>>(iter: T) -> Self {
198 let bindings = iter
199 .into_iter()
200 .filter_map(|fn_arg| {
201 if let FnArg::Typed(pat) = fn_arg {
202 Some(pat)
203 } else {
204 None
205 }
206 })
207 .collect();
208
209 Self { bindings }
210 }
211}
212
213#[cfg(test)]
214mod tests {
215 use std::iter;
216
217 use quote::{
218 format_ident,
219 quote,
220 };
221 use syn::{
222 parse::Parser,
223 parse2,
224 parse_quote,
225 punctuated::Punctuated,
226 FnArg,
227 Stmt,
228 Token,
229 };
230
231 use super::InputBindings;
232
233 #[test]
234 fn special_bindings_conversion() {
235 let parser = Punctuated::<FnArg, Token![,]>::parse_terminated;
236
237 let fn_args = parser
238 .parse2(quote! {
239 one: u32, two: u64, three: &'a str
240 })
241 .unwrap();
242
243 let input_bindings = InputBindings::from_iter(&fn_args);
244
245 assert_eq!(
246 input_bindings.iter_call_params().collect::<Vec<_>>(),
247 vec![
248 format_ident!("__ink_binding_0"),
249 format_ident!("__ink_binding_1"),
250 format_ident!("__ink_binding_2")
251 ]
252 );
253 }
254
255 #[test]
256 fn raw_special_mapping_empty() {
257 let input_bindings = InputBindings::from_iter(iter::empty());
258
259 assert_eq!(
260 parse2::<Stmt>(input_bindings.raw_special_mapping()).unwrap(),
261 parse_quote! {
262 let () = ();
263 }
264 );
265 }
266
267 #[test]
268 fn raw_special_mapping_one() {
269 let parser = Punctuated::<FnArg, Token![,]>::parse_terminated;
270
271 let fn_args = parser
272 .parse2(quote! {
273 one: u32
274 })
275 .unwrap();
276
277 let input_bindings = InputBindings::from_iter(&fn_args);
278
279 assert_eq!(
280 parse2::<Stmt>(input_bindings.raw_special_mapping()).unwrap(),
281 parse_quote! {
282 let (one,) = (&__ink_binding_0,);
283 }
284 );
285 }
286
287 #[test]
288 fn raw_special_mapping_multiple() {
289 let parser = Punctuated::<FnArg, Token![,]>::parse_terminated;
290
291 let fn_args = parser
292 .parse2(quote! {
293 one: u32, two: u64, three: &'a str
294 })
295 .unwrap();
296
297 let input_bindings = InputBindings::from_iter(&fn_args);
298
299 assert_eq!(
300 parse2::<Stmt>(input_bindings.raw_special_mapping()).unwrap(),
301 parse_quote! {
302 let (one, two, three,) = (&__ink_binding_0, &__ink_binding_1, &__ink_binding_2,);
303 }
304 );
305 }
306}