diff --git a/contracts/examples/crowdfunding-esdt/src/crowdfunding_esdt_proxy.rs b/contracts/examples/crowdfunding-esdt/src/crowdfunding_esdt_proxy.rs index ec3a555967..385d77ce82 100644 --- a/contracts/examples/crowdfunding-esdt/src/crowdfunding_esdt_proxy.rs +++ b/contracts/examples/crowdfunding-esdt/src/crowdfunding_esdt_proxy.rs @@ -62,17 +62,6 @@ where } } -#[rustfmt::skip] -impl CrowdfundingProxyMethods -where - Env: TxEnv, - Env::Api: VMApi, - From: TxFrom, - To: TxTo, - Gas: TxGas, -{ -} - #[rustfmt::skip] impl CrowdfundingProxyMethods where diff --git a/contracts/examples/digital-cash/src/digital_cash_proxy.rs b/contracts/examples/digital-cash/src/digital_cash_proxy.rs index e3df8cf541..644e17c671 100644 --- a/contracts/examples/digital-cash/src/digital_cash_proxy.rs +++ b/contracts/examples/digital-cash/src/digital_cash_proxy.rs @@ -59,17 +59,6 @@ where } } -#[rustfmt::skip] -impl DigitalCashProxyMethods -where - Env: TxEnv, - Env::Api: VMApi, - From: TxFrom, - To: TxTo, - Gas: TxGas, -{ -} - #[rustfmt::skip] impl DigitalCashProxyMethods where diff --git a/contracts/examples/rewards-distribution/src/seed_nft_minter_proxy.rs b/contracts/examples/rewards-distribution/src/seed_nft_minter_proxy.rs index c74e7a7e31..10047be064 100644 --- a/contracts/examples/rewards-distribution/src/seed_nft_minter_proxy.rs +++ b/contracts/examples/rewards-distribution/src/seed_nft_minter_proxy.rs @@ -59,17 +59,6 @@ where } } -#[rustfmt::skip] -impl SeedNftMinterProxyMethods -where - Env: TxEnv, - Env::Api: VMApi, - From: TxFrom, - To: TxTo, - Gas: TxGas, -{ -} - #[rustfmt::skip] impl SeedNftMinterProxyMethods where diff --git a/contracts/feature-tests/abi-tester/src/abi_proxy.rs b/contracts/feature-tests/abi-tester/src/abi_proxy.rs index bdb43be8f5..1f860018b5 100644 --- a/contracts/feature-tests/abi-tester/src/abi_proxy.rs +++ b/contracts/feature-tests/abi-tester/src/abi_proxy.rs @@ -459,28 +459,6 @@ pub struct OnlyShowsUpAsNested06 { pub struct OnlyShowsUpAsNested07 { } -#[derive(TopDecode, TopEncode, NestedDecode, NestedEncode, Clone, PartialEq, Eq, Debug, Copy)] -pub enum EsdtLocalRole { - None, - Mint, - Burn, - NftCreate, - NftAddQuantity, - NftBurn, - NftAddUri, - NftUpdateAttributes, - Transfer, -} - -#[derive(TopDecode, TopEncode, NestedDecode, NestedEncode, Clone, PartialEq, Eq, Debug, ManagedVecItem)] -pub enum EsdtTokenType { - Fungible, - NonFungible, - SemiFungible, - Meta, - Invalid, -} - #[derive(NestedEncode, NestedDecode, TopEncode, TopDecode)] pub struct OnlyShowsUpAsNestedInSingleValueMapper { } diff --git a/contracts/feature-tests/composability/execute-on-dest-esdt-issue-callback/parent/src/child_proxy.rs b/contracts/feature-tests/composability/execute-on-dest-esdt-issue-callback/parent/src/child_proxy.rs index 162f38d1a9..bb3e97cc56 100644 --- a/contracts/feature-tests/composability/execute-on-dest-esdt-issue-callback/parent/src/child_proxy.rs +++ b/contracts/feature-tests/composability/execute-on-dest-esdt-issue-callback/parent/src/child_proxy.rs @@ -52,17 +52,6 @@ where } } -#[rustfmt::skip] -impl ChildProxyMethods -where - Env: TxEnv, - Env::Api: VMApi, - From: TxFrom, - To: TxTo, - Gas: TxGas, -{ -} - #[rustfmt::skip] impl ChildProxyMethods where diff --git a/contracts/feature-tests/composability/recursive-caller/src/self_proxy.rs b/contracts/feature-tests/composability/recursive-caller/src/self_proxy.rs index 0b67327f44..bda44ef4b3 100644 --- a/contracts/feature-tests/composability/recursive-caller/src/self_proxy.rs +++ b/contracts/feature-tests/composability/recursive-caller/src/self_proxy.rs @@ -52,17 +52,6 @@ where } } -#[rustfmt::skip] -impl RecursiveCallerProxyMethods -where - Env: TxEnv, - Env::Api: VMApi, - From: TxFrom, - To: TxTo, - Gas: TxGas, -{ -} - #[rustfmt::skip] impl RecursiveCallerProxyMethods where diff --git a/contracts/feature-tests/panic-message-features/tests/pmf_proxy.rs b/contracts/feature-tests/panic-message-features/tests/pmf_proxy.rs index 9eeb6d69e8..7413bf0ed9 100644 --- a/contracts/feature-tests/panic-message-features/tests/pmf_proxy.rs +++ b/contracts/feature-tests/panic-message-features/tests/pmf_proxy.rs @@ -52,17 +52,6 @@ where } } -#[rustfmt::skip] -impl PanicMessageFeaturesProxyMethods -where - Env: TxEnv, - Env::Api: VMApi, - From: TxFrom, - To: TxTo, - Gas: TxGas, -{ -} - #[rustfmt::skip] impl PanicMessageFeaturesProxyMethods where diff --git a/framework/derive/src/type_abi_derive.rs b/framework/derive/src/type_abi_derive.rs index 56bd71d600..6799aac9d2 100644 --- a/framework/derive/src/type_abi_derive.rs +++ b/framework/derive/src/type_abi_derive.rs @@ -1,7 +1,7 @@ use crate::parse::attributes::extract_macro_attributes; use super::parse::attributes::extract_doc; -use quote::{quote, ToTokens}; +use quote::quote; pub struct ExplicitDiscriminant { pub variant_index: usize, @@ -122,17 +122,11 @@ pub fn type_abi_derive(input: proc_macro::TokenStream) -> proc_macro2::TokenStre let name = &ast.ident; let name_str = name.to_string(); let (impl_generics, ty_generics, where_clause) = &ast.generics.split_for_impl(); - let name_rust = extract_rust_type(ty_generics, name_str.clone()); quote! { impl #impl_generics multiversx_sc::abi::TypeAbi for #name #ty_generics #where_clause { fn type_name() -> multiversx_sc::abi::TypeName { #name_str.into() } - - fn type_name_rust() -> multiversx_sc::abi::TypeName { - #name_rust.into() - } - #type_description_impl } } @@ -189,15 +183,3 @@ pub fn get_discriminant( quote! { #next_value} } - -fn extract_rust_type(ty_generics: &syn::TypeGenerics<'_>, mut output_name: String) -> String { - let mut ty_generics_tokens = proc_macro2::TokenStream::new(); - ty_generics.to_tokens(&mut ty_generics_tokens); - - if ty_generics_tokens.to_string().is_empty() { - return output_name; - } - - output_name.push_str("<$API>"); - output_name -} diff --git a/framework/meta/src/cmd/contract/generate_proxy.rs b/framework/meta/src/cmd/contract/generate_proxy.rs index 2aa854bc2e..15f157de8a 100644 --- a/framework/meta/src/cmd/contract/generate_proxy.rs +++ b/framework/meta/src/cmd/contract/generate_proxy.rs @@ -1,6 +1,4 @@ pub mod proxy_crate_gen; pub mod proxy_gen_main; -pub mod proxy_gen_struct_enum; +mod proxy_generator; mod proxy_naming; -pub mod proxy_sc_functions_gen; -pub mod proxy_template_gen; diff --git a/framework/meta/src/cmd/contract/generate_proxy/proxy_gen_main.rs b/framework/meta/src/cmd/contract/generate_proxy/proxy_gen_main.rs index 2c0f730eca..a3a35062f6 100644 --- a/framework/meta/src/cmd/contract/generate_proxy/proxy_gen_main.rs +++ b/framework/meta/src/cmd/contract/generate_proxy/proxy_gen_main.rs @@ -1,39 +1,21 @@ -use std::fs::File; - -use multiversx_sc::abi::ContractAbi; - use super::{ - super::meta_config::MetaConfig, - proxy_crate_gen::create_file, - proxy_gen_struct_enum::write_types, - proxy_sc_functions_gen::write_content, - proxy_template_gen::{ - write_header, write_impl_for_tx_proxy, write_struct_tx_proxy_methods, - write_tx_proxy_type_def, - }, + super::meta_config::MetaConfig, proxy_crate_gen::create_file, proxy_generator::ProxyGenerator, }; const OUTPUT_PROXY_PATH: &str = "/output/proxy.rs"; impl MetaConfig { - pub fn generate_proxy(&self) { - write_proxy_with_explicit_path(OUTPUT_PROXY_PATH, &self.original_contract_abi); - for path in &self.sc_config.proxy_paths { - write_proxy_with_explicit_path(path, &self.original_contract_abi); + pub fn generate_proxy(&mut self) { + write_proxy_with_explicit_path(OUTPUT_PROXY_PATH, self); + let proxy_paths = self.sc_config.proxy_paths.clone(); + for path in proxy_paths { + write_proxy_with_explicit_path(&path, self); } } } -fn write_proxy_with_explicit_path(path: &str, abi: &ContractAbi) { - let file = create_file(path); - write_proxy_to_file(file, abi); -} - -fn write_proxy_to_file(mut file: File, abi: &ContractAbi) { - write_header(&mut file); - write_tx_proxy_type_def(&mut file, &abi.name); - write_impl_for_tx_proxy(&mut file, &abi.name); - write_struct_tx_proxy_methods(&mut file, &abi.name); - write_content(&mut file, abi.clone()); - write_types(&mut file, &abi.type_descriptions); +fn write_proxy_with_explicit_path(path: &str, meta_config: &mut MetaConfig) { + let mut file = create_file(path); + let mut proxy_generator = ProxyGenerator::new(meta_config, &mut file); + proxy_generator.write_proxy_to_file(); } diff --git a/framework/meta/src/cmd/contract/generate_proxy/proxy_gen_struct_enum.rs b/framework/meta/src/cmd/contract/generate_proxy/proxy_gen_struct_enum.rs deleted file mode 100644 index b82b071e16..0000000000 --- a/framework/meta/src/cmd/contract/generate_proxy/proxy_gen_struct_enum.rs +++ /dev/null @@ -1,135 +0,0 @@ -use std::{fs::File, io::Write}; - -use multiversx_sc::abi::{ - EnumVariantDescription, StructFieldDescription, TypeContents, TypeDescription, - TypeDescriptionContainerImpl, -}; - -const ZERO: &str = "0"; - -/// Types defined in the framework don't need to be generated again in the proxy. -const TYPES_FROM_FRAMEWORK: &[&str] = &[ - "EsdtTokenPayment<$API>", - "EgldOrEsdtTokenPayment<$API>", - "EsdtTokenData<$API>", - "EgldOrEsdtTokenIdentifier<$API>", - "EgldOrEsdtTokenPayment<$API>", - "EgldOrMultiEsdtPayment<$API>", - "EsdtTokenData<$API>", -]; - -pub(crate) fn write_types(file: &mut File, types: &TypeDescriptionContainerImpl) { - for (_, type_description) in &types.0 { - if TYPES_FROM_FRAMEWORK.contains(&type_description.names.rust.as_str()) { - continue; - } - - match &type_description.contents { - TypeContents::Enum(enum_variants) => write_enum(file, enum_variants, type_description), - TypeContents::Struct(struct_fields) => { - write_struct(file, struct_fields, type_description) - }, - TypeContents::NotSpecified => {}, - TypeContents::ExplicitEnum(_) => {}, - } - } -} - -fn start_write_type(file: &mut File, type_type: &str, type_description: &TypeDescription) { - writeln!(file).unwrap(); - let type_name = type_description.names.rust.replace("$API", "Api"); - write_macro_attributes(file, &type_description.macro_attributes); - write!(file, r#"pub {type_type} {type_name}"#).unwrap(); - - if type_name.contains("") { - writeln!( - file, - r#" -where - Api: ManagedTypeApi,"# - ) - .unwrap(); - } else { - write!(file, " ").unwrap(); - } - - writeln!(file, r#"{{"#).unwrap(); -} - -fn write_struct( - file: &mut File, - struct_fields: &Vec, - type_description: &TypeDescription, -) { - start_write_type(file, "struct", type_description); - - for field in struct_fields { - writeln!( - file, - " pub {}: {},", - field.name, - field.field_type.rust.replace("$API", "Api") - ) - .unwrap(); - } - - writeln!(file, "}}").unwrap(); -} - -fn write_enum( - file: &mut File, - enum_variants: &Vec, - type_description: &TypeDescription, -) { - start_write_type(file, "enum", type_description); - - for variant in enum_variants { - write!(file, " {}", variant.name).unwrap(); - if variant.fields.is_empty() { - writeln!(file, ",").unwrap(); - continue; - } - - if variant.fields[0].name == ZERO { - write_tuple_in_variant(file, &variant.fields); - } else { - write_struct_in_variant(file, &variant.fields); - } - } - writeln!(file, "}}").unwrap(); -} - -fn write_macro_attributes(file: &mut File, macro_attributes: &[String]) { - if macro_attributes.is_empty() { - writeln!(file, "#[derive(TopEncode, TopDecode)]").unwrap(); - } else { - writeln!(file, "#[derive({})]", macro_attributes.join(", ")).unwrap(); - } -} - -fn write_struct_in_variant(file: &mut File, fields: &[StructFieldDescription]) { - writeln!(file, " {{").unwrap(); - - for field in fields { - writeln!( - file, - " {}: {},", - field.name, - field.field_type.rust.replace("$API", "Api") - ) - .unwrap(); - } - - writeln!(file, " }},").unwrap(); -} - -fn write_tuple_in_variant(file: &mut File, fields: &[StructFieldDescription]) { - write!(file, "(").unwrap(); - write!(file, "{}", fields[0].field_type.rust.replace("$API", "Api")).unwrap(); - - for field in &fields[1..] { - write!(file, ", {}", field.field_type.rust.replace("$API", "Api")).unwrap(); - } - - writeln!(file, "),").unwrap(); -} diff --git a/framework/meta/src/cmd/contract/generate_proxy/proxy_generator.rs b/framework/meta/src/cmd/contract/generate_proxy/proxy_generator.rs new file mode 100644 index 0000000000..4528f53af6 --- /dev/null +++ b/framework/meta/src/cmd/contract/generate_proxy/proxy_generator.rs @@ -0,0 +1,632 @@ +use std::{fmt::Display, fs::File, io::Write}; + +use multiversx_sc::abi::{ + EndpointAbi, EnumVariantDescription, InputAbi, OutputAbi, StructFieldDescription, TypeContents, + TypeDescription, +}; + +use crate::cmd::contract::meta_config::MetaConfig; + +use super::proxy_naming::{extract_struct_crate, proxy_methods_type_name, proxy_type_name}; + +const PRELUDE: &str = "// Code generated by the multiversx-sc proxy generator. DO NOT EDIT. + +//////////////////////////////////////////////////// +////////////////// AUTO-GENERATED ////////////////// +//////////////////////////////////////////////////// + +#![allow(dead_code)] +#![allow(clippy::all)] + +use multiversx_sc::proxy_imports::*;"; + +const ZERO: &str = "0"; + +/// Types defined in the framework don't need to be generated again in the proxy. +const TYPES_FROM_FRAMEWORK: &[&str] = &[ + "EsdtTokenPayment", + "EgldOrEsdtTokenPayment", + "EsdtTokenData", + "EgldOrEsdtTokenIdentifier", + "EgldOrEsdtTokenPayment", + "EgldOrMultiEsdtPayment", + "EsdtTokenData", + "EsdtLocalRole", +]; + +pub struct ProxyGenerator<'a> { + pub meta_config: &'a MetaConfig, + pub file: Option<&'a mut File>, +} +impl<'a> ProxyGenerator<'a> { + pub fn new(meta_config: &'a MetaConfig, file: &'a mut File) -> Self { + Self { + meta_config, + file: Some(file), + } + } + + fn write(&mut self, s: impl Display) { + let file = self.file.as_mut().expect("output not configured"); + write!(*file, "{s}").unwrap(); + } + + fn writeln(&mut self, s: impl Display) { + self.write(s); + self.write("\n"); + } + + pub fn write_proxy_to_file(&mut self) { + self.write_header(); + self.write_tx_proxy_type_def(); + self.write_impl_for_tx_proxy(); + self.write_struct_tx_proxy_methods(); + self.write_content(); + self.write_types(); + } + + fn write_header(&mut self) { + self.writeln(PRELUDE); + } + + fn write_tx_proxy_type_def(&mut self) { + let proxy_type_name = proxy_type_name(&self.meta_config.original_contract_abi.name); + self.writeln(format!( + r#" +pub struct {proxy_type_name};"# + )); + } + + fn write_impl_for_tx_proxy(&mut self) { + let proxy_type_name = proxy_type_name(&self.meta_config.original_contract_abi.name); + let proxy_methods_type_name = + proxy_methods_type_name(&self.meta_config.original_contract_abi.name); + self.writeln(format!( + r#" +impl TxProxyTrait for {proxy_type_name} +where + Env: TxEnv, + From: TxFrom, + To: TxTo, + Gas: TxGas, +{{ + type TxProxyMethods = {proxy_methods_type_name}; + + fn proxy_methods(self, tx: Tx) -> Self::TxProxyMethods {{ + {proxy_methods_type_name} {{ wrapped_tx: tx }} + }} +}}"# + )); + } + + fn write_struct_tx_proxy_methods(&mut self) { + let proxy_methods_type_name = + proxy_methods_type_name(&self.meta_config.original_contract_abi.name); + self.writeln(format!( + r#" +pub struct {proxy_methods_type_name} +where + Env: TxEnv, + From: TxFrom, + To: TxTo, + Gas: TxGas, +{{ + wrapped_tx: Tx, +}}"# + )); + } + + fn write_content(&mut self) { + if !self + .meta_config + .original_contract_abi + .constructors + .is_empty() + { + self.write_constructors(); + } + + if !self + .meta_config + .original_contract_abi + .upgrade_constructors + .is_empty() + { + self.write_upgrades(); + } + + if !self.meta_config.original_contract_abi.endpoints.is_empty() { + self.write_endpoints(); + } + } + + fn write_types(&mut self) { + for (_, type_description) in &self.meta_config.original_contract_abi.type_descriptions.0 { + if self + .meta_config + .original_contract_abi + .get_crate_name_for_code() + != extract_struct_crate(type_description.names.rust.as_str()) + { + continue; + } + + let type_name = self.adjust_type_name_with_api(&type_description.names.rust); + if TYPES_FROM_FRAMEWORK.contains(&type_name.as_str()) { + continue; + } + + match &type_description.contents { + TypeContents::Enum(enum_variants) => { + self.write_enum(enum_variants, type_description, &type_name) + }, + TypeContents::Struct(struct_fields) => { + self.write_struct(struct_fields, type_description, &type_name) + }, + TypeContents::NotSpecified => {}, + TypeContents::ExplicitEnum(_) => {}, + } + } + } + + fn write_constructors(&mut self) { + let constructors: Vec = + self.meta_config.original_contract_abi.constructors.clone(); + + self.write_header_impl_constructor(); + for (i, constructor_abi) in constructors.into_iter().enumerate() { + if i > 0 { + self.writeln(""); + } + self.write_constructor_header(&constructor_abi); + self.write_constructor_content(constructor_abi.inputs); + self.write_end_of_function(); + } + + self.writeln("}"); + } + + fn write_upgrades(&mut self) { + self.write_header_impl_upgrade(); + for (i, upgrade) in self + .meta_config + .original_contract_abi + .upgrade_constructors + .clone() + .into_iter() + .enumerate() + { + if i > 0 { + self.writeln(""); + } + self.write_upgrade_header(&upgrade); + self.write_upgrade_content(upgrade.inputs); + self.write_end_of_function(); + } + + self.writeln("}"); + } + + fn write_endpoints(&mut self) { + let endpoints: Vec = self.meta_config.original_contract_abi.endpoints.clone(); + + self.write_header_impl_endpoints(); + for (i, endpoint_abi) in endpoints.into_iter().enumerate() { + if i > 0 { + self.writeln(""); + } + self.write_endpoint_header(&endpoint_abi); + self.write_endpoint_content(&endpoint_abi); + self.write_end_of_function(); + } + + self.writeln("}"); + } + + fn write_header_impl_constructor(&mut self) { + let proxy_methods_type_name = + proxy_methods_type_name(&self.meta_config.original_contract_abi.name); + self.writeln(format!( + r#" +#[rustfmt::skip] +impl {proxy_methods_type_name} +where + Env: TxEnv, + Env::Api: VMApi, + From: TxFrom, + Gas: TxGas, +{{"# + )); + } + + fn write_header_impl_upgrade(&mut self) { + let proxy_methods_type_name = + proxy_methods_type_name(&self.meta_config.original_contract_abi.name); + self.writeln(format!( + r#" +#[rustfmt::skip] +impl {proxy_methods_type_name} +where + Env: TxEnv, + Env::Api: VMApi, + From: TxFrom, + To: TxTo, + Gas: TxGas, +{{"# + )); + } + + fn write_header_impl_endpoints(&mut self) { + let proxy_methods_type_name = + proxy_methods_type_name(&self.meta_config.original_contract_abi.name); + self.writeln(format!( + r#" +#[rustfmt::skip] +impl {proxy_methods_type_name} +where + Env: TxEnv, + Env::Api: VMApi, + From: TxFrom, + To: TxTo, + Gas: TxGas, +{{"# + )); + } + + fn write_constructor_header(&mut self, constructor_abi: &EndpointAbi) { + self.write_fn_signature(constructor_abi); + self.write_constructor_output(&constructor_abi.outputs); + } + + fn write_upgrade_header(&mut self, constructor_abi: &EndpointAbi) { + self.write_fn_signature(constructor_abi); + self.write_upgrade_output(&constructor_abi.outputs); + } + + fn write_endpoint_header(&mut self, constructor_abi: &EndpointAbi) { + self.write_fn_signature(constructor_abi); + self.write_endpoint_output(&constructor_abi.outputs); + } + + fn write_constructor_content(&mut self, inputs: Vec) { + self.writeln( + " self.wrapped_tx + .raw_deploy()", + ); + for input in inputs.iter() { + self.writeln(format!(" .argument(&{})", input.arg_name)); + } + self.writeln(" .original_result()"); + } + + fn write_upgrade_content(&mut self, inputs: Vec) { + self.writeln( + " self.wrapped_tx + .raw_upgrade()", + ); + for input in inputs.iter() { + self.writeln(format!(" .argument(&{})", input.arg_name)); + } + self.writeln(" .original_result()"); + } + + fn write_endpoint_content(&mut self, endpoint: &EndpointAbi) { + self.writeln(format!( + " self.wrapped_tx + .raw_call(\"{}\")", + endpoint.name + )); + + for input in endpoint.inputs.iter() { + self.writeln(format!(" .argument(&{})", input.arg_name)); + } + + self.writeln(" .original_result()"); + } + + fn write_fn_signature(&mut self, endpoint: &EndpointAbi) { + self.write_endpoint_docs(&endpoint.docs); + self.write_function_header_endpoint(&endpoint.rust_method_name); + self.write_args(&endpoint.inputs); + self.write_parameters(&endpoint.inputs); + } + + fn write_endpoint_docs(&mut self, docs: &Vec) { + for doc in docs { + self.writeln(format!(" /// {doc} ")); + } + } + + fn write_function_header_endpoint(&mut self, rust_method_name: &String) { + self.write(format!(" pub fn {rust_method_name}")); + } + + fn write_args(&mut self, inputs: &[InputAbi]) { + if inputs.is_empty() { + return; + } + + self.writeln("<"); + + for (index, input) in inputs.iter().enumerate() { + self.write_argument(index, &input.type_names.rust); + } + + self.write(" >"); + } + + fn write_argument(&mut self, index: usize, rust_name: &str) { + let adjusted = self.adjust_type_name_with_env_api(rust_name); + self.writeln(format!(" Arg{index}: CodecInto<{adjusted}>,")); + } + + fn write_parameters(&mut self, inputs: &[InputAbi]) { + self.writeln("("); + self.writeln(" self,"); + for (index, input) in inputs.iter().enumerate() { + self.writeln(format!(" {}: Arg{index},", &input.arg_name)); + } + self.write(" ) "); + } + + fn write_constructor_output(&mut self, outputs: &[OutputAbi]) { + self.write("-> TxProxyDeploy {"); + } + + fn write_upgrade_output(&mut self, outputs: &[OutputAbi]) { + self.write("-> TxProxyUpgrade {"); + } + + fn write_endpoint_output(&mut self, outputs: &[OutputAbi]) { + self.write("-> TxProxyCall {"); + } + + fn parse_and_write_outputs(&mut self, outputs: &[OutputAbi]) { + match outputs.len() { + 0 => { + self.write("()"); + }, + 1 => { + let adjusted = self.adjust_type_name_with_env_api(&outputs[0].type_names.rust); + self.write(adjusted); + }, + _ => { + self.write(format!("MultiValue{}<", outputs.len())); + for (i, output) in outputs.iter().enumerate() { + if i > 0 { + self.write(", "); + } + let adjusted = self.adjust_type_name_with_env_api(&output.type_names.rust); + self.write(adjusted); + } + self.write(">"); + }, + } + } + + fn write_enum( + &mut self, + enum_variants: &Vec, + type_description: &TypeDescription, + name: &str, + ) { + self.start_write_type("enum", type_description, name); + + for variant in enum_variants { + self.write(format!(" {}", variant.name)); + if variant.fields.is_empty() { + self.writeln(","); + continue; + } + + if variant.fields[0].name == ZERO { + self.write_tuple_in_variant(&variant.fields); + } else { + self.write_struct_in_variant(&variant.fields); + } + } + self.writeln("}"); + } + + fn write_struct( + &mut self, + struct_fields: &Vec, + type_description: &TypeDescription, + name: &str, + ) { + self.start_write_type("struct", type_description, name); + + for field in struct_fields { + let adjusted_type_name = self.adjust_type_name_with_api(&field.field_type.rust); + self.writeln(format!(" pub {}: {adjusted_type_name},", field.name)); + } + + self.writeln("}"); + } + + fn write_tuple_in_variant(&mut self, fields: &[StructFieldDescription]) { + self.write("("); + for (i, field) in fields.iter().enumerate() { + if i > 0 { + self.write(", "); + } + let adjusted_type_name = self.adjust_type_name_with_api(&field.field_type.rust); + self.write(adjusted_type_name); + } + + self.writeln("),"); + } + + fn write_struct_in_variant(&mut self, fields: &[StructFieldDescription]) { + self.writeln(" {"); + + for field in fields { + let adjusted_type_name = self.adjust_type_name_with_api(&field.field_type.rust); + self.writeln(format!(" {}: {adjusted_type_name},", field.name,)); + } + + self.writeln(" },"); + } + + pub fn clean_paths(&mut self, rust_type: &str) -> String { + let delimiters = "<>,()[] "; + let words: Vec<&str> = rust_type + .split(|c| delimiters.contains(c)) + .filter(|s| !s.is_empty()) + .collect(); + + let crate_name = self + .meta_config + .original_contract_abi + .get_crate_name_for_code(); + let mut words_replacer: Vec = Vec::new(); + for word in &words { + let type_rust_name = word.split("::").last().unwrap(); + if crate_name == extract_struct_crate(word) + || TYPES_FROM_FRAMEWORK.contains(&type_rust_name) + { + words_replacer.push(type_rust_name.to_string()); + } else { + words_replacer.push(word.to_string()); + } + } + + let mut rust_type_with_cleaned_path: String = rust_type.to_string().clone(); + for index in 0..words.len() { + rust_type_with_cleaned_path = rust_type_with_cleaned_path.replace( + words.get(index).unwrap(), + words_replacer.get(index).unwrap(), + ); + } + + rust_type_with_cleaned_path + } + + fn start_write_type( + &mut self, + type_type: &str, + type_description: &TypeDescription, + name: &str, + ) { + self.writeln(""); + self.write_macro_attributes(&type_description.macro_attributes); + self.write(format!(r#"pub {type_type} {name}"#)); + + if name.contains("") { + self.writeln( + " +where + Api: ManagedTypeApi,", + ); + } else { + self.write(" "); + } + + self.writeln("{"); + } + + pub fn write_macro_attributes(&mut self, macro_attributes: &[String]) { + if macro_attributes.is_empty() { + self.writeln("#[derive(TopEncode, TopDecode)]"); + } else { + self.writeln(format!("#[derive({})]", macro_attributes.join(", "))); + } + } + + fn adjust_type_name_with_env_api(&mut self, original_rust_name: &str) -> String { + self.clean_paths( + &original_rust_name + .replace("multiversx_sc::api::uncallable::UncallableApi", "Env::Api") + .replace("$API", "Env::Api"), + ) + } + + fn adjust_type_name_with_api(&mut self, original_rust_name: &str) -> String { + self.clean_paths( + &original_rust_name + .replace("multiversx_sc::api::uncallable::UncallableApi", "Api") + .replace("$API", "Api"), + ) + } + + fn write_end_of_function(&mut self) { + self.writeln(" }"); + } +} + +#[cfg(test)] +pub mod tests { + use multiversx_sc::abi::{BuildInfoAbi, ContractAbi, ContractCrateBuildAbi, FrameworkBuildAbi}; + + use crate::cmd::contract::meta_config::MetaConfig; + + use super::ProxyGenerator; + + #[test] + fn clean_paths_unsanitized_test() { + let build_info = BuildInfoAbi { + contract_crate: ContractCrateBuildAbi { + name: "contract-crate", + version: "0.0.0", + git_version: "0.0.0", + }, + framework: FrameworkBuildAbi::create(), + }; + + let original_contract_abi = ContractAbi::new(build_info, &[""], "contract-crate", false); + let meta_config = MetaConfig::create(original_contract_abi, false); + let mut proxy_generator = ProxyGenerator { + meta_config: &meta_config, + file: None, + }; + + let cleaned_path_unsanitized = proxy_generator.clean_paths( + "(other_crate::contract_crate::TestStruct, Option>)", + ); + let expected_result_unsanitized = + "(other_crate::contract_crate::TestStruct, Option>)"; + + assert_eq!( + expected_result_unsanitized, + cleaned_path_unsanitized.as_str() + ); + } + + #[test] + fn clean_paths_sanitized_test() { + let build_info = BuildInfoAbi { + contract_crate: ContractCrateBuildAbi { + name: "contract-crate", + version: "0.0.0", + git_version: "0.0.0", + }, + framework: FrameworkBuildAbi::create(), + }; + + let original_contract_abi = ContractAbi::new(build_info, &[""], "contract-crate", false); + let meta_config = MetaConfig::create(original_contract_abi, false); + let mut proxy_generator = ProxyGenerator { + meta_config: &meta_config, + file: None, + }; + + let cleaned_path_sanitized = proxy_generator.clean_paths( + "(contract_crate::other_crate::TestStruct, Option>)", + ); + let expected_result_sanitized = "(TestStruct, Option>)"; + + assert_eq!(expected_result_sanitized, cleaned_path_sanitized.as_str()); + } +} diff --git a/framework/meta/src/cmd/contract/generate_proxy/proxy_naming.rs b/framework/meta/src/cmd/contract/generate_proxy/proxy_naming.rs index e71a6a0e64..3c4f1dac2e 100644 --- a/framework/meta/src/cmd/contract/generate_proxy/proxy_naming.rs +++ b/framework/meta/src/cmd/contract/generate_proxy/proxy_naming.rs @@ -5,3 +5,8 @@ pub(super) fn proxy_type_name(contract_trait_name: &str) -> String { pub(super) fn proxy_methods_type_name(contract_trait_name: &str) -> String { format!("{contract_trait_name}ProxyMethods") } + +pub(super) fn extract_struct_crate(struct_path: &str) -> String { + let crate_name = struct_path.split("::").next().unwrap_or(struct_path); + crate_name.to_string() +} diff --git a/framework/meta/src/cmd/contract/generate_proxy/proxy_sc_functions_gen.rs b/framework/meta/src/cmd/contract/generate_proxy/proxy_sc_functions_gen.rs deleted file mode 100644 index bb79b507c5..0000000000 --- a/framework/meta/src/cmd/contract/generate_proxy/proxy_sc_functions_gen.rs +++ /dev/null @@ -1,256 +0,0 @@ -use std::{fs::File, io::Write}; - -use multiversx_sc::abi::{ContractAbi, EndpointAbi, InputAbi, OutputAbi}; - -use super::proxy_naming::proxy_methods_type_name; - -pub(crate) fn write_content(file: &mut File, abi: ContractAbi) { - write_header_impl_constructor(file, &abi.name); - for (i, constructor_abi) in abi.constructors.into_iter().enumerate() { - if i > 0 { - writeln!(file).unwrap(); - } - write_constructor_header(file, constructor_abi.clone()); - write_constructor_content(file, constructor_abi.inputs); - write_end_of_function(file); - } - - writeln!(file, "}}").unwrap(); - - write_header_impl_upgrade_constructor(file, &abi.name); - for (i, upgrade_abi) in abi.upgrade_constructors.clone().into_iter().enumerate() { - if i > 0 { - writeln!(file).unwrap(); - } - write_upgrade_constructor_header(file, upgrade_abi.clone()); - write_upgrade_constructor_content(file, upgrade_abi.inputs); - write_end_of_function(file); - } - - writeln!(file, "}}").unwrap(); - - write_header_impl_endpoints(file, &abi.name); - for (i, endpoint_abi) in abi.endpoints.into_iter().enumerate() { - if i > 0 { - writeln!(file).unwrap(); - } - write_endpoint_header(file, endpoint_abi.clone()); - write_endpoint_content(file, endpoint_abi.name, endpoint_abi.inputs); - write_end_of_function(file); - } - - writeln!(file, "}}").unwrap(); -} - -fn write_header_impl_constructor(file: &mut File, name: &str) { - let proxy_methods_type_name = proxy_methods_type_name(name); - writeln!( - file, - r#" -#[rustfmt::skip] -impl {proxy_methods_type_name} -where - Env: TxEnv, - Env::Api: VMApi, - From: TxFrom, - Gas: TxGas, -{{"# - ) - .unwrap(); -} - -fn write_header_impl_upgrade_constructor(file: &mut File, name: &str) { - let proxy_methods_type_name = proxy_methods_type_name(name); - writeln!( - file, - r#" -#[rustfmt::skip] -impl {proxy_methods_type_name} -where - Env: TxEnv, - Env::Api: VMApi, - From: TxFrom, - To: TxTo, - Gas: TxGas, -{{"# - ) - .unwrap(); -} - -fn write_header_impl_endpoints(file: &mut File, name: &str) { - let proxy_methods_type_name = proxy_methods_type_name(name); - writeln!( - file, - r#" -#[rustfmt::skip] -impl {proxy_methods_type_name} -where - Env: TxEnv, - Env::Api: VMApi, - From: TxFrom, - To: TxTo, - Gas: TxGas, -{{"# - ) - .unwrap(); -} - -fn write_constructor_header(file: &mut File, constructor_abi: EndpointAbi) { - write_fn_signature(file, constructor_abi.clone()); - write_constructor_output(file, constructor_abi.outputs); -} - -fn write_upgrade_constructor_header(file: &mut File, upgrade_constructor_abi: EndpointAbi) { - write_fn_signature(file, upgrade_constructor_abi.clone()); - write_upgrade_constructor_output(file, upgrade_constructor_abi.outputs); -} - -fn write_endpoint_header(file: &mut File, constructor_abi: EndpointAbi) { - write_fn_signature(file, constructor_abi.clone()); - write_endpoint_output(file, constructor_abi.outputs); -} - -fn write_fn_signature(file: &mut File, endpoint_abi: EndpointAbi) { - write_endpoint_docs(file, endpoint_abi.docs); - write_function_header_endpoint(file, endpoint_abi.rust_method_name); - write_args(file, endpoint_abi.inputs.clone()); - write_parameters(file, endpoint_abi.inputs); -} - -fn write_parameters(file: &mut File, inputs: Vec) { - writeln!(file, "(").unwrap(); - writeln!(file, " self,").unwrap(); - for (index, input) in inputs.iter().enumerate() { - writeln!(file, " {}: Arg{index},", &input.arg_name).unwrap(); - } - write!(file, " ) ").unwrap(); -} - -fn write_constructor_output(file: &mut File, outputs: Vec) { - write!(file, "-> TxProxyDeploy {{").unwrap(); -} - -fn write_upgrade_constructor_output(file: &mut File, outputs: Vec) { - write!(file, "-> TxProxyUpgrade {{").unwrap(); -} - -fn write_endpoint_output(file: &mut File, outputs: Vec) { - write!(file, "-> TxProxyCall {{").unwrap(); -} - -fn write_constructor_content(file: &mut File, inputs: Vec) { - writeln!( - file, - " self.wrapped_tx - .raw_deploy()" - ) - .unwrap(); - for input in inputs.iter() { - writeln!(file, " .argument(&{})", input.arg_name).unwrap(); - } - writeln!(file, " .original_result()").unwrap(); -} - -fn write_upgrade_constructor_content(file: &mut File, inputs: Vec) { - writeln!( - file, - " self.wrapped_tx - .raw_upgrade()" - ) - .unwrap(); - for input in inputs.iter() { - writeln!(file, " .argument(&{})", input.arg_name).unwrap(); - } - writeln!(file, " .original_result()").unwrap(); -} - -fn write_endpoint_content(file: &mut File, function_name: String, inputs: Vec) { - writeln!( - file, - " self.wrapped_tx - .raw_call(\"{}\")", - function_name - ) - .unwrap(); - - for input in inputs.iter() { - writeln!(file, " .argument(&{})", input.arg_name).unwrap(); - } - - writeln!(file, " .original_result()").unwrap(); -} - -fn write_function_header_endpoint(file: &mut File, rust_method_name: String) { - write!(file, " pub fn {rust_method_name}").unwrap(); -} - -fn write_endpoint_docs(file: &mut File, docs: Vec) { - for abi_doc in docs { - writeln!(file, " /// {abi_doc} ").unwrap(); - } -} - -fn write_args(file: &mut File, inputs: Vec) { - if inputs.is_empty() { - return; - } - - writeln!(file, "<").unwrap(); - - for (index, input) in inputs.iter().enumerate() { - write_argument(file, index, &input.type_names.rust); - } - - write!(file, " >").unwrap(); -} - -fn write_argument(file: &mut File, index: usize, rust_name: &str) { - let adjusted = adjust_type_name(rust_name); - writeln!(file, " Arg{index}: CodecInto<{adjusted}>,").unwrap(); -} - -fn write_end_of_function(file: &mut File) { - writeln!(file, " }}").unwrap(); -} - -fn adjust_type_name(original_rust_name: &str) -> String { - original_rust_name - .replace("multiversx_sc::api::uncallable::UncallableApi", "Env::Api") - .replace("$API", "Env::Api") - .to_string() -} - -fn parse_and_write_outputs(file: &mut File, outputs: Vec) { - match outputs.len() { - 0 => { - write!(file, "()").unwrap(); - }, - 1 => { - let adjusted = adjust_type_name(&outputs[0].type_names.rust); - write!(file, "{adjusted}").unwrap(); - }, - _ => { - write!(file, "MultiValue{}<", outputs.len()).unwrap(); - for (i, output) in outputs.iter().enumerate() { - if i > 0 { - write!(file, ", ").unwrap(); - } - let adjusted = adjust_type_name(&output.type_names.rust); - write!(file, "{adjusted}").unwrap(); - } - write!(file, ">").unwrap(); - }, - } -} diff --git a/framework/meta/src/cmd/contract/generate_proxy/proxy_template_gen.rs b/framework/meta/src/cmd/contract/generate_proxy/proxy_template_gen.rs deleted file mode 100644 index 6145315233..0000000000 --- a/framework/meta/src/cmd/contract/generate_proxy/proxy_template_gen.rs +++ /dev/null @@ -1,69 +0,0 @@ -use std::{fs::File, io::Write}; - -use super::proxy_naming::{proxy_methods_type_name, proxy_type_name}; - -const PRELUDE: &str = "// Code generated by the multiversx-sc proxy generator. DO NOT EDIT. - -//////////////////////////////////////////////////// -////////////////// AUTO-GENERATED ////////////////// -//////////////////////////////////////////////////// - -#![allow(dead_code)] -#![allow(clippy::all)] - -use multiversx_sc::proxy_imports::*;"; - -pub(crate) fn write_header(file: &mut File) { - writeln!(file, r#"{PRELUDE}"#).unwrap(); -} - -pub(crate) fn write_tx_proxy_type_def(file: &mut File, name: &str) { - let proxy_type_name = proxy_type_name(name); - writeln!( - file, - " -pub struct {proxy_type_name};" - ) - .unwrap(); -} - -pub(crate) fn write_impl_for_tx_proxy(file: &mut File, name: &str) { - let proxy_type_name = proxy_type_name(name); - let proxy_methods_type_name = proxy_methods_type_name(name); - writeln!( - file, - r#" -impl TxProxyTrait for {proxy_type_name} -where - Env: TxEnv, - From: TxFrom, - To: TxTo, - Gas: TxGas, -{{ - type TxProxyMethods = {proxy_methods_type_name}; - - fn proxy_methods(self, tx: Tx) -> Self::TxProxyMethods {{ - {proxy_methods_type_name} {{ wrapped_tx: tx }} - }} -}}"# - ) - .unwrap(); -} - -pub(crate) fn write_struct_tx_proxy_methods(file: &mut File, name: &str) { - let proxy_methods_type_name = proxy_methods_type_name(name); - writeln!( - file, - r#" -pub struct {proxy_methods_type_name} -where - Env: TxEnv, - From: TxFrom, - To: TxTo, - Gas: TxGas, -{{ - wrapped_tx: Tx, -}}"# - ) - .unwrap(); -}