Merge branch 'master' into support_references

This commit is contained in:
vms 2021-04-01 19:44:08 +03:00
commit ea11a617cd
48 changed files with 1702 additions and 65 deletions

View File

@ -12,16 +12,20 @@ jobs:
keys: keys:
- backendsdk01-{{ checksum "Cargo.toml" }} - backendsdk01-{{ checksum "Cargo.toml" }}
- run: | #TODO: enable 'stable' and 'beta' once `allocator_api` becomes stable - run: | #TODO: enable 'stable' and 'beta' once `allocator_api` becomes stable
rustup toolchain install nightly-2020-04-20 rustup toolchain install nightly-2021-02-27
rustup default nightly-2020-04-20 rustup default nightly-2021-02-27
rustup override set nightly-2020-04-20 rustup override set nightly-2021-02-27
rustup target add wasm32-unknown-unknown rustup target add wasm32-wasi
rustup component add rustfmt rustup component add rustfmt
rustup component add clippy rustup component add clippy
cargo fmt --all -- --check --color always cargo fmt --all -- --check --color always
cargo build -v --target wasm32-unknown-unknown --all-features
(cd fluence; cargo build -v --target wasm32-wasi --all-features)
(cd fluence; cargo clippy -v --target wasm32-wasi)
(cd fluence-test; cargo build)
cargo test -v --all-features cargo test -v --all-features
cargo clippy -v --target wasm32-unknown-unknown
- save_cache: - save_cache:
paths: paths:
- ~/.cargo - ~/.cargo

View File

@ -1,38 +1,10 @@
[package]
name = "fluence"
version = "0.5.0" # remember to update html_root_url
description = "Fluence backend SDK for developing backend applications for the Fluence network"
documentation = "https://docs.rs/fluence/"
repository = "https://github.com/fluencelabs/rust-sdk"
authors = ["Fluence Labs"]
readme = "README.md"
keywords = ["fluence", "sdk", "webassembly"]
categories = ["api-bindings", "wasm"]
license = "Apache-2.0"
edition = "2018"
[package.metadata.docs.rs] # https://docs.rs/about
all-features = true
[lib]
path = "src/lib.rs"
name = "fluence"
[dependencies]
fluence-sdk-macro = { path = "crates/macro", version = "=0.5.0" }
fluence-sdk-main = { path = "crates/main", version = "=0.5.0" }
serde = "=1.0.118"
[features]
# Print some internal logs by log_utf8_string
debug = ["fluence-sdk-main/debug"]
# Enable logger (this will cause log_utf8_string to appear in imports)
logger = ["fluence-sdk-main/logger"]
[workspace] [workspace]
members = [ members = [
"crates/fce-macro",
"crates/fce-test-macro",
"crates/fce-test-macro-impl",
"crates/main", "crates/main",
"crates/macro",
"crates/wit", "crates/wit",
"fluence",
"fluence-test"
] ]

View File

@ -2,7 +2,7 @@
name = "fluence-sdk-macro" name = "fluence-sdk-macro"
version = "0.5.0" # remember to update html_root_url version = "0.5.0" # remember to update html_root_url
edition = "2018" edition = "2018"
description = "Definition of `#[invoke_handler]` attribute" description = "Definition of the `#[fce]` macro"
documentation = "https://docs.rs/fluence/fluence-sdk-macro" documentation = "https://docs.rs/fluence/fluence-sdk-macro"
repository = "https://github.com/fluencelabs/rust-sdk/crates/macro" repository = "https://github.com/fluencelabs/rust-sdk/crates/macro"
authors = ["Fluence Labs"] authors = ["Fluence Labs"]
@ -10,11 +10,12 @@ keywords = ["fluence", "sdk", "webassembly", "procedural_macros"]
categories = ["api-bindings", "wasm"] categories = ["api-bindings", "wasm"]
license = "Apache-2.0" license = "Apache-2.0"
[package.metadata.docs.rs] # https://docs.rs/about [package.metadata.docs.rs]
all-features = true all-features = true
[lib] [lib]
proc-macro = true proc-macro = true
doctest = false
[dependencies] [dependencies]
fluence-sdk-wit = { path = "../wit", version = "=0.5.0" } fluence-sdk-wit = { path = "../wit", version = "=0.5.0" }

View File

@ -23,7 +23,7 @@
//! # Examples //! # Examples
//! //!
//! This example shows how a function could be exported: //! This example shows how a function could be exported:
//! ``` //! ```ignore
//! #[fce] //! #[fce]
//! pub fn greeting(name: String) -> String { //! pub fn greeting(name: String) -> String {
//! format!("Hi {}", name) //! format!("Hi {}", name)
@ -33,23 +33,19 @@
//! This more complex example shows how a function could be imported from another Wasm module //! This more complex example shows how a function could be imported from another Wasm module
//! and how a struct could be passed: //! and how a struct could be passed:
//! //!
//! ``` //! ```ignore
//! #[fce] //! use fluence::MountedBinaryResult;
//! struct HostReturnValue {
//! pub error_code: i32,
//! pub outcome: Vec<u8>
//! }
//! //!
//! #[fce] //! #[fce]
//! pub fn read_ipfs_file(file_path: String) -> HostReturnValue { //! pub fn read_ipfs_file(file_path: String) -> MountedBinaryResult {
//! let hash = calculate_hash(file_path); //! let hash = calculate_hash(file_path);
//! ipfs(hash) //! ipfs(vec![hash])
//! } //! }
//! //!
//! #[fce] //! #[fce]
//! #[link(wasm_import_module = "ipfs_node.wasm")] //! #[link(wasm_import_module = "ipfs_node")]
//! extern "C" { //! extern "C" {
//! pub fn ipfs(file_hash: String) -> HostReturnValue; //! pub fn ipfs(file_hash: Vec<String>) -> MountedBinaryResult;
//! } //! }
//! //!
//! ``` //! ```

View File

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

View File

