From ba01fe1024f583d386a0854b23904c1ee0a39129 Mon Sep 17 00:00:00 2001 From: Jeff Charles Date: Fri, 15 Aug 2025 14:37:13 -0400 Subject: [PATCH 1/8] Support for v2 of Wasm API --- Cargo.lock | 8 +---- example_with_targets/src/main.rs | 10 ++++-- integration_tests/src/lib.rs | 38 ++++++++++----------- integration_tests/tests/integration_test.rs | 6 ++-- rust-toolchain.toml | 2 +- shopify_function/Cargo.toml | 3 +- shopify_function/src/lib.rs | 13 +++++++ shopify_function_macro/src/lib.rs | 3 +- 8 files changed, 49 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 63aac63..0661693 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1246,9 +1246,7 @@ dependencies = [ [[package]] name = "shopify_function_provider" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1eb4e60eb2f8c6e02b1f1e7634ef91738b1104b5bc2fa30458d10cd00917dbbf" +version = "2.0.0" dependencies = [ "bumpalo", "rmp", @@ -1258,8 +1256,6 @@ dependencies = [ [[package]] name = "shopify_function_wasm_api" version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7952650d134210fcd92b56a6cdc666a4ed5d0c56a8a21bf9113b62dee52d27" dependencies = [ "rmp-serde", "seq-macro", @@ -1272,8 +1268,6 @@ dependencies = [ [[package]] name = "shopify_function_wasm_api_core" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4842376f01b3abe6c78596085f29ea0d1b605873d2855f812738072c38e32c34" dependencies = [ "strum", ] diff --git a/example_with_targets/src/main.rs b/example_with_targets/src/main.rs index fa475d5..a767aff 100644 --- a/example_with_targets/src/main.rs +++ b/example_with_targets/src/main.rs @@ -1,3 +1,5 @@ +use std::process; + use shopify_function::prelude::*; use shopify_function::Result; @@ -16,11 +18,16 @@ mod schema { #[shopify_function] fn target_a(_input: schema::target_a::Input) -> Result { + log!("In target_a"); + let var = 42; + log!("With var: {var}"); + log!("With var: {}", var); Ok(schema::FunctionTargetAResult { status: Some(200) }) } #[shopify_function] fn target_b(input: schema::target_b::Input) -> Result { + log!("In target_b"); Ok(schema::FunctionTargetBResult { name: Some(format!("new name: \"{}\"", input.id())), operations: vec![ @@ -33,8 +40,7 @@ fn target_b(input: schema::target_b::Input) -> Result std::path::PathBuf { let manifest_dir = env!("CARGO_MANIFEST_DIR"); @@ -21,7 +21,7 @@ fn build_example(name: &str) -> Result<()> { "build", "--release", "--target", - "wasm32-wasip1", + "wasm32-unknown-unknown", "-p", name, ]) @@ -124,10 +124,10 @@ fn download_from_github( pub fn prepare_example(name: &str) -> Result { build_example(name)?; let wasm_path = workspace_root() - .join("target/wasm32-wasip1/release") + .join("target/wasm32-unknown-unknown/release") .join(format!("{name}.wasm")); let trampolined_path = workspace_root() - .join("target/wasm32-wasip1/release") + .join("target/wasm32-unknown-unknown/release") .join(format!("{name}-trampolined.wasm")); let trampoline_path = TRAMPOLINE_PATH .as_ref() @@ -152,7 +152,7 @@ pub fn run_example( path: PathBuf, export: &str, input: serde_json::Value, -) -> Result { +) -> Result<(serde_json::Value, String)> { let function_runner_path = FUNCTION_RUNNER_PATH .as_ref() .map_err(|e| anyhow::anyhow!("Failed to download function runner: {}", e))?; @@ -189,15 +189,16 @@ pub fn run_example( let mut output_bytes = Vec::new(); output.read_to_end(&mut output_bytes)?; - let output: serde_json::Value = serde_json::from_slice(&output_bytes)?; + let mut output: serde_json::Value = serde_json::from_slice(&output_bytes)?; - if !status.success() { - let logs = output - .get("logs") - .ok_or_else(|| anyhow::anyhow!("No logs"))? - .as_str() - .ok_or_else(|| anyhow::anyhow!("Logs are not a string"))?; + let logs = output + .get("logs") + .ok_or_else(|| anyhow::anyhow!("No logs"))? + .as_str() + .ok_or_else(|| anyhow::anyhow!("Logs are not a string"))? + .to_string(); + if !status.success() { anyhow::bail!( "Function runner returned non-zero exit code: {}, logs: {}", status, @@ -205,10 +206,9 @@ pub fn run_example( ); } - let output_json_str = output - .get("output") - .and_then(|o| o.get("humanized").and_then(|h| h.as_str())) - .ok_or_else(|| anyhow::anyhow!("No output"))?; - let output_json: serde_json::Value = serde_json::from_str(output_json_str)?; - Ok(output_json) + let output_json = output + .get_mut("output") + .ok_or_else(|| anyhow::anyhow!("No output"))? + .take(); + Ok((output_json, logs)) } diff --git a/integration_tests/tests/integration_test.rs b/integration_tests/tests/integration_test.rs index 9d812b9..83a56c9 100644 --- a/integration_tests/tests/integration_test.rs +++ b/integration_tests/tests/integration_test.rs @@ -16,13 +16,14 @@ fn test_example_with_targets_target_a() -> Result<()> { "name": "test", "country": "CA" }); - let output = run_example(path.clone(), "target_a", input)?; + let (output, logs) = run_example(path.clone(), "target_a", input)?; assert_eq!( output, serde_json::json!({ "status": 200 }) ); + assert_eq!(logs, "In target_a\nWith var: 42\nWith var: 42\n"); Ok(()) } @@ -35,7 +36,7 @@ fn test_example_with_targets_target_b() -> Result<()> { "id": "gid://shopify/Order/1234567890", "targetAResult": 200 }); - let output = run_example(path.clone(), "target_b", input)?; + let (output, logs) = run_example(path.clone(), "target_b", input)?; assert_eq!( output, serde_json::json!({ @@ -54,5 +55,6 @@ fn test_example_with_targets_target_b() -> Result<()> { ] }) ); + assert_eq!(logs, "In target_b\n"); Ok(()) } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 1a8ec07..c38c8a8 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] channel = "stable" components = ["rustfmt", "clippy"] -targets = ["wasm32-wasip1"] +targets = ["wasm32-unknown-unknown"] diff --git a/shopify_function/Cargo.toml b/shopify_function/Cargo.toml index 40e6a8c..9b39eca 100644 --- a/shopify_function/Cargo.toml +++ b/shopify_function/Cargo.toml @@ -9,7 +9,8 @@ description = "Crate to write Shopify Functions in Rust." [dependencies] serde_json = "1.0" shopify_function_macro = { version = "1.1.1", path = "../shopify_function_macro" } -shopify_function_wasm_api = "0.2.0" +# shopify_function_wasm_api = "0.2.0" +shopify_function_wasm_api = { path = "/Users/jeffcharles/src/github.com/Shopify/shopify-function-wasm-api/api" } # Use the `small` feature of ryu (transitive dependency through serde_json) # to shave off ~9kb of the Wasm binary size. diff --git a/shopify_function/src/lib.rs b/shopify_function/src/lib.rs index 345022e..28039aa 100644 --- a/shopify_function/src/lib.rs +++ b/shopify_function/src/lib.rs @@ -25,6 +25,7 @@ pub use shopify_function_macro::{shopify_function, typegen, Deserialize}; pub mod scalars; pub mod prelude { + pub use crate::log; pub use crate::scalars::*; pub use shopify_function_macro::{shopify_function, typegen, Deserialize}; } @@ -45,6 +46,18 @@ where f(input) } +#[macro_export] +macro_rules! log { + ($($args:tt)*) => { + { + use std::fmt::Write; + let mut buf = String::new(); + writeln!(&mut buf, $($args)*).unwrap(); + $crate::wasm_api::Context.log(&buf); + } + }; +} + pub use shopify_function_wasm_api as wasm_api; #[cfg(test)] diff --git a/shopify_function_macro/src/lib.rs b/shopify_function_macro/src/lib.rs index c382691..ab9fd74 100644 --- a/shopify_function_macro/src/lib.rs +++ b/shopify_function_macro/src/lib.rs @@ -104,7 +104,6 @@ pub fn shopify_function( let mut input: #input_type = shopify_function::wasm_api::Deserialize::deserialize(&root_value).expect("Failed to deserialize input"); let result = #function_name(input).expect("Failed to call function"); shopify_function::wasm_api::Serialize::serialize(&result, &mut context).expect("Failed to serialize output"); - context.finalize_output().expect("Failed to finalize output"); } #ast @@ -350,7 +349,7 @@ impl CodeGenerator for ShopifyFunctionCodeGenerator { #description pub fn #field_name_ident(&self) -> #field_type { static INTERNED_FIELD_NAME: shopify_function::wasm_api::CachedInternedStringId = shopify_function::wasm_api::CachedInternedStringId::new(#field_name_lit_str, ); - let interned_string_id = INTERNED_FIELD_NAME.load_from_value(&self.__wasm_value); + let interned_string_id = INTERNED_FIELD_NAME.load(); let value = self.#field_name_ident.get_or_init(|| { let value = self.__wasm_value.get_interned_obj_prop(interned_string_id); From a5511f9e4ece639a82940fe4af363691b0ed75bd Mon Sep 17 00:00:00 2001 From: Jeff Charles Date: Tue, 2 Sep 2025 17:36:03 -0400 Subject: [PATCH 2/8] Add panic handling --- Cargo.lock | 8 ++++---- example_with_targets/src/main.rs | 5 +++++ integration_tests/tests/integration_test.rs | 21 +++++++++++++++++++++ shopify_function_macro/src/lib.rs | 1 + 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0661693..f397d1c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -136,9 +136,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "byteorder" @@ -1305,9 +1305,9 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "strum" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ "strum_macros", ] diff --git a/example_with_targets/src/main.rs b/example_with_targets/src/main.rs index a767aff..c15bbad 100644 --- a/example_with_targets/src/main.rs +++ b/example_with_targets/src/main.rs @@ -39,6 +39,11 @@ fn target_b(input: schema::target_b::Input) -> Result Result { + panic!("Something went wrong"); +} + fn main() { process::abort() } diff --git a/integration_tests/tests/integration_test.rs b/integration_tests/tests/integration_test.rs index 83a56c9..d637d25 100644 --- a/integration_tests/tests/integration_test.rs +++ b/integration_tests/tests/integration_test.rs @@ -58,3 +58,24 @@ fn test_example_with_targets_target_b() -> Result<()> { assert_eq!(logs, "In target_b\n"); Ok(()) } + +#[test] +fn test_example_with_panic() -> Result<()> { + let path = EXAMPLE_WITH_TARGETS_RESULT + .as_ref() + .map_err(|e| anyhow::anyhow!("Failed to prepare example: {}", e))?; + let input = serde_json::json!({ + "id": "gid://shopify/Order/1234567890", + "targetAResult": "foo" + }); + let err = run_example(path.clone(), "target_panic", input) + .unwrap_err() + .to_string(); + let expected_err = + "Function runner returned non-zero exit code: exit status: 1, logs: panicked at example_with_targets/src/main.rs:44:5:\nSomething went wrong\nerror while executing at wasm backtrace:"; + assert!( + err.contains(expected_err), + "Expected error message to contain:\n`{expected_err}`\nbut was:\n`{err}`" + ); + Ok(()) +} diff --git a/shopify_function_macro/src/lib.rs b/shopify_function_macro/src/lib.rs index ab9fd74..e8e440f 100644 --- a/shopify_function_macro/src/lib.rs +++ b/shopify_function_macro/src/lib.rs @@ -99,6 +99,7 @@ pub fn shopify_function( quote! { #[export_name = #function_name_string] pub extern "C" fn #export_function_name() { + shopify_function::wasm_api::init_panic_handler(); let mut context = shopify_function::wasm_api::Context::new(); let root_value = context.input_get().expect("Failed to get input"); let mut input: #input_type = shopify_function::wasm_api::Deserialize::deserialize(&root_value).expect("Failed to deserialize input"); From 51e60ea8fae4639a35fb47885d078beed5e4f317 Mon Sep 17 00:00:00 2001 From: Jeff Charles Date: Mon, 8 Sep 2025 16:19:30 -0400 Subject: [PATCH 3/8] Missed a Cargo.lock change --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index f397d1c..b4b8f1f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1267,7 +1267,7 @@ dependencies = [ [[package]] name = "shopify_function_wasm_api_core" -version = "0.1.0" +version = "0.2.0" dependencies = [ "strum", ] From 7b81e0952a97b2e8de8cfdc827ece0bc94a2698f Mon Sep 17 00:00:00 2001 From: Jeff Charles Date: Fri, 12 Sep 2025 15:21:10 -0400 Subject: [PATCH 4/8] Switch to published crate --- Cargo.lock | 8 +++++++- shopify_function/Cargo.toml | 3 +-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 59e5ea1..db9a33d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1247,6 +1247,8 @@ dependencies = [ [[package]] name = "shopify_function_provider" version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f92a23bc3d343786dd32049e8e2453a058f8e37a4bf953504ebe52189c1b1ebf" dependencies = [ "bumpalo", "rmp", @@ -1255,7 +1257,9 @@ dependencies = [ [[package]] name = "shopify_function_wasm_api" -version = "0.2.0" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "132521368234c994edd82c1ccec033dd8128ec82d21e3dddca4ceb16d94bb2cc" dependencies = [ "rmp-serde", "seq-macro", @@ -1268,6 +1272,8 @@ dependencies = [ [[package]] name = "shopify_function_wasm_api_core" version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7367c758d2ec5644f78f662e3748564dd71c50462ad7eec679f0af139984948" dependencies = [ "strum", ] diff --git a/shopify_function/Cargo.toml b/shopify_function/Cargo.toml index 9b39eca..b11c6b7 100644 --- a/shopify_function/Cargo.toml +++ b/shopify_function/Cargo.toml @@ -9,8 +9,7 @@ description = "Crate to write Shopify Functions in Rust." [dependencies] serde_json = "1.0" shopify_function_macro = { version = "1.1.1", path = "../shopify_function_macro" } -# shopify_function_wasm_api = "0.2.0" -shopify_function_wasm_api = { path = "/Users/jeffcharles/src/github.com/Shopify/shopify-function-wasm-api/api" } +shopify_function_wasm_api = "0.3.0" # Use the `small` feature of ryu (transitive dependency through serde_json) # to shave off ~9kb of the Wasm binary size. From 4839a568e9cb493f2b587f2482dc2650b3a860c9 Mon Sep 17 00:00:00 2001 From: Jeff Charles Date: Fri, 19 Sep 2025 14:52:55 -0400 Subject: [PATCH 5/8] Update function-runner --- integration_tests/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration_tests/src/lib.rs b/integration_tests/src/lib.rs index 78a32b2..d3e09a8 100644 --- a/integration_tests/src/lib.rs +++ b/integration_tests/src/lib.rs @@ -6,7 +6,7 @@ use std::{ sync::LazyLock, }; -const FUNCTION_RUNNER_VERSION: &str = "9.0.0"; +const FUNCTION_RUNNER_VERSION: &str = "9.1.0"; const TRAMPOLINE_VERSION: &str = "2.0.0"; fn workspace_root() -> std::path::PathBuf { From 9e8f03ee80071d01f9404bf3f002644759a41267 Mon Sep 17 00:00:00 2001 From: Jeff Charles Date: Fri, 19 Sep 2025 15:19:20 -0400 Subject: [PATCH 6/8] Emit compile error when targeting WASI --- shopify_function/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/shopify_function/src/lib.rs b/shopify_function/src/lib.rs index 28039aa..5480451 100644 --- a/shopify_function/src/lib.rs +++ b/shopify_function/src/lib.rs @@ -20,6 +20,9 @@ //! } //! ``` +#[cfg(all(target_arch = "wasm32", target_os = "wasi", target_env = "p1"))] +compile_error!("Compiling to wasm32-wasip1 is unsupported, change your target to wasm32-unknown-unknown instead"); + pub use shopify_function_macro::{shopify_function, typegen, Deserialize}; pub mod scalars; From 0d77075865a1d4def0478a3088b8885a8beddbc1 Mon Sep 17 00:00:00 2001 From: Jeff Charles Date: Thu, 25 Sep 2025 14:03:10 -0400 Subject: [PATCH 7/8] Use std::string::String instead of String --- shopify_function/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shopify_function/src/lib.rs b/shopify_function/src/lib.rs index 5480451..e1d190d 100644 --- a/shopify_function/src/lib.rs +++ b/shopify_function/src/lib.rs @@ -54,7 +54,7 @@ macro_rules! log { ($($args:tt)*) => { { use std::fmt::Write; - let mut buf = String::new(); + let mut buf = std::string::String::new(); writeln!(&mut buf, $($args)*).unwrap(); $crate::wasm_api::Context.log(&buf); } From a63bb9251961c2942d08cc8280be93452d0deefc Mon Sep 17 00:00:00 2001 From: Jeff Charles Date: Thu, 25 Sep 2025 14:41:24 -0400 Subject: [PATCH 8/8] Add log statement about invoking a named export --- example_with_targets/src/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/example_with_targets/src/main.rs b/example_with_targets/src/main.rs index c1a8feb..f8e4aa4 100644 --- a/example_with_targets/src/main.rs +++ b/example_with_targets/src/main.rs @@ -46,6 +46,7 @@ fn target_panic(_input: schema::target_a::Input) -> Result