fuels_rs/code_gen/
functions_gen.rs1use crate::abi_encoder::ABIEncoder;
2use crate::code_gen::custom_types_gen::extract_struct_name_from_abi_property;
3use crate::code_gen::docs_gen::expand_doc;
4use crate::errors::Error;
5use crate::json_abi::{parse_param, ABIParser};
6use crate::types::expand_type;
7use crate::utils::{ident, safe_ident};
8use fuels_core::{ParamType, Selector};
9use inflector::Inflector;
10use sway_types::{Function, Property};
11
12use proc_macro2::{Literal, TokenStream};
13use quote::quote;
14use std::collections::HashMap;
15
16pub fn expand_function(
31 function: &Function,
32 abi_parser: &ABIParser,
33 custom_enums: &HashMap<String, Property>,
34 custom_structs: &HashMap<String, Property>,
35) -> Result<TokenStream, Error> {
36 let name = safe_ident(&function.name);
37 let fn_signature = abi_parser.build_fn_selector(&function.name, &function.inputs);
38
39 let encoded = ABIEncoder::encode_function_selector(fn_signature?.as_bytes());
40
41 let tokenized_signature = expand_selector(encoded);
42 let tokenized_output = expand_fn_outputs(&function.outputs)?;
43 let result = quote! { ContractCall<#tokenized_output> };
44
45 let (input, arg) = expand_function_arguments(function, custom_enums, custom_structs)?;
46
47 let doc = expand_doc(&format!(
48 "Calls the contract's `{}` (0x{}) function",
49 function.name,
50 hex::encode(encoded)
51 ));
52
53 let mut output_params = vec![];
57 for output in &function.outputs {
58 let mut param_type_str: String = "ParamType::".to_owned();
59 let p = parse_param(output).unwrap();
60 param_type_str.push_str(&p.to_string());
61
62 let tok: proc_macro2::TokenStream = param_type_str.parse().unwrap();
63
64 output_params.push(tok);
65 }
66
67 let output_params_token = quote! { &[#( #output_params ),*] };
68
69 Ok(quote! {
70 #doc
71 pub fn #name(&self #input) -> #result {
72 Contract::method_hash(&self.fuel_client, &self.compiled,
73 #tokenized_signature, #output_params_token, #arg).expect("method not found (this should never happen)")
74 }
75 })
76}
77
78fn expand_selector(selector: Selector) -> TokenStream {
79 let bytes = selector.iter().copied().map(Literal::u8_unsuffixed);
80 quote! { [#( #bytes ),*] }
81}
82
83fn expand_fn_outputs(outputs: &[Property]) -> Result<TokenStream, Error> {
86 match outputs.len() {
87 0 => Ok(quote! { () }),
88 1 => {
89 if outputs[0].type_field.contains("struct ") {
92 let tok: proc_macro2::TokenStream =
93 extract_struct_name_from_abi_property(&outputs[0])
94 .parse()
95 .unwrap();
96 Ok(tok)
97 } else {
98 expand_type(&parse_param(&outputs[0])?)
99 }
100 }
101 _ => {
102 let types = outputs
103 .iter()
104 .map(|param| expand_type(&parse_param(param)?))
105 .collect::<Result<Vec<_>, Error>>()?;
106 Ok(quote! { (#( #types ),*) })
107 }
108 }
109}
110
111fn expand_function_arguments(
116 fun: &Function,
117 custom_enums: &HashMap<String, Property>,
118 custom_structs: &HashMap<String, Property>,
119) -> Result<(TokenStream, TokenStream), Error> {
120 let mut args = Vec::with_capacity(fun.inputs.len());
121 let mut call_args = Vec::with_capacity(fun.inputs.len());
122
123 for (i, param) in fun.inputs.iter().enumerate() {
127 if param.name == "gas_" || param.name == "amount_" || param.name == "color_" {
140 continue;
141 }
142 let name = expand_input_name(i, ¶m.name);
144
145 let rust_enum_name = custom_enums.get(¶m.name);
146 let rust_struct_name = custom_structs.get(¶m.name);
147
148 let ty = expand_input_param(
150 fun,
151 ¶m.name,
152 &parse_param(param)?,
153 &rust_enum_name,
154 &rust_struct_name,
155 )?;
156
157 args.push(quote! { #name: #ty });
159
160 call_args.push(name);
162 }
163
164 let args = quote! { #( , #args )* };
166
167 let call_args = match call_args.len() {
172 0 => quote! { () },
173 _ => quote! { &[ #(#call_args.into_token(), )* ] },
174 };
175
176 Ok((args, call_args))
177}
178
179pub fn expand_input_name(index: usize, name: &str) -> TokenStream {
184 let name_str = match name {
185 "" => format!("p{}", index),
186 n => n.to_snake_case(),
187 };
188 let name = safe_ident(&name_str);
189
190 quote! { #name }
191}
192
193fn expand_input_param(
197 fun: &Function,
198 param: &str,
199 kind: &ParamType,
200 rust_enum_name: &Option<&Property>,
201 rust_struct_name: &Option<&Property>,
202) -> Result<TokenStream, Error> {
203 match kind {
204 ParamType::Array(ty, _) => {
205 let ty = expand_input_param(fun, param, ty, rust_enum_name, rust_struct_name)?;
206 Ok(quote! {
207 ::std::vec::Vec<#ty>
208 })
209 }
210 ParamType::Enum(_) => {
211 let ident = ident(
212 &extract_struct_name_from_abi_property(rust_enum_name.unwrap()).to_class_case(),
213 );
214 Ok(quote! { #ident })
215 }
216 ParamType::Struct(_) => {
217 let ident = ident(
218 &extract_struct_name_from_abi_property(rust_struct_name.unwrap()).to_class_case(),
219 );
220 Ok(quote! { #ident })
221 }
222 _ => expand_type(kind),
224 }
225}