@ -0,0 +1,28 @@
/*
* 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 darling::FromMeta;
/// Describes attributes of `fce_test` macro.
#[derive(Debug, Default, Clone, FromMeta)]
pub(crate) struct FCETestAttributes {
/// Path to a config file of a tested service.
pub(crate) config_path: String,
/// Path to compiled modules of a service.
#[darling(default)]
pub(crate) modules_dir: Option<String>,
}

View File

@ -0,0 +1,54 @@
/*
* 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 fluence_app_service::AppServiceError;
use darling::Error as DarlingError;
use syn::Error as SynError;
use thiserror::Error as ThisError;
#[derive(Debug, ThisError)]
pub enum TestGeneratorError {
#[error("Can't load Wasm modules into FCE: {0}")]
WITParserError(#[from] WITParserError),
#[error("{0}")]
CorruptedITSection(#[from] CorruptedITSection),
#[error("{0}")]
SynError(#[from] SynError),
#[error("Can't load Wasm modules from the provided config: {0}")]
ConfigLoadError(#[from] AppServiceError),
#[error("{0}")]
AttributesError(#[from] DarlingError),
#[error(
"neither modules_dir attribute specified nor service config contains modules_dir, please specify one of them"
)]
ModulesDirUnspecified,
#[error("a Wasm file compiled with newer version of sdk that supports multi-value")]
ManyFnOutputsUnsupported,
}
#[derive(Debug, ThisError)]
pub enum CorruptedITSection {
#[error("record with {0} is absent in embedded IT section")]
AbsentRecord(u64),
}

View File

@ -0,0 +1,88 @@
/*
* 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::TResult;
use fluence_app_service::TomlAppServiceConfig;
use fce_wit_parser::module_raw_interface;
use fce_wit_parser::interface::FCEModuleInterface;
use std::path::PathBuf;
#[derive(Debug, Clone, PartialEq, Eq)]
pub(super) struct Module<'m> {
pub name: &'m str,
pub interface: FCEModuleInterface,
}
impl<'m> Module<'m> {
fn new(name: &'m str, interface: FCEModuleInterface) -> Self {
Self { name, interface }
}
}
/// Returns all modules the provided config consists of.
pub(super) fn collect_modules(
config: &TomlAppServiceConfig,
modules_dir: PathBuf,
) -> TResult<Vec<Module<'_>>> {
let module_paths = collect_module_paths(config, modules_dir);
module_paths
.into_iter()
.map(|(name, path)| {
module_raw_interface(path).map(|interface| Module::new(name, interface))
})
.collect::<Result<Vec<_>, _>>()
.map_err(Into::into)
}
fn collect_module_paths(
config: &TomlAppServiceConfig,
modules_dir: PathBuf,
) -> Vec<(&str, PathBuf)> {
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);
// TODO: is it correct to always have .wasm extension?
let module_path = modules_dir.join(module_file_name).with_extension("wasm");
(m.name.as_str(), module_path)
})
.collect::<Vec<_>>()
}
/// Tries to determine a dir with compiled Wasm modules according to the following rules:
/// - if the modules_dir attribute is specified (by user) it will be chosen,
/// - otherwise if modules_dir is specified in AppService config it will be chosen,
/// - otherwise None will be returned.
pub(super) fn resolve_modules_dir(
config: &TomlAppServiceConfig,
modules_dir: Option<String>,
) -> Option<PathBuf> {
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)),
}
}

View File

@ -0,0 +1,35 @@
/*
* 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::attributes::FCETestAttributes;
use crate::TResult;
use crate::fce_test::glue_code_generator::generate_test_glue_code;
use proc_macro2::TokenStream;
use darling::FromMeta;
use syn::parse::Parser;
pub fn fce_test_impl(attrs: TokenStream, input: TokenStream) -> TResult<TokenStream> {
// from https://github.com/dtolnay/syn/issues/788
let parser = syn::punctuated::Punctuated::<syn::NestedMeta, syn::Token![,]>::parse_terminated;
let attrs = parser.parse2(attrs)?;
let attrs: Vec<syn::NestedMeta> = attrs.into_iter().collect();
let attrs = FCETestAttributes::from_list(&attrs)?;
let func_item = syn::parse2::<syn::ItemFn>(input)?;
generate_test_glue_code(func_item, attrs)
}

View File

@ -0,0 +1,197 @@
/*
* 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::attributes::FCETestAttributes;
use crate::TResult;
use crate::TestGeneratorError;
use crate::fce_test;
use crate::fce_test::config_utils;
use fluence_app_service::TomlAppServiceConfig;
use proc_macro2::TokenStream;
use quote::quote;
use quote::ToTokens;
use std::path::PathBuf;
/// Generates glue code for tests.
/// F.e. for this test for the greeting service
///```ignore
/// #[fce_test(
/// config_path = "/path/to/service/config/Config.toml",
/// modules_dir = "/path/to/modules/dir"
/// )]
/// fn test() {
/// let result = greeting.greeting("John".to_string());
/// assert_eq(result.as_str(), "Hi, John!");
/// }
/// ```
///
/// the following glue code would be generated:
///```ignore
/// // (0)
/// pub mod __fce_generated_greeting {
/// struct FCEGeneratedStructgreeting {
/// fce: std::rc::Rc<std::cell::RefCell<fluence_test::internal::AppService>>,
/// }
///
/// impl FCEGeneratedStructgreeting {
/// pub fn new(fce: std::rc::Rc<std::cell::RefCell<fluence_test::internal::AppService>>) -> Self {
/// Self { fce }
/// }
///
/// pub fn greeting(&mut self, name: String) -> String {
/// use std::ops::DerefMut;
/// let arguments = fluence_test::internal::json!([name]);
/// let result = self
/// .fce
/// .as_ref
/// .borrow_mut()
/// .call_with_module_name("greeting", "greeting", arguments, <_>::default())
/// .expect("call to FCE failed");
/// let result: String = serde_json::from_value(result)
/// .expect("the default deserializer shouldn't fail");
/// result
/// }
/// }
///}
/// // (1)
/// let tmp_dir = std::env::temp_dir();
/// let service_id = fluence_test::internal::Uuid::new_v4().to_string();
///
/// 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("/path/to/greeting/Config.toml".to_string())
/// .unwrap_or_else(|e| {
/// panic!(
/// "app service located at `{}` config can't be loaded: {}",
/// "/path/to/greeting/Config.toml", e
/// )
/// });
///
/// __fce__generated_fce_config.service_base_dir = Some("/path/to/tmp".to_string());
///
/// let fce = fluence_test::internal::AppService::new_with_empty_facade(
/// __fce__generated_fce_config,
/// "3640e972-92e3-47cb-b95f-4e3c5bcf0f14",
/// 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));
///
/// // (2)
///
/// let mut greeting = __fce_generated_greeting::FCEGeneratedStructgreeting::new(fce);
///
/// // (3)
///
/// let result = greeting.greeting("John".to_string());
/// assert_eq(result.as_str(), "Hi, John!");
///
/// // (4)
///```
///
/// Example code above corresponds to the macro definition in the following way:
/// [(0), (1)] - module_definitions*
/// [(1), (2)] - app_service_ctor
/// [(2), (3)] - module_ctors*
/// [(3), (4)] - original_block
pub(super) fn generate_test_glue_code(
func_item: syn::ItemFn,
attrs: FCETestAttributes,
) -> TResult<TokenStream> {
let fce_config = TomlAppServiceConfig::load(&attrs.config_path)?;
let modules_dir = match config_utils::resolve_modules_dir(&fce_config, attrs.modules_dir) {
Some(modules_dir) => modules_dir,
None => return Err(TestGeneratorError::ModulesDirUnspecified),
};
let app_service_ctor = generate_app_service_ctor(&attrs.config_path, &modules_dir);
let module_interfaces = fce_test::config_utils::collect_modules(&fce_config, modules_dir)?;
let module_definitions =
fce_test::module_generator::generate_module_definitions(module_interfaces.iter())?;
let module_iter = module_interfaces.iter().map(|module| module.name);
let module_ctors = generate_module_ctors(module_iter)?;
let original_block = func_item.block;
let signature = func_item.sig;
let glue_code = quote! {
#[test]
#signature {
// definitions for wasm modules specified in config
#(#module_definitions)*
// AppService constructor and instantiation to implicit `fce` variable
#app_service_ctor
// constructors of all modules of the tested service
#(#module_ctors)*
// original test function as is
#original_block
}
};
Ok(glue_code)
}
fn generate_app_service_ctor(config_path: &str, modules_dir: &PathBuf) -> TokenStream {
let config_path = config_path.to_token_stream();
let modules_dir = modules_dir.to_string_lossy().to_string();
quote! {
let tmp_dir = std::env::temp_dir();
let service_id = fluence_test::internal::Uuid::new_v4().to_string();
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));
}
}
fn generate_module_ctors<'n>(
module_names: impl ExactSizeIterator<Item = &'n str>,
) -> TResult<Vec<TokenStream>> {
module_names
.map(|name| -> TResult<_> {
// TODO: optimize these two call because they are called twice for each module name
// and internally allocate memory in format call.
let module_name = fce_test::utils::generate_module_name(&name)?;
let struct_name = fce_test::utils::generate_struct_name(&name)?;
let name_for_user = fce_test::utils::new_ident(&name)?;
let module_ctor =
quote! { let mut #name_for_user = #module_name::#struct_name::new(fce.clone()); };
Ok(module_ctor)
})
.collect::<TResult<_>>()
}

View File

@ -0,0 +1,23 @@
/*
* 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.
*/
mod config_utils;
mod fce_test_impl;
mod glue_code_generator;
mod module_generator;
mod utils;
pub use fce_test_impl::fce_test_impl;

