1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
use crate::info_extractor::arg_info::{ArgInfo, BindgenArgType};
use crate::info_extractor::serializer_attr::SerializerAttr;
use crate::info_extractor::SerializerType;
use quote::ToTokens;
use syn::export::Span;
use syn::spanned::Spanned;
use syn::{Attribute, Error, FnArg, Ident, Receiver, ReturnType, Signature};

/// Information extracted from method attributes and signature.
pub struct AttrSigInfo {
    /// The name of the method.
    pub ident: Ident,
    /// Attributes not related to bindgen.
    pub non_bindgen_attrs: Vec<Attribute>,
    /// All arguments of the method.
    pub args: Vec<ArgInfo>,
    /// Whether method can be used as initializer.
    pub is_init: bool,
    /// Whether method accepting $NEAR.
    pub is_payable: bool,
    /// The serializer that we use for `env::input()`.
    pub input_serializer: SerializerType,
    /// Whether the method doesn't mutate state
    pub is_view: bool,
    /// The serializer that we use for the return type.
    pub result_serializer: SerializerType,
    /// The receiver, like `mut self`, `self`, `&mut self`, `&self`, or `None`.
    pub receiver: Option<Receiver>,
    /// What this function returns.
    pub returns: ReturnType,
    /// The original method signature.
    pub original_sig: Signature,
}

impl AttrSigInfo {
    /// Process the method and extract information important for near-sdk.
    pub fn new(
        original_attrs: &mut Vec<Attribute>,
        original_sig: &mut Signature,
    ) -> syn::Result<Self> {
        if original_sig.asyncness.is_some() {
            return Err(Error::new(
                original_sig.span(),
                "Contract API is not allowed to be async.",
            ));
        }
        if original_sig.abi.is_some() {
            return Err(Error::new(
                original_sig.span(),
                "Contract API is not allowed to have binary interface.",
            ));
        }
        if original_sig.variadic.is_some() {
            return Err(Error::new(
                original_sig.span(),
                "Contract API is not allowed to have variadic arguments.",
            ));
        }

        let ident = original_sig.ident.clone();
        let mut non_bindgen_attrs = vec![];
        let mut args = vec![];
        let mut is_init = false;
        let mut is_payable = false;
        // By the default we serialize the result with JSON.
        let mut result_serializer = SerializerType::JSON;

        let mut payable_attr = None;
        for attr in original_attrs.iter() {
            let attr_str = attr.path.to_token_stream().to_string();
            match attr_str.as_str() {
                "init" => {
                    is_init = true;
                }
                "payable" => {
                    payable_attr = Some(attr);
                    is_payable = true;
                }
                "result_serializer" => {
                    let serializer: SerializerAttr = syn::parse2(attr.tokens.clone())?;
                    result_serializer = serializer.serializer_type;
                }
                _ => {
                    non_bindgen_attrs.push((*attr).clone());
                }
            }
        }

        let mut receiver = None;
        for fn_arg in &mut original_sig.inputs {
            match fn_arg {
                FnArg::Receiver(r) => receiver = Some((*r).clone()),
                FnArg::Typed(pat_typed) => {
                    args.push(ArgInfo::new(pat_typed)?);
                }
            }
        }

        let is_view = if let Some(ref receiver) = receiver {
            receiver.mutability.is_none()
        } else {
            !is_init
        };

        if let Some(payable_attr) = payable_attr {
            if is_view {
                return Err(Error::new(
                    payable_attr.span(),
                    "Payable method must be mutable (not view)",
                ));
            }
        }

        original_attrs.retain(|attr| {
            let attr_str = attr.path.to_token_stream().to_string();
            attr_str != "init" && attr_str != "result_serializer" && attr_str != "payable"
        });

        let returns = original_sig.output.clone();

        let mut result = Self {
            ident,
            non_bindgen_attrs,
            args,
            input_serializer: SerializerType::JSON,
            is_init,
            is_payable,
            is_view,
            result_serializer,
            receiver,
            returns,
            original_sig: original_sig.clone(),
        };

        let input_serializer =
            if result.input_args().all(|arg: &ArgInfo| arg.serializer_ty == SerializerType::JSON) {
                SerializerType::JSON
            } else if result.input_args().all(|arg| arg.serializer_ty == SerializerType::Borsh) {
                SerializerType::Borsh
            } else {
                return Err(Error::new(
                    Span::call_site(),
                    format!("Input arguments should be all of the same serialization type."),
                ));
            };
        result.input_serializer = input_serializer;
        Ok(result)
    }

    /// Only get args that correspond to `env::input()`.
    pub fn input_args(&self) -> impl Iterator<Item = &ArgInfo> {
        self.args.iter().filter(|arg| match arg.bindgen_ty {
            BindgenArgType::Regular => true,
            _ => false,
        })
    }
}