ink_macro/sol/
utils.rs

1// Copyright (C) Use Ink (UK) Ltd.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use proc_macro2::TokenStream as TokenStream2;
16use quote::quote;
17use syn::{
18    spanned::Spanned,
19    Field,
20    Fields,
21};
22
23/// Ensures that the given item has at least one variant.
24pub fn ensure_non_empty_enum(
25    s: &synstructure::Structure,
26    trait_name: &str,
27) -> syn::Result<()> {
28    if s.variants().is_empty() {
29        Err(syn::Error::new(
30            s.ast().span(),
31            format!(
32                "can only derive `{trait_name}` for Rust `enum` items \
33                with at least one variant"
34            ),
35        ))
36    } else {
37        Ok(())
38    }
39}
40
41/// Composes the body for the variant or struct given its fields.
42pub fn body_from_fields(
43    fields: &Fields,
44    transformer: Option<fn(TokenStream2, &Field) -> TokenStream2>,
45) -> TokenStream2 {
46    let from_params_elems = || {
47        fields.iter().enumerate().map(|(idx, field)| {
48            let idx = syn::Index::from(idx);
49            let value = match &transformer {
50                None => quote!(value.#idx),
51                Some(transformer) => transformer(quote!(value.#idx), field),
52            };
53            match &field.ident {
54                // Handles named fields.
55                None => quote!(#value),
56                // Handles tuple elements.
57                Some(ident) => {
58                    quote! {
59                        #ident: #value
60                    }
61                }
62            }
63        })
64    };
65    match fields {
66        // Handles named fields.
67        Fields::Named(_) => {
68            let self_fields = from_params_elems();
69            quote!(
70                {
71                    #( #self_fields, )*
72                }
73            )
74        }
75        // Handles tuple elements.
76        Fields::Unnamed(_) => {
77            let self_elems = from_params_elems();
78            quote! {
79                ( #( #self_elems, )* )
80            }
81        }
82        // Handles unit variants.
83        Fields::Unit => quote!(),
84    }
85}
86
87/// Composes a list of tuple elements for the variant or struct given its fields.
88pub fn tuple_elems_from_fields(
89    fields: &Fields,
90    transformer: Option<fn(TokenStream2, &Field) -> TokenStream2>,
91) -> TokenStream2 {
92    let elems = fields.iter().enumerate().map(|(idx, field)| {
93        // Accessor is either a field name or tuple index.
94        let accessor = field
95            .ident
96            .as_ref()
97            .map(|ident| quote!(#ident))
98            .unwrap_or_else(|| {
99                let idx = syn::Index::from(idx);
100                quote!(#idx)
101            });
102        match &transformer {
103            None => quote!(&self.#accessor),
104            Some(transformer) => transformer(quote!(&self.#accessor), field),
105        }
106    });
107    quote! {
108        ( #( #elems, )* )
109    }
110}