Skip to content

Commit bd25318

Browse files
authored
Support for v2 of Wasm API (#157)
* Support for v2 of Wasm API * Add panic handling * Missed a Cargo.lock change * Switch to published crate * Update function-runner * Emit compile error when targeting WASI * Use std::string::String instead of String * Add log statement about invoking a named export
1 parent dd2ea43 commit bd25318

File tree

8 files changed

+83
-31
lines changed

8 files changed

+83
-31
lines changed

Cargo.lock

Lines changed: 10 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

example_with_targets/src/main.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::process;
2+
13
use shopify_function::prelude::*;
24
use shopify_function::Result;
35

@@ -17,11 +19,16 @@ mod schema {
1719

1820
#[shopify_function]
1921
fn target_a(_input: schema::target_a::Input) -> Result<schema::FunctionTargetAResult> {
22+
log!("In target_a");
23+
let var = 42;
24+
log!("With var: {var}");
25+
log!("With var: {}", var);
2026
Ok(schema::FunctionTargetAResult { status: Some(200) })
2127
}
2228

2329
#[shopify_function]
2430
fn target_b(input: schema::target_b::Input) -> Result<schema::FunctionTargetBResult> {
31+
log!("In target_b");
2532
Ok(schema::FunctionTargetBResult {
2633
name: Some(format!("new name: \"{}\"", input.id())),
2734
operations: vec![
@@ -33,9 +40,14 @@ fn target_b(input: schema::target_b::Input) -> Result<schema::FunctionTargetBRes
3340
})
3441
}
3542

43+
#[shopify_function]
44+
fn target_panic(_input: schema::target_a::Input) -> Result<schema::FunctionTargetAResult> {
45+
panic!("Something went wrong");
46+
}
47+
3648
fn main() {
37-
eprintln!("Invoke a named import");
38-
std::process::exit(1);
49+
log!("Invoke a named export");
50+
process::abort()
3951
}
4052

4153
#[cfg(test)]

integration_tests/src/lib.rs

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ use std::{
66
sync::LazyLock,
77
};
88

9-
const FUNCTION_RUNNER_VERSION: &str = "9.0.0";
10-
const TRAMPOLINE_VERSION: &str = "1.0.0";
9+
const FUNCTION_RUNNER_VERSION: &str = "9.1.0";
10+
const TRAMPOLINE_VERSION: &str = "2.0.0";
1111

1212
fn workspace_root() -> std::path::PathBuf {
1313
let manifest_dir = env!("CARGO_MANIFEST_DIR");
@@ -21,7 +21,7 @@ fn build_example(name: &str) -> Result<()> {
2121
"build",
2222
"--release",
2323
"--target",
24-
"wasm32-wasip1",
24+
"wasm32-unknown-unknown",
2525
"-p",
2626
name,
2727
])
@@ -124,10 +124,10 @@ fn download_from_github(
124124
pub fn prepare_example(name: &str) -> Result<PathBuf> {
125125
build_example(name)?;
126126
let wasm_path = workspace_root()
127-
.join("target/wasm32-wasip1/release")
127+
.join("target/wasm32-unknown-unknown/release")
128128
.join(format!("{name}.wasm"));
129129
let trampolined_path = workspace_root()
130-
.join("target/wasm32-wasip1/release")
130+
.join("target/wasm32-unknown-unknown/release")
131131
.join(format!("{name}-trampolined.wasm"));
132132
let trampoline_path = TRAMPOLINE_PATH
133133
.as_ref()
@@ -152,7 +152,7 @@ pub fn run_example(
152152
path: PathBuf,
153153
export: &str,
154154
input: serde_json::Value,
155-
) -> Result<serde_json::Value> {
155+
) -> Result<(serde_json::Value, String)> {
156156
let function_runner_path = FUNCTION_RUNNER_PATH
157157
.as_ref()
158158
.map_err(|e| anyhow::anyhow!("Failed to download function runner: {}", e))?;
@@ -191,13 +191,14 @@ pub fn run_example(
191191

192192
let mut output: serde_json::Value = serde_json::from_slice(&output_bytes)?;
193193

194-
if !status.success() {
195-
let logs = output
196-
.get("logs")
197-
.ok_or_else(|| anyhow::anyhow!("No logs"))?
198-
.as_str()
199-
.ok_or_else(|| anyhow::anyhow!("Logs are not a string"))?;
194+
let logs = output
195+
.get("logs")
196+
.ok_or_else(|| anyhow::anyhow!("No logs"))?
197+
.as_str()
198+
.ok_or_else(|| anyhow::anyhow!("Logs are not a string"))?
199+
.to_string();
200200

201+
if !status.success() {
201202
anyhow::bail!(
202203
"Function runner returned non-zero exit code: {}, logs: {}",
203204
status,
@@ -209,5 +210,5 @@ pub fn run_example(
209210
.get_mut("output")
210211
.ok_or_else(|| anyhow::anyhow!("No output"))?
211212
.take();
212-
Ok(output_json)
213+
Ok((output_json, logs))
213214
}

integration_tests/tests/integration_test.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,14 @@ fn test_example_with_targets_target_a() -> Result<()> {
1616
"name": "test",
1717
"country": "CA"
1818
});
19-
let output = run_example(path.clone(), "target_a", input)?;
19+
let (output, logs) = run_example(path.clone(), "target_a", input)?;
2020
assert_eq!(
2121
output,
2222
serde_json::json!({
2323
"status": 200
2424
})
2525
);
26+
assert_eq!(logs, "In target_a\nWith var: 42\nWith var: 42\n");
2627
Ok(())
2728
}
2829

@@ -35,7 +36,7 @@ fn test_example_with_targets_target_b() -> Result<()> {
3536
"id": "gid://shopify/Order/1234567890",
3637
"targetAResult": 200
3738
});
38-
let output = run_example(path.clone(), "target_b", input)?;
39+
let (output, logs) = run_example(path.clone(), "target_b", input)?;
3940
assert_eq!(
4041
output,
4142
serde_json::json!({
@@ -54,5 +55,27 @@ fn test_example_with_targets_target_b() -> Result<()> {
5455
]
5556
})
5657
);
58+
assert_eq!(logs, "In target_b\n");
59+
Ok(())
60+
}
61+
62+
#[test]
63+
fn test_example_with_panic() -> Result<()> {
64+
let path = EXAMPLE_WITH_TARGETS_RESULT
65+
.as_ref()
66+
.map_err(|e| anyhow::anyhow!("Failed to prepare example: {}", e))?;
67+
let input = serde_json::json!({
68+
"id": "gid://shopify/Order/1234567890",
69+
"targetAResult": "foo"
70+
});
71+
let err = run_example(path.clone(), "target_panic", input)
72+
.unwrap_err()
73+
.to_string();
74+
let expected_err =
75+
"Function runner returned non-zero exit code: exit status: 1, logs: panicked at example_with_targets/src/main.rs:45:5:\nSomething went wrong\nerror while executing at wasm backtrace:";
76+
assert!(
77+
err.contains(expected_err),
78+
"Expected error message to contain:\n`{expected_err}`\nbut was:\n`{err}`"
79+
);
5780
Ok(())
5881
}

rust-toolchain.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
[toolchain]
22
channel = "stable"
33
components = ["rustfmt", "clippy"]
4-
targets = ["wasm32-wasip1"]
4+
targets = ["wasm32-unknown-unknown"]

shopify_function/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ description = "Crate to write Shopify Functions in Rust."
99
[dependencies]
1010
serde_json = "1.0"
1111
shopify_function_macro = { version = "1.1.1", path = "../shopify_function_macro" }
12-
shopify_function_wasm_api = "0.2.0"
12+
shopify_function_wasm_api = "0.3.0"
1313

1414
# Use the `small` feature of ryu (transitive dependency through serde_json)
1515
# to shave off ~9kb of the Wasm binary size.

shopify_function/src/lib.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,15 @@
2020
//! }
2121
//! ```
2222
23+
#[cfg(all(target_arch = "wasm32", target_os = "wasi", target_env = "p1"))]
24+
compile_error!("Compiling to wasm32-wasip1 is unsupported, change your target to wasm32-unknown-unknown instead");
25+
2326
pub use shopify_function_macro::{shopify_function, typegen, Deserialize};
2427

2528
pub mod scalars;
2629

2730
pub mod prelude {
31+
pub use crate::log;
2832
pub use crate::scalars::*;
2933
pub use shopify_function_macro::{shopify_function, typegen, Deserialize};
3034
}
@@ -45,6 +49,18 @@ where
4549
f(input)
4650
}
4751

52+
#[macro_export]
53+
macro_rules! log {
54+
($($args:tt)*) => {
55+
{
56+
use std::fmt::Write;
57+
let mut buf = std::string::String::new();
58+
writeln!(&mut buf, $($args)*).unwrap();
59+
$crate::wasm_api::Context.log(&buf);
60+
}
61+
};
62+
}
63+
4864
pub use shopify_function_wasm_api as wasm_api;
4965

5066
#[cfg(test)]

shopify_function_macro/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,12 @@ pub fn shopify_function(
9999
quote! {
100100
#[export_name = #function_name_string]
101101
pub extern "C" fn #export_function_name() {
102+
shopify_function::wasm_api::init_panic_handler();
102103
let mut context = shopify_function::wasm_api::Context::new();
103104
let root_value = context.input_get().expect("Failed to get input");
104105
let mut input: #input_type = shopify_function::wasm_api::Deserialize::deserialize(&root_value).expect("Failed to deserialize input");
105106
let result = #function_name(input).expect("Failed to call function");
106107
shopify_function::wasm_api::Serialize::serialize(&result, &mut context).expect("Failed to serialize output");
107-
context.finalize_output().expect("Failed to finalize output");
108108
}
109109

110110
#ast
@@ -350,7 +350,7 @@ impl CodeGenerator for ShopifyFunctionCodeGenerator {
350350
#description
351351
pub fn #field_name_ident(&self) -> #field_type {
352352
static INTERNED_FIELD_NAME: shopify_function::wasm_api::CachedInternedStringId = shopify_function::wasm_api::CachedInternedStringId::new(#field_name_lit_str, );
353-
let interned_string_id = INTERNED_FIELD_NAME.load_from_value(&self.__wasm_value);
353+
let interned_string_id = INTERNED_FIELD_NAME.load();
354354

355355
let value = self.#field_name_ident.get_or_init(|| {
356356
let value = self.__wasm_value.get_interned_obj_prop(interned_string_id);

0 commit comments

Comments
 (0)