From 6cb8f13fbc85b20a45cc5ac1787578357a2873a2 Mon Sep 17 00:00:00 2001
From: Pat Hickey
Date: Tue, 1 Jul 2025 11:45:22 -0700
Subject: [PATCH 1/2] create a new component-init crate with an attr macro
wraps up wit-bindgen pretty trivially
---
Cargo.lock | 17 +++++++++
Cargo.toml | 14 ++++++-
guest-rust/Cargo.toml | 8 ++++
guest-rust/macro/Cargo.toml | 11 ++++++
guest-rust/macro/src/lib.rs | 45 +++++++++++++++++++++++
guest-rust/src/lib.rs | 6 +++
test-programs/Cargo.toml | 1 +
test-programs/src/bin/{test.rs => raw.rs} | 0
test-programs/src/bin/using_macro.rs | 14 +++++++
wasmtime/tests/rust.rs | 28 +++++++++-----
10 files changed, 134 insertions(+), 10 deletions(-)
create mode 100644 guest-rust/Cargo.toml
create mode 100644 guest-rust/macro/Cargo.toml
create mode 100644 guest-rust/macro/src/lib.rs
create mode 100644 guest-rust/src/lib.rs
rename test-programs/src/bin/{test.rs => raw.rs} (100%)
create mode 100644 test-programs/src/bin/using_macro.rs
diff --git a/Cargo.lock b/Cargo.lock
index 6ebb638..873d928 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -353,6 +353,14 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
+[[package]]
+name = "component-init"
+version = "0.1.0"
+dependencies = [
+ "component-init-macro",
+ "wit-bindgen",
+]
+
[[package]]
name = "component-init-cli"
version = "0.1.0"
@@ -363,6 +371,14 @@ dependencies = [
"tokio",
]
+[[package]]
+name = "component-init-macro"
+version = "0.1.0"
+dependencies = [
+ "quote",
+ "syn",
+]
+
[[package]]
name = "component-init-transform"
version = "0.1.0"
@@ -1740,6 +1756,7 @@ dependencies = [
name = "test-programs"
version = "0.1.0"
dependencies = [
+ "component-init",
"wit-bindgen",
]
diff --git a/Cargo.toml b/Cargo.toml
index 0f0e211..f9f4adf 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,5 +1,13 @@
[workspace]
-members = ["cli", "transform", "wasmtime", "test-programs", "test-programs/artifacts"]
+members = [
+ "cli",
+ "guest-rust",
+ "guest-rust/macro",
+ "test-programs",
+ "test-programs/artifacts",
+ "transform",
+ "wasmtime",
+]
resolver = "3"
[workspace.package]
@@ -12,9 +20,13 @@ repository = "https://github.com/dicej/component-init"
anyhow = "1.0.89"
async-trait = "0.1.83"
clap = { version = "4", features = ["derive"] }
+component-init = { version = "0.1.0", path = "./guest-rust" }
+component-init-macro = { version = "0.1.0", path = "./guest-rust/macro" }
component-init-transform = { version = "0.1.0", path = "./transform" }
component-init-wasmtime = { version = "0.1.0", path = "./wasmtime" }
futures = "0.3.31"
+quote = "1.0"
+syn = "2.0"
test-programs = { path = "./test-programs" }
test-programs-artifacts = { path = "./test-programs/artifacts" }
tokio = { version = "1", features = ["full"] }
diff --git a/guest-rust/Cargo.toml b/guest-rust/Cargo.toml
new file mode 100644
index 0000000..4ad9cc9
--- /dev/null
+++ b/guest-rust/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "component-init"
+version.workspace = true
+edition.workspace = true
+
+[dependencies]
+component-init-macro.workspace = true
+wit-bindgen.workspace = true
diff --git a/guest-rust/macro/Cargo.toml b/guest-rust/macro/Cargo.toml
new file mode 100644
index 0000000..b856c16
--- /dev/null
+++ b/guest-rust/macro/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "component-init-macro"
+version.workspace = true
+edition.workspace = true
+
+[lib]
+proc-macro = true
+
+[dependencies]
+syn = { workspace = true, features = ["full"] }
+quote.workspace = true
diff --git a/guest-rust/macro/src/lib.rs b/guest-rust/macro/src/lib.rs
new file mode 100644
index 0000000..b578473
--- /dev/null
+++ b/guest-rust/macro/src/lib.rs
@@ -0,0 +1,45 @@
+use proc_macro::TokenStream;
+use quote::{quote, quote_spanned};
+use syn::{parse_macro_input, spanned::Spanned, ItemFn};
+
+#[proc_macro_attribute]
+pub fn init(_attr: TokenStream, item: TokenStream) -> TokenStream {
+ let input = parse_macro_input!(item as ItemFn);
+ if input.sig.asyncness.is_some() {
+ return quote_spanned! { input.sig.fn_token.span()=>
+ compile_error!("fn cannot be async");
+ }
+ .into();
+ }
+ if !input.sig.inputs.is_empty() {
+ return quote_spanned! { input.sig.inputs.span()=>
+ compile_error!("function cannot have arguments");
+ }
+ .into();
+ }
+ let callee = &input.sig.ident;
+
+ quote! {
+ #input
+
+ mod __component_init {
+ component_init::__bindgen::generate!({
+ inline: r"
+ package this:wit;
+ world w {
+ export component-init: func();
+ }
+ ",
+ runtime_path: "component_init::__bindgen::rt",
+ });
+ struct __S;
+ impl Guest for __S {
+ fn component_init() {
+ super::#callee()
+ }
+ }
+ export!(__S);
+ }
+ }
+ .into()
+}
diff --git a/guest-rust/src/lib.rs b/guest-rust/src/lib.rs
new file mode 100644
index 0000000..2bf8fc7
--- /dev/null
+++ b/guest-rust/src/lib.rs
@@ -0,0 +1,6 @@
+pub use component_init_macro::init;
+
+#[doc(hidden)]
+pub mod __bindgen {
+ pub use wit_bindgen::*;
+}
diff --git a/test-programs/Cargo.toml b/test-programs/Cargo.toml
index a106af2..2373369 100644
--- a/test-programs/Cargo.toml
+++ b/test-programs/Cargo.toml
@@ -5,4 +5,5 @@ edition.workspace = true
publish = false
[dependencies]
+component-init.workspace = true
wit-bindgen.workspace = true
diff --git a/test-programs/src/bin/test.rs b/test-programs/src/bin/raw.rs
similarity index 100%
rename from test-programs/src/bin/test.rs
rename to test-programs/src/bin/raw.rs
diff --git a/test-programs/src/bin/using_macro.rs b/test-programs/src/bin/using_macro.rs
new file mode 100644
index 0000000..880e53c
--- /dev/null
+++ b/test-programs/src/bin/using_macro.rs
@@ -0,0 +1,14 @@
+use std::sync::atomic::{AtomicBool, Ordering};
+
+static IS_INITIALIZED: AtomicBool = AtomicBool::new(false);
+
+#[component_init::init]
+fn init() {
+ let before = IS_INITIALIZED.swap(true, Ordering::Relaxed);
+ assert!(!before, "component should only be initialized once");
+}
+
+fn main() {
+ let initialized = IS_INITIALIZED.load(Ordering::Relaxed);
+ assert!(initialized, "component was not initialized")
+}
diff --git a/wasmtime/tests/rust.rs b/wasmtime/tests/rust.rs
index 46016d4..d734359 100644
--- a/wasmtime/tests/rust.rs
+++ b/wasmtime/tests/rust.rs
@@ -1,5 +1,5 @@
use anyhow::{Context, Result, anyhow};
-use test_programs_artifacts::TEST;
+use test_programs_artifacts::{RAW, USING_MACRO};
async fn execute(component: &[u8]) -> Result<()> {
use wasmtime::{
@@ -66,14 +66,9 @@ async fn execute(component: &[u8]) -> Result<()> {
Ok(())
}
-#[tokio::test]
-async fn init_rust() -> Result<()> {
- println!("test component: {TEST:?}");
-
- let component = std::fs::read(TEST)?;
-
+async fn inits_properly(component: &[u8]) -> Result<()> {
// Without initialization, run will trap.
- let err = execute(&component)
+ let err = execute(component)
.await
.err()
.context("uninitialized run should trap")?;
@@ -82,11 +77,26 @@ async fn init_rust() -> Result<()> {
"should die with an unreachable trap, got: {err:?}"
);
- let initialized_component = component_init_wasmtime::initialize(&component).await?;
+ let initialized_component = component_init_wasmtime::initialize(component).await?;
+ // After initialization, will not trap.
execute(&initialized_component)
.await
.context("execute initialized component")?;
Ok(())
}
+
+#[tokio::test]
+async fn raw() -> Result<()> {
+ println!("test component: {RAW:?}");
+ let component = std::fs::read(RAW)?;
+ inits_properly(&component).await
+}
+
+#[tokio::test]
+async fn using_macro() -> Result<()> {
+ println!("test component: {USING_MACRO:?}");
+ let component = std::fs::read(USING_MACRO)?;
+ inits_properly(&component).await
+}
From b0490a7d8aef5600b749b38efa49880ddd894a7f Mon Sep 17 00:00:00 2001
From: Pat Hickey
Date: Tue, 1 Jul 2025 16:25:13 -0700
Subject: [PATCH 2/2] fmt
---
guest-rust/macro/src/lib.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/guest-rust/macro/src/lib.rs b/guest-rust/macro/src/lib.rs
index b578473..9eadd30 100644
--- a/guest-rust/macro/src/lib.rs
+++ b/guest-rust/macro/src/lib.rs
@@ -1,6 +1,6 @@
use proc_macro::TokenStream;
use quote::{quote, quote_spanned};
-use syn::{parse_macro_input, spanned::Spanned, ItemFn};
+use syn::{ItemFn, parse_macro_input, spanned::Spanned};
#[proc_macro_attribute]
pub fn init(_attr: TokenStream, item: TokenStream) -> TokenStream {