From 0574adb39a38ebc29a8f16c0387541002bc75c6d Mon Sep 17 00:00:00 2001 From: vms Date: Thu, 1 Apr 2021 03:49:44 +0300 Subject: [PATCH] it works --- Cargo.toml | 1 + crates/fce-test-macro-impl/Cargo.toml | 24 ++++ .../src/attributes.rs | 2 + .../src/errors.rs | 9 +- .../src/fce_test.rs | 111 +++++++++++------- crates/fce-test-macro-impl/src/lib.rs | 37 ++++++ crates/fce-test-macro/Cargo.toml | 6 +- crates/fce-test-macro/src/lib.rs | 21 ++-- fluence-test/Cargo.toml | 5 + fluence-test/src/lib.rs | 7 ++ 10 files changed, 162 insertions(+), 61 deletions(-) create mode 100644 crates/fce-test-macro-impl/Cargo.toml rename crates/{fce-test-macro => fce-test-macro-impl}/src/attributes.rs (91%) rename crates/{fce-test-macro => fce-test-macro-impl}/src/errors.rs (88%) rename crates/{fce-test-macro => fce-test-macro-impl}/src/fce_test.rs (78%) create mode 100644 crates/fce-test-macro-impl/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 42176e5..c36734b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ members = [ "crates/fce-macro", "crates/fce-test-macro", + "crates/fce-test-macro-impl", "crates/main", "crates/wit", "fluence", diff --git a/crates/fce-test-macro-impl/Cargo.toml b/crates/fce-test-macro-impl/Cargo.toml new file mode 100644 index 0000000..f828a43 --- /dev/null +++ b/crates/fce-test-macro-impl/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "fluence-sdk-test-macro-impl" +version = "0.5.0" # 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" +authors = ["Fluence Labs"] +keywords = ["fluence", "sdk", "webassembly", "procedural_macros"] +categories = ["api-bindings", "wasm"] +license = "Apache-2.0" + +[package.metadata.docs.rs] # https://docs.rs/about +all-features = true + +[dependencies] +fluence-app-service = { version = "0.5.2", features = ["raw-module-api"] } +fce-wit-parser = "0.4.0" + +darling = "0.12.2" +quote = "1.0.9" +proc-macro2 = "1.0.24" +proc-macro-error = { version = "1.0.4", default-features = false } +syn = { version = '1.0.64', features = ['full'] } +thiserror = "1.0.24" diff --git a/crates/fce-test-macro/src/attributes.rs b/crates/fce-test-macro-impl/src/attributes.rs similarity index 91% rename from crates/fce-test-macro/src/attributes.rs rename to crates/fce-test-macro-impl/src/attributes.rs index 1393ff8..8590400 100644 --- a/crates/fce-test-macro/src/attributes.rs +++ b/crates/fce-test-macro-impl/src/attributes.rs @@ -19,4 +19,6 @@ use darling::FromMeta; #[derive(Debug, Default, Clone, FromMeta)] pub(crate) struct FCETestAttributes { pub(crate) config_path: String, + #[darling(default)] + pub(crate) modules_dir: Option, } diff --git a/crates/fce-test-macro/src/errors.rs b/crates/fce-test-macro-impl/src/errors.rs similarity index 88% rename from crates/fce-test-macro/src/errors.rs rename to crates/fce-test-macro-impl/src/errors.rs index bf96758..d1f4b9f 100644 --- a/crates/fce-test-macro/src/errors.rs +++ b/crates/fce-test-macro-impl/src/errors.rs @@ -22,7 +22,7 @@ use syn::Error as SynError; use thiserror::Error as ThisError; #[derive(Debug, ThisError)] -pub(crate) enum TestGeneratorError { +pub enum TestGeneratorError { #[error("{0}")] WITParserError(#[from] WITParserError), @@ -37,10 +37,15 @@ pub(crate) enum TestGeneratorError { #[error("{0}")] AttributesError(#[from] DarlingError), + + #[error( + "neither attribute specified nor service config contains modules_dir, please specify it" + )] + ModulesDirUnspecified, } #[derive(Debug, ThisError)] -pub(crate) enum CorruptedITSection { +pub enum CorruptedITSection { #[error("record with {0} is absent in embedded IT section")] AbsentRecord(u64), } diff --git a/crates/fce-test-macro/src/fce_test.rs b/crates/fce-test-macro-impl/src/fce_test.rs similarity index 78% rename from crates/fce-test-macro/src/fce_test.rs rename to crates/fce-test-macro-impl/src/fce_test.rs index a6bb616..8319fb6 100644 --- a/crates/fce-test-macro/src/fce_test.rs +++ b/crates/fce-test-macro-impl/src/fce_test.rs @@ -15,13 +15,14 @@ */ use crate::attributes::FCETestAttributes; -use crate::TResult; +use crate::{TResult, TestGeneratorError}; use fluence_app_service::TomlAppServiceConfig; use proc_macro2::TokenStream as TokenStream2; use quote::quote; +use quote::ToTokens; -pub(super) fn fce_test_impl(attrs: TokenStream2, func_input: syn::ItemFn) -> TResult { +pub fn fce_test_impl(attrs: TokenStream2, input: TokenStream2) -> TResult { use darling::FromMeta; // from https://github.com/dtolnay/syn/issues/788 @@ -30,21 +31,31 @@ pub(super) fn fce_test_impl(attrs: TokenStream2, func_input: syn::ItemFn) -> TRe let attrs: Vec = attrs.into_iter().collect(); let attrs = FCETestAttributes::from_list(&attrs)?; - generate_test_glue_code(func_input, &attrs.config_path) + let func_item = syn::parse2::(input)?; + + generate_test_glue_code(func_item, attrs) } -fn generate_test_glue_code(func: syn::ItemFn, config_path: &str) -> TResult { - let fce_config = TomlAppServiceConfig::load(config_path)?; - let module_interfaces = collect_module_interfaces(&fce_config)?; +fn generate_test_glue_code( + func_item: syn::ItemFn, + attrs: FCETestAttributes, +) -> TResult { + let fce_config = TomlAppServiceConfig::load(&attrs.config_path)?; + let modules_dir = match determine_modules_dir(&fce_config, attrs.modules_dir) { + Some(modules_dir) => modules_dir, + None => return Err(TestGeneratorError::ModulesDirUnspecified), + }; + + let fce_ctor = generate_fce_ctor(&attrs.config_path, &modules_dir); + let module_interfaces = collect_module_interfaces(&fce_config, modules_dir)?; let module_definitions = generate_module_definitions(module_interfaces.iter())?; - let fce_ctor = generate_fce_ctor(config_path); let module_iter = module_interfaces .iter() .map(|(module_name, _)| *module_name); let module_ctors = generate_module_ctors(module_iter)?; - let original_block = func.block; - let signature = func.sig; + let original_block = func_item.block; + let signature = func_item.sig; let glue_code = quote! { #[test] @@ -62,23 +73,24 @@ fn generate_test_glue_code(func: syn::ItemFn, config_path: &str) -> TResult TokenStream2 { - let config_path = quote! { #config_path }; - - let tmp_file_path = std::env::temp_dir(); - let random_uuid = uuid::Uuid::new_v4().to_string(); - let service_id = quote! { #random_uuid }; - - let tmp_file_path = tmp_file_path.join(random_uuid); - let tmp_file_path = tmp_file_path.to_string_lossy().to_string(); - let tmp_file_path = quote! { #tmp_file_path }; +fn generate_fce_ctor(config_path: &str, modules_dir: &PathBuf) -> TokenStream2 { + let config_path = config_path.to_token_stream(); + let modules_dir = modules_dir.to_string_lossy().to_string(); quote! { - let mut __fce__generated_fce_config = fluence_test::internal::TomlAppServiceConfig::load(#config_path.to_string()) - .unwrap_or_else(|e| panic!("app service located at `{}` config can't be loaded: {}", #config_path, e)); - __fce__generated_fce_config.service_base_dir = Some(#tmp_file_path.to_string()); + let tmp_dir = std::env::temp_dir(); + let service_id = fluence_test::internal::Uuid::new_v4().to_string(); - let fce = fluence_test::internal::AppService::new_with_empty_facade(__fce__generated_fce_config, #service_id, std::collections::HashMap::new()) + let tmp_dir = tmp_dir.join(&service_id); + let tmp_dir = tmp_dir.to_string_lossy().to_string(); + std::fs::create_dir(&tmp_dir).expect("can't create a directory for service in tmp"); + + let mut __fce_generated_fce_config = fluence_test::internal::TomlAppServiceConfig::load(#config_path.to_string()) + .unwrap_or_else(|e| panic!("app service located at `{}` config can't be loaded: {}", #config_path, e)); + __fce_generated_fce_config.service_base_dir = Some(tmp_dir); + __fce_generated_fce_config.toml_faas_config.modules_dir = Some(#modules_dir.to_string()); + + let fce = fluence_test::internal::AppService::new_with_empty_facade(__fce_generated_fce_config, service_id, std::collections::HashMap::new()) .unwrap_or_else(|e| panic!("app service can't be created: {}", e)); let fce = std::rc::Rc::new(std::cell::RefCell::new(fce)); @@ -94,12 +106,14 @@ fn generate_module_ctors<'n>( // and internally allocate memory in format let module_name = generate_module_name(&name)?; let struct_name = generate_struct_name(&name)?; + let name_for_user = new_ident(&name)?; - let module_ctor = quote! { #module_name::#struct_name { fce: fce.clone() }}; + let module_ctor = + quote! { let mut #name_for_user = #module_name::#struct_name { fce: fce.clone() }; }; module_ctors.push(module_ctor); } - let module_ctors = quote! { #(#module_ctors)*, }; + let module_ctors = quote! { #(#module_ctors),* }; Ok(module_ctors) } @@ -125,7 +139,7 @@ fn generate_module_definitions<'i>( module_definitions.push(module_definition); } - let module_definitions = quote! { #(#module_definitions)*,}; + let module_definitions = quote! { #(#module_definitions),*}; Ok(module_definitions) } @@ -147,7 +161,7 @@ fn generate_module_definition( pub mod #module_name_ident { #module_records - struct #struct_name_ident { + pub struct #struct_name_ident { pub fce: std::rc::Rc>, } @@ -225,12 +239,12 @@ fn generate_arguments_converter<'a>( } let arguments_serializer = - quote! { let arguments = fluence_test::internal::json!([#(#arguments)*,]) }; + quote! { let arguments = fluence_test::internal::json!([#(#arguments),*]); }; Ok(arguments_serializer) } fn generate_function_call(module_name: &str, method_name: &str) -> TokenStream2 { - quote! { self.fce.as_ref.borrow_mut().call_with_module_name(#module_name, #method_name, arguments, <_>::default()).expect("call to FCE failed"); } + quote! { self.fce.as_ref().borrow_mut().call_with_module_name(#module_name, #method_name, arguments, <_>::default()).expect("call to FCE failed"); } } fn generate_set_result(output_type: &Option<&IType>) -> TokenStream2 { @@ -312,7 +326,7 @@ fn generate_records(records: &FCERecordTypes) -> TResult { let record = quote! { #[derive(Clone, fluence_test::internal::Serialize, fluence_test::internal::Deserialize)] - struct #record_name_ident { + pub struct #record_name_ident { #fields } }; @@ -329,7 +343,7 @@ fn prepare_field(fields: &[IRecordFieldType], records: &FCERecordTypes) -> TResu let field_name = new_ident(&field.name)?; let field_type = itype_to_tokens(&field.ty, records)?; - let field = quote! { #field_name: #field_type }; + let field = quote! { #field_name: #field_type, }; result.extend(field); } @@ -342,7 +356,7 @@ fn generate_module_name(module_name: &str) -> TResult { } fn generate_record_name(record_name: &str) -> TResult { - let extended_record_name = format!("FCEGeneratedRecord{}", record_name); + let extended_record_name = format!("{}", record_name); new_ident(&extended_record_name) } @@ -391,8 +405,10 @@ fn itype_to_tokens(itype: &IType, records: &FCERecordTypes) -> TResult TResult> { - let module_paths = collect_module_paths(config); + let module_paths = collect_module_paths(config, modules_dir); + println!("module paths: {:?}", module_paths); module_paths .into_iter() @@ -401,14 +417,10 @@ fn collect_module_interfaces( .map_err(Into::into) } -fn collect_module_paths(config: &TomlAppServiceConfig) -> Vec<(&str, PathBuf)> { - let base_dir = config - .toml_faas_config - .modules_dir - .as_ref() - .map(|p| PathBuf::from(p)) - .unwrap_or_default(); - +fn collect_module_paths( + config: &TomlAppServiceConfig, + modules_dir: PathBuf, +) -> Vec<(&str, PathBuf)> { config .toml_faas_config .module @@ -416,9 +428,24 @@ fn collect_module_paths(config: &TomlAppServiceConfig) -> Vec<(&str, PathBuf)> { .map(|m| { let module_file_name = m.file_name.as_ref().unwrap_or_else(|| &m.name); let module_file_name = PathBuf::from(module_file_name); - let module_path = base_dir.join(module_file_name); + // TODO: is it right to always have .wasm extension? + let module_path = modules_dir.join(module_file_name).with_extension("wasm"); (m.name.as_str(), module_path) }) .collect::>() } + +fn determine_modules_dir( + config: &TomlAppServiceConfig, + modules_dir: Option, +) -> Option { + match modules_dir { + Some(modules_dir) => Some(PathBuf::from(modules_dir)), + None => config + .toml_faas_config + .modules_dir + .as_ref() + .map(|p| PathBuf::from(p)), + } +} diff --git a/crates/fce-test-macro-impl/src/lib.rs b/crates/fce-test-macro-impl/src/lib.rs new file mode 100644 index 0000000..9a16b6d --- /dev/null +++ b/crates/fce-test-macro-impl/src/lib.rs @@ -0,0 +1,37 @@ +/* + * Copyright 2020 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. + */ + +#![doc(html_root_url = "https://docs.rs/fluence-sdk-macro/0.5.0")] +#![deny( + // dead_code, + nonstandard_style, + unused_imports, + unused_mut, + unused_variables, + unused_unsafe, + unreachable_patterns +)] +#![warn(rust_2018_idioms)] +#![recursion_limit = "1024"] + +mod attributes; +mod errors; +mod fce_test; + +pub use fce_test::fce_test_impl; +pub use errors::TestGeneratorError; + +pub(crate) type TResult = std::result::Result; diff --git a/crates/fce-test-macro/Cargo.toml b/crates/fce-test-macro/Cargo.toml index f053007..5830e1f 100644 --- a/crates/fce-test-macro/Cargo.toml +++ b/crates/fce-test-macro/Cargo.toml @@ -16,13 +16,9 @@ all-features = true proc-macro = true [dependencies] -fluence-app-service = { version = "0.5.2", features = ["raw-module-api"] } -fce-wit-parser = "0.4.0" +fluence-sdk-test-macro-impl = { path = "../fce-test-macro-impl", version = "=0.5.0" } -darling = "0.12.2" quote = "1.0.9" proc-macro2 = "1.0.24" proc-macro-error = { version = "1.0.4", default-features = false } syn = { version = '1.0.64', features = ['full'] } -thiserror = "1.0.24" -uuid = { version = "0.8.2", features = ["v4"] } diff --git a/crates/fce-test-macro/src/lib.rs b/crates/fce-test-macro/src/lib.rs index b6c1652..4d3e172 100644 --- a/crates/fce-test-macro/src/lib.rs +++ b/crates/fce-test-macro/src/lib.rs @@ -14,9 +14,9 @@ * limitations under the License. */ -#![doc(html_root_url = "https://docs.rs/fluence-sdk-macro/0.4.2")] +#![doc(html_root_url = "https://docs.rs/fluence-sdk-macro/0.5.0")] #![deny( - // dead_code, + dead_code, nonstandard_style, unused_imports, unused_mut, @@ -27,15 +27,10 @@ #![warn(rust_2018_idioms)] #![recursion_limit = "1024"] -mod attributes; -mod errors; -mod fce_test; - -use fce_test::fce_test_impl; +use fluence_sdk_test_macro_impl::fce_test_impl; use proc_macro::TokenStream; use proc_macro_error::proc_macro_error; - -pub(crate) type TResult = std::result::Result; +use syn::spanned::Spanned; /// This macro allows user to write tests for services in the following form: ///```ignore @@ -59,9 +54,11 @@ pub(crate) type TResult = std::result::Result; #[proc_macro_error] #[proc_macro_attribute] pub fn fce_test(attrs: TokenStream, input: TokenStream) -> TokenStream { - let func_input = syn::parse_macro_input!(input as syn::ItemFn); - match fce_test_impl(attrs.into(), func_input) { + let attrs: proc_macro2::TokenStream = attrs.into(); + let attrs_span = attrs.span(); + + match fce_test_impl(attrs, input.into()) { Ok(stream) => stream.into(), - Err(e) => proc_macro_error::abort_call_site!(format!("{}", e)), + Err(e) => proc_macro_error::abort!(attrs_span, format!("{}", e)), } } diff --git a/fluence-test/Cargo.toml b/fluence-test/Cargo.toml index 5562f61..d92d49c 100644 --- a/fluence-test/Cargo.toml +++ b/fluence-test/Cargo.toml @@ -19,4 +19,9 @@ path = "src/lib.rs" [dependencies] fluence-sdk-test-macro = { path = "../crates/fce-test-macro", version = "=0.5.0" } +fluence-sdk-test-macro-impl = { path = "../crates/fce-test-macro-impl", version = "=0.5.0" } fluence-app-service = { version = "0.5.2", features = ["raw-module-api"] } + +serde = { version = "1.0.118", features = ["derive"] } +serde_json = "1.0.64" +uuid = { version = "0.8.2", features = ["v4"] } diff --git a/fluence-test/src/lib.rs b/fluence-test/src/lib.rs index a3a4c6d..dd21149 100644 --- a/fluence-test/src/lib.rs +++ b/fluence-test/src/lib.rs @@ -27,10 +27,17 @@ #![warn(rust_2018_idioms)] pub use fluence_sdk_test_macro::fce_test; +pub use fluence_sdk_test_macro_impl::fce_test_impl; /// These API functions are intended for internal usage in generated code. /// Normally, you shouldn't use them. pub mod internal { pub use fluence_app_service::AppService; pub use fluence_app_service::TomlAppServiceConfig; + + pub use serde::Serialize; + pub use serde::Deserialize; + pub use serde_json::json; + + pub use uuid::Uuid; }