View File

@ -0,0 +1,99 @@
/*
* 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.
*/
mod methods_generator;
mod record_type_generator;
use crate::fce_test::utils;
use crate::fce_test::config_utils::Module;
use crate::TResult;
use proc_macro2::TokenStream;
use quote::quote;
/// Generates definitions of modules and records of this modules.
/// F.e. for the greeting service the following definitions would be generated:
///```ignore
/// pub mod __fce_generated_greeting {
/// struct FCEGeneratedStructgreeting {
/// fce: std::rc::Rc<std::cell::RefCell<fluence_test::internal::AppService>>,
/// }
///
/// impl FCEGeneratedStructgreeting {
/// pub fn new(fce: std::rc::Rc<std::cell::RefCell<fluence_test::internal::AppService>>) -> Self {
/// Self { fce }
/// }
///
/// pub fn greeting(&mut self, name: String) -> String {
/// use std::ops::DerefMut;
/// let arguments = fluence_test::internal::json!([name]);
/// let result = self
/// .fce
/// .as_ref
/// .borrow_mut()
/// .call_with_module_name("greeting", "greeting", arguments, <_>::default())
/// .expect("call to FCE failed");
/// let result: String = serde_json::from_value(result)
/// .expect("the default deserializer shouldn't fail");
/// result
/// }
/// }
/// }
///```
pub(super) fn generate_module_definitions<'i>(
modules: impl ExactSizeIterator<Item = &'i Module<'i>>,
) -> TResult<Vec<TokenStream>> {
modules
.into_iter()
.map(generate_module_definition)
.collect::<TResult<Vec<_>>>()
}
fn generate_module_definition(module: &Module<'_>) -> TResult<TokenStream> {
let module_name = module.name;
let module_name_ident = utils::generate_module_name(module_name)?;
let struct_name_ident = utils::generate_struct_name(module_name)?;
let module_interface = &module.interface;
let module_records = record_type_generator::generate_records(&module_interface.record_types)?;
let module_functions = methods_generator::generate_module_methods(
module_name,
module_interface.function_signatures.iter(),
&module_interface.record_types,
)?;
let module_definition = quote! {
pub mod #module_name_ident {
#(#module_records)*
pub struct #struct_name_ident {
fce: std::rc::Rc<std::cell::RefCell<fluence_test::internal::AppService>>,
}
impl #struct_name_ident {
pub fn new(fce: std::rc::Rc<std::cell::RefCell<fluence_test::internal::AppService>>) -> Self {
Self { fce }
}
}
impl #struct_name_ident {
#(#module_functions)*
}
}
};
Ok(module_definition)
}

View File

