mirror of
https://github.com/fluencelabs/marine-rs-sdk
synced 2025-03-15 14:30:48 +00:00
progress
This commit is contained in:
parent
04c2fb51d7
commit
3ffe392e14
@ -16,8 +16,12 @@ all-features = true
|
|||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
[dependencies]
|
[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"
|
quote = "1.0.9"
|
||||||
proc-macro2 = "1.0.24"
|
proc-macro2 = "1.0.24"
|
||||||
syn = { version = '1.0.64', features = ['full'] }
|
syn = { version = '1.0.64', features = ['full'] }
|
||||||
|
thiserror = "1.0.24"
|
||||||
uuid = { version = "0.8.2", features = ["v4"] }
|
uuid = { version = "0.8.2", features = ["v4"] }
|
||||||
darling = "0.12.2"
|
|
||||||
|
38
crates/fce-test-macro/src/errors.rs
Normal file
38
crates/fce-test-macro/src/errors.rs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* 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 fce_wit_parser::WITParserError;
|
||||||
|
|
||||||
|
use syn::Error as SynError;
|
||||||
|
use thiserror::Error as ThisError;
|
||||||
|
|
||||||
|
#[derive(Debug, ThisError)]
|
||||||
|
pub(crate) enum TestGeneratorError {
|
||||||
|
#[error("{0}")]
|
||||||
|
WITParserError(#[from] WITParserError),
|
||||||
|
|
||||||
|
#[error("{0}")]
|
||||||
|
CorruptedITSection(#[from] CorruptedITSection),
|
||||||
|
|
||||||
|
#[error("{0}")]
|
||||||
|
SynError(#[from] SynError),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, ThisError)]
|
||||||
|
pub(crate) enum CorruptedITSection {
|
||||||
|
#[error("record with {0} is absent in embedded IT section")]
|
||||||
|
AbsentRecord(u64),
|
||||||
|
}
|
@ -15,7 +15,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::attributes::FCETestAttributes;
|
use crate::attributes::FCETestAttributes;
|
||||||
|
use crate::TResult;
|
||||||
|
|
||||||
|
use fluence_app_service::TomlAppServiceConfig;
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
@ -69,3 +71,221 @@ fn generate_fce_ctor(config_path: &str) -> TokenStream2 {
|
|||||||
.unwrap_or_else(|e| panic!("app service can't be created: {}", e));
|
.unwrap_or_else(|e| panic!("app service can't be created: {}", e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use fce_wit_parser::module_raw_interface;
|
||||||
|
use fce_wit_parser::interface::FCEModuleInterface;
|
||||||
|
use fce_wit_parser::interface::FCERecordTypes;
|
||||||
|
use fce_wit_parser::interface::FCEFunctionSignature;
|
||||||
|
use fce_wit_parser::interface::it::IFunctionArg;
|
||||||
|
use fce_wit_parser::interface::it::IRecordFieldType;
|
||||||
|
use fce_wit_parser::interface::it::IType;
|
||||||
|
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
fn generate_module_definition(
|
||||||
|
module_name: &str,
|
||||||
|
module_interface: &FCEModuleInterface,
|
||||||
|
) -> TResult<TokenStream2> {
|
||||||
|
let module_name = new_ident(module_name)?;
|
||||||
|
let module_records = generate_records(&module_interface.record_types)?;
|
||||||
|
let module_functions = generate_module_methods(
|
||||||
|
module_interface.function_signatures.iter(),
|
||||||
|
&module_interface.record_types,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let module_definition = quote! {
|
||||||
|
pub mod #module_name {
|
||||||
|
#module_records
|
||||||
|
|
||||||
|
struct #module_name {
|
||||||
|
pub fce: fluence_test::internal::AppService,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl #module_name {
|
||||||
|
#(#module_functions)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(module_definition)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_module_methods<'m, 'r>(
|
||||||
|
method_signatures: impl ExactSizeIterator<Item = &'m FCEFunctionSignature>,
|
||||||
|
records: &'r FCERecordTypes,
|
||||||
|
) -> TResult<Vec<TokenStream2>> {
|
||||||
|
let mut result = Vec::with_capacity(method_signatures.len());
|
||||||
|
|
||||||
|
for signature in method_signatures {
|
||||||
|
let func_name = new_ident(&signature.name)?;
|
||||||
|
let arguments = generate_arguments(signature.arguments.iter(), records)?;
|
||||||
|
let output_type = generate_output_type(&signature.outputs, records)?;
|
||||||
|
|
||||||
|
let module_method = quote! {
|
||||||
|
pub fn #func_name(&self, #(#arguments),*) #output_type {
|
||||||
|
let result
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
result.push(module_method);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_fce_call(method_signature: &FCEFunctionSignature) -> TokenStream2 {
|
||||||
|
let output_type = get_output_type(&method_signature.outputs);
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#convert_arguments
|
||||||
|
|
||||||
|
#set_result #function_call
|
||||||
|
|
||||||
|
#convert_result_to_output_type
|
||||||
|
|
||||||
|
#ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_arguments<'a, 'r>(
|
||||||
|
arguments: impl ExactSizeIterator<Item = &'a IFunctionArg>,
|
||||||
|
records: &'r FCERecordTypes,
|
||||||
|
) -> TResult<Vec<TokenStream2>> {
|
||||||
|
let mut result = Vec::with_capacity(arguments.len());
|
||||||
|
for argument in arguments {
|
||||||
|
let arg_name = new_ident(&argument.name)?;
|
||||||
|
let arg_type = itype_to_tokens(&argument.ty, records)?;
|
||||||
|
|
||||||
|
let arg = quote! { #arg_name: #arg_type };
|
||||||
|
result.push(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_output_type(output_types: &[IType], records: &FCERecordTypes) -> TResult<TokenStream2> {
|
||||||
|
let output_type = get_output_type(output_types);
|
||||||
|
match output_type {
|
||||||
|
None => Ok(TokenStream2::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]) -> Option<&IType> {
|
||||||
|
match output_types.len() {
|
||||||
|
0 => None,
|
||||||
|
1 => Some(&output_types[0]),
|
||||||
|
_ => unimplemented!("function with more than 1 arguments aren't supported now"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_records(records: &FCERecordTypes) -> TResult<TokenStream2> {
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
let mut result = TokenStream2::new();
|
||||||
|
|
||||||
|
for (_, record) in records.iter() {
|
||||||
|
let name = new_ident(&record.name)?;
|
||||||
|
let fields = prepare_field(record.fields.deref(), records)?;
|
||||||
|
|
||||||
|
let record = quote! {
|
||||||
|
struct #name {
|
||||||
|
#fields
|
||||||
|
}
|
||||||
|
};
|
||||||
|
result.extend(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare_field(fields: &[IRecordFieldType], records: &FCERecordTypes) -> TResult<TokenStream2> {
|
||||||
|
let mut result = TokenStream2::new();
|
||||||
|
|
||||||
|
for field in fields {
|
||||||
|
let field_name = new_ident(&field.name)?;
|
||||||
|
let field_type = itype_to_tokens(&field.ty, records)?;
|
||||||
|
|
||||||
|
let field = quote! { #field_name: #field_type };
|
||||||
|
result.extend(field);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_ident(ident_str: &str) -> TResult<syn::Ident> {
|
||||||
|
syn::parse_str::<syn::Ident>(ident_str).map_err(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn itype_to_tokens(itype: &IType, records: &FCERecordTypes) -> TResult<TokenStream2> {
|
||||||
|
let token_stream = match itype {
|
||||||
|
IType::Record(record_id) => {
|
||||||
|
let record = records
|
||||||
|
.get(record_id)
|
||||||
|
.ok_or_else(|| crate::errors::CorruptedITSection::AbsentRecord(*record_id))?;
|
||||||
|
let record_name = new_ident(&record.name)?;
|
||||||
|
let token_stream = quote! { #record_name };
|
||||||
|
token_stream
|
||||||
|
}
|
||||||
|
IType::Array(ty) => {
|
||||||
|
let inner_ty_token_stream = itype_to_tokens(ty, records)?;
|
||||||
|
let token_stream = quote! { Vec<#inner_ty_token_stream> };
|
||||||
|
token_stream
|
||||||
|
}
|
||||||
|
IType::String => quote! { String },
|
||||||
|
IType::S8 => quote! { i8 },
|
||||||
|
IType::S16 => quote! { i16 },
|
||||||
|
IType::S32 => quote! { i32 },
|
||||||
|
IType::S64 => quote! { i64 },
|
||||||
|
IType::U8 => quote! { u8 },
|
||||||
|
IType::U16 => quote! { u16 },
|
||||||
|
IType::U32 => quote! { u32 },
|
||||||
|
IType::U64 => quote! { u64 },
|
||||||
|
IType::I32 => quote! { i32 },
|
||||||
|
IType::I64 => quote! { i64 },
|
||||||
|
IType::F32 => quote! { f32 },
|
||||||
|
IType::F64 => quote! { f64 },
|
||||||
|
IType::Anyref => unimplemented!("anyref isn't supported and will be delete from IType"),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(token_stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_module_interfaces(
|
||||||
|
config: &TomlAppServiceConfig,
|
||||||
|
) -> TResult<Vec<(&str, FCEModuleInterface)>> {
|
||||||
|
let module_paths = collect_module_paths(config);
|
||||||
|
|
||||||
|
module_paths
|
||||||
|
.into_iter()
|
||||||
|
.map(|(name, path)| module_raw_interface(path).map(|interface| (name, interface)))
|
||||||
|
.collect::<Result<Vec<_>, _>>()
|
||||||
|
.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();
|
||||||
|
|
||||||
|
config
|
||||||
|
.toml_faas_config
|
||||||
|
.module
|
||||||
|
.iter()
|
||||||
|
.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);
|
||||||
|
|
||||||
|
(m.name.as_str(), module_path)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
}
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
#![doc(html_root_url = "https://docs.rs/fluence-sdk-macro/0.4.2")]
|
#![doc(html_root_url = "https://docs.rs/fluence-sdk-macro/0.4.2")]
|
||||||
#![deny(
|
#![deny(
|
||||||
dead_code,
|
// dead_code,
|
||||||
nonstandard_style,
|
nonstandard_style,
|
||||||
unused_imports,
|
unused_imports,
|
||||||
unused_mut,
|
unused_mut,
|
||||||
@ -28,11 +28,14 @@
|
|||||||
#![recursion_limit = "1024"]
|
#![recursion_limit = "1024"]
|
||||||
|
|
||||||
mod attributes;
|
mod attributes;
|
||||||
|
mod errors;
|
||||||
mod fce_test;
|
mod fce_test;
|
||||||
|
|
||||||
use fce_test::fce_test_impl;
|
use fce_test::fce_test_impl;
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
|
|
||||||
|
pub(crate) type TResult<T> = std::result::Result<T, errors::TestGeneratorError>;
|
||||||
|
|
||||||
/// This macro allows user to write tests for services in the following form:
|
/// This macro allows user to write tests for services in the following form:
|
||||||
///```ignore
|
///```ignore
|
||||||
/// #[fce_test(config = "/path/to/Config.toml")]
|
/// #[fce_test(config = "/path/to/Config.toml")]
|
||||||
|
@ -19,4 +19,4 @@ path = "src/lib.rs"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
fluence-sdk-test-macro = { path = "../crates/fce-test-macro", version = "=0.5.0" }
|
fluence-sdk-test-macro = { path = "../crates/fce-test-macro", version = "=0.5.0" }
|
||||||
fluence-app-service= { version = "0.5.2", features = ["raw-module-api"] }
|
fluence-app-service = { version = "0.5.2", features = ["raw-module-api"] }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user