diff --git a/.vscode/tasks.json b/.vscode/tasks.json index ca66246..4177aca 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -184,6 +184,24 @@ "kind": "build", "isDefault": true } + }, + { + "label": "rust format", + "type": "process", + "command": "cargo", + "args": ["fmt"], + "options": { + "cwd": "${workspaceRoot}/native/wasmex" + }, + "problemMatcher": [ + "$mixCompileWarning", + "$mixCompileError" + ], + "group": { + "kind": "build", + "isDefault": true + } } + ] } \ No newline at end of file diff --git a/native/wasmex/Cargo.lock b/native/wasmex/Cargo.lock index 68f28b9..0815ecf 100644 --- a/native/wasmex/Cargo.lock +++ b/native/wasmex/Cargo.lock @@ -233,6 +233,15 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -1830,6 +1839,12 @@ version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + [[package]] name = "unicode-width" version = "0.2.0" @@ -2002,6 +2017,7 @@ dependencies = [ name = "wasmex" version = "0.9.2" dependencies = [ + "convert_case", "once_cell", "rand", "rustler", diff --git a/native/wasmex/Cargo.toml b/native/wasmex/Cargo.toml index f3a6063..f529207 100644 --- a/native/wasmex/Cargo.toml +++ b/native/wasmex/Cargo.toml @@ -25,3 +25,4 @@ wasi-common = "26.0.1" wiggle = "26.0.1" wat = "1.220.0" wit-parser = "0.221.2" +convert_case = "0.6.0" diff --git a/native/wasmex/src/component_instance.rs b/native/wasmex/src/component_instance.rs index 36e0429..0386fd7 100644 --- a/native/wasmex/src/component_instance.rs +++ b/native/wasmex/src/component_instance.rs @@ -5,6 +5,8 @@ use crate::component::ComponentInstanceResource; use crate::store::ComponentStoreData; use crate::store::ComponentStoreResource; +use convert_case::{Case, Casing}; + use rustler::types::atom::nil; use rustler::types::tuple; use rustler::types::tuple::make_tuple; @@ -63,7 +65,9 @@ pub fn component_call_function<'a>( let _ = function.post_return(&mut *component_store); Ok(encode_result(env, result)) } - Err(err) => Ok(env.error_tuple(format!("Error executing function: {err}"))), + Err(err) => Err(rustler::Error::Term(Box::new(format!( + "Error executing function: {err}" + )))), } } @@ -150,13 +154,13 @@ fn term_to_val(param_term: &Term, param_type: &Type) -> Result { fn term_to_field_name(key_term: &Term) -> String { match key_term.get_type() { - TermType::Atom => key_term.atom_to_string().unwrap(), - _ => key_term.decode::().unwrap(), + TermType::Atom => key_term.atom_to_string().unwrap().to_case(Case::Kebab), + _ => key_term.decode::().unwrap().to_case(Case::Kebab), } } fn field_name_to_term<'a>(env: &rustler::Env<'a>, field_name: &str) -> Term<'a> { - rustler::serde::atoms::str_to_term(env, field_name).unwrap() + rustler::serde::atoms::str_to_term(env, &field_name.to_case(Case::Snake)).unwrap() } fn encode_result(env: rustler::Env, vals: Vec) -> Term { diff --git a/test/component_fixtures/hello_world/build.sh b/test/component_fixtures/hello_world/build.sh new file mode 100755 index 0000000..015c168 --- /dev/null +++ b/test/component_fixtures/hello_world/build.sh @@ -0,0 +1 @@ +npx jco componentize -w hello-world.wit -o hello_world.wasm hello-world.js \ No newline at end of file diff --git a/test/component_fixtures/hello_world/hello-world.js b/test/component_fixtures/hello_world/hello-world.js index 89dff04..074517a 100644 --- a/test/component_fixtures/hello_world/hello-world.js +++ b/test/component_fixtures/hello_world/hello-world.js @@ -12,4 +12,8 @@ export function multiGreet(who, times) { export function greetMany(people) { return people.map((person) => `Hello, ${person}!`); +} + +export function echoKebab(kebabRecord) { + return kebabRecord; } \ No newline at end of file diff --git a/test/component_fixtures/hello_world/hello-world.wit b/test/component_fixtures/hello_world/hello-world.wit index 6f1f853..266317a 100644 --- a/test/component_fixtures/hello_world/hello-world.wit +++ b/test/component_fixtures/hello_world/hello-world.wit @@ -1,7 +1,11 @@ package local:hello-world; world hello-world { + record kebab-record { + kebab-field: string + } export greet: func(who: string) -> string; export multi-greet: func(who: string, times: u16) -> list; export greet-many: func(people: list) -> list; + export echo-kebab: func(kebab-record: kebab-record) -> kebab-record; } \ No newline at end of file diff --git a/test/component_fixtures/hello_world/hello_world.wasm b/test/component_fixtures/hello_world/hello_world.wasm index 7a78d82..5742c85 100644 Binary files a/test/component_fixtures/hello_world/hello_world.wasm and b/test/component_fixtures/hello_world/hello_world.wasm differ diff --git a/test/components/component_types_test.exs b/test/components/component_types_test.exs index ee06a78..e88f39b 100644 --- a/test/components/component_types_test.exs +++ b/test/components/component_types_test.exs @@ -1,6 +1,8 @@ defmodule Wasm.Components.ComponentTypesTest do use ExUnit.Case, async: true + alias Wasmex.Wasi.WasiP2Options + setup do {:ok, store} = Wasmex.Components.Store.new() component_bytes = File.read!("test/component_fixtures/component_types/component_types.wasm") @@ -49,6 +51,18 @@ defmodule Wasm.Components.ComponentTypesTest do ]) end + test "record with kebab-field" do + {:ok, store} = Wasmex.Components.Store.new_wasi(%WasiP2Options{}) + component_bytes = File.read!("test/component_fixtures/hello_world/hello_world.wasm") + {:ok, component} = Wasmex.Components.Component.new(store, component_bytes) + {:ok, instance} = Wasmex.Components.Instance.new(store, component) + + assert {:ok, %{kebab_field: "foo"}} = + Wasmex.Components.Instance.call_function(instance, "echo-kebab", [ + %{kebab_field: "foo"} + ]) + end + test "lists", %{instance: instance} do assert {:ok, [1, 2, 3]} = Wasmex.Components.Instance.call_function(instance, "id-list", [[1, 2, 3]])