@ -0,0 +1,162 @@
/*
* 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;
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<Item = &'m FCEFunctionSignature>,
records: &'r FCERecordTypes,
) -> TResult<Vec<TokenStream>> {
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)?;
let module_method = quote! {
pub fn #func_name(&mut self, #(#arguments),*) #output_type {
#fce_call
}
};
Ok(module_method)
})
.collect::<TResult<Vec<_>>>()
}
fn generate_fce_call(
module_name: &str,
method_signature: &FCEFunctionSignature,
records: &FCERecordTypes,
) -> TResult<TokenStream> {
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<Item = &'a str>,
) -> TResult<TokenStream> {
let arg_idents: Vec<syn::Ident> = args.map(utils::new_ident).collect::<Result<_, _>>()?;
let args_converter =
quote! { let arguments = fluence_test::internal::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_with_module_name(#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<TokenStream> {
let result_stream = match output_type {
Some(ty) => {
let ty = utils::itype_to_tokens(ty, records)?;
quote! {
let result: #ty = 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<Item = &'a IFunctionArg>,
records: &'r FCERecordTypes,
) -> TResult<Vec<TokenStream>> {
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::<TResult<Vec<_>>>()
}
fn generate_output_type(output_types: &[IType], records: &FCERecordTypes) -> TResult<TokenStream> {
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<Option<&IType>> {
match output_types.len() {
0 => Ok(None),
1 => Ok(Some(&output_types[0])),
_ => Err(TestGeneratorError::ManyFnOutputsUnsupported),
}
}

View File

@ -0,0 +1,59 @@
/*
* 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;
use crate::TResult;
use fce_wit_parser::interface::it::IRecordFieldType;
use fce_wit_parser::interface::FCERecordTypes;
use proc_macro2::TokenStream;
use quote::quote;
pub(super) fn generate_records(records: &FCERecordTypes) -> TResult<Vec<TokenStream>> {
use std::ops::Deref;
records.iter().map(|(_, record)| -> TResult<_> {
let record_name_ident = utils::generate_record_name(&record.name)?;
let fields = prepare_field(record.fields.deref().iter(), records)?;
let generated_record = quote! {
#[derive(Clone, fluence_test::internal::Serialize, fluence_test::internal::Deserialize)]
pub struct #record_name_ident {
#(#fields),*
}
};
Ok(generated_record)
}
).collect::<TResult<Vec<_>>>()
}
fn prepare_field<'f>(
fields: impl ExactSizeIterator<Item = &'f IRecordFieldType>,
records: &FCERecordTypes,
) -> TResult<Vec<TokenStream>> {
fields
.map(|field| -> TResult<_> {
let field_name = utils::new_ident(&field.name)?;
let field_type = utils::itype_to_tokens(&field.ty, records)?;
let generated_field = quote! { #field_name: #field_type };
Ok(generated_field)
})
.collect::<TResult<Vec<_>>>()
}

View File

@ -0,0 +1,77 @@
/*
* 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::TResult;
use fce_wit_parser::interface::FCERecordTypes;
use fce_wit_parser::interface::it::IType;
use proc_macro2::TokenStream;
use quote::quote;
pub(super) fn generate_module_name(module_name: &str) -> TResult<syn::Ident> {
let extended_module_name = format!("__fce_generated_{}", module_name);
new_ident(&extended_module_name)
}
pub(super) fn generate_record_name(record_name: &str) -> TResult<syn::Ident> {
let extended_record_name = format!("{}", record_name);
new_ident(&extended_record_name)
}
pub(super) fn generate_struct_name(struct_name: &str) -> TResult<syn::Ident> {
let extended_struct_name = format!("FCEGeneratedStruct{}", struct_name);
new_ident(&extended_struct_name)
}
pub(super) fn new_ident(ident_str: &str) -> TResult<syn::Ident> {
syn::parse_str::<syn::Ident>(ident_str).map_err(Into::into)
}
pub(super) fn itype_to_tokens(itype: &IType, records: &FCERecordTypes) -> TResult<TokenStream> {
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!("anyrefs aren't supported and will be deleted from IType soon")
}
};
Ok(token_stream)
}

View File

@ -0,0 +1,36 @@
/*
* 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.
*/
#![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<T> = std::result::Result<T, TestGeneratorError>;

View File

@ -0,0 +1,25 @@
[package]
name = "fluence-sdk-test-macro"
version = "0.5.0" # 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"
authors = ["Fluence Labs"]
keywords = ["fluence", "sdk", "webassembly", "procedural_macros"]
categories = ["api-bindings", "wasm"]
license = "Apache-2.0"
[package.metadata.docs.rs]
all-features = true
[lib]
proc-macro = true
doctest = false
[dependencies]
fluence-sdk-test-macro-impl = { path = "../fce-test-macro-impl", version = "=0.5.0" }
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'] }

View File

