diff --git a/crates/fce-test-macro-impl/Cargo.toml b/crates/fce-test-macro-impl/Cargo.toml index 28541a6..a3dbf50 100644 --- a/crates/fce-test-macro-impl/Cargo.toml +++ b/crates/fce-test-macro-impl/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fluence-sdk-test-macro-impl" -version = "0.1.4" # remember to update html_root_url +version = "0.1.5" # remember to update html_root_url edition = "2018" description = "Implementation of the `#[fce_test]` macro" repository = "https://github.com/fluencelabs/rust-sdk/crates/macro-test" diff --git a/crates/fce-test-macro-impl/src/fce_test/module_generator.rs b/crates/fce-test-macro-impl/src/fce_test/module_generator.rs index 06356c3..904c1c5 100644 --- a/crates/fce-test-macro-impl/src/fce_test/module_generator.rs +++ b/crates/fce-test-macro-impl/src/fce_test/module_generator.rs @@ -15,6 +15,7 @@ */ mod methods_generator; +mod methods_generator_utils; mod record_type_generator; use crate::fce_test::utils; diff --git a/crates/fce-test-macro-impl/src/fce_test/module_generator/methods_generator.rs b/crates/fce-test-macro-impl/src/fce_test/module_generator/methods_generator.rs index 0fbb3dc..61e4c3a 100644 --- a/crates/fce-test-macro-impl/src/fce_test/module_generator/methods_generator.rs +++ b/crates/fce-test-macro-impl/src/fce_test/module_generator/methods_generator.rs @@ -14,149 +14,30 @@ * limitations under the License. */ -use crate::fce_test::utils; +use super::methods_generator_utils::*; use crate::TResult; -use crate::TestGeneratorError; -use fce_wit_parser::interface::it::IType; -use fce_wit_parser::interface::it::IFunctionArg; use fce_wit_parser::interface::FCERecordTypes; use fce_wit_parser::interface::FCEFunctionSignature; -use proc_macro2::TokenStream; -use quote::quote; - pub(super) fn generate_module_methods<'m, 'r>( module_name: &str, - method_signatures: impl ExactSizeIterator, + mut method_signatures: impl ExactSizeIterator, records: &'r FCERecordTypes, -) -> TResult> { - method_signatures - .map(|signature| -> TResult<_> { - let func_name = utils::new_ident(&signature.name)?; - let arguments = generate_arguments(signature.arguments.iter(), records)?; - let output_type = generate_output_type(&signature.outputs, records)?; - let fce_call = generate_fce_call(module_name, &signature, records)?; +) -> TResult> { + use CallParametersSettings::*; - let module_method = quote! { - pub fn #func_name(&mut self, #(#arguments),*) #output_type { - #fce_call - } - }; + let methods_count = 2 * method_signatures.len(); + method_signatures.try_fold::<_, _, TResult<_>>( + Vec::with_capacity(methods_count), + |mut methods, signature| { + let default_cp = generate_module_method(module_name, &signature, Default, records)?; + let user_cp = generate_module_method(module_name, &signature, UserDefined, records)?; - Ok(module_method) - }) - .collect::>>() -} - -fn generate_fce_call( - module_name: &str, - method_signature: &FCEFunctionSignature, - records: &FCERecordTypes, -) -> TResult { - let args = method_signature.arguments.iter().map(|a| a.name.as_str()); - let convert_arguments = generate_arguments_converter(args)?; - - let output_type = get_output_type(&method_signature.outputs)?; - let set_result = generate_set_result(&output_type); - let function_call = generate_function_call(module_name, &method_signature.name); - let convert_result_to_output_type = generate_convert_to_output(&output_type, records)?; - let ret = generate_ret(&output_type); - - let function_call = quote! { - use std::ops::DerefMut; - - #convert_arguments - - #set_result #function_call - - #convert_result_to_output_type - - #ret - }; - - Ok(function_call) -} - -/// Generates type convertor to json because of AppService receives them in json. -fn generate_arguments_converter<'a>( - args: impl ExactSizeIterator, -) -> TResult { - let arg_idents: Vec = args.map(utils::new_ident).collect::>()?; - - let args_converter = - quote! { let arguments = fluence_test::internal::serde_json::json!([#(#arg_idents),*]); }; - - Ok(args_converter) -} - -fn generate_function_call(module_name: &str, method_name: &str) -> TokenStream { - quote! { self.fce.as_ref().borrow_mut().call_module(#module_name, #method_name, arguments, <_>::default()).expect("call to FCE failed"); } -} - -fn generate_set_result(output_type: &Option<&IType>) -> TokenStream { - match output_type { - Some(_) => quote! { let result = }, - None => TokenStream::new(), - } -} - -fn generate_convert_to_output( - output_type: &Option<&IType>, - records: &FCERecordTypes, -) -> TResult { - let result_stream = match output_type { - Some(ty) => { - let ty = utils::itype_to_tokens(ty, records)?; - quote! { - let result: #ty = fluence_test::internal::serde_json::from_value(result).expect("the default deserializer shouldn't fail"); - } - } - None => TokenStream::new(), - }; - - Ok(result_stream) -} - -fn generate_ret(output_type: &Option<&IType>) -> TokenStream { - match output_type { - Some(_) => quote! { result }, - None => TokenStream::new(), - } -} - -fn generate_arguments<'a, 'r>( - arguments: impl ExactSizeIterator, - records: &'r FCERecordTypes, -) -> TResult> { - arguments - .map(|argument| -> TResult<_> { - let arg_name = utils::new_ident(&argument.name)?; - let arg_type = utils::itype_to_tokens(&argument.ty, records)?; - - let arg = quote! { #arg_name: #arg_type }; - Ok(arg) - }) - .collect::>>() -} - -fn generate_output_type(output_types: &[IType], records: &FCERecordTypes) -> TResult { - let output_type = get_output_type(output_types)?; - match output_type { - None => Ok(TokenStream::new()), - Some(ty) => { - let output_type = utils::itype_to_tokens(&ty, records)?; - let output_type = quote! { -> #output_type }; - - Ok(output_type) - } - } -} - -fn get_output_type(output_types: &[IType]) -> TResult> { - match output_types.len() { - 0 => Ok(None), - 1 => Ok(Some(&output_types[0])), - _ => Err(TestGeneratorError::ManyFnOutputsUnsupported), - } + methods.push(default_cp); + methods.push(user_cp); + + Ok(methods) + }, + ) } diff --git a/crates/fce-test-macro-impl/src/fce_test/module_generator/methods_generator_utils.rs b/crates/fce-test-macro-impl/src/fce_test/module_generator/methods_generator_utils.rs new file mode 100644 index 0000000..90a9e14 --- /dev/null +++ b/crates/fce-test-macro-impl/src/fce_test/module_generator/methods_generator_utils.rs @@ -0,0 +1,195 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::fce_test::utils::new_ident; +use crate::fce_test::utils::itype_to_tokens; +use crate::TResult; + +use fce_wit_parser::interface::it::IType; +use fce_wit_parser::interface::it::IFunctionArg; +use fce_wit_parser::interface::FCERecordTypes; +use fce_wit_parser::interface::FCEFunctionSignature; + +use proc_macro2::TokenStream; +use quote::quote; + +#[derive(Clone, Copy)] +pub(super) enum CallParametersSettings { + Default, + UserDefined, +} + +pub(super) fn generate_module_method( + module_name: &str, + signature: &FCEFunctionSignature, + cp_setting: CallParametersSettings, + records: &FCERecordTypes, +) -> TResult { + let arguments = generate_arguments(signature.arguments.iter(), records)?; + let output_type = generate_output_type(&signature.outputs, records)?; + let fce_call = generate_fce_call(module_name, cp_setting, &signature, records)?; + + let (cp, func_name) = match cp_setting { + CallParametersSettings::Default => { + let func_name = new_ident(&signature.name)?; + (TokenStream::new(), func_name) + } + CallParametersSettings::UserDefined => { + let maybe_comma = if signature.arguments.is_empty() { + TokenStream::new() + } else { + quote! { , } + }; + + let cp = quote! { #maybe_comma cp: fluence_test::CallParameters }; + let func_name = format!("{}_cp", signature.name); + let func_name = new_ident(&func_name)?; + (cp, func_name) + } + }; + + let module_method = quote! { + pub fn #func_name(&mut self, #(#arguments),* #cp) #output_type { + #fce_call + } + }; + + Ok(module_method) +} + +fn generate_fce_call( + module_name: &str, + cp_settings: CallParametersSettings, + method_signature: &FCEFunctionSignature, + records: &FCERecordTypes, +) -> TResult { + let args = method_signature.arguments.iter().map(|a| a.name.as_str()); + let convert_arguments = generate_arguments_converter(args)?; + + let output_type = get_output_type(&method_signature.outputs)?; + let set_result = generate_set_result(&output_type); + let function_call = generate_function_call(module_name, &method_signature.name, cp_settings); + let convert_result_to_output_type = generate_convert_to_output(&output_type, records)?; + let ret = generate_ret(&output_type); + + let function_call = quote! { + use std::ops::DerefMut; + + #convert_arguments + + #set_result #function_call + + #convert_result_to_output_type + + #ret + }; + + Ok(function_call) +} + +/// Generates type convertor to json because of AppService receives them in json. +fn generate_arguments_converter<'a>( + args: impl ExactSizeIterator, +) -> TResult { + let arg_idents: Vec = args.map(new_ident).collect::>()?; + + let args_converter = + quote! { let arguments = fluence_test::internal::serde_json::json!([#(#arg_idents),*]); }; + + Ok(args_converter) +} + +fn generate_function_call( + module_name: &str, + method_name: &str, + cp_setting: CallParametersSettings, +) -> TokenStream { + let cp = match cp_setting { + CallParametersSettings::Default => quote! { <_>::default() }, + CallParametersSettings::UserDefined => quote! { cp }, + }; + + quote! { self.fce.as_ref().borrow_mut().call_module(#module_name, #method_name, arguments, #cp).expect("call to Marine failed"); } +} + +fn generate_set_result(output_type: &Option<&IType>) -> TokenStream { + match output_type { + Some(_) => quote! { let result = }, + None => TokenStream::new(), + } +} + +fn generate_convert_to_output( + output_type: &Option<&IType>, + records: &FCERecordTypes, +) -> TResult { + let result_stream = match output_type { + Some(ty) => { + let ty = itype_to_tokens(ty, records)?; + quote! { + let result: #ty = fluence_test::internal::serde_json::from_value(result).expect("the default deserializer shouldn't fail"); + } + } + None => TokenStream::new(), + }; + + Ok(result_stream) +} + +fn generate_ret(output_type: &Option<&IType>) -> TokenStream { + match output_type { + Some(_) => quote! { result }, + None => TokenStream::new(), + } +} + +fn generate_arguments<'a, 'r>( + arguments: impl ExactSizeIterator, + records: &'r FCERecordTypes, +) -> TResult> { + arguments + .map(|argument| -> TResult<_> { + let arg_name = new_ident(&argument.name)?; + let arg_type = itype_to_tokens(&argument.ty, records)?; + + let arg = quote! { #arg_name: #arg_type }; + Ok(arg) + }) + .collect::>>() +} + +fn generate_output_type(output_types: &[IType], records: &FCERecordTypes) -> TResult { + let output_type = get_output_type(output_types)?; + match output_type { + None => Ok(TokenStream::new()), + Some(ty) => { + let output_type = itype_to_tokens(&ty, records)?; + let output_type = quote! { -> #output_type }; + + Ok(output_type) + } + } +} + +fn get_output_type(output_types: &[IType]) -> TResult> { + use crate::TestGeneratorError::ManyFnOutputsUnsupported; + + match output_types.len() { + 0 => Ok(None), + 1 => Ok(Some(&output_types[0])), + _ => Err(ManyFnOutputsUnsupported), + } +} diff --git a/crates/fce-test-macro/Cargo.toml b/crates/fce-test-macro/Cargo.toml index 9b2fd6e..213cfd3 100644 --- a/crates/fce-test-macro/Cargo.toml +++ b/crates/fce-test-macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fluence-sdk-test-macro" -version = "0.1.4" # remember to update html_root_url +version = "0.1.5" # remember to update html_root_url edition = "2018" description = "Definition of the `#[fce_test]` macro" repository = "https://github.com/fluencelabs/rust-sdk/crates/macro-test" @@ -17,7 +17,7 @@ proc-macro = true doctest = false [dependencies] -fluence-sdk-test-macro-impl = { path = "../fce-test-macro-impl", version = "0.1.4" } +fluence-sdk-test-macro-impl = { path = "../fce-test-macro-impl", version = "0.1.5" } quote = "1.0.9" proc-macro2 = "1.0.24" diff --git a/fluence-test/Cargo.toml b/fluence-test/Cargo.toml index 26a4384..e4c8ef8 100644 --- a/fluence-test/Cargo.toml +++ b/fluence-test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fluence-test" -version = "0.1.4" # remember to update html_root_url +version = "0.1.5" # remember to update html_root_url description = "Fluence backend SDK for testing" documentation = "https://docs.rs/fluence/" repository = "https://github.com/fluencelabs/rust-sdk" @@ -19,7 +19,7 @@ path = "src/lib.rs" doctest = false [dependencies] -fluence-sdk-test-macro = { path = "../crates/fce-test-macro", version = "0.1.4" } +fluence-sdk-test-macro = { path = "../crates/fce-test-macro", version = "0.1.5" } fluence-app-service = { version = "0.7.0", features = ["raw-module-api"] } serde = { version = "1.0.118", features = ["derive"] } diff --git a/fluence-test/src/lib.rs b/fluence-test/src/lib.rs index c5e397e..e5b2a9b 100644 --- a/fluence-test/src/lib.rs +++ b/fluence-test/src/lib.rs @@ -27,6 +27,8 @@ #![warn(rust_2018_idioms)] pub use fluence_sdk_test_macro::fce_test; +pub use fluence_app_service::CallParameters; +pub use fluence_app_service::SecurityTetraplet; /// These API functions are intended for internal usage in generated code. /// Normally, you shouldn't use them.