obce_codegen/
utils.rs

1// Copyright (c) 2012-2022 Supercolony
2//
3// Permission is hereby granted, free of charge, to any person obtaining
4// a copy of this software and associated documentation files (the"Software"),
5// to deal in the Software without restriction, including
6// without limitation the rights to use, copy, modify, merge, publish,
7// distribute, sublicense, and/or sell copies of the Software, and to
8// permit persons to whom the Software is furnished to do so, subject to
9// the following conditions:
10//
11// The above copyright notice and this permission notice shall be
12// included in all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22use 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    /// Iterate over "special" bindings identifiers.
146    ///
147    /// For example, it converts `(one: u32, two: u32)` into `(__ink_binding_0, __ink_binding_1)`.
148    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    /// Iterate over raw bindings patterns.
156    ///
157    /// The provided iterator makes no conversions from the inner values stored inside [`InputBindings`].
158    pub fn iter_raw_call_params(&self) -> impl Iterator<Item = &Pat> + ExactSizeIterator + '_ {
159        self.bindings.iter().map(|pat| &*pat.pat)
160    }
161
162    /// Create a LHS pattern from the input bindings.
163    ///
164    /// The returned [`TokenStream`] contains a pattern that is suitable for,
165    /// for example, `scale` decoding.
166    ///
167    /// You can also provide an optional type, that will be used to constrain
168    /// a pattern when it has `>= 1` bindings.
169    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    /// Create a "mapping" from "special" identifiers to raw patterns.
185    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}