@ -0,0 +1,53 @@
/*
* 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"]
use fluence_sdk_test_macro_impl::fce_test_impl;
use proc_macro::TokenStream;
use proc_macro_error::proc_macro_error;
use syn::spanned::Spanned;
/// This macro allows user to write tests for services in the following form:
///```ignore
/// #[fce_test(config = "/path/to/Config.toml", modules_dir = "path/to/service/modules")]
/// fn test() {
/// let service_result = greeting.greeting("John".to_string());
/// assert_eq!(&service_result, "Hi, name!");
/// }
///```
#[proc_macro_error]
#[proc_macro_attribute]
pub fn fce_test(attrs: TokenStream, input: TokenStream) -> TokenStream {
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!(attrs_span, format!("{}", e)),
}
}

View File

@ -10,15 +10,16 @@ keywords = ["fluence", "sdk", "webassembly"]
categories = ["api-bindings", "wasm"] categories = ["api-bindings", "wasm"]
license = "Apache-2.0" license = "Apache-2.0"
[package.metadata.docs.rs] # https://docs.rs/about [package.metadata.docs.rs]
all-features = true all-features = true
[lib] [lib]
path = "src/lib.rs" path = "src/lib.rs"
crate-type = ["rlib"] crate-type = ["rlib"]
doctest = false
[dependencies] [dependencies]
fluence-sdk-macro = { path = "../macro", version = "=0.5.0" } fluence-sdk-macro = { path = "../fce-macro", version = "=0.5.0" }
log = { version = "0.4.8", features = ["std"] } log = { version = "0.4.8", features = ["std"] }
serde = "=1.0.118" serde = "=1.0.118"

View File

@ -34,7 +34,9 @@
mod export_allocator; mod export_allocator;
#[cfg(any(feature = "debug", feature = "logger"))] #[cfg(any(feature = "debug", feature = "logger"))]
mod logger; mod logger;
mod module_manifest;
mod result; mod result;
mod sdk_version_embedder;
pub use export_allocator::allocate; pub use export_allocator::allocate;
pub use export_allocator::deallocate; pub use export_allocator::deallocate;
@ -53,6 +55,9 @@ pub use result::set_result_size;
pub use result::release_objects; pub use result::release_objects;
pub use result::add_object_to_release; pub use result::add_object_to_release;
pub use module_manifest::MANIFEST_SECTION_NAME;
pub use sdk_version_embedder::VERSION_SECTION_NAME;
#[allow(unused_variables)] #[allow(unused_variables)]
pub(crate) fn log<S: AsRef<str>>(msg: S) { pub(crate) fn log<S: AsRef<str>>(msg: S) {
// logs will be printed only if debug feature is enabled // logs will be printed only if debug feature is enabled

View File

@ -0,0 +1,88 @@
/*
* 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.
*/
// TODO: avoid duplication with the link_section when key-value attributes become stable
pub const MANIFEST_SECTION_NAME: &str = "__fluence_wasm_module_manifest";
#[macro_export]
macro_rules! module_manifest {
($authors:expr, $version:expr, $description:expr, $repository:expr) => {
const __FCE_SDK_AUTHORS_SIZE: usize = $authors.as_bytes().len();
const __FCE_SDK_VERSION_SIZE: usize = $version.as_bytes().len();
const __FCE_SDK_DESCRIPTION_SIZE: usize = $description.as_bytes().len();
const __FCE_SDK_REPOSITORY_SIZE: usize = $repository.as_bytes().len();
const __FCE_SDK_FIELD_PREFIX_SIZE: usize = std::mem::size_of::<u64>();
const __FCE_MANIFEST_SIZE: usize = __FCE_SDK_AUTHORS_SIZE
+ __FCE_SDK_VERSION_SIZE
+ __FCE_SDK_DESCRIPTION_SIZE
+ __FCE_SDK_REPOSITORY_SIZE
+ __FCE_SDK_FIELD_PREFIX_SIZE * 4;
const fn __fce_sdk_append_data(
mut manifest: [u8; __FCE_MANIFEST_SIZE],
data: &'static str,
offset: usize,
) -> ([u8; __FCE_MANIFEST_SIZE], usize) {
let data_as_bytes = data.as_bytes();
let data_len = data_as_bytes.len();
// write data prefix with data size in LE
let data_len_u64 = data_len as u64;
let data_len_le_bytes = data_len_u64.to_le_bytes();
let mut byte_idx = 0;
while byte_idx < __FCE_SDK_FIELD_PREFIX_SIZE {
manifest[offset + byte_idx] = data_len_le_bytes[byte_idx];
byte_idx += 1;
}
// write data
let mut byte_idx = 0;
while byte_idx < data_len {
manifest[__FCE_SDK_FIELD_PREFIX_SIZE + offset + byte_idx] = data_as_bytes[byte_idx];
byte_idx += 1;
}
(manifest, offset + __FCE_SDK_FIELD_PREFIX_SIZE + data_len)
}
const fn generate_manifest() -> [u8; __FCE_MANIFEST_SIZE] {
let manifest: [u8; __FCE_MANIFEST_SIZE] = [0; __FCE_MANIFEST_SIZE];
let offset = 0;
let (manifest, offset) = __fce_sdk_append_data(manifest, $authors, offset);
let (manifest, offset) = __fce_sdk_append_data(manifest, $version, offset);
let (manifest, offset) = __fce_sdk_append_data(manifest, $description, offset);
let (manifest, _) = __fce_sdk_append_data(manifest, $repository, offset);
manifest
}
#[cfg(target_arch = "wasm32")]
#[link_section = "__fluence_wasm_module_manifest"]
#[doc(hidden)]
pub static __FCE_WASM_MODULE_MANIFEST: [u8; __FCE_MANIFEST_SIZE] = generate_manifest();
};
() => {
module_manifest!(
env!("CARGO_PKG_AUTHORS"),
env!("CARGO_PKG_VERSION"),
env!("CARGO_PKG_DESCRIPTION"),
env!("CARGO_PKG_REPOSITORY")
);
};
}

View File

@ -0,0 +1,41 @@
/*
* 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.
*/
#![allow(dead_code)]
const PKG_VERSION: &str = env!("CARGO_PKG_VERSION");
const VERSION_SIZE: usize = PKG_VERSION.len();
const fn sdk_version() -> [u8; VERSION_SIZE] {
let version_as_slice = PKG_VERSION.as_bytes();
let mut version_as_array: [u8; VERSION_SIZE] = [0; VERSION_SIZE];
let mut byte_id = 0;
while byte_id < VERSION_SIZE {
version_as_array[byte_id] = version_as_slice[byte_id];
byte_id += 1;
}
version_as_array
}
// TODO: avoid duplication with the link_section when key-value attributes become stable
pub const VERSION_SECTION_NAME: &str = "__fluence_sdk_version";
#[cfg(target_arch = "wasm32")]
#[link_section = "__fluence_sdk_version"]
#[doc(hidden)]
pub static __FCE_SDK_VERSION: [u8; VERSION_SIZE] = sdk_version();

View File

@ -10,13 +10,13 @@ keywords = ["fluence", "sdk", "webassembly", "wit", "interface-types"]
categories = ["api-bindings", "wasm"] categories = ["api-bindings", "wasm"]
license = "Apache-2.0" license = "Apache-2.0"
[package.metadata.docs.rs] # https://docs.rs/about [package.metadata.docs.rs]
all-features = true all-features = true
[dependencies] [dependencies]
quote = "1.0.7" quote = "1.0.9"
proc-macro2 = "1.0.18" proc-macro2 = "1.0.24"
serde = { version = "=1.0.118", features = ["derive"] } serde = { version = "=1.0.118", features = ["derive"] }
serde_json = "1.0.56" serde_json = "1.0.56"
syn = { version = '1.0.33', features = ['full'] } syn = { version = '1.0.64', features = ['full'] }
uuid = { version = "0.8.1", features = ["v4"] } uuid = { version = "0.8.2", features = ["v4"] }

