From 9219339b82154bb207723998b4c3c16743d852cd Mon Sep 17 00:00:00 2001 From: Valery Antopol Date: Wed, 23 Feb 2022 16:40:43 +0300 Subject: [PATCH] decouple json serde into separate crate, add module and function names to args serde errors --- Cargo.lock | 14 + Cargo.toml | 1 + crates/it-json-serde/Cargo.toml | 20 ++ .../it-json-serde/src/errors.rs | 13 +- .../it-json-serde/src}/ivalues_to_json.rs | 28 +- .../it-json-serde/src}/json_to_ivalues.rs | 57 ++-- crates/it-json-serde/src/lib.rs | 41 +++ fluence-faas/Cargo.toml | 1 + fluence-faas/src/errors.rs | 38 ++- fluence-faas/src/faas.rs | 23 +- fluence-faas/src/json/json_to_ivalues.rs | 244 ------------------ fluence-faas/src/json/mod.rs | 21 -- fluence-faas/src/lib.rs | 1 - web-runtime/Cargo.toml | 1 + web-runtime/src/faas/errors.rs | 38 ++- web-runtime/src/faas/faas.rs | 23 +- web-runtime/src/faas/json/ivalues_to_json.rs | 123 --------- web-runtime/src/faas/mod.rs | 1 - 18 files changed, 231 insertions(+), 457 deletions(-) create mode 100644 crates/it-json-serde/Cargo.toml rename web-runtime/src/faas/json/mod.rs => crates/it-json-serde/src/errors.rs (70%) rename {fluence-faas/src/json => crates/it-json-serde/src}/ivalues_to_json.rs (88%) rename {web-runtime/src/faas/json => crates/it-json-serde/src}/json_to_ivalues.rs (85%) create mode 100644 crates/it-json-serde/src/lib.rs delete mode 100644 fluence-faas/src/json/json_to_ivalues.rs delete mode 100644 fluence-faas/src/json/mod.rs delete mode 100644 web-runtime/src/faas/json/ivalues_to_json.rs diff --git a/Cargo.lock b/Cargo.lock index 6aa37c82..e50e2011 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -832,6 +832,7 @@ dependencies = [ "bytesize", "cmd_lib", "env_logger 0.7.1", + "it-json-serde", "itertools 0.9.0", "log", "marine-module-interface 0.2.0", @@ -1335,6 +1336,18 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" +[[package]] +name = "it-json-serde" +version = "0.1.0" +dependencies = [ + "serde", + "serde_derive", + "serde_json", + "serde_with", + "thiserror", + "wasmer-interface-types-fl 0.21.1", +] + [[package]] name = "it-lilo" version = "0.1.0" @@ -1871,6 +1884,7 @@ dependencies = [ "bytesize", "console_error_panic_hook", "fluence-it-types", + "it-json-serde", "it-lilo 0.2.0", "it-memory-traits", "itertools 0.10.3", diff --git a/Cargo.toml b/Cargo.toml index 5c55c160..018ff55b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ members = [ "crates/it-generator", "crates/it-interfaces", "crates/it-parser", + "crates/it-json-serde", "crates/min-it-version", "crates/module-info-parser", "crates/module-interface", diff --git a/crates/it-json-serde/Cargo.toml b/crates/it-json-serde/Cargo.toml new file mode 100644 index 00000000..8f0bbca5 --- /dev/null +++ b/crates/it-json-serde/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "it-json-serde" +description = "Fluence Marine interface-types serde tools" +version = "0.1.0" +authors = ["Fluence Labs"] +license = "Apache-2.0" +edition = "2018" + +[lib] +name = "it_json_serde" +path = "src/lib.rs" + +[dependencies] +wasmer-it = { package = "wasmer-interface-types-fl", version = "0.21.1" } + +serde = { version = "1.0.118", features = ["derive"] } +serde_json = "1.0.53" +serde_derive = "1.0.118" +serde_with = "1.11.0" +thiserror = "1.0.23" \ No newline at end of file diff --git a/web-runtime/src/faas/json/mod.rs b/crates/it-json-serde/src/errors.rs similarity index 70% rename from web-runtime/src/faas/json/mod.rs rename to crates/it-json-serde/src/errors.rs index 8f0cdbdd..e4ed2de3 100644 --- a/web-runtime/src/faas/json/mod.rs +++ b/crates/it-json-serde/src/errors.rs @@ -14,8 +14,13 @@ * limitations under the License. */ -mod ivalues_to_json; -mod json_to_ivalues; +use thiserror::Error; -pub(crate) use ivalues_to_json::ivalues_to_json; -pub(crate) use json_to_ivalues::json_to_ivalues; +#[derive(Debug, Error)] +pub enum ItJsonSerdeError { + #[error("cannot serialize IValues to Json: {0}")] + SerializationError(String), + + #[error("cannot deserialize Json to IValues: {0}")] + DeserializationError(String), +} diff --git a/fluence-faas/src/json/ivalues_to_json.rs b/crates/it-json-serde/src/ivalues_to_json.rs similarity index 88% rename from fluence-faas/src/json/ivalues_to_json.rs rename to crates/it-json-serde/src/ivalues_to_json.rs index 66a755a5..028d7445 100644 --- a/fluence-faas/src/json/ivalues_to_json.rs +++ b/crates/it-json-serde/src/ivalues_to_json.rs @@ -1,5 +1,5 @@ /* - * Copyright 2020 Fluence Labs Limited + * Copyright 2022 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. @@ -16,19 +16,19 @@ use crate::IValue; use crate::IType; -use crate::FaaSResult; -use crate::errors::FaaSError::JsonOutputSerializationError as OutputDeError; +use crate::ItJsonSerdeError::SerializationError; +use crate::JsonResult; -use marine::MRecordTypes; +use crate::MRecordTypes; use serde_json::Value as JValue; -pub(crate) fn ivalues_to_json( +pub fn ivalues_to_json( mut ivalues: Vec, outputs: &[IType], record_types: &MRecordTypes, -) -> FaaSResult { +) -> JsonResult { if outputs.len() != ivalues.len() { - return Err(OutputDeError(format!( + return Err(SerializationError(format!( "resulted values {:?} and function signature {:?} aren't compatible", ivalues, outputs ))); @@ -46,7 +46,7 @@ fn ivalue_to_json( ivalue: IValue, output: &IType, record_types: &MRecordTypes, -) -> FaaSResult { +) -> JsonResult { use serde_json::json; // clone here needed because binding by-value and by-ref in the same pattern in unstable @@ -70,7 +70,7 @@ fn ivalue_to_json( Ok(JValue::Array(result)) } (IValue::Array(value), IType::ByteArray) => { - let result: FaaSResult> = value + let result: JsonResult> = value .into_iter() .map(|v| ivalue_to_json(v, &IType::U8, record_types)) .collect(); @@ -78,7 +78,7 @@ fn ivalue_to_json( Ok(JValue::Array(result?)) } (IValue::ByteArray(value), IType::Array(array_ty)) => { - let result: FaaSResult> = value + let result: JsonResult> = value .into_iter() .map(|v| ivalue_to_json(IValue::U8(v), &array_ty, record_types)) .collect(); @@ -86,7 +86,7 @@ fn ivalue_to_json( Ok(JValue::Array(result?)) } (IValue::Array(value), IType::Array(array_ty)) => { - let result: FaaSResult> = value + let result: JsonResult> = value .into_iter() .map(|v| ivalue_to_json(v, &array_ty, record_types)) .collect(); @@ -95,7 +95,7 @@ fn ivalue_to_json( } (IValue::Record(field_values), IType::Record(record_id)) => { let record_type = record_types.get(&record_id).ok_or_else(|| { - OutputDeError(format!( + SerializationError(format!( "record id {} wasn't found in module record types", record_id )) @@ -103,7 +103,7 @@ fn ivalue_to_json( let field_types = &record_type.fields; if field_values.len() != field_types.len() { - return Err(OutputDeError(format!( + return Err(SerializationError(format!( "output record {:?} isn't compatible to output record fields {:?}", field_values, field_types ))); @@ -119,7 +119,7 @@ fn ivalue_to_json( Ok(JValue::Object(result)) } - (ivalue, itype) => Err(OutputDeError(format!( + (ivalue, itype) => Err(SerializationError(format!( "value {:?} is incompatible to type {:?}", ivalue, itype ))), diff --git a/web-runtime/src/faas/json/json_to_ivalues.rs b/crates/it-json-serde/src/json_to_ivalues.rs similarity index 85% rename from web-runtime/src/faas/json/json_to_ivalues.rs rename to crates/it-json-serde/src/json_to_ivalues.rs index 84639591..27e1b50c 100644 --- a/web-runtime/src/faas/json/json_to_ivalues.rs +++ b/crates/it-json-serde/src/json_to_ivalues.rs @@ -16,22 +16,22 @@ use crate::IValue; use crate::IType; -use crate::faas::Result; -use crate::faas::FaaSError::JsonArgumentsDeserializationError as ArgDeError; -use crate::MRecordTypes; +use super::ItJsonSerdeError::DeserializationError; +use super::JsonResult; +use crate::MRecordTypes; +use serde_json::Value as JValue; use wasmer_it::NEVec; -use serde_json::Value as JValue; use std::collections::HashMap; use std::iter::ExactSizeIterator; /// Convert json to an array of ivalues according to the supplied argument types. -pub(crate) fn json_to_ivalues<'a, 'b>( +pub fn json_to_ivalues<'a, 'b>( json_args: JValue, arg_types: impl Iterator + ExactSizeIterator, record_types: &'b MRecordTypes, -) -> Result> { +) -> JsonResult> { let ivalues = match json_args { JValue::Object(json_map) => json_map_to_ivalues(json_map, arg_types, record_types)?, JValue::Array(json_array) => { @@ -49,19 +49,19 @@ fn json_map_to_ivalues<'a, 'b>( mut json_map: serde_json::Map, arg_types: impl Iterator, record_types: &'b MRecordTypes, -) -> Result> { +) -> JsonResult> { let mut iargs = Vec::new(); for (arg_name, arg_type) in arg_types { - let json_value = json_map - .remove(arg_name) - .ok_or_else(|| ArgDeError(format!("missing argument with name {}", arg_name)))?; + let json_value = json_map.remove(arg_name).ok_or_else(|| { + DeserializationError(format!("missing argument with name {}", arg_name)) + })?; let iarg = jvalue_to_ivalue(json_value, arg_type, record_types)?; iargs.push(iarg); } if !json_map.is_empty() { - return Err(ArgDeError(format!( + return Err(DeserializationError(format!( "function requires {} arguments, {} provided", iargs.len(), iargs.len() + json_map.len() @@ -76,9 +76,9 @@ fn json_array_to_ivalues<'a, 'b>( json_array: Vec, arg_types: impl Iterator + ExactSizeIterator, record_types: &'b MRecordTypes, -) -> Result> { +) -> JsonResult> { if json_array.len() != arg_types.len() { - return Err(ArgDeError(format!( + return Err(DeserializationError(format!( "function requires {} arguments, {} provided", arg_types.len(), json_array.len() @@ -89,7 +89,7 @@ fn json_array_to_ivalues<'a, 'b>( .into_iter() .zip(arg_types) .map(|(json_value, arg_type)| jvalue_to_ivalue(json_value, arg_type, record_types)) - .collect::>>()?; + .collect::>>()?; Ok(iargs) } @@ -98,9 +98,9 @@ fn json_array_to_ivalues<'a, 'b>( fn json_value_to_ivalues<'a>( json_value: JValue, mut arg_types: impl Iterator + ExactSizeIterator, -) -> Result> { +) -> JsonResult> { if arg_types.len() != 1 { - return Err(ArgDeError(format!( + return Err(DeserializationError(format!( "called function has the following signature: '{:?}', and it isn't suitable for an argument '{:?}' provided", arg_types.collect::>(), json_value, @@ -117,9 +117,9 @@ fn json_value_to_ivalues<'a>( /// Convert json Null to an empty array of ivalues. fn json_null_to_ivalues<'a>( arg_types: impl Iterator + ExactSizeIterator, -) -> Result> { +) -> JsonResult> { if arg_types.len() != 0 { - return Err(ArgDeError(format!( + return Err(DeserializationError(format!( "the called function has the following signature: {:?}, but no arguments is provided", arg_types.collect::>() ))); @@ -129,7 +129,7 @@ fn json_null_to_ivalues<'a>( } /// Convert one JValue to an array of ivalues according to the supplied argument type. -fn jvalue_to_ivalue(jvalue: JValue, ty: &IType, record_types: &MRecordTypes) -> Result { +fn jvalue_to_ivalue(jvalue: JValue, ty: &IType, record_types: &MRecordTypes) -> JsonResult { macro_rules! to_ivalue( ($json_value:expr, $ty:ident) => { { @@ -142,7 +142,7 @@ fn jvalue_to_ivalue(jvalue: JValue, ty: &IType, record_types: &MRecordTypes) -> }, jvalue => serde_json::from_value(jvalue), }.map_err(|e| - ArgDeError(format!("error {:?} occurred while deserialize output result to a json value",e)) + DeserializationError(format!("error {:?} occurred while deserialize output result to a json value",e)) )?; Ok(IValue::$ty(value)) @@ -169,11 +169,14 @@ fn jvalue_to_ivalue(jvalue: JValue, ty: &IType, record_types: &MRecordTypes) -> let iargs = json_array .into_iter() .map(|json_value| jvalue_to_ivalue(json_value, &IType::U8, record_types)) - .collect::>>()?; + .collect::>>()?; Ok(iargs) } - _ => Err(ArgDeError(format!("expected bytearray, got {:?}", jvalue))), + _ => Err(DeserializationError(format!( + "expected bytearray, got {:?}", + jvalue + ))), }?; Ok(IValue::Array(value)) @@ -184,11 +187,11 @@ fn jvalue_to_ivalue(jvalue: JValue, ty: &IType, record_types: &MRecordTypes) -> let iargs = json_array .into_iter() .map(|json_value| jvalue_to_ivalue(json_value, value_type, record_types)) - .collect::>>()?; + .collect::>>()?; Ok(iargs) } - _ => Err(ArgDeError(format!( + _ => Err(DeserializationError(format!( "expected array of {:?} types, got {:?}", value_type, jvalue ))), @@ -212,9 +215,9 @@ fn json_record_type_to_ivalue( json_value: JValue, record_type_id: &u64, record_types: &MRecordTypes, -) -> Result> { +) -> JsonResult> { let record_type = record_types.get(record_type_id).ok_or_else(|| { - ArgDeError(format!( + DeserializationError(format!( "record with type id `{}` wasn't found", record_type_id )) @@ -236,7 +239,7 @@ fn json_record_type_to_ivalue( record_types, )?) .unwrap()), - _ => Err(ArgDeError(format!( + _ => Err(DeserializationError(format!( "record with type id `{}` should be encoded as array or map of fields", record_type_id ))), diff --git a/crates/it-json-serde/src/lib.rs b/crates/it-json-serde/src/lib.rs new file mode 100644 index 00000000..f19a73c8 --- /dev/null +++ b/crates/it-json-serde/src/lib.rs @@ -0,0 +1,41 @@ +/* + * Copyright 2022 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. + */ +#![warn(rust_2018_idioms)] +#![deny( + dead_code, + nonstandard_style, + unused_imports, + unused_mut, + unused_variables, + unused_unsafe, + unreachable_patterns +)] +mod ivalues_to_json; +mod json_to_ivalues; +mod errors; + +pub type JsonResult = Result; +pub use errors::ItJsonSerdeError; +pub use ivalues_to_json::ivalues_to_json; +pub use json_to_ivalues::json_to_ivalues; + +use std::collections::HashMap; +use std::rc::Rc; + +pub(crate) use wasmer_it::IValue; +pub(crate) use wasmer_it::IType; +pub(crate) use wasmer_it::IRecordType; +pub(crate) type MRecordTypes = HashMap>; diff --git a/fluence-faas/Cargo.toml b/fluence-faas/Cargo.toml index 0c3df924..7303011d 100644 --- a/fluence-faas/Cargo.toml +++ b/fluence-faas/Cargo.toml @@ -12,6 +12,7 @@ marine-module-interface = { path = "../crates/module-interface", version = "0.2. marine-utils = { path = "../crates/utils", version = "0.4.0" } marine-rs-sdk-main = { version = "0.6.15", features = ["logger"] } marine-rs-sdk = { version = "0.6.15", features = ["logger"] } +it-json-serde = { path = "../crates/it-json-serde", version = "0.1.0" } wasmer-runtime = { package = "wasmer-runtime-fl", version = "=0.17.1" } # dynamicfunc-fat-closures allows using state inside DynamicFunc diff --git a/fluence-faas/src/errors.rs b/fluence-faas/src/errors.rs index 46ee1fbb..7db85226 100644 --- a/fluence-faas/src/errors.rs +++ b/fluence-faas/src/errors.rs @@ -54,12 +54,20 @@ pub enum FaaSError { NoSuchModule(String), /// Provided arguments aren't compatible with a called function signature. - #[error("arguments from json deserialization error: {0}")] - JsonArgumentsDeserializationError(String), + #[error("arguments from json deserialization error in module {module_name}, function {function_name}: {message}")] + JsonArgumentsDeserializationError { + module_name: String, + function_name: String, + message: String, + }, /// Returned outputs aren't compatible with a called function signature. - #[error("output to json serialization error: {0}")] - JsonOutputSerializationError(String), + #[error("output to json serialization error in module {module_name}, function {function_name}: {message}")] + JsonOutputSerializationError { + module_name: String, + function_name: String, + message: String, + }, /// Errors related to invalid config. #[error("parsing config error: {0}")] @@ -90,3 +98,25 @@ impl From for FaaSError { unreachable!() } } + +#[macro_export] +macro_rules! json_to_faas_err { + ($json_expr:expr, $module_name:expr, $function_name:expr) => { + $json_expr.map_err(|e| match e { + it_json_serde::ItJsonSerdeError::SerializationError(message) => { + FaaSError::JsonArgumentsDeserializationError { + module_name: $module_name, + function_name: $function_name, + message, + } + } + it_json_serde::ItJsonSerdeError::DeserializationError(message) => { + FaaSError::JsonOutputSerializationError { + module_name: $module_name, + function_name: $function_name, + message, + } + } + }) + }; +} diff --git a/fluence-faas/src/faas.rs b/fluence-faas/src/faas.rs index 3cc04f1d..826c790f 100644 --- a/fluence-faas/src/faas.rs +++ b/fluence-faas/src/faas.rs @@ -25,6 +25,7 @@ use crate::module_loading::load_modules_from_fs; use crate::module_loading::ModulesLoadStrategy; use crate::host_imports::logger::LoggerFilter; use crate::host_imports::logger::WASM_LOG_ENV_NAME; +use crate::json_to_faas_err; use marine::Marine; use marine::IFunctionArg; @@ -158,24 +159,32 @@ impl FluenceFaaS { json_args: JValue, call_parameters: marine_rs_sdk::CallParameters, ) -> FaaSResult { - use crate::json::json_to_ivalues; - use crate::json::ivalues_to_json; + use it_json_serde::json_to_ivalues; + use it_json_serde::ivalues_to_json; let module_name = module_name.as_ref(); let func_name = func_name.as_ref(); let (func_signature, output_types, record_types) = self.lookup_module_interface(module_name, func_name)?; - let iargs = json_to_ivalues( - json_args, - func_signature.iter().map(|arg| (&arg.name, &arg.ty)), - &record_types, + let iargs = json_to_faas_err!( + json_to_ivalues( + json_args, + func_signature.iter().map(|arg| (&arg.name, &arg.ty)), + &record_types, + ), + module_name.to_string(), + func_name.to_string() )?; self.call_parameters.replace(call_parameters); let result = self.marine.call(module_name, func_name, &iargs)?; - ivalues_to_json(result, &output_types, &record_types) + json_to_faas_err!( + ivalues_to_json(result, &output_types, &record_types), + module_name.to_string(), + func_name.to_string() + ) } /// Return all export functions (name and signatures) of loaded modules. diff --git a/fluence-faas/src/json/json_to_ivalues.rs b/fluence-faas/src/json/json_to_ivalues.rs deleted file mode 100644 index 4cb414f2..00000000 --- a/fluence-faas/src/json/json_to_ivalues.rs +++ /dev/null @@ -1,244 +0,0 @@ -/* - * 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 crate::IValue; -use crate::IType; -use crate::FaaSResult; -use crate::FaaSError::JsonArgumentsDeserializationError as ArgDeError; - -use marine::MRecordTypes; -use serde_json::Value as JValue; -use wasmer_it::NEVec; - -use std::collections::HashMap; -use std::iter::ExactSizeIterator; - -/// Convert json to an array of ivalues according to the supplied argument types. -pub(crate) fn json_to_ivalues<'a, 'b>( - json_args: JValue, - arg_types: impl Iterator + ExactSizeIterator, - record_types: &'b MRecordTypes, -) -> FaaSResult> { - let ivalues = match json_args { - JValue::Object(json_map) => json_map_to_ivalues(json_map, arg_types, record_types)?, - JValue::Array(json_array) => { - json_array_to_ivalues(json_array, arg_types.map(|arg| arg.1), record_types)? - } - JValue::Null => json_null_to_ivalues(arg_types)?, - json_value => json_value_to_ivalues(json_value, arg_types)?, - }; - - Ok(ivalues) -} - -/// Convert json map to an array of ivalues according to the supplied argument types. -fn json_map_to_ivalues<'a, 'b>( - mut json_map: serde_json::Map, - arg_types: impl Iterator, - record_types: &'b MRecordTypes, -) -> FaaSResult> { - let mut iargs = Vec::new(); - - for (arg_name, arg_type) in arg_types { - let json_value = json_map - .remove(arg_name) - .ok_or_else(|| ArgDeError(format!("missing argument with name {}", arg_name)))?; - let iarg = jvalue_to_ivalue(json_value, arg_type, record_types)?; - iargs.push(iarg); - } - - if !json_map.is_empty() { - return Err(ArgDeError(format!( - "function requires {} arguments, {} provided", - iargs.len(), - iargs.len() + json_map.len() - ))); - } - - Ok(iargs) -} - -/// Convert json array to an array of ivalues according to the supplied argument types. -fn json_array_to_ivalues<'a, 'b>( - json_array: Vec, - arg_types: impl Iterator + ExactSizeIterator, - record_types: &'b MRecordTypes, -) -> FaaSResult> { - if json_array.len() != arg_types.len() { - return Err(ArgDeError(format!( - "function requires {} arguments, {} provided", - arg_types.len(), - json_array.len() - ))); - } - - let iargs = json_array - .into_iter() - .zip(arg_types) - .map(|(json_value, arg_type)| jvalue_to_ivalue(json_value, arg_type, record_types)) - .collect::>>()?; - - Ok(iargs) -} - -/// Convert json value (Number, String or Bool) to an array of ivalues according to the supplied argument types. -fn json_value_to_ivalues<'a>( - json_value: JValue, - mut arg_types: impl Iterator + ExactSizeIterator, -) -> FaaSResult> { - if arg_types.len() != 1 { - return Err(ArgDeError(format!( - "called function has the following signature: '{:?}', and it isn't suitable for an argument '{:?}' provided", - arg_types.collect::>(), - json_value, - ))); - } - - // unwrap is safe here because iterator size's been checked - let arg_type = arg_types.next().unwrap().1; - let ivalue = jvalue_to_ivalue(json_value, arg_type, &HashMap::new())?; - - Ok(vec![ivalue]) -} - -/// Convert json Null to an empty array of ivalues. -fn json_null_to_ivalues<'a>( - arg_types: impl Iterator + ExactSizeIterator, -) -> FaaSResult> { - if arg_types.len() != 0 { - return Err(ArgDeError(format!( - "the called function has the following signature: {:?}, but no arguments is provided", - arg_types.collect::>() - ))); - } - - Ok(vec![]) -} - -/// Convert one JValue to an array of ivalues according to the supplied argument type. -fn jvalue_to_ivalue(jvalue: JValue, ty: &IType, record_types: &MRecordTypes) -> FaaSResult { - macro_rules! to_ivalue( - ($json_value:expr, $ty:ident) => { - { - let value = match $json_value { - // if there is an array with only one element try to implicitly flatten it, - // this is needed mostly because jsonpath lib returns Vec<&JValue> and - // could be changed in future - JValue::Array(mut json_array) if json_array.len() == 1 => { - serde_json::from_value(json_array.remove(0)) - }, - jvalue => serde_json::from_value(jvalue), - }.map_err(|e| - ArgDeError(format!("error {:?} occurred while deserialize output result to a json value",e)) - )?; - - Ok(IValue::$ty(value)) - } - } - ); - - match ty { - IType::Boolean => to_ivalue!(jvalue, Boolean), - IType::S8 => to_ivalue!(jvalue, S8), - IType::S16 => to_ivalue!(jvalue, S16), - IType::S32 => to_ivalue!(jvalue, S32), - IType::S64 => to_ivalue!(jvalue, S64), - IType::U8 => to_ivalue!(jvalue, U8), - IType::U16 => to_ivalue!(jvalue, U16), - IType::U32 => to_ivalue!(jvalue, U32), - IType::U64 => to_ivalue!(jvalue, U64), - IType::F32 => to_ivalue!(jvalue, F32), - IType::F64 => to_ivalue!(jvalue, F64), - IType::String => to_ivalue!(jvalue, String), - IType::ByteArray => { - let value = match jvalue { - JValue::Array(json_array) => { - let iargs = json_array - .into_iter() - .map(|json_value| jvalue_to_ivalue(json_value, &IType::U8, record_types)) - .collect::>>()?; - - Ok(iargs) - } - _ => Err(ArgDeError(format!("expected bytearray, got {:?}", jvalue))), - }?; - - Ok(IValue::Array(value)) - } - IType::Array(value_type) => { - let value = match jvalue { - JValue::Array(json_array) => { - let iargs = json_array - .into_iter() - .map(|json_value| jvalue_to_ivalue(json_value, value_type, record_types)) - .collect::>>()?; - - Ok(iargs) - } - _ => Err(ArgDeError(format!( - "expected array of {:?} types, got {:?}", - value_type, jvalue - ))), - }?; - - Ok(IValue::Array(value)) - } - IType::I32 => to_ivalue!(jvalue, I32), - IType::I64 => to_ivalue!(jvalue, I64), - IType::Record(record_type_id) => { - let value = json_record_type_to_ivalue(jvalue, record_type_id, record_types)?; - Ok(IValue::Record(value)) - } - } -} - -#[allow(clippy::ptr_arg)] -/// Convert JValue of array or object types to an IValue record type. -// TODO: after introducing new Record type wrapper change the result type -fn json_record_type_to_ivalue( - json_value: JValue, - record_type_id: &u64, - record_types: &MRecordTypes, -) -> FaaSResult> { - let record_type = record_types.get(record_type_id).ok_or_else(|| { - ArgDeError(format!( - "record with type id `{}` wasn't found", - record_type_id - )) - })?; - - match json_value { - JValue::Object(json_map) => Ok(NEVec::new(json_map_to_ivalues( - json_map, - record_type - .fields - .iter() - .map(|field| (&field.name, &field.ty)), - record_types, - )?) - .unwrap()), - JValue::Array(json_array) => Ok(NEVec::new(json_array_to_ivalues( - json_array, - record_type.fields.iter().map(|field| (&field.ty)), - record_types, - )?) - .unwrap()), - _ => Err(ArgDeError(format!( - "record with type id `{}` should be encoded as array or map of fields", - record_type_id - ))), - } -} diff --git a/fluence-faas/src/json/mod.rs b/fluence-faas/src/json/mod.rs deleted file mode 100644 index c3cc9c46..00000000 --- a/fluence-faas/src/json/mod.rs +++ /dev/null @@ -1,21 +0,0 @@ -/* - * 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. - */ - -mod ivalues_to_json; -mod json_to_ivalues; - -pub(crate) use ivalues_to_json::ivalues_to_json; -pub(crate) use json_to_ivalues::json_to_ivalues; diff --git a/fluence-faas/src/lib.rs b/fluence-faas/src/lib.rs index 7762c387..93620781 100644 --- a/fluence-faas/src/lib.rs +++ b/fluence-faas/src/lib.rs @@ -26,7 +26,6 @@ mod config; mod host_imports; -mod json; mod errors; mod faas; mod faas_interface; diff --git a/web-runtime/Cargo.toml b/web-runtime/Cargo.toml index 14b44ccf..66da0af1 100644 --- a/web-runtime/Cargo.toml +++ b/web-runtime/Cargo.toml @@ -20,6 +20,7 @@ wasmer-it = { package = "wasmer-interface-types-fl", version = "0.21.1" } fluence-it-types = {version = "0.3.1", features = ["impls"] } it-lilo = "0.2.0" it-memory-traits = "0.1.0" +it-json-serde = { path = "../crates/it-json-serde", version = "0.1.0" } wasm-bindgen = "0.2" nom = "5.1" diff --git a/web-runtime/src/faas/errors.rs b/web-runtime/src/faas/errors.rs index f3171a0f..bb208501 100644 --- a/web-runtime/src/faas/errors.rs +++ b/web-runtime/src/faas/errors.rs @@ -34,12 +34,20 @@ pub enum FaaSError { NoSuchModule(String), /// Provided arguments aren't compatible with a called function signature. - #[error("arguments from json deserialization error: {0}")] - JsonArgumentsDeserializationError(String), + #[error("arguments from json deserialization error in module {module_name}, function {function_name}: {message}")] + JsonArgumentsDeserializationError { + module_name: String, + function_name: String, + message: String, + }, /// Returned outputs aren't compatible with a called function signature. - #[error("output to json serialization error: {0}")] - JsonOutputSerializationError(String), + #[error("output to json serialization error in module {module_name}, function {function_name}: {message}")] + JsonOutputSerializationError { + module_name: String, + function_name: String, + message: String, + }, /// Errors related to invalid config. #[error("parsing config error: {0}")] @@ -61,3 +69,25 @@ impl From for FaaSError { unreachable!() } } + +#[macro_export] +macro_rules! json_to_faas_err { + ($json_expr:expr, $module_name:expr, $function_name:expr) => { + $json_expr.map_err(|e| match e { + it_json_serde::ItJsonSerdeError::SerializationError(message) => { + FaaSError::JsonArgumentsDeserializationError { + module_name: $module_name, + function_name: $function_name, + message, + } + } + it_json_serde::ItJsonSerdeError::DeserializationError(message) => { + FaaSError::JsonOutputSerializationError { + module_name: $module_name, + function_name: $function_name, + message, + } + } + }) + }; +} diff --git a/web-runtime/src/faas/faas.rs b/web-runtime/src/faas/faas.rs index f7794fc0..d2875668 100644 --- a/web-runtime/src/faas/faas.rs +++ b/web-runtime/src/faas/faas.rs @@ -22,6 +22,7 @@ use crate::IType; use crate::Marine; use crate::IFunctionArg; use crate::MRecordTypes; +use crate::json_to_faas_err; //use marine_utils::SharedString; use marine_rs_sdk::CallParameters; @@ -95,24 +96,32 @@ impl FluenceFaaS { json_args: JValue, call_parameters: marine_rs_sdk::CallParameters, ) -> Result { - use crate::faas::json::json_to_ivalues; - use crate::faas::json::ivalues_to_json; + use it_json_serde::json_to_ivalues; + use it_json_serde::ivalues_to_json; let module_name = module_name.as_ref(); let func_name = func_name.as_ref(); let (func_signature, output_types, record_types) = self.lookup_module_interface(module_name, func_name)?; - let iargs = json_to_ivalues( - json_args, - func_signature.iter().map(|arg| (&arg.name, &arg.ty)), - &record_types, + let iargs = json_to_faas_err!( + json_to_ivalues( + json_args, + func_signature.iter().map(|arg| (&arg.name, &arg.ty)), + &record_types, + ), + module_name.to_string(), + func_name.to_string() )?; self.call_parameters.replace(call_parameters); let result = self.marine.call(module_name, func_name, &iargs)?; - ivalues_to_json(result, &output_types, &record_types) + json_to_faas_err!( + ivalues_to_json(result, &output_types, &record_types), + module_name.to_string(), + func_name.to_string() + ) } /// Return all export functions (name and signatures) of loaded modules. diff --git a/web-runtime/src/faas/json/ivalues_to_json.rs b/web-runtime/src/faas/json/ivalues_to_json.rs deleted file mode 100644 index 84e43371..00000000 --- a/web-runtime/src/faas/json/ivalues_to_json.rs +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 2022 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::IValue; -use crate::IType; -use crate::faas::Result; -use crate::faas::errors::FaaSError::JsonOutputSerializationError as OutputDeError; -use crate::MRecordTypes; - -use serde_json::Value as JValue; - -pub(crate) fn ivalues_to_json( - mut ivalues: Vec, - outputs: &[IType], - record_types: &MRecordTypes, -) -> Result { - if outputs.len() != ivalues.len() { - return Err(OutputDeError(format!( - "resulted values {:?} and function signature {:?} aren't compatible", - ivalues, outputs - ))); - } - match ivalues.len() { - 0 => Ok(JValue::Null), - 1 => ivalue_to_json(ivalues.remove(0), outputs.first().unwrap(), record_types), - _ => unimplemented!( - "multi-values aren't supported now - more then one result values aren't possible" - ), - } -} - -fn ivalue_to_json(ivalue: IValue, output: &IType, record_types: &MRecordTypes) -> Result { - use serde_json::json; - - // clone here needed because binding by-value and by-ref in the same pattern in unstable - match (ivalue, output.clone()) { - (IValue::Boolean(value), IType::Boolean) => Ok(json!(value)), - (IValue::S8(value), IType::S8) => Ok(json!(value)), - (IValue::S16(value), IType::S16) => Ok(json!(value)), - (IValue::S32(value), IType::S32) => Ok(json!(value)), - (IValue::S64(value), IType::S64) => Ok(json!(value)), - (IValue::U8(value), IType::U8) => Ok(json!(value)), - (IValue::U16(value), IType::U16) => Ok(json!(value)), - (IValue::U32(value), IType::U32) => Ok(json!(value)), - (IValue::U64(value), IType::U64) => Ok(json!(value)), - (IValue::I32(value), IType::I32) => Ok(json!(value)), - (IValue::I64(value), IType::I64) => Ok(json!(value)), - (IValue::F32(value), IType::F32) => Ok(json!(value)), - (IValue::F64(value), IType::F64) => Ok(json!(value)), - (IValue::String(value), IType::String) => Ok(json!(value)), - (IValue::ByteArray(value), IType::ByteArray) => { - let result = value.into_iter().map(|v| json!(v)).collect(); - Ok(JValue::Array(result)) - } - (IValue::Array(value), IType::ByteArray) => { - let result: Result> = value - .into_iter() - .map(|v| ivalue_to_json(v, &IType::U8, record_types)) - .collect(); - - Ok(JValue::Array(result?)) - } - (IValue::ByteArray(value), IType::Array(array_ty)) => { - let result: Result> = value - .into_iter() - .map(|v| ivalue_to_json(IValue::U8(v), &array_ty, record_types)) - .collect(); - - Ok(JValue::Array(result?)) - } - (IValue::Array(value), IType::Array(array_ty)) => { - let result: Result> = value - .into_iter() - .map(|v| ivalue_to_json(v, &array_ty, record_types)) - .collect(); - - Ok(JValue::Array(result?)) - } - (IValue::Record(field_values), IType::Record(record_id)) => { - let record_type = record_types.get(&record_id).ok_or_else(|| { - OutputDeError(format!( - "record id {} wasn't found in module record types", - record_id - )) - })?; - let field_types = &record_type.fields; - - if field_values.len() != field_types.len() { - return Err(OutputDeError(format!( - "output record {:?} isn't compatible to output record fields {:?}", - field_values, field_types - ))); - } - - let field_values = field_values.into_vec(); - let mut result = serde_json::Map::with_capacity(field_values.len()); - - for (field_value, field_type) in field_values.into_iter().zip(field_types.iter()) { - let json_field_value = ivalue_to_json(field_value, &field_type.ty, record_types)?; - result.insert(field_type.name.clone(), json_field_value); - } - - Ok(JValue::Object(result)) - } - (ivalue, itype) => Err(OutputDeError(format!( - "value {:?} is incompatible to type {:?}", - ivalue, itype - ))), - } -} diff --git a/web-runtime/src/faas/mod.rs b/web-runtime/src/faas/mod.rs index f4dab0ee..36f1825a 100644 --- a/web-runtime/src/faas/mod.rs +++ b/web-runtime/src/faas/mod.rs @@ -14,7 +14,6 @@ * limitations under the License. */ -mod json; mod errors; mod faas; mod faas_interface;