diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml index 5c8b567a..8f037a35 100644 --- a/.github/workflows/cd.yaml +++ b/.github/workflows/cd.yaml @@ -36,6 +36,7 @@ jobs: - name: Check for changes in URI.txt run: | + git add . git diff --name-only | grep ^URI.txt$ echo "changed=$?" >> $GITHUB_ENV @@ -43,7 +44,6 @@ jobs: run: | git config --local user.email "action@github.com" git config --local user.name "GitHub Action" - git add . git commit -m "Add deployments" -a if: env.changed == 0 @@ -101,6 +101,7 @@ jobs: - name: Check for changes in URI.txt run: | + git add . git diff --name-only | grep ^URI.txt$ echo "changed=$?" >> $GITHUB_ENV @@ -108,7 +109,6 @@ jobs: run: | git config --local user.email "action@github.com" git config --local user.name "GitHub Action" - git add . git commit -m "Add deployments" -a if: env.changed == 0 diff --git a/implementations/app-typescript/URI.txt b/implementations/app-typescript/URI.txt index 1193ba31..54eb6533 100644 --- a/implementations/app-typescript/URI.txt +++ b/implementations/app-typescript/URI.txt @@ -1 +1 @@ -wrap://ipfs/QmNhvrKWXkkvLBknymaoGUpAnd7sE17HTmabDCtyLKFrLa \ No newline at end of file +wrap://ipfs/QmekvC2sggWkuJcK5eKvVWeSNLJmmNzMphsbm3ZT6FfofZ \ No newline at end of file diff --git a/implementations/app-typescript/build/wrap.wasm b/implementations/app-typescript/build/wrap.wasm index 2ceb89fe..906be312 100755 Binary files a/implementations/app-typescript/build/wrap.wasm and b/implementations/app-typescript/build/wrap.wasm differ diff --git a/implementations/app-typescript/src/helpers/mod.rs b/implementations/app-typescript/src/helpers/mod.rs index cd02f643..a0556446 100644 --- a/implementations/app-typescript/src/helpers/mod.rs +++ b/implementations/app-typescript/src/helpers/mod.rs @@ -10,6 +10,9 @@ mod to_graphql_type; mod to_typescript; mod pretty; +// helpers for helpers +mod util; + pub fn register(handlebars: &mut Handlebars) -> () { handlebars.register_helper( "array_has_length", diff --git a/implementations/app-typescript/src/helpers/to_typescript.rs b/implementations/app-typescript/src/helpers/to_typescript.rs index 7d766938..2eb872f3 100644 --- a/implementations/app-typescript/src/helpers/to_typescript.rs +++ b/implementations/app-typescript/src/helpers/to_typescript.rs @@ -1,6 +1,7 @@ use handlebars::handlebars_helper; use serde_json::value::Value; use crate::helpers::detect_keyword::_detect_keyword; +use crate::helpers::util::{array_type, map_types}; handlebars_helper!(to_typescript: |value: Value| { _to_typescript(value.as_str().unwrap(), false) @@ -17,11 +18,11 @@ fn _to_typescript(value: &str, undefinable: bool) -> String { } if type_val.starts_with("[") { - return to_typescript_array(&type_val, optional); + return to_typescript_array(&type_val, optional).unwrap(); } if type_val.starts_with("Map<") { - return to_typescript_map(&type_val, optional); + return to_typescript_map(&type_val, optional).unwrap(); } match type_val.as_str() { @@ -42,32 +43,19 @@ fn _to_typescript(value: &str, undefinable: bool) -> String { } } -fn to_typescript_array(type_val: &str, optional: bool) -> String { - let mut iter = type_val.char_indices(); - - let first_bracket = iter.find(|&(_, c)| c == '[').map(|(i, _)| i).unwrap(); - let last_bracket = iter.rfind(|&(_, c)| c == ']').map(|(i, _)| i).unwrap(); - - let inner_type = &type_val[(first_bracket+1)..last_bracket]; - let ts_type = _to_typescript(inner_type, false); - - apply_optional(&format!("Array<{}>", ts_type), optional) +fn to_typescript_array(value: &str, optional: bool) -> Result { + let inner_type = array_type(value)?; + let ts_type = _to_typescript(&inner_type, false); + let ts_array = format!("Array<{}>", ts_type); + Ok(apply_optional(&ts_array, optional)) } -fn to_typescript_map(type_val: &str, optional: bool) -> String { - let open_angle_bracket_idx = type_val.find('<').unwrap(); - let close_angle_bracket_idx = type_val.rfind('>').unwrap(); - - let key_val_types = &type_val[(open_angle_bracket_idx+1)..close_angle_bracket_idx]; - - let first_comma_idx = key_val_types.find(',').unwrap(); - let key_type = key_val_types[..first_comma_idx].trim(); - let val_type = key_val_types[(first_comma_idx+1)..].trim(); - - let ts_key_type = _to_typescript(key_type, false); - let ts_val_type = _to_typescript(val_type, true); - - apply_optional(&format!("Map<{}, {}>", ts_key_type, ts_val_type), optional) +fn to_typescript_map(value: &str, optional: bool) -> Result { + let (key_type, val_type) = map_types(value)?; + let ts_key_type = _to_typescript(&key_type, false); + let ts_val_type = _to_typescript(&val_type, true); + let ts_map = format!("Map<{}, {}>", ts_key_type, ts_val_type); + Ok(apply_optional(&ts_map, optional)) } fn apply_optional(type_val: &str, optional: bool) -> String { diff --git a/implementations/app-typescript/src/helpers/util.rs b/implementations/app-typescript/src/helpers/util.rs new file mode 100644 index 00000000..826415c5 --- /dev/null +++ b/implementations/app-typescript/src/helpers/util.rs @@ -0,0 +1,38 @@ +pub fn array_type(value: &str) -> Result { + let mut iter = value.char_indices(); + + let first_bracket = match iter.find(|&(_, c)| c == '[').map(|(i, _)| i) { + Some(idx) => idx, + None => return Err(format!("Invalid Array: {}", value)), + }; + let last_bracket = match iter.rfind(|&(_, c)| c == ']').map(|(i, _)| i) { + Some(idx) => idx, + None => return Err(format!("Invalid Array: {}", value)), + }; + + let inner_type = &value[(first_bracket+1)..last_bracket]; + Ok(inner_type.to_string()) +} + +pub fn map_types(value: &str) -> Result<(String, String), String> { + let first_open_bracket_idx = match value.find('<') { + Some(idx) => idx, + None => return Err(format!("Invalid Map: {}", value)), + }; + let last_close_bracket_idx = match value.rfind('>') { + Some(idx) => idx, + None => return Err(format!("Invalid Map: {}", value)), + }; + + let key_val_types = &value[(first_open_bracket_idx + 1)..last_close_bracket_idx]; + + let first_comma_idx = match key_val_types.find(',') { + Some(idx) => idx, + None => return Err(format!("Invalid Map: {}", value)), + }; + + let key_type = key_val_types[..first_comma_idx].trim(); + let val_type = key_val_types[(first_comma_idx + 1)..].trim(); + + Ok((key_type.to_string(), val_type.to_string())) +} \ No newline at end of file diff --git a/implementations/plugin-kotlin/URI.txt b/implementations/plugin-kotlin/URI.txt index efb708c0..932abdfd 100644 --- a/implementations/plugin-kotlin/URI.txt +++ b/implementations/plugin-kotlin/URI.txt @@ -1 +1 @@ -wrap://ipfs/QmcLWEBAgFaNRzSj7aYxyKNm9bRC172kibLPgd2yob18fW \ No newline at end of file +wrap://ipfs/QmYbzNmcXaFEAuWQP6f9cBiZbcKoLQbwNjKQMDEbATaefq \ No newline at end of file diff --git a/implementations/plugin-kotlin/build/wrap.wasm b/implementations/plugin-kotlin/build/wrap.wasm index b7cff46c..83fe54b2 100755 Binary files a/implementations/plugin-kotlin/build/wrap.wasm and b/implementations/plugin-kotlin/build/wrap.wasm differ diff --git a/implementations/plugin-kotlin/src/helpers/mod.rs b/implementations/plugin-kotlin/src/helpers/mod.rs index 21b9bf95..6562bd83 100644 --- a/implementations/plugin-kotlin/src/helpers/mod.rs +++ b/implementations/plugin-kotlin/src/helpers/mod.rs @@ -16,6 +16,9 @@ mod to_kotlin_byte_array; mod to_package_id; mod to_upper; +// helpers for helpers +mod util; + pub fn register(handlebars: &mut Handlebars) -> () { handlebars.register_helper( "array_has_length", diff --git a/implementations/plugin-kotlin/src/helpers/to_kotlin.rs b/implementations/plugin-kotlin/src/helpers/to_kotlin.rs index 90239009..ca068070 100644 --- a/implementations/plugin-kotlin/src/helpers/to_kotlin.rs +++ b/implementations/plugin-kotlin/src/helpers/to_kotlin.rs @@ -2,6 +2,7 @@ use handlebars::handlebars_helper; use serde_json::value::Value; use crate::helpers::detect_keyword::_detect_keyword; use crate::helpers::to_upper::_to_upper; +use crate::helpers::util::{array_type, map_types}; handlebars_helper!(to_kotlin: |value: Value| { _to_kotlin(value.as_str().unwrap()) @@ -53,47 +54,16 @@ fn _to_kotlin(value: &str) -> String { } fn to_kotlin_array(value: &str, optional: bool) -> Result { - let mut iter = value.char_indices(); - - let first_bracket = match iter.find(|&(_, c)| c == '[').map(|(i, _)| i) { - Some(idx) => idx, - None => return Err(format!("Invalid Array: {}", value)), - }; - let last_bracket = match iter.rfind(|&(_, c)| c == ']').map(|(i, _)| i) { - Some(idx) => idx, - None => return Err(format!("Invalid Array: {}", value)), - }; - - let inner_type = &value[(first_bracket+1)..last_bracket]; - let kt_type = _to_kotlin(inner_type); - + let inner_type = array_type(value)?; + let kt_type = _to_kotlin(&inner_type); let kt_array = format!("List<{}>", kt_type); Ok(apply_optional(&kt_array, optional)) } fn to_kotlin_map(value: &str, optional: bool) -> Result { - let first_open_bracket_idx = match value.find('<') { - Some(idx) => idx, - None => return Err(format!("Invalid Map: {}", value)), - }; - let last_close_bracket_idx = match value.rfind('>') { - Some(idx) => idx, - None => return Err(format!("Invalid Map: {}", value)), - }; - - let key_val_types = &value[(first_open_bracket_idx + 1)..last_close_bracket_idx]; - - let first_comma_idx = match key_val_types.find(',') { - Some(idx) => idx, - None => return Err(format!("Invalid Map: {}", value)), - }; - - let key_type = key_val_types[..first_comma_idx].trim(); - let val_type = key_val_types[(first_comma_idx+1)..].trim(); - - let kt_key_type = _to_kotlin(key_type); - let kt_val_type = _to_kotlin(val_type); - + let (key_type, val_type) = map_types(value)?; + let kt_key_type = _to_kotlin(&key_type); + let kt_val_type = _to_kotlin(&val_type); let kt_map = format!("MsgPackMap<{}, {}>", kt_key_type, kt_val_type); Ok(apply_optional(&kt_map, optional)) } diff --git a/implementations/plugin-kotlin/src/helpers/util.rs b/implementations/plugin-kotlin/src/helpers/util.rs new file mode 100644 index 00000000..826415c5 --- /dev/null +++ b/implementations/plugin-kotlin/src/helpers/util.rs @@ -0,0 +1,38 @@ +pub fn array_type(value: &str) -> Result { + let mut iter = value.char_indices(); + + let first_bracket = match iter.find(|&(_, c)| c == '[').map(|(i, _)| i) { + Some(idx) => idx, + None => return Err(format!("Invalid Array: {}", value)), + }; + let last_bracket = match iter.rfind(|&(_, c)| c == ']').map(|(i, _)| i) { + Some(idx) => idx, + None => return Err(format!("Invalid Array: {}", value)), + }; + + let inner_type = &value[(first_bracket+1)..last_bracket]; + Ok(inner_type.to_string()) +} + +pub fn map_types(value: &str) -> Result<(String, String), String> { + let first_open_bracket_idx = match value.find('<') { + Some(idx) => idx, + None => return Err(format!("Invalid Map: {}", value)), + }; + let last_close_bracket_idx = match value.rfind('>') { + Some(idx) => idx, + None => return Err(format!("Invalid Map: {}", value)), + }; + + let key_val_types = &value[(first_open_bracket_idx + 1)..last_close_bracket_idx]; + + let first_comma_idx = match key_val_types.find(',') { + Some(idx) => idx, + None => return Err(format!("Invalid Map: {}", value)), + }; + + let key_type = key_val_types[..first_comma_idx].trim(); + let val_type = key_val_types[(first_comma_idx + 1)..].trim(); + + Ok((key_type.to_string(), val_type.to_string())) +} \ No newline at end of file diff --git a/implementations/plugin-python/URI.txt b/implementations/plugin-python/URI.txt index 96946fd7..660b40af 100644 --- a/implementations/plugin-python/URI.txt +++ b/implementations/plugin-python/URI.txt @@ -1 +1 @@ -wrap://ipfs/Qmap4wqfZoFbQxZdMUC4z7Y3YdK6NbXixGM939ofEuwLFU \ No newline at end of file +wrap://ipfs/QmV9xedSz6Y4tc1qUSVcrQSF63wfibWhB18oao99mByghK \ No newline at end of file diff --git a/implementations/plugin-python/build/wrap.wasm b/implementations/plugin-python/build/wrap.wasm index 24aded24..f074143d 100755 Binary files a/implementations/plugin-python/build/wrap.wasm and b/implementations/plugin-python/build/wrap.wasm differ diff --git a/implementations/plugin-python/src/helpers/to_python.rs b/implementations/plugin-python/src/helpers/to_python.rs index 8e7adda8..aa9ac880 100644 --- a/implementations/plugin-python/src/helpers/to_python.rs +++ b/implementations/plugin-python/src/helpers/to_python.rs @@ -2,6 +2,7 @@ use handlebars::handlebars_helper; use serde_json::value::Value; use crate::helpers::detect_keyword::_detect_keyword; use crate::helpers::to_upper::_to_upper; +use crate::helpers::util::{array_type, map_types}; handlebars_helper!(to_python: |value: Value| { _to_python(value.as_str().unwrap()) @@ -45,47 +46,16 @@ fn _to_python(value: &str) -> String { } fn to_python_array(value: &str, optional: bool) -> Result { - let mut iter = value.char_indices(); - - let first_bracket = match iter.find(|&(_, c)| c == '[').map(|(i, _)| i) { - Some(idx) => idx, - None => return Err(format!("Invalid Array: {}", value)), - }; - let last_bracket = match iter.rfind(|&(_, c)| c == ']').map(|(i, _)| i) { - Some(idx) => idx, - None => return Err(format!("Invalid Array: {}", value)), - }; - - let inner_type = &value[(first_bracket+1)..last_bracket]; - let py_type = _to_python(inner_type); - + let inner_type = array_type(value)?; + let py_type = _to_python(&inner_type); let py_array = format!("list[{}]", py_type); Ok(apply_optional(&py_array, optional)) } fn to_python_map(value: &str, optional: bool) -> Result { - let first_open_bracket_idx = match value.find('<') { - Some(idx) => idx, - None => return Err(format!("Invalid Map: {}", value)), - }; - let last_close_bracket_idx = match value.rfind('>') { - Some(idx) => idx, - None => return Err(format!("Invalid Map: {}", value)), - }; - - let key_val_types = &value[(first_open_bracket_idx + 1)..last_close_bracket_idx]; - - let first_comma_idx = match key_val_types.find(',') { - Some(idx) => idx, - None => return Err(format!("Invalid Map: {}", value)), - }; - - let key_type = key_val_types[..first_comma_idx].trim(); - let val_type = key_val_types[(first_comma_idx+1)..].trim(); - - let py_key_type = _to_python(key_type); - let py_val_type = _to_python(val_type); - + let (key_type, val_type) = map_types(value)?; + let py_key_type = _to_python(&key_type); + let py_val_type = _to_python(&val_type); let py_map = format!("GenericMap[{}, {}]", py_key_type, py_val_type); Ok(apply_optional(&py_map, optional)) } diff --git a/implementations/plugin-python/src/helpers/util.rs b/implementations/plugin-python/src/helpers/util.rs index 1e4c8c89..49e8375d 100644 --- a/implementations/plugin-python/src/helpers/util.rs +++ b/implementations/plugin-python/src/helpers/util.rs @@ -16,3 +16,41 @@ pub fn remove_at(s: &str, idx: usize) -> String { format!("{}{}", start, end) } +pub fn array_type(value: &str) -> Result { + let mut iter = value.char_indices(); + + let first_bracket = match iter.find(|&(_, c)| c == '[').map(|(i, _)| i) { + Some(idx) => idx, + None => return Err(format!("Invalid Array: {}", value)), + }; + let last_bracket = match iter.rfind(|&(_, c)| c == ']').map(|(i, _)| i) { + Some(idx) => idx, + None => return Err(format!("Invalid Array: {}", value)), + }; + + let inner_type = &value[(first_bracket+1)..last_bracket]; + Ok(inner_type.to_string()) +} + +pub fn map_types(value: &str) -> Result<(String, String), String> { + let first_open_bracket_idx = match value.find('<') { + Some(idx) => idx, + None => return Err(format!("Invalid Map: {}", value)), + }; + let last_close_bracket_idx = match value.rfind('>') { + Some(idx) => idx, + None => return Err(format!("Invalid Map: {}", value)), + }; + + let key_val_types = &value[(first_open_bracket_idx + 1)..last_close_bracket_idx]; + + let first_comma_idx = match key_val_types.find(',') { + Some(idx) => idx, + None => return Err(format!("Invalid Map: {}", value)), + }; + + let key_type = key_val_types[..first_comma_idx].trim(); + let val_type = key_val_types[(first_comma_idx + 1)..].trim(); + + Ok((key_type.to_string(), val_type.to_string())) +} \ No newline at end of file diff --git a/implementations/plugin-rust/URI.txt b/implementations/plugin-rust/URI.txt index 5f26eeba..00f6e678 100644 --- a/implementations/plugin-rust/URI.txt +++ b/implementations/plugin-rust/URI.txt @@ -1 +1 @@ -wrap://ipfs/QmTC8oqchRJMqNeKtvRdF2pQc6TMLNS9LFiXwefHaUfUF5 \ No newline at end of file +wrap://ipfs/QmSYCtTtHMyenvFu7b4J914LTZLPNkyme2Dfa9QLvNWUiV \ No newline at end of file diff --git a/implementations/plugin-rust/build/wrap.wasm b/implementations/plugin-rust/build/wrap.wasm index edff22be..351db1d2 100755 Binary files a/implementations/plugin-rust/build/wrap.wasm and b/implementations/plugin-rust/build/wrap.wasm differ diff --git a/implementations/plugin-rust/src/__tests__/cases/000-sanity/output/module.rs b/implementations/plugin-rust/src/__tests__/cases/000-sanity/output/module.rs index db0deb79..41f024eb 100644 --- a/implementations/plugin-rust/src/__tests__/cases/000-sanity/output/module.rs +++ b/implementations/plugin-rust/src/__tests__/cases/000-sanity/output/module.rs @@ -4,6 +4,19 @@ use std::sync::Arc; use polywrap_core::invoke::Invoker; use polywrap_plugin::{error::PluginError, module::PluginModule}; +use polywrap_msgpack_serde::{ + to_vec, + from_slice, + BigInt, + BigNumber, + JSON, + bytes, + wrappers::{ + polywrap_bigint as bigint, + polywrap_json as json + } +}; +use std::collections::BTreeMap; use serde::{Serialize, Deserialize}; use super::types::*; @@ -19,15 +32,15 @@ pub struct ArgsModuleMethod { pub enum_array: Vec, #[serde(rename = "optEnumArray")] pub opt_enum_array: Option>>, - pub map: Map, + pub map: BTreeMap, #[serde(rename = "mapOfArr")] - pub map_of_arr: Map>, + pub map_of_arr: BTreeMap>, #[serde(rename = "mapOfMap")] - pub map_of_map: Map>, + pub map_of_map: BTreeMap>, #[serde(rename = "mapOfObj")] - pub map_of_obj: Map, + pub map_of_obj: BTreeMap, #[serde(rename = "mapOfArrOfObj")] - pub map_of_arr_of_obj: Map>, + pub map_of_arr_of_obj: BTreeMap>, } #[derive(Clone, Debug, Deserialize, Serialize)] diff --git a/implementations/plugin-rust/src/__tests__/cases/000-sanity/output/types.rs b/implementations/plugin-rust/src/__tests__/cases/000-sanity/output/types.rs index fe3c1855..3579dad2 100644 --- a/implementations/plugin-rust/src/__tests__/cases/000-sanity/output/types.rs +++ b/implementations/plugin-rust/src/__tests__/cases/000-sanity/output/types.rs @@ -4,8 +4,20 @@ // NOTE: This is an auto-generated file. // All modifications will be overwritten. use polywrap_core::{invoke::Invoker, uri::Uri}; -use polywrap_msgpack::{decode, serialize}; -use polywrap_plugin::{error::PluginError, BigInt, BigNumber, Map, JSON}; +use polywrap_plugin::error::PluginError; +use polywrap_msgpack_serde::{ + to_vec, + from_slice, + BigInt, + BigNumber, + JSON, + bytes, + wrappers::{ + polywrap_bigint as bigint, + polywrap_json as json + } +}; +use std::collections::BTreeMap; use serde::{Serialize, Deserialize}; use std::sync::Arc; @@ -17,7 +29,7 @@ pub struct Env { #[serde(rename = "optProp")] pub opt_prop: Option, #[serde(rename = "optMap")] - pub opt_map: Option>>, + pub opt_map: Option>>, } // Env END // @@ -38,18 +50,22 @@ pub struct CustomType { pub i8: i8, pub i16: i16, pub i32: i32, + #[serde(with = "bigint")] pub bigint: BigInt, + #[serde(with = "bigint")] #[serde(rename = "optBigint")] pub opt_bigint: Option, pub bignumber: BigNumber, #[serde(rename = "optBignumber")] pub opt_bignumber: Option, + #[serde(with = "json")] pub json: JSON::Value, + #[serde(with = "json")] #[serde(rename = "optJson")] pub opt_json: Option, - #[serde(with = "serde_bytes")] + #[serde(with = "bytes")] pub bytes: Vec, - #[serde(with = "serde_bytes")] + #[serde(with = "bytes")] #[serde(rename = "optBytes")] pub opt_bytes: Option>, pub boolean: bool, @@ -84,15 +100,15 @@ pub struct CustomType { pub enum_array: Vec, #[serde(rename = "optEnumArray")] pub opt_enum_array: Option>>, - pub map: Map, + pub map: BTreeMap, #[serde(rename = "mapOfArr")] - pub map_of_arr: Map>, + pub map_of_arr: BTreeMap>, #[serde(rename = "mapOfObj")] - pub map_of_obj: Map, + pub map_of_obj: BTreeMap, #[serde(rename = "mapOfArrOfObj")] - pub map_of_arr_of_obj: Map>, + pub map_of_arr_of_obj: BTreeMap>, #[serde(rename = "mapCustomValue")] - pub map_custom_value: Map>, + pub map_custom_value: BTreeMap>, } #[derive(Clone, Debug, Deserialize, Serialize)] pub struct AnotherType { @@ -228,7 +244,7 @@ impl<'a> TestImportModule<'a> { pub fn imported_method(&self, args: &TestImportModuleArgsImportedMethod) -> Result, PluginError> { let uri = self.uri; - let serialized_args = serialize(args.clone()).unwrap(); + let serialized_args = to_vec(args.clone()).unwrap(); let result = invoker.invoke_raw( uri, "importedMethod", @@ -243,12 +259,12 @@ impl<'a> TestImportModule<'a> { exception: e.to_string(), })?; - Ok(Some(decode(result.as_slice())?)) + Ok(Some(from_slice(result.as_slice())?)) } pub fn another_method(&self, args: &TestImportModuleArgsAnotherMethod) -> Result { let uri = self.uri; - let serialized_args = serialize(args.clone()).unwrap(); + let serialized_args = to_vec(args.clone()).unwrap(); let result = invoker.invoke_raw( uri, "anotherMethod", @@ -263,12 +279,12 @@ impl<'a> TestImportModule<'a> { exception: e.to_string(), })?; - Ok(decode(result.as_slice())?) + Ok(from_slice(result.as_slice())?) } pub fn returns_array_of_enums(&self, args: &TestImportModuleArgsReturnsArrayOfEnums) -> Result>, PluginError> { let uri = self.uri; - let serialized_args = serialize(args.clone()).unwrap(); + let serialized_args = to_vec(args.clone()).unwrap(); let result = invoker.invoke_raw( uri, "returnsArrayOfEnums", @@ -283,7 +299,7 @@ impl<'a> TestImportModule<'a> { exception: e.to_string(), })?; - Ok(decode(result.as_slice())?) + Ok(from_slice(result.as_slice())?) } } // Imported Modules END // diff --git a/implementations/plugin-rust/src/__tests__/cases/001-module-functions/output/module.rs b/implementations/plugin-rust/src/__tests__/cases/001-module-functions/output/module.rs index 5c556535..4fae902f 100644 --- a/implementations/plugin-rust/src/__tests__/cases/001-module-functions/output/module.rs +++ b/implementations/plugin-rust/src/__tests__/cases/001-module-functions/output/module.rs @@ -4,6 +4,19 @@ use std::sync::Arc; use polywrap_core::invoke::Invoker; use polywrap_plugin::{error::PluginError, module::PluginModule}; +use polywrap_msgpack_serde::{ + to_vec, + from_slice, + BigInt, + BigNumber, + JSON, + bytes, + wrappers::{ + polywrap_bigint as bigint, + polywrap_json as json + } +}; +use std::collections::BTreeMap; use serde::{Serialize, Deserialize}; use super::types::*; @@ -16,7 +29,7 @@ pub struct ArgsFunction1 { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ArgsFunction2 { pub arg1: Option, - #[serde(with = "serde_bytes")] + #[serde(with = "bytes")] pub arg2: Option>, } diff --git a/implementations/plugin-rust/src/__tests__/cases/001-module-functions/output/types.rs b/implementations/plugin-rust/src/__tests__/cases/001-module-functions/output/types.rs index a52e57b5..16df38c9 100644 --- a/implementations/plugin-rust/src/__tests__/cases/001-module-functions/output/types.rs +++ b/implementations/plugin-rust/src/__tests__/cases/001-module-functions/output/types.rs @@ -4,8 +4,20 @@ // NOTE: This is an auto-generated file. // All modifications will be overwritten. use polywrap_core::{invoke::Invoker, uri::Uri}; -use polywrap_msgpack::{decode, serialize}; -use polywrap_plugin::{error::PluginError, BigInt, BigNumber, Map, JSON}; +use polywrap_plugin::error::PluginError; +use polywrap_msgpack_serde::{ + to_vec, + from_slice, + BigInt, + BigNumber, + JSON, + bytes, + wrappers::{ + polywrap_bigint as bigint, + polywrap_json as json + } +}; +use std::collections::BTreeMap; use serde::{Serialize, Deserialize}; // Env START // diff --git a/implementations/plugin-rust/src/__tests__/cases/002-object-types/output/module.rs b/implementations/plugin-rust/src/__tests__/cases/002-object-types/output/module.rs index 78a190e2..407ab89b 100644 --- a/implementations/plugin-rust/src/__tests__/cases/002-object-types/output/module.rs +++ b/implementations/plugin-rust/src/__tests__/cases/002-object-types/output/module.rs @@ -4,6 +4,19 @@ use std::sync::Arc; use polywrap_core::invoke::Invoker; use polywrap_plugin::{error::PluginError, module::PluginModule}; +use polywrap_msgpack_serde::{ + to_vec, + from_slice, + BigInt, + BigNumber, + JSON, + bytes, + wrappers::{ + polywrap_bigint as bigint, + polywrap_json as json + } +}; +use std::collections::BTreeMap; use serde::{Serialize, Deserialize}; use super::types::*; diff --git a/implementations/plugin-rust/src/__tests__/cases/002-object-types/output/types.rs b/implementations/plugin-rust/src/__tests__/cases/002-object-types/output/types.rs index 8bdeb0df..7dd04dff 100644 --- a/implementations/plugin-rust/src/__tests__/cases/002-object-types/output/types.rs +++ b/implementations/plugin-rust/src/__tests__/cases/002-object-types/output/types.rs @@ -4,8 +4,20 @@ // NOTE: This is an auto-generated file. // All modifications will be overwritten. use polywrap_core::{invoke::Invoker, uri::Uri}; -use polywrap_msgpack::{decode, serialize}; -use polywrap_plugin::{error::PluginError, BigInt, BigNumber, Map, JSON}; +use polywrap_plugin::error::PluginError; +use polywrap_msgpack_serde::{ + to_vec, + from_slice, + BigInt, + BigNumber, + JSON, + bytes, + wrappers::{ + polywrap_bigint as bigint, + polywrap_json as json + } +}; +use std::collections::BTreeMap; use serde::{Serialize, Deserialize}; // Env START // @@ -29,18 +41,22 @@ pub struct CustomType { pub i8: i8, pub i16: i16, pub i32: i32, + #[serde(with = "bigint")] pub bigint: BigInt, + #[serde(with = "bigint")] #[serde(rename = "optBigint")] pub opt_bigint: Option, pub bignumber: BigNumber, #[serde(rename = "optBignumber")] pub opt_bignumber: Option, + #[serde(with = "json")] pub json: JSON::Value, + #[serde(with = "json")] #[serde(rename = "optJson")] pub opt_json: Option, - #[serde(with = "serde_bytes")] + #[serde(with = "bytes")] pub bytes: Vec, - #[serde(with = "serde_bytes")] + #[serde(with = "bytes")] #[serde(rename = "optBytes")] pub opt_bytes: Option>, pub boolean: bool, @@ -68,15 +84,15 @@ pub struct CustomType { pub object_array: Vec, #[serde(rename = "optObjectArray")] pub opt_object_array: Option>>, - pub map: Map, + pub map: BTreeMap, #[serde(rename = "mapOfArr")] - pub map_of_arr: Map>, + pub map_of_arr: BTreeMap>, #[serde(rename = "mapOfObj")] - pub map_of_obj: Map, + pub map_of_obj: BTreeMap, #[serde(rename = "mapOfArrOfObj")] - pub map_of_arr_of_obj: Map>, + pub map_of_arr_of_obj: BTreeMap>, #[serde(rename = "mapCustomValue")] - pub map_custom_value: Map>, + pub map_custom_value: BTreeMap>, } #[derive(Clone, Debug, Deserialize, Serialize)] pub struct AnotherType { diff --git a/implementations/plugin-rust/src/helpers/serde_annotate_if_bytes.rs b/implementations/plugin-rust/src/helpers/serde_annotate_if_bytes.rs index 2305b54b..9b4ca5e7 100644 --- a/implementations/plugin-rust/src/helpers/serde_annotate_if_bytes.rs +++ b/implementations/plugin-rust/src/helpers/serde_annotate_if_bytes.rs @@ -1,14 +1,16 @@ use handlebars::handlebars_helper; -use serde_json::{Value}; +use serde_json::Value; handlebars_helper!(serde_annotate_if_bytes: |val: Value| { let name = val.as_str().unwrap(); - _serde_annotate_if_bytes(name) + _serde_annotate_if_wrapper(name) }); -pub fn _serde_annotate_if_bytes(val: &str) -> String { - if val == "Bytes" { - return "#[serde(with = \"serde_bytes\")]\n ".to_string(); +pub fn _serde_annotate_if_wrapper(val: &str) -> String { + match val { + "Bytes" => "#[serde(with = \"bytes\")]\n ".to_string(), + "JSON" => "#[serde(with = \"json\")]\n ".to_string(), + "BigInt" => "#[serde(with = \"bigint\")]\n ".to_string(), + _ => "".to_string(), } - "".to_string() } diff --git a/implementations/plugin-rust/src/helpers/to_rust.rs b/implementations/plugin-rust/src/helpers/to_rust.rs index 225bb8a3..9e4eed7f 100644 --- a/implementations/plugin-rust/src/helpers/to_rust.rs +++ b/implementations/plugin-rust/src/helpers/to_rust.rs @@ -2,6 +2,7 @@ use handlebars::handlebars_helper; use serde_json::{Value}; use crate::helpers::detect_keyword::_detect_keyword; use crate::helpers::to_upper::_to_upper; +use crate::helpers::util::{array_type, map_types}; handlebars_helper!(to_rust: |val: Value| { let type_val = val.as_str().unwrap(); @@ -53,48 +54,17 @@ pub fn _to_rust(value: &str) -> String { } pub fn _to_rust_array(value: &str, optional: bool) -> Result { - let mut iter = value.char_indices(); - - let first_bracket = match iter.find(|&(_, c)| c == '[').map(|(i, _)| i) { - Some(idx) => idx, - None => return Err(format!("Invalid Array: {}", value)), - }; - let last_bracket = match iter.rfind(|&(_, c)| c == ']').map(|(i, _)| i) { - Some(idx) => idx, - None => return Err(format!("Invalid Array: {}", value)), - }; - - let inner_type = &value[(first_bracket+1)..last_bracket]; - let rs_type = _to_rust(inner_type); - + let inner_type = array_type(value)?; + let rs_type = _to_rust(&inner_type); let rs_array = format!("Vec<{}>", rs_type); Ok(_apply_optional(&rs_array, optional)) } pub fn _to_rust_map(value: &str, optional: bool) -> Result { - let first_open_bracket_idx = match value.find('<') { - Some(idx) => idx, - None => return Err(format!("Invalid Map: {}", value)), - }; - let last_close_bracket_idx = match value.rfind('>') { - Some(idx) => idx, - None => return Err(format!("Invalid Map: {}", value)), - }; - - let key_val_types = &value[(first_open_bracket_idx + 1)..last_close_bracket_idx]; - - let first_comma_idx = match key_val_types.find(',') { - Some(idx) => idx, - None => return Err(format!("Invalid Map: {}", value)), - }; - - let key_type = key_val_types[..first_comma_idx].trim(); - let val_type = key_val_types[(first_comma_idx + 1)..].trim(); - - let rs_key_type = _to_rust(key_type); - let rs_val_type = _to_rust(val_type); - - let rs_map = format!("Map<{}, {}>", rs_key_type, rs_val_type); + let (key_type, val_type) = map_types(value)?; + let rs_key_type = _to_rust(&key_type); + let rs_val_type = _to_rust(&val_type); + let rs_map = format!("BTreeMap<{}, {}>", &rs_key_type, &rs_val_type); Ok(_apply_optional(&rs_map, optional)) } diff --git a/implementations/plugin-rust/src/helpers/util.rs b/implementations/plugin-rust/src/helpers/util.rs index 1e4c8c89..9fc89e82 100644 --- a/implementations/plugin-rust/src/helpers/util.rs +++ b/implementations/plugin-rust/src/helpers/util.rs @@ -16,3 +16,42 @@ pub fn remove_at(s: &str, idx: usize) -> String { format!("{}{}", start, end) } +pub fn array_type(value: &str) -> Result { + let mut iter = value.char_indices(); + + let first_bracket = match iter.find(|&(_, c)| c == '[').map(|(i, _)| i) { + Some(idx) => idx, + None => return Err(format!("Invalid Array: {}", value)), + }; + let last_bracket = match iter.rfind(|&(_, c)| c == ']').map(|(i, _)| i) { + Some(idx) => idx, + None => return Err(format!("Invalid Array: {}", value)), + }; + + let inner_type = &value[(first_bracket+1)..last_bracket]; + Ok(inner_type.to_string()) +} + +pub fn map_types(value: &str) -> Result<(String, String), String> { + let first_open_bracket_idx = match value.find('<') { + Some(idx) => idx, + None => return Err(format!("Invalid Map: {}", value)), + }; + let last_close_bracket_idx = match value.rfind('>') { + Some(idx) => idx, + None => return Err(format!("Invalid Map: {}", value)), + }; + + let key_val_types = &value[(first_open_bracket_idx + 1)..last_close_bracket_idx]; + + let first_comma_idx = match key_val_types.find(',') { + Some(idx) => idx, + None => return Err(format!("Invalid Map: {}", value)), + }; + + let key_type = key_val_types[..first_comma_idx].trim(); + let val_type = key_val_types[(first_comma_idx + 1)..].trim(); + + Ok((key_type.to_string(), val_type.to_string())) +} + diff --git a/implementations/plugin-rust/src/templates/module_rs.rs b/implementations/plugin-rust/src/templates/module_rs.rs index 70d8dd43..85fb3675 100644 --- a/implementations/plugin-rust/src/templates/module_rs.rs +++ b/implementations/plugin-rust/src/templates/module_rs.rs @@ -6,6 +6,19 @@ lazy_static! { use std::sync::Arc; use polywrap_core::invoke::Invoker; use polywrap_plugin::{error::PluginError, module::PluginModule}; +use polywrap_msgpack_serde::{ + to_vec, + from_slice, + BigInt, + BigNumber, + JSON, + bytes, + wrappers::{ + polywrap_bigint as bigint, + polywrap_json as json + } +}; +use std::collections::BTreeMap; use serde::{Serialize, Deserialize}; use super::types::*; diff --git a/implementations/plugin-rust/src/templates/types_rs.rs b/implementations/plugin-rust/src/templates/types_rs.rs index ede0b088..33fcb81b 100644 --- a/implementations/plugin-rust/src/templates/types_rs.rs +++ b/implementations/plugin-rust/src/templates/types_rs.rs @@ -6,8 +6,20 @@ lazy_static! { // NOTE: This is an auto-generated file. // All modifications will be overwritten. use polywrap_core::{invoke::Invoker, uri::Uri}; -use polywrap_msgpack::{decode, serialize}; -use polywrap_plugin::{error::PluginError, BigInt, BigNumber, Map, JSON}; +use polywrap_plugin::error::PluginError; +use polywrap_msgpack_serde::{ + to_vec, + from_slice, + BigInt, + BigNumber, + JSON, + bytes, + wrappers::{ + polywrap_bigint as bigint, + polywrap_json as json + } +}; +use std::collections::BTreeMap; use serde::{Serialize, Deserialize}; {{#each importedModuleTypes}} use std::sync::Arc; @@ -116,7 +128,7 @@ impl<'a> {{detect_keyword (to_upper type)}}<'a> { {{#each methods}} pub fn {{to_lower name}}(&self, args: &{{to_upper ../type}}Args{{to_upper name}}) -> Result<{{#with return}}{{to_rust (to_graphql_type this)}}{{/with}}, PluginError> { let uri = self.uri; - let serialized_args = serialize(args.clone()).unwrap(); + let serialized_args = to_vec(args.clone()).unwrap(); let result = invoker.invoke_raw( uri, "{{name}}", @@ -131,7 +143,7 @@ impl<'a> {{detect_keyword (to_upper type)}}<'a> { exception: e.to_string(), })?; - Ok({{#with return}}{{#if required}}{{else}}Some({{/if}}{{/with}}decode(result.as_slice())?{{#with return}}{{#if required}}{{else}}){{/if}}{{/with}}) + Ok({{#with return}}{{#if required}}{{else}}Some({{/if}}{{/with}}from_slice(result.as_slice())?{{#with return}}{{#if required}}{{else}}){{/if}}{{/with}}) } {{#if (is_not_last @index ../methods)}} @@ -152,7 +164,7 @@ impl {{detect_keyword (to_upper type)}} { {{#each methods}} let uri = {{to_upper ../type}}::URI; pub fn {{detect_keyword (to_lower name)}}(args: &{{to_upper ../type}}Args{{to_upper name}}, invoker: Arc) -> Result<{{#with return}}{{to_rust (to_graphql_type this)}}{{/with}}, PluginError> { - let serialized_args = serialize(args.clone()).unwrap(); + let serialized_args = to_vec(args.clone()).unwrap(); let opt_args = Some(serialized_args.as_slice()); let uri = Uri::try_from(uri).unwrap(); let result = invoker.invoke_raw( @@ -169,7 +181,7 @@ impl {{detect_keyword (to_upper type)}} { exception: e.to_string(), })?; - Ok({{#with return}}{{#if required}}{{else}}Some({{/if}}{{/with}}decode(result.as_slice())?{{#with return}}{{#if required}}{{else}}){{/if}}{{/with}}) + Ok({{#with return}}{{#if required}}{{else}}Some({{/if}}{{/with}}from_slice(result.as_slice())?{{#with return}}{{#if required}}{{else}}){{/if}}{{/with}}) } {{#if (is_not_last @index ../methods)}} diff --git a/implementations/plugin-typescript/Cargo.lock b/implementations/plugin-typescript/Cargo.lock index 8c08f46b..42b834cf 100644 --- a/implementations/plugin-typescript/Cargo.lock +++ b/implementations/plugin-typescript/Cargo.lock @@ -209,6 +209,7 @@ dependencies = [ "handlebars", "lazy_static", "polywrap-wasm-rs", + "polywrap_msgpack_serde", "serde", "serde_json", ] @@ -227,20 +228,36 @@ dependencies = [ "thiserror", ] +[[package]] +name = "polywrap_msgpack_serde" +version = "0.0.2-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1b711e6e4649770c77940dbf1cb0362ed16a2c7a41b82f607a613847f6f6ddc" +dependencies = [ + "bigdecimal", + "byteorder", + "num-bigint", + "num-traits", + "serde", + "serde_bytes", + "serde_json", + "thiserror", +] + [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.27" +version = "1.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" dependencies = [ "proc-macro2", ] @@ -253,18 +270,27 @@ checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "serde" -version = "1.0.162" +version = "1.0.167" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71b2f6e1ab5c2b98c05f0f35b236b22e8df7ead6ffbf51d7808da7f8817e7ab6" +checksum = "7daf513456463b42aa1d94cff7e0c24d682b429f020b9afa4f5ba5c40a22b237" dependencies = [ "serde_derive", ] +[[package]] +name = "serde_bytes" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a16be4fe5320ade08736447e3198294a5ea9a6d44dde6f35f0a5e06859c427a" +dependencies = [ + "serde", +] + [[package]] name = "serde_derive" -version = "1.0.162" +version = "1.0.167" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2a0814352fd64b58489904a44ea8d90cb1a91dcb6b4f5ebabc32c8318e93cb6" +checksum = "b69b106b68bc8054f0e974e70d19984040f8a5cf9215ca82626ea4853f82c4b9" dependencies = [ "proc-macro2", "quote", @@ -295,9 +321,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.15" +version = "2.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "59fb7d6d8281a51045d62b8eb3a7d1ce347b76f312af50cd3dc0af39c87c1737" dependencies = [ "proc-macro2", "quote", diff --git a/implementations/plugin-typescript/Cargo.toml b/implementations/plugin-typescript/Cargo.toml index 6c6d3e51..0d51a0d6 100644 --- a/implementations/plugin-typescript/Cargo.toml +++ b/implementations/plugin-typescript/Cargo.toml @@ -9,6 +9,7 @@ edition = "2021" [dependencies] polywrap-wasm-rs = { version = "~0.10.5" } +polywrap_msgpack_serde = { version = "0.0.2-beta.3" } serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0.97" } handlebars = { version = "4.3.7" } diff --git a/implementations/plugin-typescript/URI.txt b/implementations/plugin-typescript/URI.txt index 72af69f2..143e67be 100644 --- a/implementations/plugin-typescript/URI.txt +++ b/implementations/plugin-typescript/URI.txt @@ -1 +1 @@ -wrap://ipfs/Qmb9Wo9cGPr3JZC2Wn7rUNnfYJJDnagRz5rqAw5NnhgGEh \ No newline at end of file +wrap://ipfs/QmVcovXjD7QrDxLoPTgMHWPKvQfpAGcaac76YQbU3GCbT2 \ No newline at end of file diff --git a/implementations/plugin-typescript/build/wrap.wasm b/implementations/plugin-typescript/build/wrap.wasm index 65cb2358..b40c308d 100755 Binary files a/implementations/plugin-typescript/build/wrap.wasm and b/implementations/plugin-typescript/build/wrap.wasm differ diff --git a/implementations/plugin-typescript/src/helpers/mod.rs b/implementations/plugin-typescript/src/helpers/mod.rs index cd02f643..a0556446 100644 --- a/implementations/plugin-typescript/src/helpers/mod.rs +++ b/implementations/plugin-typescript/src/helpers/mod.rs @@ -10,6 +10,9 @@ mod to_graphql_type; mod to_typescript; mod pretty; +// helpers for helpers +mod util; + pub fn register(handlebars: &mut Handlebars) -> () { handlebars.register_helper( "array_has_length", diff --git a/implementations/plugin-typescript/src/helpers/pretty.rs b/implementations/plugin-typescript/src/helpers/pretty.rs index 1c6e6e92..ceef86c7 100644 --- a/implementations/plugin-typescript/src/helpers/pretty.rs +++ b/implementations/plugin-typescript/src/helpers/pretty.rs @@ -2,5 +2,8 @@ use handlebars::handlebars_helper; use serde_json::{Value}; handlebars_helper!(pretty: |value: Value| { - serde_json::to_string_pretty(&value).unwrap() + let pretty_print = |v: Value| serde_json::to_string_pretty(&v).unwrap(); + value.as_str() + .and_then(|s| serde_json::from_str::(&s).ok()) + .map_or_else(|| pretty_print(value), pretty_print) }); diff --git a/implementations/plugin-typescript/src/helpers/to_typescript.rs b/implementations/plugin-typescript/src/helpers/to_typescript.rs index d89b26e7..2eb872f3 100644 --- a/implementations/plugin-typescript/src/helpers/to_typescript.rs +++ b/implementations/plugin-typescript/src/helpers/to_typescript.rs @@ -1,6 +1,7 @@ use handlebars::handlebars_helper; use serde_json::value::Value; use crate::helpers::detect_keyword::_detect_keyword; +use crate::helpers::util::{array_type, map_types}; handlebars_helper!(to_typescript: |value: Value| { _to_typescript(value.as_str().unwrap(), false) @@ -43,47 +44,16 @@ fn _to_typescript(value: &str, undefinable: bool) -> String { } fn to_typescript_array(value: &str, optional: bool) -> Result { - let mut iter = value.char_indices(); - - let first_bracket = match iter.find(|&(_, c)| c == '[').map(|(i, _)| i) { - Some(idx) => idx, - None => return Err(format!("Invalid Array: {}", value)), - }; - let last_bracket = match iter.rfind(|&(_, c)| c == ']').map(|(i, _)| i) { - Some(idx) => idx, - None => return Err(format!("Invalid Array: {}", value)), - }; - - let inner_type = &value[(first_bracket+1)..last_bracket]; - let ts_type = _to_typescript(inner_type, false); - + let inner_type = array_type(value)?; + let ts_type = _to_typescript(&inner_type, false); let ts_array = format!("Array<{}>", ts_type); Ok(apply_optional(&ts_array, optional)) } fn to_typescript_map(value: &str, optional: bool) -> Result { - let first_open_bracket_idx = match value.find('<') { - Some(idx) => idx, - None => return Err(format!("Invalid Map: {}", value)), - }; - let last_close_bracket_idx = match value.rfind('>') { - Some(idx) => idx, - None => return Err(format!("Invalid Map: {}", value)), - }; - - let key_val_types = &value[(first_open_bracket_idx + 1)..last_close_bracket_idx]; - - let first_comma_idx = match key_val_types.find(',') { - Some(idx) => idx, - None => return Err(format!("Invalid Map: {}", value)), - }; - - let key_type = key_val_types[..first_comma_idx].trim(); - let val_type = key_val_types[(first_comma_idx + 1)..].trim(); - - let ts_key_type = _to_typescript(key_type, false); - let ts_val_type = _to_typescript(val_type, true); - + let (key_type, val_type) = map_types(value)?; + let ts_key_type = _to_typescript(&key_type, false); + let ts_val_type = _to_typescript(&val_type, true); let ts_map = format!("Map<{}, {}>", ts_key_type, ts_val_type); Ok(apply_optional(&ts_map, optional)) } diff --git a/implementations/plugin-typescript/src/helpers/util.rs b/implementations/plugin-typescript/src/helpers/util.rs new file mode 100644 index 00000000..826415c5 --- /dev/null +++ b/implementations/plugin-typescript/src/helpers/util.rs @@ -0,0 +1,38 @@ +pub fn array_type(value: &str) -> Result { + let mut iter = value.char_indices(); + + let first_bracket = match iter.find(|&(_, c)| c == '[').map(|(i, _)| i) { + Some(idx) => idx, + None => return Err(format!("Invalid Array: {}", value)), + }; + let last_bracket = match iter.rfind(|&(_, c)| c == ']').map(|(i, _)| i) { + Some(idx) => idx, + None => return Err(format!("Invalid Array: {}", value)), + }; + + let inner_type = &value[(first_bracket+1)..last_bracket]; + Ok(inner_type.to_string()) +} + +pub fn map_types(value: &str) -> Result<(String, String), String> { + let first_open_bracket_idx = match value.find('<') { + Some(idx) => idx, + None => return Err(format!("Invalid Map: {}", value)), + }; + let last_close_bracket_idx = match value.rfind('>') { + Some(idx) => idx, + None => return Err(format!("Invalid Map: {}", value)), + }; + + let key_val_types = &value[(first_open_bracket_idx + 1)..last_close_bracket_idx]; + + let first_comma_idx = match key_val_types.find(',') { + Some(idx) => idx, + None => return Err(format!("Invalid Map: {}", value)), + }; + + let key_type = key_val_types[..first_comma_idx].trim(); + let val_type = key_val_types[(first_comma_idx + 1)..].trim(); + + Ok((key_type.to_string(), val_type.to_string())) +} \ No newline at end of file diff --git a/implementations/wrap-assemblyscript/src/helpers/mod.rs b/implementations/wrap-assemblyscript/src/helpers/mod.rs index 75b5b95a..806c1007 100644 --- a/implementations/wrap-assemblyscript/src/helpers/mod.rs +++ b/implementations/wrap-assemblyscript/src/helpers/mod.rs @@ -17,6 +17,9 @@ mod to_wasm_init; mod to_wasm_map; mod to_wasm; +// helpers for helpers +mod util; + pub fn register(handlebars: &mut Handlebars) -> () { handlebars.register_helper( "has_required", diff --git a/implementations/wrap-assemblyscript/src/helpers/to_wasm_array.rs b/implementations/wrap-assemblyscript/src/helpers/to_wasm_array.rs index 8f5e67af..bdb93e4c 100644 --- a/implementations/wrap-assemblyscript/src/helpers/to_wasm_array.rs +++ b/implementations/wrap-assemblyscript/src/helpers/to_wasm_array.rs @@ -1,26 +1,14 @@ use handlebars::handlebars_helper; use serde_json::{Value}; +use crate::helpers::util::array_type; use super::apply_optional::apply_optional_fn; use super::to_wasm::to_wasm_fn; pub fn to_wasm_array_fn(value: &str, optional: bool) -> Result { - let mut iter = value.char_indices(); - - let first_bracket = match iter.find(|&(_, c)| c == '[').map(|(i, _)| i) { - Some(idx) => idx, - None => return Err(format!("Invalid Array: {}", value)), - }; - let last_bracket = match iter.rfind(|&(_, c)| c == ']').map(|(i, _)| i) { - Some(idx) => idx, - None => return Err(format!("Invalid Array: {}", value)), - }; - - let inner_type = &value[(first_bracket+1)..last_bracket]; - let wasm_type = to_wasm_fn(inner_type); - + let inner_type = array_type(value)?; + let wasm_type = to_wasm_fn(&inner_type); let wasm_array = format!("Array<{}>", wasm_type); - Ok(apply_optional_fn(&wasm_array, optional, false)) } diff --git a/implementations/wrap-assemblyscript/src/helpers/to_wasm_init.rs b/implementations/wrap-assemblyscript/src/helpers/to_wasm_init.rs index d02f3fe8..b1525859 100644 --- a/implementations/wrap-assemblyscript/src/helpers/to_wasm_init.rs +++ b/implementations/wrap-assemblyscript/src/helpers/to_wasm_init.rs @@ -1,5 +1,6 @@ use handlebars::handlebars_helper; use serde_json::{Value}; +use crate::helpers::util::map_types; use super::to_wasm::to_wasm_fn; use super::detect_keyword::detect_keyword_fn; @@ -23,34 +24,9 @@ pub fn to_wasm_init_fn(value: &str) -> String { } if type_str.starts_with("Map<") { - let first_open_bracket_idx = type_str.find('<').unwrap_or_else(|| { - panic!("Invalid Map: {}", type_str); - }); - let last_close_bracket_idx = type_str - .rfind('>') - .unwrap_or_else(|| panic!("Invalid Map: {}", type_str)); - - let key_val_types = type_str - .get(first_open_bracket_idx + 1..last_close_bracket_idx) - .unwrap_or_else(|| panic!("Invalid Map: {}", type_str)); - - let first_comma_idx = key_val_types.find(',').unwrap_or_else(|| { - panic!("Invalid Map: {}", type_str); - }); - let key_type = key_val_types - .get(0..first_comma_idx) - .unwrap_or_else(|| panic!("Invalid Map: {}", type_str)) - .trim() - .to_string(); - let val_type = key_val_types - .get(first_comma_idx + 1..) - .unwrap_or_else(|| panic!("Invalid Map: {}", type_str)) - .trim() - .to_string(); - + let (key_type, val_type) = map_types(&type_str).unwrap(); let wasm_key_type = to_wasm_fn(&key_type); let wasm_val_type = to_wasm_fn(&val_type); - return format!("new Map<{}, {}>()", wasm_key_type, wasm_val_type); } diff --git a/implementations/wrap-assemblyscript/src/helpers/to_wasm_map.rs b/implementations/wrap-assemblyscript/src/helpers/to_wasm_map.rs index f07ecebd..fca05a78 100644 --- a/implementations/wrap-assemblyscript/src/helpers/to_wasm_map.rs +++ b/implementations/wrap-assemblyscript/src/helpers/to_wasm_map.rs @@ -1,32 +1,14 @@ use handlebars::handlebars_helper; use serde_json::{Value}; +use crate::helpers::util::map_types; use super::apply_optional::apply_optional_fn; use super::to_wasm::to_wasm_fn; pub fn to_wasm_map_fn(value: &str, optional: bool) -> Result { - let first_open_bracket_idx = match value.find('<') { - Some(idx) => idx, - None => return Err(format!("Invalid Map: {}", value)), - }; - let last_close_bracket_idx = match value.rfind('>') { - Some(idx) => idx, - None => return Err(format!("Invalid Map: {}", value)), - }; - - let key_val_types = &value[(first_open_bracket_idx + 1)..last_close_bracket_idx]; - - let first_comma_idx = match key_val_types.find(',') { - Some(idx) => idx, - None => return Err(format!("Invalid Map: {}", value)), - }; - - let key_type = key_val_types[..first_comma_idx].trim(); - let val_type = key_val_types[(first_comma_idx + 1)..].trim(); - - let wasm_key_type = to_wasm_fn(&key_type.to_string()); - let wasm_val_type = to_wasm_fn(&val_type.to_string()); - + let (key_type, val_type) = map_types(value)?; + let wasm_key_type = to_wasm_fn(&key_type); + let wasm_val_type = to_wasm_fn(&val_type); let wasm_map = format!("Map<{}, {}>", wasm_key_type, wasm_val_type); Ok(apply_optional_fn(&wasm_map, optional, false)) } diff --git a/implementations/wrap-assemblyscript/src/helpers/util.rs b/implementations/wrap-assemblyscript/src/helpers/util.rs new file mode 100644 index 00000000..826415c5 --- /dev/null +++ b/implementations/wrap-assemblyscript/src/helpers/util.rs @@ -0,0 +1,38 @@ +pub fn array_type(value: &str) -> Result { + let mut iter = value.char_indices(); + + let first_bracket = match iter.find(|&(_, c)| c == '[').map(|(i, _)| i) { + Some(idx) => idx, + None => return Err(format!("Invalid Array: {}", value)), + }; + let last_bracket = match iter.rfind(|&(_, c)| c == ']').map(|(i, _)| i) { + Some(idx) => idx, + None => return Err(format!("Invalid Array: {}", value)), + }; + + let inner_type = &value[(first_bracket+1)..last_bracket]; + Ok(inner_type.to_string()) +} + +pub fn map_types(value: &str) -> Result<(String, String), String> { + let first_open_bracket_idx = match value.find('<') { + Some(idx) => idx, + None => return Err(format!("Invalid Map: {}", value)), + }; + let last_close_bracket_idx = match value.rfind('>') { + Some(idx) => idx, + None => return Err(format!("Invalid Map: {}", value)), + }; + + let key_val_types = &value[(first_open_bracket_idx + 1)..last_close_bracket_idx]; + + let first_comma_idx = match key_val_types.find(',') { + Some(idx) => idx, + None => return Err(format!("Invalid Map: {}", value)), + }; + + let key_type = key_val_types[..first_comma_idx].trim(); + let val_type = key_val_types[(first_comma_idx + 1)..].trim(); + + Ok((key_type.to_string(), val_type.to_string())) +} \ No newline at end of file diff --git a/implementations/wrap-rust/.gitignore b/implementations/wrap-rust/.gitignore new file mode 100644 index 00000000..20591e12 --- /dev/null +++ b/implementations/wrap-rust/.gitignore @@ -0,0 +1,6 @@ +node_modules +.polywrap +target +workflows/output.json +wrap +debug \ No newline at end of file diff --git a/implementations/wrap-rust/.nvmrc b/implementations/wrap-rust/.nvmrc new file mode 100644 index 00000000..50157062 --- /dev/null +++ b/implementations/wrap-rust/.nvmrc @@ -0,0 +1 @@ +v17.9.1 \ No newline at end of file diff --git a/implementations/wrap-rust/Cargo.lock b/implementations/wrap-rust/Cargo.lock new file mode 100644 index 00000000..f1825d93 --- /dev/null +++ b/implementations/wrap-rust/Cargo.lock @@ -0,0 +1,349 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bigdecimal" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6773ddc0eafc0e509fb60e48dff7f450f8e674a0686ae8605e8d9901bd5eefa" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", + "serde", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cpufeatures" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "handlebars" +version = "4.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83c3372087601b532857d332f5957cbae686da52bb7810bf038c3e3c3cc2fa0d" +dependencies = [ + "log", + "pest", + "pest_derive", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.144" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", + "serde", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "pest" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e68e84bfb01f0507134eac1e9b410a12ba379d064eab48c50ba4ce329a527b70" +dependencies = [ + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b79d4c71c865a25a4322296122e3924d30bc8ee0834c8bfc8b95f7f054afbfb" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c435bf1076437b851ebc8edc3a18442796b30f1728ffea6262d59bbe28b077e" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "745a452f8eb71e39ffd8ee32b3c5f51d03845f99786fa9b68db6ff509c505411" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + +[[package]] +name = "polywrap-wasm-rs" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bb675089ed7e61f0492789b0cfd197efd4b9239b7b8e2651707dee77f44e679" +dependencies = [ + "bigdecimal", + "byteorder", + "num-bigint", + "num-traits", + "serde_json", + "thiserror", +] + +[[package]] +name = "proc-macro2" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "serde" +version = "1.0.162" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71b2f6e1ab5c2b98c05f0f35b236b22e8df7ead6ffbf51d7808da7f8817e7ab6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.162" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2a0814352fd64b58489904a44ea8d90cb1a91dcb6b4f5ebabc32c8318e93cb6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "syn" +version = "2.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "ucd-trie" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wrap-rust-wrap-abi-bindgen" +version = "0.1.0" +dependencies = [ + "handlebars", + "lazy_static", + "polywrap-wasm-rs", + "serde", + "serde_json", +] diff --git a/implementations/wrap-rust/Cargo.toml b/implementations/wrap-rust/Cargo.toml new file mode 100644 index 00000000..26939da2 --- /dev/null +++ b/implementations/wrap-rust/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "wrap-rust-wrap-abi-bindgen" +version = "0.1.0" +description = "Generates Rust wrap bindings from a WRAP ABI" +authors = ["Polywrap"] +repository = "https://github.com/polywrap/wrap-abi-bindgen" +license = "MIT" +edition = "2021" + +[dependencies] +polywrap-wasm-rs = { version = "~0.10.5" } +serde = { version = "1.0", features = ["derive"] } +serde_json = { version = "1.0.99" } +handlebars = { version = "4.3.7" } +lazy_static = { version = "1.4.0" } + +[lib] +crate-type = ["cdylib"] + +[profile.release] +opt-level = 's' +lto = true +panic = 'abort' diff --git a/implementations/wrap-rust/README.md b/implementations/wrap-rust/README.md new file mode 100644 index 00000000..cbce544c --- /dev/null +++ b/implementations/wrap-rust/README.md @@ -0,0 +1,15 @@ +# Polywrap Wasm Wrapper Template +A simple starter template for a Rust Wasm wrapper. For more information on how this project works, and a step by step on how to extend its behavior, see the documentation [here](https://docs.polywrap.io/). + +# How To Run + +## Install Dependencies +`nvm install && nvm use` +`yarn` + +## Codegen & Build +`yarn codegen` +`yarn build` + +## Test +`yarn test` diff --git a/implementations/wrap-rust/URI.txt b/implementations/wrap-rust/URI.txt new file mode 100644 index 00000000..6160c7c4 --- /dev/null +++ b/implementations/wrap-rust/URI.txt @@ -0,0 +1 @@ +wrap://ipfs/QmNenpmWMFM5DUAp4G2NpDRc3WMFo2R3BrBYcgMNCU8Vwp \ No newline at end of file diff --git a/implementations/wrap-rust/build/wrap.info b/implementations/wrap-rust/build/wrap.info new file mode 100644 index 00000000..348eb0f9 Binary files /dev/null and b/implementations/wrap-rust/build/wrap.info differ diff --git a/implementations/wrap-rust/build/wrap.wasm b/implementations/wrap-rust/build/wrap.wasm new file mode 100755 index 00000000..f7005a0b Binary files /dev/null and b/implementations/wrap-rust/build/wrap.wasm differ diff --git a/implementations/wrap-rust/jest.config.js b/implementations/wrap-rust/jest.config.js new file mode 100644 index 00000000..4a4c022f --- /dev/null +++ b/implementations/wrap-rust/jest.config.js @@ -0,0 +1,15 @@ +module.exports = { + collectCoverage: false, + preset: "ts-jest", + testEnvironment: "node", + testMatch: ["**/__tests__/**/?(*.)+(spec|test).[jt]s?(x)"], + globals: { + "ts-jest": { + tsconfig: "tsconfig.json", + diagnostics: false, + }, + }, + testPathIgnorePatterns: [ + "/.polywrap/" + ], +}; diff --git a/implementations/wrap-rust/package.json b/implementations/wrap-rust/package.json new file mode 100644 index 00000000..a049b5ee --- /dev/null +++ b/implementations/wrap-rust/package.json @@ -0,0 +1,20 @@ +{ + "name": "wrap-rust-wrap-abi-bindgen", + "private": true, + "scripts": { + "codegen": "npx polywrap codegen", + "build": "npx polywrap build", + "deploy": "npx polywrap deploy", + "test": "yarn test:codegen && jest --passWithNoTests --runInBand --verbose", + "test:codegen": "npx polywrap codegen -m ./src/__tests__/polywrap.yaml -g ./src/__tests__/wrap" + }, + "devDependencies": { + "@types/jest": "26.0.8", + "@polywrap/schema-parse": "0.10.6", + "jest": "26.6.3", + "jest-diff": "26.6.2", + "polywrap": "0.10.6", + "ts-jest": "26.5.4", + "typescript": "4.9.5" + } +} diff --git a/implementations/wrap-rust/polywrap.deploy.yaml b/implementations/wrap-rust/polywrap.deploy.yaml new file mode 100644 index 00000000..2b9ab565 --- /dev/null +++ b/implementations/wrap-rust/polywrap.deploy.yaml @@ -0,0 +1,7 @@ +format: 0.1.0 +stages: + ipfs_deploy: + package: ipfs + uri: fs/./build + config: + gatewayUri: "https://ipfs.wrappers.io" \ No newline at end of file diff --git a/implementations/wrap-rust/polywrap.graphql b/implementations/wrap-rust/polywrap.graphql new file mode 100644 index 00000000..3704ef4a --- /dev/null +++ b/implementations/wrap-rust/polywrap.graphql @@ -0,0 +1 @@ +#import * from "wrap://ens/wraps.eth:abi-bindgen@0.1" diff --git a/implementations/wrap-rust/polywrap.yaml b/implementations/wrap-rust/polywrap.yaml new file mode 100644 index 00000000..cd733105 --- /dev/null +++ b/implementations/wrap-rust/polywrap.yaml @@ -0,0 +1,10 @@ +format: 0.3.0 +project: + name: wrap-rust-wrap-abi-bindgen + type: wasm/rust +source: + module: ./Cargo.toml + schema: ./polywrap.graphql + import_abis: + - uri: wrap://ens/wraps.eth:abi-bindgen@0.1 + abi: ../../interface/polywrap.graphql diff --git a/implementations/wrap-rust/src/__tests__/cases/000-sanity/input.graphql b/implementations/wrap-rust/src/__tests__/cases/000-sanity/input.graphql new file mode 100644 index 00000000..2755c645 --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/cases/000-sanity/input.graphql @@ -0,0 +1,255 @@ +### Polywrap Header START ### +scalar UInt +scalar UInt8 +scalar UInt16 +scalar UInt32 +scalar Int +scalar Int8 +scalar Int16 +scalar Int32 +scalar Bytes +scalar BigInt +scalar BigNumber +scalar JSON +scalar Map + +directive @imported( + uri: String! + namespace: String! + nativeType: String! +) on OBJECT | ENUM + +directive @imports( + types: [String!]! +) on OBJECT + +directive @capability( + type: String! + uri: String! + namespace: String! +) repeatable on OBJECT + +directive @enabled_interface on OBJECT + +directive @annotate(type: String!) on FIELD + +### Polywrap Header END ### + +type Module @imports( + types: [ + "TestImport_Module", + "TestImport_Object", + "TestImport_AnotherObject", + "TestImport_Enum", + "TestImport_Enum_Return" + ] +) @capability( + type: "getImplementations", + uri: "testimport.uri.eth", + namespace: "TestImport" +) { + moduleMethod( + str: String! + optStr: String + en: CustomEnum! + optEnum: CustomEnum + enumArray: [CustomEnum!]! + optEnumArray: [CustomEnum] + map: Map! @annotate(type: "Map!") + mapOfArr: Map! @annotate(type: "Map!") + mapOfMap: Map! @annotate(type: "Map!>!") + mapOfObj: Map! @annotate(type: "Map!") + mapOfArrOfObj: Map! @annotate(type: "Map!") + ): Int! + + objectMethod( + object: AnotherType! + optObject: AnotherType + objectArray: [AnotherType!]! + optObjectArray: [AnotherType] + ): AnotherType @env(required: true) + + optionalEnvMethod( + object: AnotherType! + optObject: AnotherType + objectArray: [AnotherType!]! + optObjectArray: [AnotherType] + ): AnotherType @env(required: false) + + if( + if: else! + ): else! +} + +type Env { + prop: String! + optProp: String + optMap: Map @annotate(type: "Map") +} + +type CustomType { + str: String! + optStr: String + u: UInt! + optU: UInt + u8: UInt8! + u16: UInt16! + u32: UInt32! + i: Int! + i8: Int8! + i16: Int16! + i32: Int32! + bigint: BigInt! + optBigint: BigInt + bignumber: BigNumber! + optBignumber: BigNumber + json: JSON! + optJson: JSON + bytes: Bytes! + optBytes: Bytes + boolean: Boolean! + optBoolean: Boolean + u_array: [UInt!]! + uOpt_array: [UInt!] + _opt_uOptArray: [UInt] + optStrOptArray: [String] + uArrayArray: [[UInt!]!]! + uOptArrayOptArray: [[UInt32]]! + uArrayOptArrayArray: [[[UInt32!]!]]! + crazyArray: [[[[UInt32!]]!]] + object: AnotherType! + optObject: AnotherType + objectArray: [AnotherType!]! + optObjectArray: [AnotherType] + en: CustomEnum! + optEnum: CustomEnum + enumArray: [CustomEnum!]! + optEnumArray: [CustomEnum] + map: Map! @annotate(type: "Map!") + mapOfArr: Map! @annotate(type: "Map!") + mapOfObj: Map! @annotate(type: "Map!") + mapOfArrOfObj: Map! @annotate(type: "Map!") + mapCustomValue: Map! @annotate(type: "Map!") +} + +type AnotherType { + prop: String + circular: CustomType + const: String +} + +enum CustomEnum { + STRING + BYTES +} + +type CustomMapValue { + foo: String! +} + +type else { + else: String! +} + +enum while { + for, + in, +} + +### Imported Modules START ### + +type TestImport_Module @imported( + uri: "testimport.uri.eth", + namespace: "TestImport", + nativeType: "Module" +) @enabled_interface { + importedMethod( + str: String! + optStr: String + u: UInt! + optU: UInt + uArrayArray: [[UInt]]! + object: TestImport_Object! + optObject: TestImport_Object + objectArray: [TestImport_Object!]! + optObjectArray: [TestImport_Object] + en: TestImport_Enum! + optEnum: TestImport_Enum + enumArray: [TestImport_Enum!]! + optEnumArray: [TestImport_Enum] + ): TestImport_Object @env(required: true) + + anotherMethod( + arg: [String!]! + ): Int32! + + returnsArrayOfEnums( + arg: String! + ): [TestImport_Enum_Return]! +} + +### Imported Modules END ### + +### Imported Objects START ### + +type TestImport_Object @imported( + uri: "testimport.uri.eth", + namespace: "TestImport", + nativeType: "Object" +) { + object: TestImport_AnotherObject! + optObject: TestImport_AnotherObject + objectArray: [TestImport_AnotherObject!]! + optObjectArray: [TestImport_AnotherObject] + en: TestImport_Enum! + optEnum: TestImport_Enum + enumArray: [TestImport_Enum!]! + optEnumArray: [TestImport_Enum] +} + +type TestImport_AnotherObject @imported( + uri: "testimport.uri.eth", + namespace: "TestImport", + nativeType: "AnotherObject" +) { + prop: String! +} + +enum TestImport_Enum @imported( + uri: "testimport.uri.eth", + namespace: "TestImport", + nativeType: "Enum" +) { + STRING + BYTES +} + +enum TestImport_Enum_Return @imported( + uri: "testimport.uri.eth", + namespace: "TestImport", + nativeType: "Enum" +) { + STRING + BYTES +} + +### Imported Objects END ### + +### Imported Envs START ### + +type TestImport_Env @imported( + uri: "testimport.uri.eth", + namespace: "TestImport", + nativeType: "Env" +) { + object: TestImport_AnotherObject! + optObject: TestImport_AnotherObject + objectArray: [TestImport_AnotherObject!]! + optObjectArray: [TestImport_AnotherObject] + en: TestImport_Enum! + optEnum: TestImport_Enum + enumArray: [TestImport_Enum!]! + optEnumArray: [TestImport_Enum] +} + +### Imported Envs END ### \ No newline at end of file diff --git a/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/_else/mod.rs b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/_else/mod.rs new file mode 100644 index 00000000..1e13036d --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/_else/mod.rs @@ -0,0 +1,25 @@ +use serde::{Serialize, Deserialize}; +use polywrap_msgpack_serde::{ + wrappers::polywrap_json::JSONString, + wrappers::polywrap_bigint::BigIntWrapper +}; +use polywrap_wasm_rs::{ + BigInt, + BigNumber, + Map, + JSON +}; + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Else { + #[serde(rename = "else")] + pub _else: String, +} + +impl Else { + pub fn new() -> Else { + Else { + _else: String::new(), + } + } +} diff --git a/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/_while/mod.rs b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/_while/mod.rs new file mode 100644 index 00000000..94f61f45 --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/_while/mod.rs @@ -0,0 +1,53 @@ +use polywrap_wasm_rs::{EnumTypeError}; +use serde::{Serialize, Deserialize}; +use std::convert::TryFrom; + +#[derive(Clone, Copy, Debug, Deserialize, Serialize)] +pub enum While { + #[serde(rename = "for")] + _for, + #[serde(rename = "in")] + _in, + _MAX_ +} + +pub fn sanitize_while_value(value: i32) -> Result<(), EnumTypeError> { + if value < 0 && value >= While::_MAX_ as i32 { + return Err(EnumTypeError::EnumProcessingError(format!("Invalid value for enum 'While': {}", value.to_string()))); + } + Ok(()) +} + +pub fn get_while_value(key: &str) -> Result { + match key { + "_for" => Ok(While::_for), + "_in" => Ok(While::_in), + "_MAX_" => Ok(While::_MAX_), + err => Err(EnumTypeError::EnumProcessingError(format!("Invalid key for enum 'While': {}", err))) + } +} + +pub fn get_while_key(value: While) -> Result { + if sanitize_while_value(value as i32).is_ok() { + match value { + While::_for => Ok("_for".to_string()), + While::_in => Ok("_in".to_string()), + While::_MAX_ => Ok("_MAX_".to_string()), + } + } else { + Err(EnumTypeError::EnumProcessingError(format!("Invalid value for enum 'While': {}", (value as i32).to_string()))) + } +} + +impl TryFrom for While { + type Error = EnumTypeError; + + fn try_from(v: i32) -> Result { + match v { + x if x == While::_for as i32 => Ok(While::_for), + x if x == While::_in as i32 => Ok(While::_in), + x if x == While::_MAX_ as i32 => Ok(While::_MAX_), + _ => Err(EnumTypeError::ParseEnumError(format!("Invalid value for enum 'While': {}", (v as i32).to_string()))), + } + } +} diff --git a/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/another_type/mod.rs b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/another_type/mod.rs new file mode 100644 index 00000000..171fa644 --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/another_type/mod.rs @@ -0,0 +1,30 @@ +use serde::{Serialize, Deserialize}; +use polywrap_msgpack_serde::{ + wrappers::polywrap_json::JSONString, + wrappers::polywrap_bigint::BigIntWrapper +}; +use polywrap_wasm_rs::{ + BigInt, + BigNumber, + Map, + JSON +}; +use crate::CustomType; + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct AnotherType { + pub prop: Option, + pub circular: Option, + #[serde(rename = "const")] + pub _const: Option, +} + +impl AnotherType { + pub fn new() -> AnotherType { + AnotherType { + prop: None, + circular: None, + _const: None, + } + } +} diff --git a/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/custom_enum/mod.rs b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/custom_enum/mod.rs new file mode 100644 index 00000000..477827bd --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/custom_enum/mod.rs @@ -0,0 +1,53 @@ +use polywrap_wasm_rs::{EnumTypeError}; +use serde::{Serialize, Deserialize}; +use std::convert::TryFrom; + +#[derive(Clone, Copy, Debug, Deserialize, Serialize)] +pub enum CustomEnum { + #[serde(rename = "STRING")] + STRING, + #[serde(rename = "BYTES")] + BYTES, + _MAX_ +} + +pub fn sanitize_custom_enum_value(value: i32) -> Result<(), EnumTypeError> { + if value < 0 && value >= CustomEnum::_MAX_ as i32 { + return Err(EnumTypeError::EnumProcessingError(format!("Invalid value for enum 'CustomEnum': {}", value.to_string()))); + } + Ok(()) +} + +pub fn get_custom_enum_value(key: &str) -> Result { + match key { + "STRING" => Ok(CustomEnum::STRING), + "BYTES" => Ok(CustomEnum::BYTES), + "_MAX_" => Ok(CustomEnum::_MAX_), + err => Err(EnumTypeError::EnumProcessingError(format!("Invalid key for enum 'CustomEnum': {}", err))) + } +} + +pub fn get_custom_enum_key(value: CustomEnum) -> Result { + if sanitize_custom_enum_value(value as i32).is_ok() { + match value { + CustomEnum::STRING => Ok("STRING".to_string()), + CustomEnum::BYTES => Ok("BYTES".to_string()), + CustomEnum::_MAX_ => Ok("_MAX_".to_string()), + } + } else { + Err(EnumTypeError::EnumProcessingError(format!("Invalid value for enum 'CustomEnum': {}", (value as i32).to_string()))) + } +} + +impl TryFrom for CustomEnum { + type Error = EnumTypeError; + + fn try_from(v: i32) -> Result { + match v { + x if x == CustomEnum::STRING as i32 => Ok(CustomEnum::STRING), + x if x == CustomEnum::BYTES as i32 => Ok(CustomEnum::BYTES), + x if x == CustomEnum::_MAX_ as i32 => Ok(CustomEnum::_MAX_), + _ => Err(EnumTypeError::ParseEnumError(format!("Invalid value for enum 'CustomEnum': {}", (v as i32).to_string()))), + } + } +} diff --git a/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/custom_map_value/mod.rs b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/custom_map_value/mod.rs new file mode 100644 index 00000000..29689350 --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/custom_map_value/mod.rs @@ -0,0 +1,24 @@ +use serde::{Serialize, Deserialize}; +use polywrap_msgpack_serde::{ + wrappers::polywrap_json::JSONString, + wrappers::polywrap_bigint::BigIntWrapper +}; +use polywrap_wasm_rs::{ + BigInt, + BigNumber, + Map, + JSON +}; + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct CustomMapValue { + pub foo: String, +} + +impl CustomMapValue { + pub fn new() -> CustomMapValue { + CustomMapValue { + foo: String::new(), + } + } +} diff --git a/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/custom_type/mod.rs b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/custom_type/mod.rs new file mode 100644 index 00000000..2e53a74f --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/custom_type/mod.rs @@ -0,0 +1,135 @@ +use serde::{Serialize, Deserialize}; +use polywrap_msgpack_serde::{ + wrappers::polywrap_json::JSONString, + wrappers::polywrap_bigint::BigIntWrapper +}; +use polywrap_wasm_rs::{ + BigInt, + BigNumber, + Map, + JSON +}; +use crate::AnotherType; +use crate::CustomEnum; +use crate::CustomMapValue; + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct CustomType { + pub str: String, + #[serde(rename = "optStr")] + pub opt_str: Option, + pub u: u32, + #[serde(rename = "optU")] + pub opt_u: Option, + pub u8: u8, + pub u16: u16, + pub u32: u32, + pub i: i32, + pub i8: i8, + pub i16: i16, + pub i32: i32, + pub bigint: BigIntWrapper, + #[serde(rename = "optBigint")] + pub opt_bigint: Option, + pub bignumber: BigNumber, + #[serde(rename = "optBignumber")] + pub opt_bignumber: Option, + pub json: JSONString, + #[serde(rename = "optJson")] + pub opt_json: Option, + #[serde(with = "serde_bytes")] + pub bytes: Vec, + #[serde(with = "serde_bytes")] + #[serde(rename = "optBytes")] + pub opt_bytes: Option>, + pub boolean: bool, + #[serde(rename = "optBoolean")] + pub opt_boolean: Option, + pub u_array: Vec, + #[serde(rename = "uOpt_array")] + pub u_opt_array: Option>, + #[serde(rename = "_opt_uOptArray")] + pub _opt_u_opt_array: Option>>, + #[serde(rename = "optStrOptArray")] + pub opt_str_opt_array: Option>>, + #[serde(rename = "uArrayArray")] + pub u_array_array: Vec>, + #[serde(rename = "uOptArrayOptArray")] + pub u_opt_array_opt_array: Vec>>>, + #[serde(rename = "uArrayOptArrayArray")] + pub u_array_opt_array_array: Vec>>>, + #[serde(rename = "crazyArray")] + pub crazy_array: Option>>>>>>, + pub object: AnotherType, + #[serde(rename = "optObject")] + pub opt_object: Option, + #[serde(rename = "objectArray")] + pub object_array: Vec, + #[serde(rename = "optObjectArray")] + pub opt_object_array: Option>>, + pub en: CustomEnum, + #[serde(rename = "optEnum")] + pub opt_enum: Option, + #[serde(rename = "enumArray")] + pub enum_array: Vec, + #[serde(rename = "optEnumArray")] + pub opt_enum_array: Option>>, + pub map: Map, + #[serde(rename = "mapOfArr")] + pub map_of_arr: Map>, + #[serde(rename = "mapOfObj")] + pub map_of_obj: Map, + #[serde(rename = "mapOfArrOfObj")] + pub map_of_arr_of_obj: Map>, + #[serde(rename = "mapCustomValue")] + pub map_custom_value: Map>, +} + +impl CustomType { + pub fn new() -> CustomType { + CustomType { + str: String::new(), + opt_str: None, + u: 0, + opt_u: None, + u8: 0, + u16: 0, + u32: 0, + i: 0, + i8: 0, + i16: 0, + i32: 0, + bigint: BigIntWrapper(BigInt::default()), + opt_bigint: None, + bignumber: BigNumber::default(), + opt_bignumber: None, + json: JSONString::from(JSON::Value::Null), + opt_json: None, + bytes: vec![], + opt_bytes: None, + boolean: false, + opt_boolean: None, + u_array: vec![], + u_opt_array: None, + _opt_u_opt_array: None, + opt_str_opt_array: None, + u_array_array: vec![], + u_opt_array_opt_array: vec![], + u_array_opt_array_array: vec![], + crazy_array: None, + object: Option::new(), + opt_object: None, + object_array: vec![], + opt_object_array: None, + en: Option::_MAX_, + opt_enum: None, + enum_array: vec![], + opt_enum_array: None, + map: Map::::new(), + map_of_arr: Map::>::new(), + map_of_obj: Map::::new(), + map_of_arr_of_obj: Map::>::new(), + map_custom_value: Map::>::new(), + } + } +} diff --git a/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/entry.rs b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/entry.rs new file mode 100644 index 00000000..25d939ff --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/entry.rs @@ -0,0 +1,41 @@ +use crate::{ + module_method_wrapped, + object_method_wrapped, + optional_env_method_wrapped, + if_wrapped +}; +use polywrap_wasm_rs::{ + abort, + invoke, + InvokeArgs, +}; + +#[no_mangle] +pub extern "C" fn _wrap_invoke(method_size: u32, args_size: u32, env_size: u32) -> bool { + // Ensure the abort handler is properly setup + abort::wrap_abort_setup(); + + let args: InvokeArgs = invoke::wrap_invoke_args(method_size, args_size); + let result: Vec; + + match args.method.as_str() { + "moduleMethod" => { + result = module_method_wrapped(args.args.as_slice(), env_size); + } + "objectMethod" => { + result = object_method_wrapped(args.args.as_slice(), env_size); + } + "optionalEnvMethod" => { + result = optional_env_method_wrapped(args.args.as_slice(), env_size); + } + "if" => { + result = if_wrapped(args.args.as_slice(), env_size); + } + _ => { + invoke::wrap_invoke_error(format!("Could not find invoke function {}", args.method)); + return false; + } + }; + invoke::wrap_invoke_result(result); + return true; +} diff --git a/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/env/mod.rs b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/env/mod.rs new file mode 100644 index 00000000..cc57defa --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/env/mod.rs @@ -0,0 +1,30 @@ +use serde::{Serialize, Deserialize}; +use polywrap_msgpack_serde::{ + wrappers::polywrap_json::JSONString, + wrappers::polywrap_bigint::BigIntWrapper +}; +use polywrap_wasm_rs::{ + BigInt, + BigNumber, + Map, + JSON +}; + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Env { + pub prop: String, + #[serde(rename = "optProp")] + pub opt_prop: Option, + #[serde(rename = "optMap")] + pub opt_map: Option>>, +} + +impl Env { + pub fn new() -> Env { + Env { + prop: String::new(), + opt_prop: None, + opt_map: None, + } + } +} diff --git a/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/imported/mod.rs b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/imported/mod.rs new file mode 100644 index 00000000..6d97b84f --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/imported/mod.rs @@ -0,0 +1,12 @@ +pub mod test_import_object; +pub use test_import_object::*; +pub mod test_import_another_object; +pub use test_import_another_object::*; +pub mod test_import_enum; +pub use test_import_enum::*; +pub mod test_import_enum_return; +pub use test_import_enum_return::*; +pub mod test_import_module; +pub use test_import_module::*; +pub mod test_import_env; +pub use test_import_env::*; diff --git a/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/imported/test_import_another_object/mod.rs b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/imported/test_import_another_object/mod.rs new file mode 100644 index 00000000..d5b29d8a --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/imported/test_import_another_object/mod.rs @@ -0,0 +1,26 @@ +use serde::{Serialize, Deserialize}; +use polywrap_msgpack_serde::{ + wrappers::polywrap_json::JSONString, + wrappers::polywrap_bigint::BigIntWrapper +}; +use polywrap_wasm_rs::{ + BigInt, + BigNumber, + Map, + JSON +}; + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct TestImportAnotherObject { + pub prop: String, +} + +impl TestImportAnotherObject { + pub const URI: &'static str = "testimport.uri.eth"; + + pub fn new() -> TestImportAnotherObject { + TestImportAnotherObject { + prop: String::new(), + } + } +} diff --git a/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/imported/test_import_enum/mod.rs b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/imported/test_import_enum/mod.rs new file mode 100644 index 00000000..4a22dd00 --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/imported/test_import_enum/mod.rs @@ -0,0 +1,53 @@ +use polywrap_wasm_rs::EnumTypeError; +use serde::{Serialize, Deserialize}; +use std::convert::TryFrom; + +#[derive(Clone, Copy, Debug, Deserialize, Serialize)] +pub enum TestImportEnum { + #[serde(rename = "STRING")] + STRING, + #[serde(rename = "BYTES")] + BYTES, + _MAX_ +} + +pub fn sanitize_test_import_enum_value(value: i32) -> Result<(), EnumTypeError> { + if value < 0 && value >= TestImportEnum::_MAX_ as i32 { + return Err(EnumTypeError::EnumProcessingError(format!("Invalid value for enum 'TestImportEnum': {}", value.to_string()))); + } + Ok(()) +} + +pub fn get_test_import_enum_value(key: &str) -> Result { + match key { + "STRING" => Ok(TestImportEnum::STRING), + "BYTES" => Ok(TestImportEnum::BYTES), + "_MAX_" => Ok(TestImportEnum::_MAX_), + err => Err(EnumTypeError::EnumProcessingError(format!("Invalid key for enum 'TestImportEnum': {}", err))) + } +} + +pub fn get_test_import_enum_key(value: TestImportEnum) -> Result { + if sanitize_test_import_enum_value(value as i32).is_ok() { + match value { + TestImportEnum::STRING => Ok("STRING".to_string()), + TestImportEnum::BYTES => Ok("BYTES".to_string()), + TestImportEnum::_MAX_ => Ok("_MAX_".to_string()), + } + } else { + Err(EnumTypeError::EnumProcessingError(format!("Invalid value for enum 'TestImportEnum': {}", (value as i32).to_string()))) + } +} + +impl TryFrom for TestImportEnum { + type Error = EnumTypeError; + + fn try_from(v: i32) -> Result { + match v { + x if x == TestImportEnum::STRING as i32 => Ok(TestImportEnum::STRING), + x if x == TestImportEnum::BYTES as i32 => Ok(TestImportEnum::BYTES), + x if x == TestImportEnum::_MAX_ as i32 => Ok(TestImportEnum::_MAX_), + _ => Err(EnumTypeError::ParseEnumError(format!("Invalid value for enum 'TestImportEnum': {}", (v as i32).to_string()))), + } + } +} diff --git a/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/imported/test_import_enum_return/mod.rs b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/imported/test_import_enum_return/mod.rs new file mode 100644 index 00000000..8d668d29 --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/imported/test_import_enum_return/mod.rs @@ -0,0 +1,53 @@ +use polywrap_wasm_rs::EnumTypeError; +use serde::{Serialize, Deserialize}; +use std::convert::TryFrom; + +#[derive(Clone, Copy, Debug, Deserialize, Serialize)] +pub enum TestImportEnumReturn { + #[serde(rename = "STRING")] + STRING, + #[serde(rename = "BYTES")] + BYTES, + _MAX_ +} + +pub fn sanitize_test_import_enum_return_value(value: i32) -> Result<(), EnumTypeError> { + if value < 0 && value >= TestImportEnumReturn::_MAX_ as i32 { + return Err(EnumTypeError::EnumProcessingError(format!("Invalid value for enum 'TestImportEnumReturn': {}", value.to_string()))); + } + Ok(()) +} + +pub fn get_test_import_enum_return_value(key: &str) -> Result { + match key { + "STRING" => Ok(TestImportEnumReturn::STRING), + "BYTES" => Ok(TestImportEnumReturn::BYTES), + "_MAX_" => Ok(TestImportEnumReturn::_MAX_), + err => Err(EnumTypeError::EnumProcessingError(format!("Invalid key for enum 'TestImportEnumReturn': {}", err))) + } +} + +pub fn get_test_import_enum_return_key(value: TestImportEnumReturn) -> Result { + if sanitize_test_import_enum_return_value(value as i32).is_ok() { + match value { + TestImportEnumReturn::STRING => Ok("STRING".to_string()), + TestImportEnumReturn::BYTES => Ok("BYTES".to_string()), + TestImportEnumReturn::_MAX_ => Ok("_MAX_".to_string()), + } + } else { + Err(EnumTypeError::EnumProcessingError(format!("Invalid value for enum 'TestImportEnumReturn': {}", (value as i32).to_string()))) + } +} + +impl TryFrom for TestImportEnumReturn { + type Error = EnumTypeError; + + fn try_from(v: i32) -> Result { + match v { + x if x == TestImportEnumReturn::STRING as i32 => Ok(TestImportEnumReturn::STRING), + x if x == TestImportEnumReturn::BYTES as i32 => Ok(TestImportEnumReturn::BYTES), + x if x == TestImportEnumReturn::_MAX_ as i32 => Ok(TestImportEnumReturn::_MAX_), + _ => Err(EnumTypeError::ParseEnumError(format!("Invalid value for enum 'TestImportEnumReturn': {}", (v as i32).to_string()))), + } + } +} diff --git a/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/imported/test_import_env/mod.rs b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/imported/test_import_env/mod.rs new file mode 100644 index 00000000..ea2dfd1b --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/imported/test_import_env/mod.rs @@ -0,0 +1,42 @@ +use serde::{Serialize, Deserialize}; +use polywrap_msgpack_serde::{ + wrappers::polywrap_json::JSONString, + wrappers::polywrap_bigint::BigIntWrapper +}; +use polywrap_wasm_rs::{ + BigInt, + BigNumber, + Map, + JSON +}; +use crate::TestImportAnotherObject; +use crate::TestImportEnum; + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct TestImportEnv { + pub object: TestImportAnotherObject, + pub opt_object: Option, + pub object_array: Vec, + pub opt_object_array: Option>>, + pub en: TestImportEnum, + pub opt_enum: Option, + pub enum_array: Vec, + pub opt_enum_array: Option>>, +} + +impl TestImportEnv { + pub const URI: &'static str = "testimport.uri.eth"; + + pub fn new() -> TestImportEnv { + TestImportEnv { + object: Option::new(), + opt_object: None, + object_array: vec![], + opt_object_array: None, + en: Option::_MAX_, + opt_enum: None, + enum_array: vec![], + opt_enum_array: None, + } + } +} diff --git a/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/imported/test_import_module/mod.rs b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/imported/test_import_module/mod.rs new file mode 100644 index 00000000..873e07c4 --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/imported/test_import_module/mod.rs @@ -0,0 +1,99 @@ +use serde::{Serialize, Deserialize}; +use polywrap_msgpack_serde::{ + from_slice, + to_vec, + wrappers::polywrap_json::JSONString, + wrappers::polywrap_bigint::BigIntWrapper +}; +use polywrap_wasm_rs::{ + BigInt, + BigNumber, + Map, + JSON, + subinvoke +}; +use crate::TestImportObject; +use crate::TestImportEnum; +use crate::TestImportEnumReturn; + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct ArgsImportedMethod { + pub str: String, + #[serde(rename = "optStr")] + pub opt_str: Option, + pub u: u32, + #[serde(rename = "optU")] + pub opt_u: Option, + #[serde(rename = "uArrayArray")] + pub u_array_array: Vec>>>, + pub object: TestImportObject, + #[serde(rename = "optObject")] + pub opt_object: Option, + #[serde(rename = "objectArray")] + pub object_array: Vec, + #[serde(rename = "optObjectArray")] + pub opt_object_array: Option>>, + pub en: TestImportEnum, + #[serde(rename = "optEnum")] + pub opt_enum: Option, + #[serde(rename = "enumArray")] + pub enum_array: Vec, + #[serde(rename = "optEnumArray")] + pub opt_enum_array: Option>>, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct ArgsAnotherMethod { + pub arg: Vec, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct ArgsReturnsArrayOfEnums { + pub arg: String, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct TestImportModule { + uri: String +} + +impl TestImportModule { + pub const INTERFACE_URI: &'static str = "testimport.uri.eth"; + + pub fn new(uri: String) -> TestImportModule { + TestImportModule { uri } + } + + pub fn imported_method(&self, args: &ArgsImportedMethod) -> Result, String> { + let ref uri = self.uri; + let args = to_vec(args).map_err(|e| e.to_string())?; + let result = subinvoke::wrap_subinvoke( + uri.as_str(), + "importedMethod", + args, + )?; + from_slice(result.as_slice()).map_err(|e| e.to_string()) + } + + pub fn another_method(&self, args: &ArgsAnotherMethod) -> Result { + let ref uri = self.uri; + let args = to_vec(args).map_err(|e| e.to_string())?; + let result = subinvoke::wrap_subinvoke( + uri.as_str(), + "anotherMethod", + args, + )?; + from_slice(result.as_slice()).map_err(|e| e.to_string()) + } + + pub fn returns_array_of_enums(&self, args: &ArgsReturnsArrayOfEnums) -> Result>, String> { + let ref uri = self.uri; + let args = to_vec(args).map_err(|e| e.to_string())?; + let result = subinvoke::wrap_subinvoke( + uri.as_str(), + "returnsArrayOfEnums", + args, + )?; + from_slice(result.as_slice()).map_err(|e| e.to_string()) + } +} diff --git a/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/imported/test_import_object/mod.rs b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/imported/test_import_object/mod.rs new file mode 100644 index 00000000..b40e4db3 --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/imported/test_import_object/mod.rs @@ -0,0 +1,48 @@ +use serde::{Serialize, Deserialize}; +use polywrap_msgpack_serde::{ + wrappers::polywrap_json::JSONString, + wrappers::polywrap_bigint::BigIntWrapper +}; +use polywrap_wasm_rs::{ + BigInt, + BigNumber, + Map, + JSON +}; +use crate::TestImportAnotherObject; +use crate::TestImportEnum; + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct TestImportObject { + pub object: TestImportAnotherObject, + #[serde(rename = "optObject")] + pub opt_object: Option, + #[serde(rename = "objectArray")] + pub object_array: Vec, + #[serde(rename = "optObjectArray")] + pub opt_object_array: Option>>, + pub en: TestImportEnum, + #[serde(rename = "optEnum")] + pub opt_enum: Option, + #[serde(rename = "enumArray")] + pub enum_array: Vec, + #[serde(rename = "optEnumArray")] + pub opt_enum_array: Option>>, +} + +impl TestImportObject { + pub const URI: &'static str = "testimport.uri.eth"; + + pub fn new() -> TestImportObject { + TestImportObject { + object: Option::new(), + opt_object: None, + object_array: vec![], + opt_object_array: None, + en: Option::_MAX_, + opt_enum: None, + enum_array: vec![], + opt_enum_array: None, + } + } +} diff --git a/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/mod.rs b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/mod.rs new file mode 100644 index 00000000..5fa8c40b --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/mod.rs @@ -0,0 +1,64 @@ +pub mod entry; +pub mod custom_type; +pub use custom_type::CustomType; +pub mod another_type; +pub use another_type::AnotherType; +pub mod custom_map_value; +pub use custom_map_value::CustomMapValue; +pub mod _else; +pub use _else::Else; +pub mod custom_enum; +pub use custom_enum::{ + get_custom_enum_key, + get_custom_enum_value, + sanitize_custom_enum_value, + CustomEnum +}; +pub mod _while; +pub use _while::{ + get_while_key, + get_while_value, + sanitize_while_value, + While +}; +pub mod env; +pub use env::Env; +pub mod imported; + +pub use imported::test_import_object::TestImportObject; +pub use imported::test_import_another_object::TestImportAnotherObject; +pub use imported::test_import_enum::{ + get_test_import_enum_key, + get_test_import_enum_value, + sanitize_test_import_enum_value, + TestImportEnum +}; +pub use imported::test_import_enum_return::{ + get_test_import_enum_return_key, + get_test_import_enum_return_value, + sanitize_test_import_enum_return_value, + TestImportEnumReturn +}; +pub use imported::test_import_env::TestImportEnv; +pub use imported::test_import_module::TestImportModule; +pub mod test_import; +pub use test_import::TestImport; +pub mod module; +pub use module::{ + Module, + ModuleTrait, + module_method_wrapped, + ArgsModuleMethod, + object_method_wrapped, + ArgsObjectMethod, + optional_env_method_wrapped, + ArgsOptionalEnvMethod, + if_wrapped, + ArgsIf +}; + +// Override print!(...) & println!(...) macros +#[macro_export] +macro_rules! println { ($($args:tt)*) => { polywrap_wasm_rs::wrap_debug_log(format!($($args)*).as_str()); } } +#[macro_export] +macro_rules! print { ($($args:tt)*) => { polywrap_wasm_rs::wrap_debug_log(format!($($args)*).as_str()); } } diff --git a/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/module/mod.rs b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/module/mod.rs new file mode 100644 index 00000000..1216287f --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/module/mod.rs @@ -0,0 +1,14 @@ +pub mod wrapped; +pub use wrapped::{ + module_method_wrapped, + ArgsModuleMethod, + object_method_wrapped, + ArgsObjectMethod, + optional_env_method_wrapped, + ArgsOptionalEnvMethod, + if_wrapped, + ArgsIf +}; + +pub mod module; +pub use module::*; diff --git a/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/module/module.rs b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/module/module.rs new file mode 100644 index 00000000..e5cf0918 --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/module/module.rs @@ -0,0 +1,32 @@ +use polywrap_msgpack_serde::{ + wrappers::polywrap_json::JSONString, + wrappers::polywrap_bigint::BigIntWrapper +}; +use polywrap_wasm_rs::{ + BigInt, + BigNumber, + Map, + JSON +}; +use crate::{ + ArgsModuleMethod, + ArgsObjectMethod, + ArgsOptionalEnvMethod, + ArgsIf, +}; +use crate::CustomEnum; +use crate::AnotherType; +use crate::Else; +use crate::env::Env; + +pub struct Module; + +pub trait ModuleTrait { + fn module_method(args: ArgsModuleMethod) -> Result; + + fn object_method(args: ArgsObjectMethod, env: Env) -> Result, String>; + + fn optional_env_method(args: ArgsOptionalEnvMethod, env: Option) -> Result, String>; + + fn _if(args: ArgsIf) -> Result; +} diff --git a/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/module/wrapped.rs b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/module/wrapped.rs new file mode 100644 index 00000000..16da48c5 --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/module/wrapped.rs @@ -0,0 +1,183 @@ +use serde::{Deserialize, Serialize}; +use polywrap_msgpack_serde::{ + from_slice, + to_vec, + wrappers::polywrap_json::JSONString, + wrappers::polywrap_bigint::BigIntWrapper +}; +use polywrap_wasm_rs::{ + BigInt, + BigNumber, + Map, + JSON, + wrap_load_env +}; +use crate::module::{ModuleTrait, Module}; +use crate::CustomEnum; +use crate::AnotherType; +use crate::Else; +use crate::Env; + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct ArgsModuleMethod { + pub str: String, + #[serde(rename = "optStr")] + pub opt_str: Option, + pub en: CustomEnum, + #[serde(rename = "optEnum")] + pub opt_enum: Option, + #[serde(rename = "enumArray")] + pub enum_array: Vec, + #[serde(rename = "optEnumArray")] + pub opt_enum_array: Option>>, + pub map: Map, + #[serde(rename = "mapOfArr")] + pub map_of_arr: Map>, + #[serde(rename = "mapOfMap")] + pub map_of_map: Map>, + #[serde(rename = "mapOfObj")] + pub map_of_obj: Map, + #[serde(rename = "mapOfArrOfObj")] + pub map_of_arr_of_obj: Map>, +} + +pub fn module_method_wrapped(args: &[u8], env_size: u32) -> Vec { + match from_slice::(args) { + Ok(args) => { + let result = Module::module_method(ArgsModuleMethod { + str: args.str, + opt_str: args.opt_str, + en: args.en, + opt_enum: args.opt_enum, + enum_array: args.enum_array, + opt_enum_array: args.opt_enum_array, + map: args.map, + map_of_arr: args.map_of_arr, + map_of_map: args.map_of_map, + map_of_obj: args.map_of_obj, + map_of_arr_of_obj: args.map_of_arr_of_obj, + }); + match result { + Ok(res) => { + to_vec(&res).unwrap() + } + Err(e) => { + panic!("{}", e.to_string()) + } + } + } + Err(e) => { + panic!("{}", e.to_string()) + } + } +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct ArgsObjectMethod { + pub object: AnotherType, + #[serde(rename = "optObject")] + pub opt_object: Option, + #[serde(rename = "objectArray")] + pub object_array: Vec, + #[serde(rename = "optObjectArray")] + pub opt_object_array: Option>>, +} + +pub fn object_method_wrapped(args: &[u8], env_size: u32) -> Vec { + if env_size == 0 { + panic!("Environment is not set, and it is required by method 'objectMethod'"); + } + + let env_buf = wrap_load_env(env_size); + let env = Env::from_buffer(&env_buf).unwrap(); + + match from_slice::(args) { + Ok(args) => { + let result = Module::object_method(ArgsObjectMethod { + object: args.object, + opt_object: args.opt_object, + object_array: args.object_array, + opt_object_array: args.opt_object_array, + }, env); + match result { + Ok(res) => { + to_vec(&res).unwrap() + } + Err(e) => { + panic!("{}", e.to_string()) + } + } + } + Err(e) => { + panic!("{}", e.to_string()) + } + } +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct ArgsOptionalEnvMethod { + pub object: AnotherType, + #[serde(rename = "optObject")] + pub opt_object: Option, + #[serde(rename = "objectArray")] + pub object_array: Vec, + #[serde(rename = "optObjectArray")] + pub opt_object_array: Option>>, +} + +pub fn optional_env_method_wrapped(args: &[u8], env_size: u32) -> Vec { + let mut env: Option = None; + if env_size > 0 { + let env_buf = wrap_load_env(env_size); + env = Some(Env::from_buffer(&env_buf).unwrap()); + } + + match from_slice::(args) { + Ok(args) => { + let result = Module::optional_env_method(ArgsOptionalEnvMethod { + object: args.object, + opt_object: args.opt_object, + object_array: args.object_array, + opt_object_array: args.opt_object_array, + }, env); + match result { + Ok(res) => { + to_vec(&res).unwrap() + } + Err(e) => { + panic!("{}", e.to_string()) + } + } + } + Err(e) => { + panic!("{}", e.to_string()) + } + } +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct ArgsIf { + #[serde(rename = "if")] + pub _if: Else, +} + +pub fn if_wrapped(args: &[u8], env_size: u32) -> Vec { + match from_slice::(args) { + Ok(args) => { + let result = Module::_if(ArgsIf { + _if: args._if, + }); + match result { + Ok(res) => { + to_vec(&res).unwrap() + } + Err(e) => { + panic!("{}", e.to_string()) + } + } + } + Err(e) => { + panic!("{}", e.to_string()) + } + } +} diff --git a/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/test_import/mod.rs b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/test_import/mod.rs new file mode 100644 index 00000000..e50df13b --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/cases/000-sanity/output/test_import/mod.rs @@ -0,0 +1,11 @@ +use polywrap_wasm_rs::wrap_get_implementations; + +pub struct TestImport {} + +impl TestImport { + const uri: &'static str = "testimport.uri.eth"; + + pub fn get_implementations() -> Vec { + wrap_get_implementations(Self::uri) + } +} diff --git a/implementations/wrap-rust/src/__tests__/cases/001-module-functions/input.graphql b/implementations/wrap-rust/src/__tests__/cases/001-module-functions/input.graphql new file mode 100644 index 00000000..2d6ae2f8 --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/cases/001-module-functions/input.graphql @@ -0,0 +1,11 @@ +type Module { + function1( + arg1: UInt32! + arg2: Boolean! + ): String! + + function2( + arg1: Int32 + arg2: Bytes + ): String +} diff --git a/implementations/wrap-rust/src/__tests__/cases/001-module-functions/output/entry.rs b/implementations/wrap-rust/src/__tests__/cases/001-module-functions/output/entry.rs new file mode 100644 index 00000000..eda44fd2 --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/cases/001-module-functions/output/entry.rs @@ -0,0 +1,33 @@ +use crate::{ + function1_wrapped, + function2_wrapped +}; +use polywrap_wasm_rs::{ + abort, + invoke, + InvokeArgs, +}; + +#[no_mangle] +pub extern "C" fn _wrap_invoke(method_size: u32, args_size: u32, env_size: u32) -> bool { + // Ensure the abort handler is properly setup + abort::wrap_abort_setup(); + + let args: InvokeArgs = invoke::wrap_invoke_args(method_size, args_size); + let result: Vec; + + match args.method.as_str() { + "function1" => { + result = function1_wrapped(args.args.as_slice(), env_size); + } + "function2" => { + result = function2_wrapped(args.args.as_slice(), env_size); + } + _ => { + invoke::wrap_invoke_error(format!("Could not find invoke function {}", args.method)); + return false; + } + }; + invoke::wrap_invoke_result(result); + return true; +} diff --git a/implementations/wrap-rust/src/__tests__/cases/001-module-functions/output/mod.rs b/implementations/wrap-rust/src/__tests__/cases/001-module-functions/output/mod.rs new file mode 100644 index 00000000..3ea77d55 --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/cases/001-module-functions/output/mod.rs @@ -0,0 +1,17 @@ +pub mod entry; + +pub mod module; +pub use module::{ + Module, + ModuleTrait, + function1_wrapped, + ArgsFunction1, + function2_wrapped, + ArgsFunction2 +}; + +// Override print!(...) & println!(...) macros +#[macro_export] +macro_rules! println { ($($args:tt)*) => { polywrap_wasm_rs::wrap_debug_log(format!($($args)*).as_str()); } } +#[macro_export] +macro_rules! print { ($($args:tt)*) => { polywrap_wasm_rs::wrap_debug_log(format!($($args)*).as_str()); } } diff --git a/implementations/wrap-rust/src/__tests__/cases/001-module-functions/output/module/mod.rs b/implementations/wrap-rust/src/__tests__/cases/001-module-functions/output/module/mod.rs new file mode 100644 index 00000000..cdc3317a --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/cases/001-module-functions/output/module/mod.rs @@ -0,0 +1,10 @@ +pub mod wrapped; +pub use wrapped::{ + function1_wrapped, + ArgsFunction1, + function2_wrapped, + ArgsFunction2 +}; + +pub mod module; +pub use module::*; diff --git a/implementations/wrap-rust/src/__tests__/cases/001-module-functions/output/module/module.rs b/implementations/wrap-rust/src/__tests__/cases/001-module-functions/output/module/module.rs new file mode 100644 index 00000000..3f77b6e5 --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/cases/001-module-functions/output/module/module.rs @@ -0,0 +1,22 @@ +use polywrap_msgpack_serde::{ + wrappers::polywrap_json::JSONString, + wrappers::polywrap_bigint::BigIntWrapper +}; +use polywrap_wasm_rs::{ + BigInt, + BigNumber, + Map, + JSON +}; +use crate::{ + ArgsFunction1, + ArgsFunction2, +}; + +pub struct Module; + +pub trait ModuleTrait { + fn function1(args: ArgsFunction1) -> Result; + + fn function2(args: ArgsFunction2) -> Result, String>; +} diff --git a/implementations/wrap-rust/src/__tests__/cases/001-module-functions/output/module/wrapped.rs b/implementations/wrap-rust/src/__tests__/cases/001-module-functions/output/module/wrapped.rs new file mode 100644 index 00000000..9ce24074 --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/cases/001-module-functions/output/module/wrapped.rs @@ -0,0 +1,72 @@ +use serde::{Deserialize, Serialize}; +use polywrap_msgpack_serde::{ + from_slice, + to_vec, + wrappers::polywrap_json::JSONString, + wrappers::polywrap_bigint::BigIntWrapper +}; +use polywrap_wasm_rs::{ + BigInt, + BigNumber, + Map, + JSON, + wrap_load_env +}; +use crate::module::{ModuleTrait, Module}; + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct ArgsFunction1 { + pub arg1: u32, + pub arg2: bool, +} + +pub fn function1_wrapped(args: &[u8], env_size: u32) -> Vec { + match from_slice::(args) { + Ok(args) => { + let result = Module::function1(ArgsFunction1 { + arg1: args.arg1, + arg2: args.arg2, + }); + match result { + Ok(res) => { + to_vec(&res).unwrap() + } + Err(e) => { + panic!("{}", e.to_string()) + } + } + } + Err(e) => { + panic!("{}", e.to_string()) + } + } +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct ArgsFunction2 { + pub arg1: Option, + #[serde(with = "serde_bytes")] + pub arg2: Option>, +} + +pub fn function2_wrapped(args: &[u8], env_size: u32) -> Vec { + match from_slice::(args) { + Ok(args) => { + let result = Module::function2(ArgsFunction2 { + arg1: args.arg1, + arg2: args.arg2, + }); + match result { + Ok(res) => { + to_vec(&res).unwrap() + } + Err(e) => { + panic!("{}", e.to_string()) + } + } + } + Err(e) => { + panic!("{}", e.to_string()) + } + } +} diff --git a/implementations/wrap-rust/src/__tests__/cases/002-object-types/input.graphql b/implementations/wrap-rust/src/__tests__/cases/002-object-types/input.graphql new file mode 100644 index 00000000..0aa7c068 --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/cases/002-object-types/input.graphql @@ -0,0 +1,68 @@ +scalar UInt +scalar UInt8 +scalar UInt16 +scalar UInt32 +scalar Int +scalar Int8 +scalar Int16 +scalar Int32 +scalar Bytes +scalar BigInt +scalar BigNumber +scalar JSON +scalar Map + +type CustomType { + str: String! + optStr: String + u: UInt! + optU: UInt + u8: UInt8! + u16: UInt16! + u32: UInt32! + i: Int! + i8: Int8! + i16: Int16! + i32: Int32! + bigint: BigInt! + optBigint: BigInt + bignumber: BigNumber! + optBignumber: BigNumber + json: JSON! + optJson: JSON + bytes: Bytes! + optBytes: Bytes + boolean: Boolean! + optBoolean: Boolean + u_array: [UInt!]! + uOpt_array: [UInt!] + _opt_uOptArray: [UInt] + optStrOptArray: [String] + uArrayArray: [[UInt!]!]! + uOptArrayOptArray: [[UInt32]]! + uArrayOptArrayArray: [[[UInt32!]!]]! + crazyArray: [[[[UInt32!]]!]] + object: AnotherType! + optObject: AnotherType + objectArray: [AnotherType!]! + optObjectArray: [AnotherType] + map: Map! @annotate(type: "Map!") + mapOfArr: Map! @annotate(type: "Map!") + mapOfObj: Map! @annotate(type: "Map!") + mapOfArrOfObj: Map! @annotate(type: "Map!") + mapCustomValue: Map! @annotate(type: "Map!") +} + +type AnotherType { + prop: String + circular: CustomType + const: String +} + +type CustomMapValue { + foo: String! +} + +type else { + else: String! +} diff --git a/implementations/wrap-rust/src/__tests__/cases/002-object-types/output/_else/mod.rs b/implementations/wrap-rust/src/__tests__/cases/002-object-types/output/_else/mod.rs new file mode 100644 index 00000000..1e13036d --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/cases/002-object-types/output/_else/mod.rs @@ -0,0 +1,25 @@ +use serde::{Serialize, Deserialize}; +use polywrap_msgpack_serde::{ + wrappers::polywrap_json::JSONString, + wrappers::polywrap_bigint::BigIntWrapper +}; +use polywrap_wasm_rs::{ + BigInt, + BigNumber, + Map, + JSON +}; + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Else { + #[serde(rename = "else")] + pub _else: String, +} + +impl Else { + pub fn new() -> Else { + Else { + _else: String::new(), + } + } +} diff --git a/implementations/wrap-rust/src/__tests__/cases/002-object-types/output/another_type/mod.rs b/implementations/wrap-rust/src/__tests__/cases/002-object-types/output/another_type/mod.rs new file mode 100644 index 00000000..171fa644 --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/cases/002-object-types/output/another_type/mod.rs @@ -0,0 +1,30 @@ +use serde::{Serialize, Deserialize}; +use polywrap_msgpack_serde::{ + wrappers::polywrap_json::JSONString, + wrappers::polywrap_bigint::BigIntWrapper +}; +use polywrap_wasm_rs::{ + BigInt, + BigNumber, + Map, + JSON +}; +use crate::CustomType; + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct AnotherType { + pub prop: Option, + pub circular: Option, + #[serde(rename = "const")] + pub _const: Option, +} + +impl AnotherType { + pub fn new() -> AnotherType { + AnotherType { + prop: None, + circular: None, + _const: None, + } + } +} diff --git a/implementations/wrap-rust/src/__tests__/cases/002-object-types/output/custom_map_value/mod.rs b/implementations/wrap-rust/src/__tests__/cases/002-object-types/output/custom_map_value/mod.rs new file mode 100644 index 00000000..29689350 --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/cases/002-object-types/output/custom_map_value/mod.rs @@ -0,0 +1,24 @@ +use serde::{Serialize, Deserialize}; +use polywrap_msgpack_serde::{ + wrappers::polywrap_json::JSONString, + wrappers::polywrap_bigint::BigIntWrapper +}; +use polywrap_wasm_rs::{ + BigInt, + BigNumber, + Map, + JSON +}; + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct CustomMapValue { + pub foo: String, +} + +impl CustomMapValue { + pub fn new() -> CustomMapValue { + CustomMapValue { + foo: String::new(), + } + } +} diff --git a/implementations/wrap-rust/src/__tests__/cases/002-object-types/output/custom_type/mod.rs b/implementations/wrap-rust/src/__tests__/cases/002-object-types/output/custom_type/mod.rs new file mode 100644 index 00000000..413b6366 --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/cases/002-object-types/output/custom_type/mod.rs @@ -0,0 +1,123 @@ +use serde::{Serialize, Deserialize}; +use polywrap_msgpack_serde::{ + wrappers::polywrap_json::JSONString, + wrappers::polywrap_bigint::BigIntWrapper +}; +use polywrap_wasm_rs::{ + BigInt, + BigNumber, + Map, + JSON +}; +use crate::AnotherType; +use crate::CustomMapValue; + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct CustomType { + pub str: String, + #[serde(rename = "optStr")] + pub opt_str: Option, + pub u: u32, + #[serde(rename = "optU")] + pub opt_u: Option, + pub u8: u8, + pub u16: u16, + pub u32: u32, + pub i: i32, + pub i8: i8, + pub i16: i16, + pub i32: i32, + pub bigint: BigIntWrapper, + #[serde(rename = "optBigint")] + pub opt_bigint: Option, + pub bignumber: BigNumber, + #[serde(rename = "optBignumber")] + pub opt_bignumber: Option, + pub json: JSONString, + #[serde(rename = "optJson")] + pub opt_json: Option, + #[serde(with = "serde_bytes")] + pub bytes: Vec, + #[serde(with = "serde_bytes")] + #[serde(rename = "optBytes")] + pub opt_bytes: Option>, + pub boolean: bool, + #[serde(rename = "optBoolean")] + pub opt_boolean: Option, + pub u_array: Vec, + #[serde(rename = "uOpt_array")] + pub u_opt_array: Option>, + #[serde(rename = "_opt_uOptArray")] + pub _opt_u_opt_array: Option>>, + #[serde(rename = "optStrOptArray")] + pub opt_str_opt_array: Option>>, + #[serde(rename = "uArrayArray")] + pub u_array_array: Vec>, + #[serde(rename = "uOptArrayOptArray")] + pub u_opt_array_opt_array: Vec>>>, + #[serde(rename = "uArrayOptArrayArray")] + pub u_array_opt_array_array: Vec>>>, + #[serde(rename = "crazyArray")] + pub crazy_array: Option>>>>>>, + pub object: AnotherType, + #[serde(rename = "optObject")] + pub opt_object: Option, + #[serde(rename = "objectArray")] + pub object_array: Vec, + #[serde(rename = "optObjectArray")] + pub opt_object_array: Option>>, + pub map: Map, + #[serde(rename = "mapOfArr")] + pub map_of_arr: Map>, + #[serde(rename = "mapOfObj")] + pub map_of_obj: Map, + #[serde(rename = "mapOfArrOfObj")] + pub map_of_arr_of_obj: Map>, + #[serde(rename = "mapCustomValue")] + pub map_custom_value: Map>, +} + +impl CustomType { + pub fn new() -> CustomType { + CustomType { + str: String::new(), + opt_str: None, + u: 0, + opt_u: None, + u8: 0, + u16: 0, + u32: 0, + i: 0, + i8: 0, + i16: 0, + i32: 0, + bigint: BigIntWrapper(BigInt::default()), + opt_bigint: None, + bignumber: BigNumber::default(), + opt_bignumber: None, + json: JSONString::from(JSON::Value::Null), + opt_json: None, + bytes: vec![], + opt_bytes: None, + boolean: false, + opt_boolean: None, + u_array: vec![], + u_opt_array: None, + _opt_u_opt_array: None, + opt_str_opt_array: None, + u_array_array: vec![], + u_opt_array_opt_array: vec![], + u_array_opt_array_array: vec![], + crazy_array: None, + object: Option::new(), + opt_object: None, + object_array: vec![], + opt_object_array: None, + map: Map::::new(), + map_of_arr: Map::>::new(), + map_of_obj: Map::::new(), + map_of_arr_of_obj: Map::>::new(), + map_custom_value: Map::>::new(), + } + } +} diff --git a/implementations/wrap-rust/src/__tests__/cases/002-object-types/output/entry.rs b/implementations/wrap-rust/src/__tests__/cases/002-object-types/output/entry.rs new file mode 100644 index 00000000..640d46b3 --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/cases/002-object-types/output/entry.rs @@ -0,0 +1,23 @@ +use polywrap_wasm_rs::{ + abort, + invoke, + InvokeArgs, +}; + +#[no_mangle] +pub extern "C" fn _wrap_invoke(method_size: u32, args_size: u32, env_size: u32) -> bool { + // Ensure the abort handler is properly setup + abort::wrap_abort_setup(); + + let args: InvokeArgs = invoke::wrap_invoke_args(method_size, args_size); + let result: Vec; + + match args.method.as_str() { + _ => { + invoke::wrap_invoke_error(format!("Could not find invoke function {}", args.method)); + return false; + } + }; + invoke::wrap_invoke_result(result); + return true; +} diff --git a/implementations/wrap-rust/src/__tests__/cases/002-object-types/output/mod.rs b/implementations/wrap-rust/src/__tests__/cases/002-object-types/output/mod.rs new file mode 100644 index 00000000..6f9f9379 --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/cases/002-object-types/output/mod.rs @@ -0,0 +1,16 @@ +pub mod entry; +pub mod custom_type; +pub use custom_type::CustomType; +pub mod another_type; +pub use another_type::AnotherType; +pub mod custom_map_value; +pub use custom_map_value::CustomMapValue; +pub mod _else; +pub use _else::Else; + + +// Override print!(...) & println!(...) macros +#[macro_export] +macro_rules! println { ($($args:tt)*) => { polywrap_wasm_rs::wrap_debug_log(format!($($args)*).as_str()); } } +#[macro_export] +macro_rules! print { ($($args:tt)*) => { polywrap_wasm_rs::wrap_debug_log(format!($($args)*).as_str()); } } diff --git a/implementations/wrap-rust/src/__tests__/cases/index.ts b/implementations/wrap-rust/src/__tests__/cases/index.ts new file mode 100644 index 00000000..704f98ab --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/cases/index.ts @@ -0,0 +1,35 @@ +import { Output } from "../wrap"; +import { loadOutput } from "../output"; + +import fs from "fs"; +import path from "path"; + +export interface TestCase { + name: string; + input: string; + output: Output; +} + +export function loadTestCases(): TestCase[] { + const testCases: TestCase[] = []; + const casesDirents = fs.readdirSync(__dirname, { withFileTypes: true }); + + for (const casesDirent of casesDirents) { + if (!casesDirent.isDirectory()) { + continue; + } + + const caseDir = path.join(__dirname, casesDirent.name); + const caseInputPath = path.join(caseDir, "input.graphql"); + const caseInput = fs.readFileSync(caseInputPath, "utf-8"); + const caseOutputDir = path.join(caseDir, "output"); + const caseOutput = loadOutput(caseOutputDir, ["input.graphql"]); + testCases.push({ + name: casesDirent.name, + input: caseInput, + output: caseOutput + }); + } + + return testCases; +} diff --git a/implementations/wrap-rust/src/__tests__/e2e.spec.ts b/implementations/wrap-rust/src/__tests__/e2e.spec.ts new file mode 100644 index 00000000..cbf95777 --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/e2e.spec.ts @@ -0,0 +1,79 @@ +import { Output, WrapInfo, File, Directory } from "./wrap"; +import { loadTestCases, TestCase } from "./cases"; +import { orderOutput } from "./output"; + +import { PolywrapClient } from "@polywrap/client-js"; +import { parseSchema } from "@polywrap/schema-parse"; +import diff from "jest-diff"; +import path from "path"; +import fs from "fs"; + +jest.setTimeout(60000); + +describe("e2e", () => { + + const client: PolywrapClient = new PolywrapClient(); + let wrapperUri: string; + let testCases: TestCase[] = loadTestCases(); + + beforeAll(() => { + // Cache wrap URI in build dir + const dirname: string = path.resolve(__dirname); + const wrapperPath: string = path.join(dirname, "..", ".."); + wrapperUri = `fs/${wrapperPath}/build`; + }); + + for (const testCase of testCases) { + it(testCase.name, async () => { + const abi = parseSchema(testCase.input); + + const wrapInfo: WrapInfo = { + version: "0.1", + name: testCase.name, + type: "plugin", + abi: JSON.stringify(abi), + } + + const result = await client.invoke({ + uri: wrapperUri, + method: "generateBindings", + args: { wrapInfo } + }); + + if (!result.ok) fail(result.error); + + const received = orderOutput(result.value); + const expected = orderOutput(testCase.output); + + const removeWhiteSpace = (str: string): string => str.replace(/\s/g, ""); + const removeWhiteSpaceFromFiles = (files: File[]): File[] => files.map((file) => ({ + name: file.name, + data: removeWhiteSpace(file.data), + })); + const removeWhiteSpaceFromDirs = (dirs: Directory[]): Directory[] => dirs.map((dir) => ({ + name: dir.name, + dirs: removeWhiteSpaceFromDirs(dir.dirs), + files: removeWhiteSpaceFromFiles(dir.files), + })); + const removeWhiteSpaceFromOutput = (output: Output): Output => ({ + dirs: removeWhiteSpaceFromDirs(output.dirs), + files: removeWhiteSpaceFromFiles(output.files), + }); + const receivedWithoutWhiteSpace = removeWhiteSpaceFromOutput(received) + const expectedWithoutWhiteSpace = removeWhiteSpaceFromOutput(expected) + + const debugDir = path.join(__dirname, "debug", testCase.name); + const receivedPath = path.join(debugDir, "received.json"); + const expectedPath = path.join(debugDir, "expected.json"); + fs.mkdirSync(debugDir, { recursive: true }); + fs.writeFileSync(receivedPath, JSON.stringify(received, null, 2)); + fs.writeFileSync(expectedPath, JSON.stringify(expected, null, 2)); + + const differences = diff(expectedWithoutWhiteSpace, receivedWithoutWhiteSpace, { expand: false }); + + if (differences && !differences.includes("Compared values have no visual difference")) { + fail(differences); + } + }); + } +}); diff --git a/implementations/wrap-rust/src/__tests__/output.ts b/implementations/wrap-rust/src/__tests__/output.ts new file mode 100644 index 00000000..d0d8b12e --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/output.ts @@ -0,0 +1,80 @@ +import { Output, File, Directory } from "./wrap"; + +import fs from "fs"; +import path from "path"; + +export function orderOutput(output: Output): Output { + orderDirectory(output); + return output; +} + +function orderDirectory(directory: Output | Directory): void { + directory.dirs.sort((a, b) => a.name > b.name ? -1 : 1); + directory.files.sort((a, b) => a.name > b.name ? -1 : 1); + for (const dir of directory.dirs) { + orderDirectory(dir); + } +} + +export function loadOutput(dir: string, ignore: string[]): Output { + const output: Output = { + files: [], + dirs: [] + }; + + const outputDirents = fs.readdirSync(dir, { withFileTypes: true }); + + for (const outputDirent of outputDirents) { + if (ignore.includes(outputDirent.name)) { + continue; + } + + if (outputDirent.isDirectory()) { + output.dirs.push(loadDirectory(dir, outputDirent.name, ignore)); + } else { + output.files.push(loadFile(dir, outputDirent.name)); + } + } + + return output; +} + +function loadFile(dir: string, name: string): File { + const filePath = path.join(dir, name); + const fileData = fs.readFileSync(filePath, "utf-8"); + return { + name: name, + data: fileData + }; +} + +function loadDirectory(dir: string, name: string, ignore: string[]): Directory { + const directory: Directory = { + name: name, + files: [], + dirs: [] + }; + const directoryPath = path.join(dir, name); + const directoryEnts = fs.readdirSync( + directoryPath, + { withFileTypes: true } + ); + + for (const directoryEnt of directoryEnts) { + if (ignore.includes(directoryEnt.name)) { + continue; + } + + if (directoryEnt.isDirectory()) { + directory.dirs.push( + loadDirectory(directoryPath, directoryEnt.name, ignore) + ); + } else { + directory.files.push( + loadFile(directoryPath, directoryEnt.name) + ); + } + } + + return directory; +} diff --git a/implementations/wrap-rust/src/__tests__/polywrap.yaml b/implementations/wrap-rust/src/__tests__/polywrap.yaml new file mode 100644 index 00000000..655f8c41 --- /dev/null +++ b/implementations/wrap-rust/src/__tests__/polywrap.yaml @@ -0,0 +1,6 @@ +format: 0.3.0 +project: + name: test-types + type: app/typescript +source: + schema: ../../../../interface/polywrap.graphql diff --git a/implementations/wrap-rust/src/helpers/array_has_length.rs b/implementations/wrap-rust/src/helpers/array_has_length.rs new file mode 100644 index 00000000..e24aa2dc --- /dev/null +++ b/implementations/wrap-rust/src/helpers/array_has_length.rs @@ -0,0 +1,6 @@ +use handlebars::handlebars_helper; +use serde_json::{Value}; + +handlebars_helper!(array_has_length: |arr: Value| { + arr.is_array() && arr.as_array().unwrap().len() > 0 +}); diff --git a/implementations/wrap-rust/src/helpers/array_length.rs b/implementations/wrap-rust/src/helpers/array_length.rs new file mode 100644 index 00000000..75819d16 --- /dev/null +++ b/implementations/wrap-rust/src/helpers/array_length.rs @@ -0,0 +1,6 @@ +use handlebars::handlebars_helper; +use serde_json::{Value}; + +handlebars_helper!(array_length: |arr: Value| { + arr.as_array().unwrap().len() +}); diff --git a/implementations/wrap-rust/src/helpers/detect_keyword.rs b/implementations/wrap-rust/src/helpers/detect_keyword.rs new file mode 100644 index 00000000..df90e81e --- /dev/null +++ b/implementations/wrap-rust/src/helpers/detect_keyword.rs @@ -0,0 +1,15 @@ +use handlebars::handlebars_helper; +use serde_json::{Value}; +use crate::helpers::is_keyword::_is_keyword; + +handlebars_helper!(detect_keyword: |val: Value| { + let type_val = val.as_str().unwrap(); + _detect_keyword(type_val) +}); + +pub fn _detect_keyword(type_val: &str) -> String { + if _is_keyword(type_val) { + return format!("_{}", type_val); + } + type_val.to_string() +} \ No newline at end of file diff --git a/implementations/wrap-rust/src/helpers/is_keyword.rs b/implementations/wrap-rust/src/helpers/is_keyword.rs new file mode 100644 index 00000000..39ea1da4 --- /dev/null +++ b/implementations/wrap-rust/src/helpers/is_keyword.rs @@ -0,0 +1,24 @@ +use handlebars::handlebars_helper; +use serde_json::{Value}; + +handlebars_helper!(is_keyword: |val: Value| { + let s = val.as_str().unwrap(); + _is_keyword(s) +}); + +pub fn _is_keyword(s: &str) -> bool { + match s { + "as" | "break" | "const" | "continue" | "crate" + | "else" | "enum" | "extern" | "false" | "fn" + | "for" | "if" | "impl" | "in" | "let" | "loop" + | "match" | "mod" | "move" | "mut" | "pub" | "ref" + | "return" | "self" | "Self" | "static" | "struct" | "super" + | "trait" | "true" | "type" | "unsafe" | "use" | "where" + | "while" | "async" | "await" | "dyn" | "abstract" + | "become" | "box" | "Box" | "do" | "final" | "macro" + | "override" | "priv" | "typeof" | "unsized" + | "virtual" | "yield" | "try" | "macro_rules" + | "union" => true, + _ => false, + } +} diff --git a/implementations/wrap-rust/src/helpers/is_not_first.rs b/implementations/wrap-rust/src/helpers/is_not_first.rs new file mode 100644 index 00000000..3a6b8957 --- /dev/null +++ b/implementations/wrap-rust/src/helpers/is_not_first.rs @@ -0,0 +1,7 @@ +use handlebars::handlebars_helper; +use serde_json::{Value}; + +handlebars_helper!(is_not_first: |index: Value| { + let index_u64 = index.as_u64().unwrap(); + index_u64 != 0 +}); diff --git a/implementations/wrap-rust/src/helpers/is_not_last.rs b/implementations/wrap-rust/src/helpers/is_not_last.rs new file mode 100644 index 00000000..be543cf4 --- /dev/null +++ b/implementations/wrap-rust/src/helpers/is_not_last.rs @@ -0,0 +1,8 @@ +use handlebars::handlebars_helper; +use serde_json::{Value}; + +handlebars_helper!(is_not_last: |index: Value, array: Value| { + let index_u64 = index.as_u64().unwrap(); + let array_len = array.as_array().unwrap().len() as u64; + array_len != (index_u64 + 1) +}); diff --git a/implementations/wrap-rust/src/helpers/mod.rs b/implementations/wrap-rust/src/helpers/mod.rs new file mode 100644 index 00000000..20638183 --- /dev/null +++ b/implementations/wrap-rust/src/helpers/mod.rs @@ -0,0 +1,83 @@ +use handlebars::Handlebars; + +mod array_has_length; +mod array_length; +pub mod detect_keyword; +mod is_keyword; +mod is_not_first; +mod is_not_last; +mod property_deps; +mod serde_annotate_if_bytes; +mod serde_keyword; +mod serde_rename_if_case_mismatch; +mod to_graphql_type; +pub mod to_lower; +mod to_rust; +mod to_rust_init; +mod to_upper; + +// helpers for helpers +mod util; + +pub fn register(handlebars: &mut Handlebars) -> () { + handlebars.register_helper( + "array_has_length", + Box::new(array_has_length::array_has_length) + ); + handlebars.register_helper( + "array_length", + Box::new(array_length::array_length) + ); + handlebars.register_helper( + "detect_keyword", + Box::new(detect_keyword::detect_keyword) + ); + handlebars.register_helper( + "is_keyword", + Box::new(is_keyword::is_keyword) + ); + handlebars.register_helper( + "is_not_first", + Box::new(is_not_first::is_not_first) + ); + handlebars.register_helper( + "is_not_last", + Box::new(is_not_last::is_not_last) + ); + handlebars.register_helper( + "property_deps", + Box::new(property_deps::property_deps) + ); + handlebars.register_helper( + "serde_annotate_if_bytes", + Box::new(serde_annotate_if_bytes::serde_annotate_if_bytes) + ); + handlebars.register_helper( + "serde_keyword", + Box::new(serde_keyword::serde_keyword) + ); + handlebars.register_helper( + "serde_rename_if_case_mismatch", + Box::new(serde_rename_if_case_mismatch::serde_rename_if_case_mismatch) + ); + handlebars.register_helper( + "to_graphql_type", + Box::new(to_graphql_type::to_graphql_type) + ); + handlebars.register_helper( + "to_lower", + Box::new(to_lower::to_lower) + ); + handlebars.register_helper( + "to_rust", + Box::new(to_rust::to_rust) + ); + handlebars.register_helper( + "to_rust_init", + Box::new(to_rust_init::to_rust_init) + ); + handlebars.register_helper( + "to_upper", + Box::new(to_upper::to_upper) + ); +} diff --git a/implementations/wrap-rust/src/helpers/property_deps.rs b/implementations/wrap-rust/src/helpers/property_deps.rs new file mode 100644 index 00000000..bede4248 --- /dev/null +++ b/implementations/wrap-rust/src/helpers/property_deps.rs @@ -0,0 +1,142 @@ +use handlebars::handlebars_helper; +use polywrap_wasm_rs::JSON; +use serde::{Deserialize, Serialize}; +use serde_json::{Value, Map}; +use crate::helpers::util::DefinitionKind; + +#[derive(Serialize, Deserialize, Debug, Clone)] +struct PropertyDep { + _crate: String, + _type: String, + is_enum: bool, +} + +handlebars_helper!(property_deps: |value: Value| { + let obj = value.as_object().unwrap(); + _property_deps(obj).unwrap() +}); + +pub fn _property_deps(def: &Map) -> Result { + let root_type = def.get("type") + .and_then(|t| t.as_str()) + .ok_or_else(|| format!("Invalid type for 'type' property of ABI definition. Expected string."))?; + let mut deps: Vec = Vec::new(); + _search_property_deps(root_type, def, &mut deps)?; + let result = JSON::to_value(deps) + .map_err(|e| format!("Failed to serialize property dependencies: {}", e))?; + Ok(result) +} + +fn _search_property_deps(root_type: &str, def: &Map, deps: &mut Vec) -> Result<(), String> { + let kind: DefinitionKind = match def.get("kind") + .and_then(|k| k.as_u64()) + .map(|k| DefinitionKind::from(k as u32)) { + Some(k) => k, + None => return Ok(()) + }; + + match kind { + DefinitionKind::Scalar + | DefinitionKind::Method => {} + _ => _append_property_dep(root_type, deps, &def)?, + } + + for v in def.values() { + match v.as_object() { + Some(obj) => _search_property_deps(root_type, obj, deps)?, + None => match v.as_array() { + Some(arr) => { + for item in arr { + if let Some(item) = item.as_object() { + _search_property_deps(root_type, item, deps)?; + } + } + } + None => {} + } + } + } + + Ok(()) +} + +fn _append_property_dep( + root_type: &str, + vec: &mut Vec, + def: &Map +) -> Result<(), String> { + let mut type_name = def.get("type") + .and_then(|t| t.as_str()) + .map(|s| s.to_string()) + .ok_or_else(|| "Invalid type for 'type' property of ABI definition. Expected string.".to_string())?; + + if type_name.starts_with("[") { + type_name = type_name.replace(&['[', ']', '!', '?'], ""); + } + + if type_name.starts_with("Map<") { + // def.map?.object?.type ?? def.map?.enum?.type + let value_name = def.get("map") + .and_then(|m| m.get("object")) + .and_then(|o| o.get("type")) + .or_else(|| { + def.get("map") + .and_then(|m| m.get("enum")) + .and_then(|e| e.get("type")) + }) + .and_then(|t| t.as_str()); + + + if let Some(value_name) = value_name { + if !is_known_type(value_name, root_type) { + // def.map?.enum?.type + let type_if_enum = def.get("map") + .and_then(|m| m.get("enum")) + .and_then(|e| e.get("type")) + .and_then(|e| e.as_str()) + .unwrap_or(""); + append_unique(vec, PropertyDep { + _crate: "crate".to_string(), + _type: value_name.to_string(), + is_enum: value_name == type_if_enum + }); + } + } + } else if !is_known_type(&type_name, root_type) { + append_unique(vec, PropertyDep { + _crate: "crate".to_string(), + _type: type_name.clone(), + // !!def.enum || !!def.array?.enum + is_enum: def.contains_key("enum") || def.get("array").and_then(|a| a.get("enum")).is_some(), + }); + } + + Ok(()) +} + +fn is_base_type(s: &str) -> bool { + match s { + "UInt" | "UInt8" | "UInt16" | "UInt32" | "UInt64" + | "Int" | "Int8" | "Int16" | "Int32" | "Int64" + | "String" | "Boolean" | "Bytes" => true, + _ => false, + } +} + +fn is_builtin_type(s: &str) -> bool { + match s { + "BigInt" | "BigNumber" | "JSON" => true, + _ => false, + } +} + +fn append_unique(vec: &mut Vec, item: PropertyDep) { + if vec.iter().any(|i| i._crate == item._crate && i._type == item._type) { + return; + } + vec.push(item); +} + +fn is_known_type(name: &str, root_type: &str) -> bool { + is_base_type(name) || is_builtin_type(name) || name == root_type +} diff --git a/implementations/wrap-rust/src/helpers/serde_annotate_if_bytes.rs b/implementations/wrap-rust/src/helpers/serde_annotate_if_bytes.rs new file mode 100644 index 00000000..2305b54b --- /dev/null +++ b/implementations/wrap-rust/src/helpers/serde_annotate_if_bytes.rs @@ -0,0 +1,14 @@ +use handlebars::handlebars_helper; +use serde_json::{Value}; + +handlebars_helper!(serde_annotate_if_bytes: |val: Value| { + let name = val.as_str().unwrap(); + _serde_annotate_if_bytes(name) +}); + +pub fn _serde_annotate_if_bytes(val: &str) -> String { + if val == "Bytes" { + return "#[serde(with = \"serde_bytes\")]\n ".to_string(); + } + "".to_string() +} diff --git a/implementations/wrap-rust/src/helpers/serde_keyword.rs b/implementations/wrap-rust/src/helpers/serde_keyword.rs new file mode 100644 index 00000000..d87e54f8 --- /dev/null +++ b/implementations/wrap-rust/src/helpers/serde_keyword.rs @@ -0,0 +1,16 @@ +use handlebars::handlebars_helper; +use serde_json::{Value}; +use crate::helpers::is_keyword::_is_keyword; + +handlebars_helper!(serde_keyword: |val: Value| { + let type_val = val.as_str().unwrap(); + _serde_keyword(type_val) +}); + +pub fn _serde_keyword(type_val: &str) -> String { + if _is_keyword(type_val) { + // return `#[serde(rename = "${type}")]\n `; + return format!("#[serde(rename = \"{}\")]\n ", type_val); + } + "".to_string() +} diff --git a/implementations/wrap-rust/src/helpers/serde_rename_if_case_mismatch.rs b/implementations/wrap-rust/src/helpers/serde_rename_if_case_mismatch.rs new file mode 100644 index 00000000..e5691421 --- /dev/null +++ b/implementations/wrap-rust/src/helpers/serde_rename_if_case_mismatch.rs @@ -0,0 +1,19 @@ +use handlebars::handlebars_helper; +use serde_json::{Value}; +use crate::helpers::is_keyword::_is_keyword; + +handlebars_helper!(serde_rename_if_case_mismatch: |val: Value| { + let name = val.as_str().unwrap(); + _serde_rename_if_case_mismatch(name) +}); + +pub fn _serde_rename_if_case_mismatch(val: &str) -> String { + if has_uppercase(val) || _is_keyword(val) { + return format!("#[serde(rename = \"{}\")]\n ", val); + } + "".to_string() +} + +fn has_uppercase(value: &str) -> bool { + value != value.to_lowercase() +} \ No newline at end of file diff --git a/implementations/wrap-rust/src/helpers/to_graphql_type.rs b/implementations/wrap-rust/src/helpers/to_graphql_type.rs new file mode 100644 index 00000000..2902e913 --- /dev/null +++ b/implementations/wrap-rust/src/helpers/to_graphql_type.rs @@ -0,0 +1,124 @@ +use handlebars::handlebars_helper; +use serde_json::{Value, Map}; +use crate::helpers::util::DefinitionKind; + +fn apply_required(type_str: &str, required: bool) -> String { + format!("{}{}", type_str, match required { + true => "!", + false => "" + }) +} + +fn any_to_graphql(any: &Map, prefixed: bool) -> String { + if let Some(object) = any.get("object") { + return to_graphql_type_fn(object.as_object().unwrap(), prefixed); + } else if let Some(array) = any.get("array") { + return to_graphql_type_fn(array.as_object().unwrap(), prefixed); + } else if let Some(scalar) = any.get("scalar") { + return to_graphql_type_fn(scalar.as_object().unwrap(), prefixed); + } else if let Some(enum_value) = any.get("enum") { + return to_graphql_type_fn(enum_value.as_object().unwrap(), prefixed); + } else if let Some(map) = any.get("map") { + return to_graphql_type_fn(map.as_object().unwrap(), prefixed); + } else { + panic!("anyToGraphQL: Any type is invalid."); + } +} + +pub fn to_graphql_type_fn(obj: &Map, prefixed: bool) -> String { + let type_str = obj.get("type").unwrap().as_str().unwrap(); + let required = match obj.get("required") { + Some(x) => x.as_bool().unwrap(), + None => false + }; + let kind = DefinitionKind::from(obj.get("kind").unwrap().as_u64().unwrap() as u32); + + match kind { + DefinitionKind::Object + | DefinitionKind::ObjectRef + | DefinitionKind::Scalar + | DefinitionKind::ImportedObject => apply_required(type_str, required), + DefinitionKind::Enum + | DefinitionKind::EnumRef + | DefinitionKind::ImportedEnum => { + if prefixed { + apply_required(format!("Enum_{}", type_str).as_str(), required) + } else { + apply_required(type_str, required) + } + } + DefinitionKind::Any | DefinitionKind::Property => { + any_to_graphql(obj, prefixed) + } + DefinitionKind::Array => { + if let Some(item) = obj.get("item") { + apply_required(format!("[{}]", to_graphql_type_fn(item.as_object().unwrap(), prefixed)).as_str(), required) + } else { + panic!( + "toGraphQL: ArrayDefinition's item type is undefined.\n{:?}", + obj + ); + } + } + DefinitionKind::Map => { + if let Some(key) = obj.get("key") { + if let Some(value) = obj.get("value") { + apply_required( + format!( + "Map<{}, {}>", + to_graphql_type_fn(key.as_object().unwrap(), prefixed), + to_graphql_type_fn(value.as_object().unwrap(), prefixed) + ).as_str(), + required, + ) + } else { + panic!( + "toGraphQL: MapDefinition's value type is undefined.\n{:?}", + obj + ); + } + } else { + panic!( + "toGraphQL: MapDefinition's key type is undefined.\n{:?}", + obj + ); + } + } + DefinitionKind::Method => { + if let Some(return_type) = obj.get("returnType") { + let arguments = obj.get("arguments").unwrap(); + let result = format!( + "{}(\n {}\n): {}", + obj.get("name").unwrap().as_str().unwrap(), + arguments + .as_array().unwrap() + .into_iter() + .map(|arg| { + let arg_obj = arg.as_object().unwrap(); + format!("{}: {}", arg_obj.get("name").unwrap().as_str().unwrap(), to_graphql_type_fn(arg_obj, prefixed)) + }) + .collect::>() + .join("\n "), + to_graphql_type_fn(return_type.as_object().unwrap(), prefixed) + ); + result + } else { + panic!( + "toGraphQL: MethodDefinition's return type is undefined.\n{:?}", + obj + ); + } + } + DefinitionKind::Module => obj.get("type").unwrap().as_str().unwrap().to_string(), + DefinitionKind::ImportedModule => obj.get("type").unwrap().as_str().unwrap().to_string(), + _ => panic!( + "toGraphQL: Unrecognized DefinitionKind.\n{:?}", + obj + ), + } +} + +handlebars_helper!(to_graphql_type: |value: Value| { + let obj = value.as_object().unwrap(); + to_graphql_type_fn(&obj, true) +}); diff --git a/implementations/wrap-rust/src/helpers/to_lower.rs b/implementations/wrap-rust/src/helpers/to_lower.rs new file mode 100644 index 00000000..81794c9e --- /dev/null +++ b/implementations/wrap-rust/src/helpers/to_lower.rs @@ -0,0 +1,25 @@ +use handlebars::handlebars_helper; +use serde_json::{Value}; +use crate::helpers::util::{insert_at, replace_at}; + +handlebars_helper!(to_lower: |val: Value| { + let str = val.as_str().unwrap(); + _to_lower(str) +}); + +pub fn _to_lower(s: &str) -> String { + let mut result = s.to_string(); + let mut i = 0; + while i < result.len() { + let char = result.chars().nth(i).unwrap(); + if char.is_uppercase() { + let lower = char.to_lowercase().collect::(); + result = replace_at(&result, i, &lower); + if i != 0 && result.chars().nth(i - 1).unwrap() != '_' { + result = insert_at(&result, i, "_"); + } + } + i += 1; + } + result +} diff --git a/implementations/wrap-rust/src/helpers/to_rust.rs b/implementations/wrap-rust/src/helpers/to_rust.rs new file mode 100644 index 00000000..359bd027 --- /dev/null +++ b/implementations/wrap-rust/src/helpers/to_rust.rs @@ -0,0 +1,77 @@ +use handlebars::handlebars_helper; +use serde_json::{Value}; +use crate::helpers::detect_keyword::_detect_keyword; +use crate::helpers::to_upper::_to_upper; +use crate::helpers::util::{array_type, map_types}; + +handlebars_helper!(to_rust: |val: Value| { + let type_val = val.as_str().unwrap(); + _to_rust(type_val) +}); + +pub fn _to_rust(value: &str) -> String { + let mut res = value.to_string(); + let mut optional = false; + if res.ends_with("!") { + res.pop(); + } else { + optional = true; + } + + if res.starts_with("[") { + return _to_rust_array(&res, optional).unwrap(); + } + + if res.starts_with("Map<") { + return _to_rust_map(&res, optional).unwrap(); + } + + res = match res.as_str() { + "Int" | "Int32" => "i32".to_string(), + "Int8" => "i8".to_string(), + "Int16" => "i16".to_string(), + "Int64" => "i64".to_string(), + "UInt" | "UInt32" => "u32".to_string(), + "UInt8" => "u8".to_string(), + "UInt16" => "u16".to_string(), + "UInt64" => "u64".to_string(), + "String" => "String".to_string(), + "Boolean" => "bool".to_string(), + "Bytes" => "Vec".to_string(), + "BigInt" => "BigIntWrapper".to_string(), + "BigNumber" => "BigNumber".to_string(), + "JSON" => "JSONString".to_string(), + _ => { + if res.starts_with("Enum_") { + res = res.replacen("Enum_", "", 1); + } + res = _to_upper(&res); + _detect_keyword(&res) + } + }; + + _apply_optional(&res, optional) +} + +pub fn _to_rust_array(value: &str, optional: bool) -> Result { + let inner_type = array_type(value)?; + let rs_type = _to_rust(&inner_type); + let rs_array = format!("Vec<{}>", rs_type); + Ok(_apply_optional(&rs_array, optional)) +} + +pub fn _to_rust_map(value: &str, optional: bool) -> Result { + let (key_type, val_type) = map_types(value)?; + let rs_key_type = _to_rust(&key_type); + let rs_val_type = _to_rust(&val_type); + let rs_map = format!("Map<{}, {}>", &rs_key_type, &rs_val_type); + Ok(_apply_optional(&rs_map, optional)) +} + +pub fn _apply_optional(value: &str, optional: bool) -> String { + return if optional { + format!("Option<{}>", value) + } else { + value.to_string() + } +} \ No newline at end of file diff --git a/implementations/wrap-rust/src/helpers/to_rust_init.rs b/implementations/wrap-rust/src/helpers/to_rust_init.rs new file mode 100644 index 00000000..3aea8aef --- /dev/null +++ b/implementations/wrap-rust/src/helpers/to_rust_init.rs @@ -0,0 +1,58 @@ +use handlebars::handlebars_helper; +use serde_json::{Value}; +use crate::helpers::to_rust::_to_rust; +use crate::helpers::util::map_types; + +handlebars_helper!(to_rust_init: |value: Value| { + let value_str = value.as_str().unwrap(); + _to_rust_init(value_str) +}); + +pub fn _to_rust_init(value: &str) -> String { + let mut type_str = String::from(value); + let mut optional = false; + + if type_str.ends_with("!") { + type_str.pop(); + } else { + optional = true; + } + + if type_str.starts_with("[") { + return optional_modifier("vec![]", optional); + } + + if type_str.starts_with("Map<") { + let (key_type, val_type) = map_types(value).unwrap(); + let rs_key_type = _to_rust(&key_type); + let rs_val_type = _to_rust(&val_type); + return optional_modifier(&format!("Map::<{}, {}>::new()", rs_key_type, rs_val_type), optional); + } + + match type_str.as_str() { + "Int" | "Int8" | "Int16" | "Int32" | "Int64" | + "UInt" | "UInt8" | "UInt16" | "UInt32" | "UInt64" => optional_modifier("0", optional), + "String" => optional_modifier("String::new()", optional), + "Boolean" => optional_modifier("false", optional), + "Bytes" => optional_modifier("vec![]", optional), + "BigInt" => optional_modifier("BigIntWrapper(BigInt::default())", optional), + "BigNumber" => optional_modifier("BigNumber::default()", optional), + "JSON" => optional_modifier("JSONString::from(JSON::Value::Null)", optional), + _ => { + let rs_type = _to_rust(&type_str); + if type_str.starts_with("Enum_") { + optional_modifier(&format!("{}::_MAX_", rs_type), optional) + } else { + optional_modifier(&format!("{}::new()", rs_type), optional) + } + } + } +} + +fn optional_modifier(str: &str, optional: bool) -> String { + if optional { + "None".to_string() + } else { + str.to_string() + } +} \ No newline at end of file diff --git a/implementations/wrap-rust/src/helpers/to_upper.rs b/implementations/wrap-rust/src/helpers/to_upper.rs new file mode 100644 index 00000000..21bd1268 --- /dev/null +++ b/implementations/wrap-rust/src/helpers/to_upper.rs @@ -0,0 +1,27 @@ +use handlebars::handlebars_helper; +use serde_json::{Value}; +use crate::helpers::util::{remove_at, replace_at}; + +handlebars_helper!(to_upper: |val: Value| { + let str = val.as_str().unwrap(); + _to_upper(str) +}); + +pub fn _to_upper(s: &str) -> String { + let mut result = s.to_string(); + let first_char = result.chars().nth(0).unwrap(); + let first_upper = first_char.to_uppercase().collect::(); + result = replace_at(&result, 0, &first_upper); + let mut i = 0; + while i < result.len() { + if let Some('_') = result.chars().nth(i) { + if let Some(next_char) = result.chars().nth(i + 1) { + let next_char_upper = next_char.to_uppercase().collect::(); + result = replace_at(&result, i + 1, &next_char_upper); + result = remove_at(&result, i); + } + } + i += 1; + } + result +} diff --git a/implementations/wrap-rust/src/helpers/util.rs b/implementations/wrap-rust/src/helpers/util.rs new file mode 100644 index 00000000..6d941567 --- /dev/null +++ b/implementations/wrap-rust/src/helpers/util.rs @@ -0,0 +1,110 @@ +pub fn replace_at(s: &str, idx: usize, replacement: &str) -> String { + let start = s[..idx].to_string(); + let end = s[idx + replacement.len()..].to_string(); + format!("{}{}{}", start, replacement, end) +} + +pub fn insert_at(s: &str, idx: usize, insert: &str) -> String { + let start = s[..idx].to_string(); + let end = s[idx..].to_string(); + format!("{}{}{}", start, insert, end) +} + +pub fn remove_at(s: &str, idx: usize) -> String { + let start = s[..idx].to_string(); + let end = s[idx + 1..].to_string(); + format!("{}{}", start, end) +} + +pub fn array_type(value: &str) -> Result { + let mut iter = value.char_indices(); + + let first_bracket = match iter.find(|&(_, c)| c == '[').map(|(i, _)| i) { + Some(idx) => idx, + None => return Err(format!("Invalid Array: {}", value)), + }; + let last_bracket = match iter.rfind(|&(_, c)| c == ']').map(|(i, _)| i) { + Some(idx) => idx, + None => return Err(format!("Invalid Array: {}", value)), + }; + + let inner_type = &value[(first_bracket+1)..last_bracket]; + Ok(inner_type.to_string()) +} + +pub fn map_types(value: &str) -> Result<(String, String), String> { + let first_open_bracket_idx = match value.find('<') { + Some(idx) => idx, + None => return Err(format!("Invalid Map: {}", value)), + }; + let last_close_bracket_idx = match value.rfind('>') { + Some(idx) => idx, + None => return Err(format!("Invalid Map: {}", value)), + }; + + let key_val_types = &value[(first_open_bracket_idx + 1)..last_close_bracket_idx]; + + let first_comma_idx = match key_val_types.find(',') { + Some(idx) => idx, + None => return Err(format!("Invalid Map: {}", value)), + }; + + let key_type = key_val_types[..first_comma_idx].trim(); + let val_type = key_val_types[(first_comma_idx + 1)..].trim(); + + Ok((key_type.to_string(), val_type.to_string())) +} + +#[derive(Debug)] +pub enum DefinitionKind { + Generic = 0, + Object = 1 << 0, + Any = 1 << 1, + Scalar = 1 << 2, + Enum = 1 << 3, + Array = ((1 << 4) as u32 | DefinitionKind::Any as u32) as isize, + Property = ((1 << 5) as u32 | DefinitionKind::Any as u32) as isize, + Method = 1 << 6, + Module = 1 << 7, + ImportedModule = 1 << 8, + ImportedEnum = ((1 << 9) as u32 | DefinitionKind::Enum as u32) as isize, + ImportedObject = ((1 << 10) as u32 | DefinitionKind::Object as u32) as isize, + InterfaceImplemented = 1 << 11, + UnresolvedObjectOrEnum = 1 << 12, + ObjectRef = 1 << 13, + EnumRef = 1 << 14, + Interface = 1 << 15, + Env = 1 << 16, + MapKey = 1 << 17, + Map = ((1 << 18) as u32 | DefinitionKind::Any as u32) as isize, + ImportedEnv = 1 << 19, +} + +impl From for DefinitionKind { + fn from(value: u32) -> Self { + match value { + v if v == Self::Generic as u32 => Self::Generic, + v if v == Self::Object as u32 => Self::Object, + v if v == Self::Any as u32 => Self::Any, + v if v == Self::Scalar as u32 => Self::Scalar, + v if v == Self::Enum as u32 => Self::Enum, + v if v == Self::Array as u32 => Self::Array, + v if v == Self::Property as u32 => Self::Property, + v if v == Self::Method as u32 => Self::Method, + v if v == Self::Module as u32 => Self::Module, + v if v == Self::ImportedModule as u32 => Self::ImportedModule, + v if v == Self::ImportedEnum as u32 => Self::ImportedEnum, + v if v == Self::ImportedObject as u32 => Self::ImportedObject, + v if v == Self::InterfaceImplemented as u32 => Self::InterfaceImplemented, + v if v == Self::UnresolvedObjectOrEnum as u32 => Self::UnresolvedObjectOrEnum, + v if v == Self::ObjectRef as u32 => Self::ObjectRef, + v if v == Self::EnumRef as u32 => Self::EnumRef, + v if v == Self::Interface as u32 => Self::Interface, + v if v == Self::Env as u32 => Self::Env, + v if v == Self::MapKey as u32 => Self::MapKey, + v if v == Self::Map as u32 => Self::Map, + v if v == Self::ImportedEnv as u32 => Self::ImportedEnv, + _ => panic!("Invalid value for DefinitionKind"), + } + } +} \ No newline at end of file diff --git a/implementations/wrap-rust/src/lib.rs b/implementations/wrap-rust/src/lib.rs new file mode 100644 index 00000000..af9efbfb --- /dev/null +++ b/implementations/wrap-rust/src/lib.rs @@ -0,0 +1,236 @@ +#[macro_use] +extern crate lazy_static; + +pub mod wrap; +pub use wrap::*; + +pub mod templates; +pub mod helpers; +mod renderer; +use renderer::Renderer; +use crate::helpers::detect_keyword::_detect_keyword; +use crate::helpers::to_lower::_to_lower; +use polywrap_wasm_rs::{JSON}; + +impl ModuleTrait for Module { + fn generate_bindings(args: ArgsGenerateBindings) -> Result { + let version = &args.wrap_info.version; + + // First, ensure version is "0.1" + if version != "0.1" { + return Err( + format!("Unsupported ABI Version - {}; Supported - 0.1", version) + ); + } + + let wrap_info = args.wrap_info; + let renderer = Renderer::new(); + let mut output = Output::new(); + + output.files.push(File { + name: "mod.rs".to_string(), + data: renderer.render( + "mod.rs", + &wrap_info.abi + ) + }); + + output.files.push(File { + name: "entry.rs".to_string(), + data: renderer.render( + "entry.rs", + &wrap_info.abi + ) + }); + + let abi = wrap_info.abi.as_object().unwrap(); + + let get_dir_name = |value: &JSON::Value| -> String { + let dir_name = value.get("type").unwrap().as_str().unwrap().to_string(); + _to_lower(&dir_name) + }; + + if let Some(object_types) = abi.get("objectTypes") { + let objects = object_types.as_array().unwrap(); + + for object in objects.iter() { + let dir = Directory { + name: _detect_keyword(&get_dir_name(object)), + files: vec!( + File { + name: "mod.rs".to_string(), + data: renderer.render("object_type/mod.rs", object) + } + ), + dirs: vec!() + }; + output.dirs.push(dir); + } + } + + if let Some(module_type) = abi.get("moduleType") { + let dir = Directory { + name: "module".to_string(), + files: vec!( + File { + name: "mod.rs".to_string(), + data: renderer.render("module_type/mod.rs", module_type) + }, + File { + name: "module.rs".to_string(), + data: renderer.render("module_type/module.rs", abi) + }, + File { + name: "wrapped.rs".to_string(), + data: renderer.render("module_type/wrapped.rs", abi) + }, + ), + dirs: vec!() + }; + output.dirs.push(dir); + } + + if let Some(enum_types) = abi.get("enumTypes") { + let enums = enum_types.as_array().unwrap(); + + for it in enums.iter() { + let dir = Directory { + name: _detect_keyword(&get_dir_name(it)), + files: vec!( + File { + name: "mod.rs".to_string(), + data: renderer.render("enum_type/mod.rs", it) + }, + ), + dirs: vec!() + }; + output.dirs.push(dir); + } + } + + if let Some(interface_types) = abi.get("interfaceTypes") { + let interfaces = interface_types.as_array().unwrap(); + + for it in interfaces.iter() { + let dir = Directory { + name: get_dir_name(it), + files: vec!( + File { + name: "mod.rs".to_string(), + data: renderer.render("interface_type/mod.rs", it) + }, + ), + dirs: vec!() + }; + output.dirs.push(dir); + } + } + + // imported dirs go within subdirectory + let mut imported = Directory { + name: "imported".to_string(), + files: vec![], + dirs: vec![], + }; + + if let Some(imported_object_types) = abi.get("importedObjectTypes") { + let objects = imported_object_types.as_array().unwrap(); + + for object in objects.iter() { + let dir = Directory { + name: _detect_keyword(&get_dir_name(object)), + files: vec!( + File { + name: "mod.rs".to_string(), + data: renderer.render("imported/object_type/mod.rs", object) + } + ), + dirs: vec!() + }; + imported.dirs.push(dir); + } + } + + if let Some(imported_module_types) = abi.get("importedModuleTypes") { + let modules = imported_module_types.as_array().unwrap(); + + for it in modules.iter() { + let dir = Directory { + name: get_dir_name(it), + files: vec!( + File { + name: "mod.rs".to_string(), + data: renderer.render("imported/module_type/mod.rs", it) + } + ), + dirs: vec!() + }; + imported.dirs.push(dir); + } + } + + if let Some(imported_enum_types) = abi.get("importedEnumTypes") { + let enums = imported_enum_types.as_array().unwrap(); + + for it in enums.iter() { + let dir = Directory { + name: _detect_keyword(&get_dir_name(it)), + files: vec!( + File { + name: "mod.rs".to_string(), + data: renderer.render("imported/enum_type/mod.rs", it) + }, + ), + dirs: vec!() + }; + imported.dirs.push(dir); + } + } + + if let Some(imported_env_types) = abi.get("importedEnvTypes") { + let envs = imported_env_types.as_array().unwrap(); + + for it in envs.iter() { + let dir = Directory { + name: _detect_keyword(&get_dir_name(it)), + files: vec!( + File { + name: "mod.rs".to_string(), + data: renderer.render("imported/env_type/mod.rs", it) + } + ), + dirs: vec!() + }; + imported.dirs.push(dir); + } + } + + // add imported dirs to output + if abi.get("importedObjectTypes").is_some() || + abi.get("importedModuleTypes").is_some() || + abi.get("importedEnumTypes").is_some() || + abi.get("importedEnvTypes").is_some() { + imported.files.push(File { + name: "mod.rs".to_string(), + data: renderer.render("imported/mod.rs", &wrap_info.abi) + }); + output.dirs.push(imported); + } + + if let Some(env_type) = abi.get("envType") { + let dir = Directory { + name: "env".to_string(), + files: vec!( + File { + name: "mod.rs".to_string(), + data: renderer.render("env_type/mod.rs", env_type) + } + ), + dirs: vec!() + }; + output.dirs.push(dir); + } + + Ok(output) + } +} diff --git a/implementations/wrap-rust/src/renderer.rs b/implementations/wrap-rust/src/renderer.rs new file mode 100644 index 00000000..07b091a1 --- /dev/null +++ b/implementations/wrap-rust/src/renderer.rs @@ -0,0 +1,44 @@ +use handlebars::{ + Handlebars, + no_escape +}; +use serde::Serialize; + +use crate::templates; +use crate::helpers; + +pub struct Renderer<'reg> { + instance: Handlebars<'reg> +} + +impl<'reg> Renderer<'reg> { + pub fn new() -> Renderer<'reg> { + let mut handlebars: Handlebars = Handlebars::new(); + + // Remove the HTML escape function + handlebars.register_escape_fn(no_escape); + + // Register all templates + let templates = templates::load_templates(); + for template in templates.iter() { + handlebars.register_template_string( + &template.name, + &template.source + ).unwrap(); + } + + // Register all helpers + helpers::register(&mut handlebars); + + Renderer { + instance: handlebars + } + } + + pub fn render(self: &Renderer<'reg>, name: &str, data: &T) -> String + where + T: Serialize, + { + self.instance.render(name, data).unwrap() + } +} diff --git a/implementations/wrap-rust/src/templates/entry_rs.rs b/implementations/wrap-rust/src/templates/entry_rs.rs new file mode 100644 index 00000000..58a051df --- /dev/null +++ b/implementations/wrap-rust/src/templates/entry_rs.rs @@ -0,0 +1,52 @@ +lazy_static! { + static ref NAME: String = "entry.rs".to_string(); + static ref SOURCE: String = r#"{{#with moduleType}} +{{#if (array_has_length methods)}} +use crate::{ + {{#each methods}} + {{to_lower name}}_wrapped{{#if (is_not_last @index ../methods)}},{{/if}} + {{/each}} +}; +{{/if}} +{{/with}} +use polywrap_wasm_rs::{ + abort, + invoke, + InvokeArgs, +}; + +#[no_mangle] +pub extern "C" fn _wrap_invoke(method_size: u32, args_size: u32, env_size: u32) -> bool { + // Ensure the abort handler is properly setup + abort::wrap_abort_setup(); + + let args: InvokeArgs = invoke::wrap_invoke_args(method_size, args_size); + let result: Vec; + + match args.method.as_str() { + {{#with moduleType}} + {{#each methods}} + "{{name}}" => { + result = {{to_lower name}}_wrapped(args.args.as_slice(), env_size); + } + {{/each}} + {{/with}} + _ => { + invoke::wrap_invoke_error(format!("Could not find invoke function {}", args.method)); + return false; + } + }; + invoke::wrap_invoke_result(result); + return true; +} +"#.to_string(); +} + +use super::Template; + +pub fn load() -> Template { + Template { + name: &*NAME, + source: &*SOURCE + } +} diff --git a/implementations/wrap-rust/src/templates/enum_type/mod.rs b/implementations/wrap-rust/src/templates/enum_type/mod.rs new file mode 100644 index 00000000..217f27f6 --- /dev/null +++ b/implementations/wrap-rust/src/templates/enum_type/mod.rs @@ -0,0 +1 @@ +pub mod mod_rs; \ No newline at end of file diff --git a/implementations/wrap-rust/src/templates/enum_type/mod_rs.rs b/implementations/wrap-rust/src/templates/enum_type/mod_rs.rs new file mode 100644 index 00000000..e1dccb12 --- /dev/null +++ b/implementations/wrap-rust/src/templates/enum_type/mod_rs.rs @@ -0,0 +1,68 @@ +lazy_static! { + static ref NAME: String = "enum_type/mod.rs".to_string(); + static ref SOURCE: String = r#"use polywrap_wasm_rs::{EnumTypeError}; +use serde::{Serialize, Deserialize}; +use std::convert::TryFrom; + +#[derive(Clone, Copy, Debug, Deserialize, Serialize)] +pub enum {{detect_keyword (to_upper type)}} { + {{#each constants}} + {{serde_rename_if_case_mismatch this}}{{detect_keyword this}}, + {{/each}} + _MAX_ +} + +pub fn sanitize_{{to_lower type}}_value(value: i32) -> Result<(), EnumTypeError> { + if value < 0 && value >= {{detect_keyword (to_upper type)}}::_MAX_ as i32 { + return Err(EnumTypeError::EnumProcessingError(format!("Invalid value for enum '{{detect_keyword (to_upper type)}}': {}", value.to_string()))); + } + Ok(()) +} + +pub fn get_{{to_lower type}}_value(key: &str) -> Result<{{detect_keyword (to_upper type)}}, EnumTypeError> { + match key { + {{#each constants}} + "{{detect_keyword this}}" => Ok({{detect_keyword (to_upper ../type)}}::{{detect_keyword this}}), + {{/each}} + "_MAX_" => Ok({{detect_keyword (to_upper type)}}::_MAX_), + err => Err(EnumTypeError::EnumProcessingError(format!("Invalid key for enum '{{detect_keyword (to_upper type)}}': {}", err))) + } +} + +pub fn get_{{to_lower type}}_key(value: {{detect_keyword (to_upper type)}}) -> Result { + if sanitize_{{to_lower type}}_value(value as i32).is_ok() { + match value { + {{#each constants}} + {{detect_keyword (to_upper ../type)}}::{{detect_keyword this}} => Ok("{{detect_keyword this}}".to_string()), + {{/each}} + {{detect_keyword (to_upper type)}}::_MAX_ => Ok("_MAX_".to_string()), + } + } else { + Err(EnumTypeError::EnumProcessingError(format!("Invalid value for enum '{{detect_keyword (to_upper type)}}': {}", (value as i32).to_string()))) + } +} + +impl TryFrom for {{detect_keyword (to_upper type)}} { + type Error = EnumTypeError; + + fn try_from(v: i32) -> Result<{{detect_keyword (to_upper type)}}, Self::Error> { + match v { + {{#each constants}} + x if x == {{detect_keyword (to_upper ../type)}}::{{detect_keyword this}} as i32 => Ok({{detect_keyword (to_upper ../type)}}::{{detect_keyword this}}), + {{/each}} + x if x == {{detect_keyword (to_upper type)}}::_MAX_ as i32 => Ok({{detect_keyword (to_upper type)}}::_MAX_), + _ => Err(EnumTypeError::ParseEnumError(format!("Invalid value for enum '{{detect_keyword (to_upper type)}}': {}", (v as i32).to_string()))), + } + } +} +"#.to_string(); +} + +use crate::templates::Template; + +pub fn load() -> Template { + Template { + name: &*NAME, + source: &*SOURCE + } +} diff --git a/implementations/wrap-rust/src/templates/env_type/mod.rs b/implementations/wrap-rust/src/templates/env_type/mod.rs new file mode 100644 index 00000000..1ad2c381 --- /dev/null +++ b/implementations/wrap-rust/src/templates/env_type/mod.rs @@ -0,0 +1 @@ +pub mod mod_rs; diff --git a/implementations/wrap-rust/src/templates/env_type/mod_rs.rs b/implementations/wrap-rust/src/templates/env_type/mod_rs.rs new file mode 100644 index 00000000..34ff385e --- /dev/null +++ b/implementations/wrap-rust/src/templates/env_type/mod_rs.rs @@ -0,0 +1,44 @@ +lazy_static! { + static ref NAME: String = "env_type/mod.rs".to_string(); + static ref SOURCE: String = r#"use serde::{Serialize, Deserialize}; +use polywrap_msgpack_serde::{ + wrappers::polywrap_json::JSONString, + wrappers::polywrap_bigint::BigIntWrapper +}; +use polywrap_wasm_rs::{ + BigInt, + BigNumber, + Map, + JSON +}; +{{#each (property_deps this)}} +use {{_crate}}::{{detect_keyword (to_upper _type)}}; +{{/each}} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct {{detect_keyword (to_upper type)}} { + {{#each properties}} + {{#with scalar}}{{serde_annotate_if_bytes type}}{{/with}}{{serde_rename_if_case_mismatch name}}pub {{detect_keyword (to_lower name)}}: {{to_rust (to_graphql_type this)}}, + {{/each}} +} + +impl {{detect_keyword (to_upper type)}} { + pub fn new() -> {{detect_keyword (to_upper type)}} { + {{detect_keyword (to_upper type)}} { + {{#each properties}} + {{detect_keyword (to_lower name)}}: {{to_rust_init (to_graphql_type this)}}, + {{/each}} + } + } +} +"#.to_string(); +} + +use crate::templates::Template; + +pub fn load() -> Template { + Template { + name: &*NAME, + source: &*SOURCE + } +} diff --git a/implementations/wrap-rust/src/templates/imported/enum_type/mod.rs b/implementations/wrap-rust/src/templates/imported/enum_type/mod.rs new file mode 100644 index 00000000..1ad2c381 --- /dev/null +++ b/implementations/wrap-rust/src/templates/imported/enum_type/mod.rs @@ -0,0 +1 @@ +pub mod mod_rs; diff --git a/implementations/wrap-rust/src/templates/imported/enum_type/mod_rs.rs b/implementations/wrap-rust/src/templates/imported/enum_type/mod_rs.rs new file mode 100644 index 00000000..46d6bd3d --- /dev/null +++ b/implementations/wrap-rust/src/templates/imported/enum_type/mod_rs.rs @@ -0,0 +1,68 @@ +lazy_static! { + static ref NAME: String = "imported/enum_type/mod.rs".to_string(); + static ref SOURCE: String = r#"use polywrap_wasm_rs::EnumTypeError; +use serde::{Serialize, Deserialize}; +use std::convert::TryFrom; + +#[derive(Clone, Copy, Debug, Deserialize, Serialize)] +pub enum {{detect_keyword (to_upper type)}} { + {{#each constants}} + {{serde_rename_if_case_mismatch this}}{{detect_keyword this}}, + {{/each}} + _MAX_ +} + +pub fn sanitize_{{to_lower type}}_value(value: i32) -> Result<(), EnumTypeError> { + if value < 0 && value >= {{detect_keyword (to_upper type)}}::_MAX_ as i32 { + return Err(EnumTypeError::EnumProcessingError(format!("Invalid value for enum '{{detect_keyword (to_upper type)}}': {}", value.to_string()))); + } + Ok(()) +} + +pub fn get_{{to_lower type}}_value(key: &str) -> Result<{{detect_keyword (to_upper type)}}, EnumTypeError> { + match key { + {{#each constants}} + "{{detect_keyword this}}" => Ok({{detect_keyword (to_upper ../type)}}::{{detect_keyword this}}), + {{/each}} + "_MAX_" => Ok({{detect_keyword (to_upper type)}}::_MAX_), + err => Err(EnumTypeError::EnumProcessingError(format!("Invalid key for enum '{{detect_keyword (to_upper type)}}': {}", err))) + } +} + +pub fn get_{{to_lower type}}_key(value: {{detect_keyword (to_upper type)}}) -> Result { + if sanitize_{{to_lower type}}_value(value as i32).is_ok() { + match value { + {{#each constants}} + {{detect_keyword (to_upper ../type)}}::{{detect_keyword this}} => Ok("{{detect_keyword this}}".to_string()), + {{/each}} + {{detect_keyword (to_upper type)}}::_MAX_ => Ok("_MAX_".to_string()), + } + } else { + Err(EnumTypeError::EnumProcessingError(format!("Invalid value for enum '{{detect_keyword (to_upper type)}}': {}", (value as i32).to_string()))) + } +} + +impl TryFrom for {{detect_keyword (to_upper type)}} { + type Error = EnumTypeError; + + fn try_from(v: i32) -> Result<{{detect_keyword (to_upper type)}}, Self::Error> { + match v { + {{#each constants}} + x if x == {{detect_keyword (to_upper ../type)}}::{{detect_keyword this}} as i32 => Ok({{detect_keyword (to_upper ../type)}}::{{detect_keyword this}}), + {{/each}} + x if x == {{detect_keyword (to_upper type)}}::_MAX_ as i32 => Ok({{detect_keyword (to_upper type)}}::_MAX_), + _ => Err(EnumTypeError::ParseEnumError(format!("Invalid value for enum '{{detect_keyword (to_upper type)}}': {}", (v as i32).to_string()))), + } + } +} +"#.to_string(); +} + +use crate::templates::Template; + +pub fn load() -> Template { + Template { + name: &*NAME, + source: &*SOURCE + } +} diff --git a/implementations/wrap-rust/src/templates/imported/env_type/mod.rs b/implementations/wrap-rust/src/templates/imported/env_type/mod.rs new file mode 100644 index 00000000..1ad2c381 --- /dev/null +++ b/implementations/wrap-rust/src/templates/imported/env_type/mod.rs @@ -0,0 +1 @@ +pub mod mod_rs; diff --git a/implementations/wrap-rust/src/templates/imported/env_type/mod_rs.rs b/implementations/wrap-rust/src/templates/imported/env_type/mod_rs.rs new file mode 100644 index 00000000..4c8f4a39 --- /dev/null +++ b/implementations/wrap-rust/src/templates/imported/env_type/mod_rs.rs @@ -0,0 +1,46 @@ +lazy_static! { + static ref NAME: String = "imported/env_type/mod.rs".to_string(); + static ref SOURCE: String = r#"use serde::{Serialize, Deserialize}; +use polywrap_msgpack_serde::{ + wrappers::polywrap_json::JSONString, + wrappers::polywrap_bigint::BigIntWrapper +}; +use polywrap_wasm_rs::{ + BigInt, + BigNumber, + Map, + JSON +}; +{{#each (property_deps this)}} +use {{_crate}}::{{detect_keyword (to_upper _type)}}; +{{/each}} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct {{detect_keyword (to_upper type)}} { + {{#each properties}} + pub {{detect_keyword (to_lower name)}}: {{to_rust (to_graphql_type this)}}, + {{/each}} +} + +impl {{detect_keyword (to_upper type)}} { + pub const URI: &'static str = "{{uri}}"; + + pub fn new() -> {{detect_keyword (to_upper type)}} { + {{detect_keyword (to_upper type)}} { + {{#each properties}} + {{detect_keyword (to_lower name)}}: {{to_rust_init (to_graphql_type this)}}, + {{/each}} + } + } +} +"#.to_string(); +} + +use crate::templates::Template; + +pub fn load() -> Template { + Template { + name: &*NAME, + source: &*SOURCE + } +} diff --git a/implementations/wrap-rust/src/templates/imported/mod.rs b/implementations/wrap-rust/src/templates/imported/mod.rs new file mode 100644 index 00000000..e435d9e3 --- /dev/null +++ b/implementations/wrap-rust/src/templates/imported/mod.rs @@ -0,0 +1,5 @@ +pub mod enum_type; +pub mod env_type; +pub mod module_type; +pub mod object_type; +pub mod mod_rs; diff --git a/implementations/wrap-rust/src/templates/imported/mod_rs.rs b/implementations/wrap-rust/src/templates/imported/mod_rs.rs new file mode 100644 index 00000000..16e6e44f --- /dev/null +++ b/implementations/wrap-rust/src/templates/imported/mod_rs.rs @@ -0,0 +1,29 @@ +lazy_static! { + static ref NAME: String = "imported/mod.rs".to_string(); + static ref SOURCE: String = r#"{{#each importedObjectTypes}} +pub mod {{to_lower type}}; +pub use {{to_lower type}}::*; +{{/each}} +{{#each importedEnumTypes}} +pub mod {{to_lower type}}; +pub use {{to_lower type}}::*; +{{/each}} +{{#each importedModuleTypes}} +pub mod {{to_lower type}}; +pub use {{to_lower type}}::*; +{{/each}} +{{#each importedEnvTypes}} +pub mod {{to_lower type}}; +pub use {{to_lower type}}::*; +{{/each}} +"#.to_string(); +} + +use crate::templates::Template; + +pub fn load() -> Template { + Template { + name: &*NAME, + source: &*SOURCE + } +} diff --git a/implementations/wrap-rust/src/templates/imported/module_type/mod.rs b/implementations/wrap-rust/src/templates/imported/module_type/mod.rs new file mode 100644 index 00000000..1ad2c381 --- /dev/null +++ b/implementations/wrap-rust/src/templates/imported/module_type/mod.rs @@ -0,0 +1 @@ +pub mod mod_rs; diff --git a/implementations/wrap-rust/src/templates/imported/module_type/mod_rs.rs b/implementations/wrap-rust/src/templates/imported/module_type/mod_rs.rs new file mode 100644 index 00000000..a031c85a --- /dev/null +++ b/implementations/wrap-rust/src/templates/imported/module_type/mod_rs.rs @@ -0,0 +1,102 @@ +lazy_static! { + static ref NAME: String = "imported/module_type/mod.rs".to_string(); + static ref SOURCE: String = r#"use serde::{Serialize, Deserialize}; +use polywrap_msgpack_serde::{ + from_slice, + to_vec, + wrappers::polywrap_json::JSONString, + wrappers::polywrap_bigint::BigIntWrapper +}; +use polywrap_wasm_rs::{ + BigInt, + BigNumber, + Map, + JSON, + subinvoke +}; +{{#each (property_deps this)}} +use {{_crate}}::{{detect_keyword (to_upper _type)}}; +{{/each}} + +{{#if (array_has_length methods)}} +{{#each methods}} +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Args{{to_upper name}} { + {{#each arguments}} + {{#with scalar}}{{serde_annotate_if_bytes type}}{{/with}}{{serde_rename_if_case_mismatch name}}pub {{detect_keyword (to_lower name)}}: {{to_rust (to_graphql_type this)}}, + {{/each}} +} +{{#if (is_not_last @index ../methods)}} + +{{/if}} +{{/each}} +{{/if}} + +{{#if isInterface}} +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct {{detect_keyword (to_upper type)}} { + uri: String +} + +impl {{detect_keyword (to_upper type)}} { + pub const INTERFACE_URI: &'static str = "{{uri}}"; + + pub fn new(uri: String) -> {{detect_keyword (to_upper type)}} { + {{detect_keyword (to_upper type)}} { uri } + } + + {{#each methods}} + pub fn {{to_lower name}}(&self, args: &Args{{to_upper name}}) -> Result<{{#with return}}{{to_rust (to_graphql_type this)}}{{/with}}, String> { + let ref uri = self.uri; + let args = to_vec(args).map_err(|e| e.to_string())?; + let result = subinvoke::wrap_subinvoke( + uri.as_str(), + "{{name}}", + args, + )?; + from_slice(result.as_slice()).map_err(|e| e.to_string()) + } + {{#if (is_not_last @index ../methods)}} + + {{/if}} + {{/each}} +} +{{else}} +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct {{detect_keyword (to_upper type)}} {} + +impl {{detect_keyword (to_upper type)}} { + pub const URI: &'static str = "{{uri}}"; + + pub fn new() -> {{detect_keyword (to_upper type)}} { + {{detect_keyword (to_upper type)}} {} + } + + {{#each methods}} + pub fn {{detect_keyword (to_lower name)}}(args: &Args{{to_upper name}}) -> Result<{{#with return}}{{to_rust (to_graphql_type this)}}{{/with}}, String> { + let uri = {{to_upper ../type}}::URI; + let args = to_vec(args).map_err(|e| e.to_string())?; + let result = subinvoke::wrap_subinvoke( + uri, + "{{name}}", + args, + )?; + from_slice(result.as_slice()).map_err(|e| e.to_string()) + } + {{#if (is_not_last @index ../methods)}} + + {{/if}} + {{/each}} +} +{{/if}} +"#.to_string(); +} + +use crate::templates::Template; + +pub fn load() -> Template { + Template { + name: &*NAME, + source: &*SOURCE + } +} diff --git a/implementations/wrap-rust/src/templates/imported/object_type/mod.rs b/implementations/wrap-rust/src/templates/imported/object_type/mod.rs new file mode 100644 index 00000000..217f27f6 --- /dev/null +++ b/implementations/wrap-rust/src/templates/imported/object_type/mod.rs @@ -0,0 +1 @@ +pub mod mod_rs; \ No newline at end of file diff --git a/implementations/wrap-rust/src/templates/imported/object_type/mod_rs.rs b/implementations/wrap-rust/src/templates/imported/object_type/mod_rs.rs new file mode 100644 index 00000000..0dd471f5 --- /dev/null +++ b/implementations/wrap-rust/src/templates/imported/object_type/mod_rs.rs @@ -0,0 +1,46 @@ +lazy_static! { + static ref NAME: String = "imported/object_type/mod.rs".to_string(); + static ref SOURCE: String = r#"use serde::{Serialize, Deserialize}; +use polywrap_msgpack_serde::{ + wrappers::polywrap_json::JSONString, + wrappers::polywrap_bigint::BigIntWrapper +}; +use polywrap_wasm_rs::{ + BigInt, + BigNumber, + Map, + JSON +}; +{{#each (property_deps this)}} +use {{_crate}}::{{detect_keyword (to_upper _type)}}; +{{/each}} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct {{detect_keyword (to_upper type)}} { + {{#each properties}} + {{#with scalar}}{{serde_annotate_if_bytes type}}{{/with}}{{serde_rename_if_case_mismatch name}}pub {{detect_keyword (to_lower name)}}: {{to_rust (to_graphql_type this)}}, + {{/each}} +} + +impl {{detect_keyword (to_upper type)}} { + pub const URI: &'static str = "{{uri}}"; + + pub fn new() -> {{detect_keyword (to_upper type)}} { + {{detect_keyword (to_upper type)}} { + {{#each properties}} + {{detect_keyword (to_lower name)}}: {{to_rust_init (to_graphql_type this)}}, + {{/each}} + } + } +} +"#.to_string(); +} + +use crate::templates::Template; + +pub fn load() -> Template { + Template { + name: &*NAME, + source: &*SOURCE + } +} diff --git a/implementations/wrap-rust/src/templates/interface_type/mod.rs b/implementations/wrap-rust/src/templates/interface_type/mod.rs new file mode 100644 index 00000000..217f27f6 --- /dev/null +++ b/implementations/wrap-rust/src/templates/interface_type/mod.rs @@ -0,0 +1 @@ +pub mod mod_rs; \ No newline at end of file diff --git a/implementations/wrap-rust/src/templates/interface_type/mod_rs.rs b/implementations/wrap-rust/src/templates/interface_type/mod_rs.rs new file mode 100644 index 00000000..a35ee8e1 --- /dev/null +++ b/implementations/wrap-rust/src/templates/interface_type/mod_rs.rs @@ -0,0 +1,36 @@ +lazy_static! { + static ref NAME: String = "interface_type/mod.rs".to_string(); + static ref SOURCE: String = r#"{{#with capabilities}} +{{#with getImplementations}} +{{#if enabled}} +use polywrap_wasm_rs::wrap_get_implementations; +{{/if}} +{{/with}} +{{/with}} + +pub struct {{detect_keyword (to_upper namespace)}} {} + +impl {{detect_keyword (to_upper namespace)}} { + const uri: &'static str = "{{uri}}"; + + {{#with capabilities}} + {{#with getImplementations}} + {{#if enabled}} + pub fn get_implementations() -> Vec { + wrap_get_implementations(Self::uri) + } + {{/if}} + {{/with}} + {{/with}} +} +"#.to_string(); +} + +use crate::templates::Template; + +pub fn load() -> Template { + Template { + name: &*NAME, + source: &*SOURCE + } +} diff --git a/implementations/wrap-rust/src/templates/mod.rs b/implementations/wrap-rust/src/templates/mod.rs new file mode 100644 index 00000000..dfd6a52e --- /dev/null +++ b/implementations/wrap-rust/src/templates/mod.rs @@ -0,0 +1,32 @@ +mod entry_rs; +mod mod_rs; +mod enum_type; +mod env_type; +mod interface_type; +mod module_type; +mod object_type; +mod imported; + +pub struct Template { + pub name: &'static str, + pub source: &'static str, +} + +pub fn load_templates() -> Vec