From fbd021e3e235d147f8139f8ae252e722ff8d09c6 Mon Sep 17 00:00:00 2001 From: vms Date: Sun, 28 Mar 2021 17:05:35 +0300 Subject: [PATCH] intermediate --- crates/macro-test/Cargo.toml | 9 ++++- crates/macro-test/src/attributes.rs | 61 +++++++++++++++++++++++++++++ crates/macro-test/src/fce_test.rs | 54 +++++++++++++++++++++++++ crates/macro-test/src/lib.rs | 38 +++++++++++++----- crates/wit/Cargo.toml | 8 ++-- 5 files changed, 156 insertions(+), 14 deletions(-) create mode 100644 crates/macro-test/src/attributes.rs diff --git a/crates/macro-test/Cargo.toml b/crates/macro-test/Cargo.toml index fd30b9a..ef3c7ab 100644 --- a/crates/macro-test/Cargo.toml +++ b/crates/macro-test/Cargo.toml @@ -17,5 +17,12 @@ all-features = true proc-macro = true [dependencies] -fluence-app-service = "0.5.2" +fluence-faas = "0.5.2" +quote = "1.0.9" +proc-macro2 = "1.0.24" +serde = { version = "=1.0.118", features = ["derive"] } +serde_json = "1.0.56" +syn = { version = '1.0.65', features = ['full'] } +uuid = { version = "0.8.2", features = ["v4"] } + #fluence-sdk-wit = { path = "../wit", version = "=0.4.2" } diff --git a/crates/macro-test/src/attributes.rs b/crates/macro-test/src/attributes.rs new file mode 100644 index 0000000..49f36c3 --- /dev/null +++ b/crates/macro-test/src/attributes.rs @@ -0,0 +1,61 @@ +/* + * 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. + */ + +use syn::parse::Parse; +use syn::parse::ParseStream; + +#[derive(Debug, Default, Clone)] +pub(crate) struct FCETestAttributes { + pub(crate) config_path: String, +} + +impl Parse for FCETestAttributes { + fn parse(input: ParseStream<'_>) -> syn::Result { + let config_file_path = parse_config_file_path(input)?; + let attributes = FCETestAttributes { + config_path: config_file_path, + }; + + Ok(attributes) + } +} + +pub(crate) fn parse_config_file_path(token_stream: ParseStream<'_>) -> syn::Result { + let attr_name = token_stream.step(|cursor| match cursor.ident() { + Some((ident, rem)) => Ok((ident, rem)), + None => Err(cursor.error("Expected a valid identifier")), + })?; + + match attr_name.to_string().as_str() { + "config" => { + // trying to parse `=` + token_stream.parse::()?; + + match token_stream.parse::() { + Ok(config_file_path) => Ok(config_file_path.to_string()), + Err(e) => Err(syn::Error::new( + attr_name.span(), + format!("failed to parse a config file path: {}", e), + )), + } + } + + attr => Err(syn::Error::new( + attr_name.span(), + format!("Expected 'config' identifier, but {} found", attr), + )), + } +} diff --git a/crates/macro-test/src/fce_test.rs b/crates/macro-test/src/fce_test.rs index 3c7d24b..143967e 100644 --- a/crates/macro-test/src/fce_test.rs +++ b/crates/macro-test/src/fce_test.rs @@ -14,3 +14,57 @@ * limitations under the License. */ +use proc_macro2::TokenStream; +use quote::quote; + +pub(super) fn fce_test_impl( + attr: TokenStream, + func_input: syn::ItemFn, +) -> Result { + use crate::attributes::FCETestAttributes; + + let attrs = syn::parse2::(attr).map_err(|e| e.into_compile_error())?; + let generated_test = generate_test_glue_code(func_input, &attrs.config_path); + + Ok(generated_test) +} + +fn generate_test_glue_code(func: syn::ItemFn, config_path: &str) -> TokenStream { + let fce_ctor = generate_fce_ctor(config_path); + let original_block = func.block; + let signature = func.sig; + + quote! { + #[test] + #signature { + #fce_ctor + + #original_block + } + } +} + +fn generate_fce_ctor(config_path: &str) -> TokenStream { + let config_path = new_ident(config_path); + + let tmp_file_path = std::env::temp_dir(); + let random_uuid = uuid::Uuid::new_v4().to_string(); + let service_id = new_ident(&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 = new_ident(&tmp_file_path); + + quote! { + let mut __fce__generated_fce_config = fluence_faas::TomlAppServiceConfig::load(#config_path) + .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); + + let fce = fce_app_service::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)); + } +} + +fn new_ident(name: &str) -> syn::Ident { + syn::Ident::new(name, proc_macro2::Span::call_site()) +} diff --git a/crates/macro-test/src/lib.rs b/crates/macro-test/src/lib.rs index 9bf03aa..4080b56 100644 --- a/crates/macro-test/src/lib.rs +++ b/crates/macro-test/src/lib.rs @@ -15,6 +15,7 @@ */ #![doc(html_root_url = "https://docs.rs/fluence-sdk-macro/0.4.2")] +/* #![deny( dead_code, nonstandard_style, @@ -23,21 +24,40 @@ unused_unsafe, unreachable_patterns )] + */ #![warn(rust_2018_idioms)] #![recursion_limit = "1024"] +mod attributes; mod fce_test; +use fce_test::fce_test_impl; use proc_macro::TokenStream; +/// This macro allows user to write tests for services in the following form: +///```rust +/// #[fce_test(config = "/path/to/Config.toml")] +/// fn test() { +/// let service_result = fce.call("greeting", "name"); +/// assert_eq!(&service_result, "Hi, name!"); +/// } +///``` +/// +/// This function is desugrated in the following way: +///```rust +/// #[test] +/// fn test() { +/// let fce = fluence_faas::FluenceFaaS::with_raw_config("/path/to/Config.toml") +/// .unwrap_or_else(|e| panic!("test instance can't be instantiated: {}", e)); +/// let service_result = fce.call("greeting", "name"); +/// assert_eq!(&service_result, "Hi, name!"); +/// } +///``` + #[proc_macro_attribute] -pub fn fce_test(attr: TokenStream, input: TokenStream) -> TokenStream { - // into converts proc_macro::TokenStream to proc_macro2::TokenStream - match fce_impl(input.into()) { - Ok(v) => v, - // converts syn:error to proc_macro2::TokenStream - Err(e) => e.to_compile_error(), - } - // converts proc_macro2::TokenStream to proc_macro::TokenStream - .into() +pub fn fce_test(attrs: TokenStream, input: TokenStream) -> TokenStream { + let func_input = syn::parse_macro_input!(input as syn::ItemFn); + let result = fce_test_impl(attrs.into(), func_input).unwrap_or_else(std::convert::identity); + + result.into() } diff --git a/crates/wit/Cargo.toml b/crates/wit/Cargo.toml index 791c5d0..ba91fe6 100644 --- a/crates/wit/Cargo.toml +++ b/crates/wit/Cargo.toml @@ -14,9 +14,9 @@ license = "Apache-2.0" all-features = true [dependencies] -quote = "1.0.7" -proc-macro2 = "1.0.18" +quote = "1.0.9" +proc-macro2 = "1.0.24" serde = { version = "=1.0.118", features = ["derive"] } serde_json = "1.0.56" -syn = { version = '1.0.33', features = ['full'] } -uuid = { version = "0.8.1", features = ["v4"] } +syn = { version = '1.0.65', features = ['full'] } +uuid = { version = "0.8.2", features = ["v4"] }