intermediate

This commit is contained in:
vms 2021-03-28 17:05:35 +03:00
parent 9811e476ed
commit fbd021e3e2
5 changed files with 156 additions and 14 deletions

View File

@ -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" }

View File

@ -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<Self> {
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<String> {
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::<syn::token::Eq>()?;
match token_stream.parse::<syn::Ident>() {
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),
)),
}
}

View File

@ -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<TokenStream, TokenStream> {
use crate::attributes::FCETestAttributes;
let attrs = syn::parse2::<FCETestAttributes>(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())
}

View File

@ -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()
}

View File

@ -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"] }