View File

@ -99,7 +99,7 @@ impl ParsedType {
} }
_ if !type_segment.arguments.is_empty() => Err(Error::new( _ if !type_segment.arguments.is_empty() => Err(Error::new(
type_segment.span(), type_segment.span(),
"type with lifetimes or generics aren't allowed".to_string(), "types with lifetimes or generics aren't allowed".to_string(),
)), )),
_ => Ok(ParsedType::Record( _ => Ok(ParsedType::Record(
(&type_segment.ident).into_token_stream().to_string(), (&type_segment.ident).into_token_stream().to_string(),

View File

@ -31,7 +31,7 @@ pub(crate) struct FnEpilogDescriptor {
/// This trait could be used to generate various parts needed to construct epilog of an export /// This trait could be used to generate various parts needed to construct epilog of an export
/// function. They are marked with # in the following example: /// function. They are marked with # in the following example:
/// ``` /// ```ignore
/// quote! { /// quote! {
/// pub unsafe fn foo(...) #fn_return_type { /// pub unsafe fn foo(...) #fn_return_type {
/// ... /// ...

View File

@ -34,7 +34,7 @@ pub(crate) struct FnPrologDescriptor {
/// This trait could be used to generate various parts needed to construct prolog of an export /// This trait could be used to generate various parts needed to construct prolog of an export
/// function. They are marked with # in the following example: /// function. They are marked with # in the following example:
/// ``` /// ```ignore
/// quote! { /// quote! {
/// fn foo(#(#raw_arg_names: #raw_arg_types),*) { /// fn foo(#(#raw_arg_names: #raw_arg_types),*) {
/// #prolog /// #prolog

View File

@ -34,7 +34,7 @@ pub(crate) struct ExternDescriptor {
/// This trait could be used to generate various parts needed to construct prolog of an wrapper /// This trait could be used to generate various parts needed to construct prolog of an wrapper
/// function or extern block. They are marked with # in the following examples: /// function or extern block. They are marked with # in the following examples:
/// ``` /// ```ignore
/// quote! { /// quote! {
/// fn foo(#(#arg_names: #arg_types), *) { /// fn foo(#(#arg_names: #arg_types), *) {
/// let arg_1 = std::mem::ManuallyDrop::new(arg_1); /// let arg_1 = std::mem::ManuallyDrop::new(arg_1);
@ -45,7 +45,7 @@ pub(crate) struct ExternDescriptor {
/// } /// }
/// ``` /// ```
/// ///
/// ``` /// ```ignore
/// quote! { /// quote! {
/// extern "C" { /// extern "C" {
/// #[link_name = "foo_link_name"] /// #[link_name = "foo_link_name"]

27
fluence-test/Cargo.toml Normal file
View File

@ -0,0 +1,27 @@
[package]
name = "fluence-test"
version = "0.5.0" # remember to update html_root_url
description = "Fluence backend SDK for testing"
documentation = "https://docs.rs/fluence/"
repository = "https://github.com/fluencelabs/rust-sdk"
authors = ["Fluence Labs"]
readme = "README.md"
keywords = ["fluence", "sdk", "webassembly"]
categories = ["api-bindings", "wasm"]
license = "Apache-2.0"
edition = "2018"
[package.metadata.docs.rs]
all-features = true
[lib]
path = "src/lib.rs"
doctest = false
[dependencies]
fluence-sdk-test-macro = { path = "../crates/fce-test-macro", 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"] }

42
fluence-test/src/lib.rs Normal file
View File

@ -0,0 +1,42 @@
/*
* 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-test/0.5.0")]
#![deny(
dead_code,
nonstandard_style,
unused_imports,
unused_mut,
unused_variables,
unused_unsafe,
unreachable_patterns
)]
#![warn(rust_2018_idioms)]
pub use fluence_sdk_test_macro::fce_test;
/// 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;
}

35
fluence/Cargo.toml Normal file
View File

@ -0,0 +1,35 @@
[package]
name = "fluence"
version = "0.5.0" # remember to update html_root_url
description = "Fluence backend SDK for developing backend applications for the Fluence network"
documentation = "https://docs.rs/fluence/"
repository = "https://github.com/fluencelabs/rust-sdk"
authors = ["Fluence Labs"]
readme = "README.md"
keywords = ["fluence", "sdk", "webassembly"]
categories = ["api-bindings", "wasm"]
license = "Apache-2.0"
edition = "2018"
[package.metadata.docs.rs]
all-features = true
[lib]
path = "src/lib.rs"
doctest = false
[dependencies]
fluence-sdk-macro = { path = "../crates/fce-macro", version = "=0.5.0" }
fluence-sdk-main = { path = "../crates/main", version = "=0.5.0" }
serde = { version = "1.0.118", features = ["derive"]}
[dev-dependencies]
trybuild = "1.0"
[features]
# Print some internal logs by log_utf8_string
debug = ["fluence-sdk-main/debug"]
# Enable logger (this will cause log_utf8_string to appear in imports)
logger = ["fluence-sdk-main/logger"]

View File

@ -70,9 +70,11 @@
mod call_parameters; mod call_parameters;
mod mounted_binary; mod mounted_binary;
extern crate self as fluence; // extern crate self as fluence;
pub use fluence_sdk_macro::fce; pub use fluence_sdk_macro::fce;
#[cfg(feature = "fce-test")]
pub use fluence_sdk_test_macro::fce_test;
pub use call_parameters::CallParameters; pub use call_parameters::CallParameters;
pub use call_parameters::SecurityTetraplet; pub use call_parameters::SecurityTetraplet;
@ -88,6 +90,8 @@ pub use mounted_binary::Result as MountedBinaryResult;
pub use mounted_binary::StringResult as MountedBinaryStringResult; pub use mounted_binary::StringResult as MountedBinaryStringResult;
pub use mounted_binary::SUCCESS_CODE as BINARY_SUCCESS_CODE; pub use mounted_binary::SUCCESS_CODE as BINARY_SUCCESS_CODE;
pub use fluence_sdk_main::module_manifest;
/// These API functions are intended for internal usage in generated code. /// These API functions are intended for internal usage in generated code.
/// Normally, you shouldn't use them. /// Normally, you shouldn't use them.
pub mod internal { pub mod internal {

View File

@ -0,0 +1,67 @@
#![allow(improper_ctypes)]
use fluence::fce;
pub fn main() {}
#[fce]
pub fn byte_type( __arg: Vec<u8>) -> Vec<u8> {
unimplemented!()
}
#[fce]
pub fn inner_arrays_1(_arg: Vec<Vec<Vec<Vec<u8>>>>) -> Vec<Vec<Vec<Vec<u8>>>> {
unimplemented!()
}
#[fce]
#[derive(Default)]
pub struct TestRecord {
pub field_0: i32,
pub field_1: Vec<Vec<u8>>,
}
#[fce]
pub fn inner_arrays_2(_arg: Vec<Vec<Vec<Vec<TestRecord>>>>) -> Vec<Vec<Vec<Vec<TestRecord>>>> {
unimplemented!()
}
#[fce]
pub fn string_type(_arg: Vec<String>) -> Vec<String> {
unimplemented!()
}
#[fce]
pub fn f32_type(_arg: Vec<f32>) -> Vec<f32> {
unimplemented!()
}
#[fce]
pub fn f64_type(_arg: Vec<f64>) -> Vec<f64> {
unimplemented!()
}
#[fce]
pub fn u32_type(_arg: Vec<u32>) -> Vec<u32> {
unimplemented!()
}
#[fce]
pub fn u64_type(_arg: Vec<u64>) -> Vec<u64> {
unimplemented!()
}
#[fce]
pub fn i32_type(_arg: Vec<i32>) -> Vec<i32> {
unimplemented!()
}
#[fce]
pub fn i64_type(_arg: Vec<i64>) -> Vec<i64> {
unimplemented!()
}
#[fce]
pub fn empty_type() -> Vec<String> {
unimplemented!()
}

View File

@ -0,0 +1,73 @@
#![allow(improper_ctypes)]
use fluence::fce;
pub fn main() {}
#[fce]
pub fn all_types(
_arg_0: i8,
_arg_1: i16,
_arg_2: i32,
_arg_3: i64,
_arg_4: u8,
_arg_5: u16,
_arg_6: u32,
_arg_7: u64,
_arg_8: f32,
_arg_9: f64,
_arg_10: String,
_arg_11: Vec<u8>,
) -> Vec<u8> {
unimplemented!()
}
#[fce]
pub fn string_type(_arg: String) -> String {
unimplemented!()
}
#[fce]
pub fn bytearray_type(_arg: Vec<u8>) -> Vec<u8> {
unimplemented!()
}
#[fce]
pub fn bool_type(_arg: bool) -> bool {
unimplemented!()
}
#[fce]
pub fn f32_type(_arg: f32) -> f32 {
unimplemented!()
}
#[fce]
pub fn f64_type(_arg: f64) -> f64 {
unimplemented!()
}
#[fce]
pub fn u32_type(_arg: u32) -> u32 {
unimplemented!()
}
#[fce]
pub fn u64_type(_arg: u64) -> u64 {
unimplemented!()
}
#[fce]
pub fn i32_type(_arg: i32) -> i32 {
unimplemented!()
}
#[fce]
pub fn i64_type(_arg: i64) -> i64 {
unimplemented!()
}
#[fce]
pub fn empty_type() -> String {
unimplemented!()
}

View File

@ -0,0 +1,24 @@
#![allow(improper_ctypes)]
use fluence::fce;
pub fn main() {}
#[fce]
fn test(_arg_1: Box<i32>) {}
#[fce]
fn test2(_arg_1: std::rc::Rc<i32>) {}
#[fce]
fn test3(_arg_1: std::collections::HashMap<i32, String>) {}
#[fce]
fn test4(_arg_1: i32) -> (i32, i32) {
unimplemented!()
}
#[fce]
fn test5(_arg_1: i32) -> Box<i32> {
unimplemented!()
}

View File

@ -0,0 +1,29 @@
error: types with lifetimes or generics aren't allowed
--> $DIR/improper_types.rs:8:17
|
8 | fn test(_arg_1: Box<i32>) {}
| ^^^^^^^^
error: types with lifetimes or generics aren't allowed
--> $DIR/improper_types.rs:11:27
|
11 | fn test2(_arg_1: std::rc::Rc<i32>) {}
| ^^^^^^^
error: types with lifetimes or generics aren't allowed
--> $DIR/improper_types.rs:14:36
|
14 | fn test3(_arg_1: std::collections::HashMap<i32, String>) {}
| ^^^^^^^^^^^^^^^^^^^^
error: Incorrect argument type - passing only by value is supported now
--> $DIR/improper_types.rs:17:26
|
17 | fn test4(_arg_1: i32) -> (i32, i32) {
| ^^^^^^^^^^
error: types with lifetimes or generics aren't allowed
--> $DIR/improper_types.rs:22:26
|
22 | fn test5(_arg_1: i32) -> Box<i32> {
| ^^^^^^^^

View File

@ -0,0 +1,40 @@
#![allow(improper_ctypes)]
use fluence::fce;
pub fn main() {}
#[fce]
#[derive(Default)]
pub struct TestRecord {
pub field_0: i32,
pub field_1: Vec<Vec<u8>>,
}
#[fce]
#[link(wasm_import_module = "arrays_passing_effector")]
extern "C" {
pub fn inner_arrays_1(arg: Vec<Vec<Vec<Vec<u8>>>>) -> Vec<Vec<Vec<Vec<u8>>>>;
pub fn inner_arrays_2(
arg: Vec<Vec<Vec<Vec<TestRecord>>>>,
) -> Vec<Vec<Vec<Vec<TestRecord>>>>;
pub fn string_type(arg: Vec<String>) -> Vec<String>;
pub fn byte_type(arg: Vec<u8>) -> Vec<u8>;
pub fn f32_type(arg: Vec<f32>) -> Vec<f32>;
pub fn f64_type(arg: Vec<f64>) -> Vec<f64>;
pub fn u32_type(arg: Vec<u32>) -> Vec<u32>;
pub fn u64_type(arg: Vec<u64>) -> Vec<u64>;
pub fn i32_type(arg: Vec<i32>) -> Vec<i32>;
pub fn i64_type(arg: Vec<i64>) -> Vec<i64>;
pub fn empty_type() -> Vec<String>;
}

View File

@ -0,0 +1,40 @@
#![allow(improper_ctypes)]
use fluence::fce;
fn main() {}
#[fce]
#[link(wasm_import_module = "arguments_passing_effector")]
extern "C" {
pub fn all_types(
arg_0: i8,
arg_1: i16,
arg_2: i32,
arg_3: i64,
arg_4: u8,
arg_5: u16,
arg_6: u32,
arg_7: u64,
arg_8: f32,
arg_9: f64,
arg_10: String,
arg_11: Vec<u8>,
) -> Vec<u8>;
pub fn string_type(arg: String) -> String;
pub fn bytearray_type(arg: Vec<u8>) -> Vec<u8>;
pub fn bool_type(arg: bool) -> bool;
pub fn f32_type(arg: f32) -> f32;
pub fn f64_type(arg: f64) -> f64;
pub fn u32_type(arg: u32) -> u32;
pub fn u64_type(arg: u64) -> u64;
pub fn i32_type(arg: i32) -> i32;
pub fn i64_type(arg: i64) -> i64;
pub fn empty_type() -> String;
}

View File

@ -0,0 +1,24 @@
#![allow(improper_ctypes)]
use fluence::fce;
pub fn main() {}
#[fce]
#[link(wasm_import_module = "arguments_passing_effector")]
extern "C" {
#[fce]
fn test(_arg_1: Box<i32>);
#[fce]
fn test2(_arg_1: std::rc::Rc<i32>);
#[fce]
fn test3(_arg_1: std::collections::HashMap<i32, String>);
#[fce]
fn test4(_arg_1: i32) -> (i32, i32);
#[fce]
fn test5(_arg_1: i32) -> Box<i32>;
}

View File

@ -0,0 +1,5 @@
error: types with lifetimes or generics aren't allowed
--> $DIR/improper_types.rs:11:21
|
11 | fn test(_arg_1: Box<i32>);
| ^^^^^^^^

View File

@ -0,0 +1,82 @@
#![allow(improper_ctypes)]
use fluence::fce;
fn main() {}
#[fce]
pub struct TestRecord {
pub field_0: bool,
pub field_1: i8,
pub field_2: i16,
pub field_3: i32,
pub field_4: i64,
pub field_5: u8,
pub field_6: u16,
pub field_7: u32,
pub field_8: u64,
pub field_9: f32,
pub field_10: f64,
pub field_11: String,
pub field_12: Vec<u8>,
}
#[fce]
pub struct Tx {
pub block_hash: String,
pub block_number: String,
pub from: String,
pub gas: String,
pub gas_price: String,
pub hash: String,
pub input: String,
pub nonce: String,
pub to: String,
pub transaction_index: String,
pub value: String,
}
#[fce]
#[derive(Debug)]
pub struct JsonRpcResult {
pub json_rpc: String,
pub result: String,
pub error: String,
pub id: u64,
}
#[fce]
#[derive(Clone, Debug, Default, Eq, PartialEq, Hash)]
pub struct User {
pub peer_id: String,
pub relay_id: String,
pub signature: String,
pub name: String,
}
#[fce]
pub struct GetUsersServiceResult {
pub ret_code: i32,
pub err_msg: String,
pub users: Vec<User>,
}
#[fce]
pub struct EmptyServiceResult {
pub ret_code: i32,
pub err_msg: String,
}
#[fce]
pub struct ExistsServiceResult {
pub ret_code: i32,
pub err_msg: String,
pub is_exists: bool,
}
#[fce]
pub struct AuthResult {
pub ret_code: i32,
pub err_msg: String,
pub is_authenticated: bool,
}

View File

@ -0,0 +1,8 @@
#![allow(improper_ctypes)]
use fluence::fce;
fn main() {}
#[fce]
struct A {}

View File

@ -0,0 +1,20 @@
#![allow(improper_ctypes)]
use fluence::fce;
fn main() {}
#[fce]
struct StructWithBox {
pub a: Box<i32>,
}
#[fce]
struct StructWithRc {
pub a: std::rc::Rc<i32>,
}
#[fce]
struct StructWithHashMap {
pub a: std::collections::HashMap<i32, String>,
}

View File

@ -0,0 +1,17 @@
error: types with lifetimes or generics aren't allowed
--> $DIR/struct_with_improper_types.rs:9:12
|
9 | pub a: Box<i32>,
| ^^^^^^^^
error: types with lifetimes or generics aren't allowed
--> $DIR/struct_with_improper_types.rs:14:21
|
14 | pub a: std::rc::Rc<i32>,
| ^^^^^^^
error: types with lifetimes or generics aren't allowed
--> $DIR/struct_with_improper_types.rs:19:30
|
19 | pub a: std::collections::HashMap<i32, String>,
| ^^^^^^^^^^^^^^^^^^^^

View File

@ -0,0 +1,11 @@
#![allow(improper_ctypes)]
use fluence::fce;
fn main() {}
#[fce]
struct StructWithPrivateFields {
a: i32,
b: usize,
}

View File

@ -0,0 +1,5 @@
error: #[fce] could be applied only to struct with all public fields
--> $DIR/struct_with_private_fields.rs:9:5
|
9 | a: i32,
| ^^^^^^

View File

@ -0,0 +1,16 @@
#[test]
fn test() {
let tests = trybuild::TestCases::new();
tests.pass("tests/export_functions/arrays.rs");
tests.pass("tests/export_functions/basic_types.rs");
tests.compile_fail("tests/export_functions/improper_types.rs");
tests.pass("tests/import_functions/arrays.rs");
tests.pass("tests/import_functions/basic_types.rs");
tests.compile_fail("tests/import_functions/improper_types.rs");
tests.pass("tests/records/basic_structs.rs");
tests.pass("tests/records/empty_struct.rs");
tests.compile_fail("tests/records/struct_with_improper_types.rs");
tests.compile_fail("tests/records/struct_with_private_fields.rs");
}