Skip to content

Commit cabda1f

Browse files
committed
Add wstd::main and wstd::test macros for WASIp3
1 parent 443ef72 commit cabda1f

6 files changed

Lines changed: 170 additions & 2 deletions

File tree

.cargo/config.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
# wasmtime is given:
33
# * AWS auth environment variables, for running the wstd-aws integration tests.
44
# * . directory is available at .
5-
runner = "wasmtime run -Shttp --env AWS_ACCESS_KEY_ID --env AWS_SECRET_ACCESS_KEY --env AWS_SESSION_TOKEN --dir .::."
5+
runner = "wasmtime run -Sp3 -Shttp --env AWS_ACCESS_KEY_ID --env AWS_SECRET_ACCESS_KEY --env AWS_SESSION_TOKEN --dir .::."

macro/src/lib.rs

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@ use proc_macro::TokenStream;
22
use quote::{quote, quote_spanned};
33
use syn::{ItemFn, parse_macro_input, spanned::Spanned};
44

5+
fn returns_unit(output: &syn::ReturnType) -> bool {
6+
match output {
7+
syn::ReturnType::Default => true,
8+
syn::ReturnType::Type(_, ty) => {
9+
matches!(&**ty, syn::Type::Tuple(tuple) if tuple.elems.is_empty())
10+
}
11+
}
12+
}
13+
514
#[proc_macro_attribute]
615
pub fn attr_macro_main(_attr: TokenStream, item: TokenStream) -> TokenStream {
716
let input = parse_macro_input!(item as ItemFn);
@@ -84,6 +93,149 @@ pub fn attr_macro_test(_attr: TokenStream, item: TokenStream) -> TokenStream {
8493
.into()
8594
}
8695

96+
#[proc_macro_attribute]
97+
pub fn attr_macro_main_p3(_attr: TokenStream, item: TokenStream) -> TokenStream {
98+
let input = parse_macro_input!(item as ItemFn);
99+
100+
if input.sig.asyncness.is_none() {
101+
return quote_spanned! { input.sig.fn_token.span()=>
102+
compile_error!("fn must be `async fn`");
103+
}
104+
.into();
105+
}
106+
107+
if input.sig.ident != "main" {
108+
return quote_spanned! { input.sig.ident.span()=>
109+
compile_error!("only `async fn main` can be used for #[wstd::main]");
110+
}
111+
.into();
112+
}
113+
114+
if !input.sig.inputs.is_empty() {
115+
return quote_spanned! { input.sig.inputs.span()=>
116+
compile_error!("arguments to main are not supported");
117+
}
118+
.into();
119+
}
120+
let attrs = input.attrs;
121+
let output = input.sig.output;
122+
let block = input.block;
123+
let run_result = if returns_unit(&output) {
124+
quote! {
125+
__run().await;
126+
::core::result::Result::Ok(())
127+
}
128+
} else {
129+
quote! {
130+
::core::result::Result::map_err(__run().await, |_| ())
131+
}
132+
};
133+
quote! {
134+
struct __WstdCliRunner;
135+
136+
impl ::wstd::__internal::wasip3::exports::cli::run::Guest for __WstdCliRunner {
137+
async fn run() -> ::core::result::Result<(), ()> {
138+
#(#attrs)*
139+
async fn __run() #output {
140+
#block
141+
}
142+
143+
#run_result
144+
}
145+
}
146+
147+
::wstd::__internal::wasip3::cli::command::export!(
148+
__WstdCliRunner with_types_in ::wstd::__internal::wasip3
149+
);
150+
151+
// Provide a `main` so users don't have to write `#![no_main]`.
152+
fn main() {
153+
::core::unreachable!(
154+
"wstd p3 components run via the wasi:cli/run@0.3.0 export, not `main`"
155+
)
156+
}
157+
}
158+
.into()
159+
}
160+
161+
#[proc_macro_attribute]
162+
pub fn attr_macro_test_p3(_attr: TokenStream, item: TokenStream) -> TokenStream {
163+
let input = parse_macro_input!(item as ItemFn);
164+
165+
if input.sig.asyncness.is_none() {
166+
return quote_spanned! { input.sig.fn_token.span()=>
167+
compile_error!("fn must be `async fn`");
168+
}
169+
.into();
170+
}
171+
172+
if !input.sig.inputs.is_empty() {
173+
return quote_spanned! { input.sig.inputs.span()=>
174+
compile_error!("arguments to a test are not supported");
175+
}
176+
.into();
177+
}
178+
let attrs = input.attrs;
179+
let output = input.sig.output;
180+
let block = input.block;
181+
let name = input.sig.ident;
182+
let message = format!("test {name} ... ");
183+
let run_result = if returns_unit(&output) {
184+
quote! {
185+
::std::println!("");
186+
::std::print!(#message);
187+
__run().await;
188+
::std::println!("ok");
189+
::std::println!("");
190+
::core::result::Result::Ok(())
191+
}
192+
} else {
193+
quote! {
194+
::std::println!("");
195+
::std::print!(#message);
196+
match __run().await {
197+
::core::result::Result::Ok(_) => {
198+
::std::println!("ok");
199+
::std::println!("");
200+
::core::result::Result::Ok(())
201+
}
202+
::core::result::Result::Err(err) => {
203+
::std::println!("failed");
204+
::std::println!("Error {:?}", err);
205+
::std::println!("");
206+
::core::result::Result::Err(())
207+
}
208+
}
209+
}
210+
};
211+
quote! {
212+
struct __WstdCliRunner;
213+
214+
impl ::wstd::__internal::wasip3::exports::cli::run::Guest for __WstdCliRunner {
215+
async fn run() -> ::core::result::Result<(), ()> {
216+
#(#attrs)*
217+
async fn __run() #output {
218+
#block
219+
}
220+
221+
#run_result
222+
}
223+
}
224+
225+
::wstd::__internal::wasip3::cli::command::export!(
226+
__WstdCliRunner with_types_in ::wstd::__internal::wasip3
227+
);
228+
229+
// Provide a `main` so users don't have to write `#![no_main]`.
230+
fn main() {
231+
::core::unreachable!(
232+
"wstd p3 components run via the wasi:cli/run@0.3.0 export, not `main`"
233+
)
234+
}
235+
}
236+
.into()
237+
}
238+
87239
/// Enables a HTTP server main function, for creating [HTTP servers].
88240
///
89241
/// [HTTP servers]: https://docs.rs/wstd/latest/wstd/http/server/index.html

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ pub use wstd_macro::{
8787
attr_macro_http_server as http_server, attr_macro_main as main, attr_macro_test as test,
8888
};
8989

90+
#[cfg(p3)]
91+
pub use wstd_macro::{attr_macro_main_p3 as main, attr_macro_test_p3 as test};
92+
9093
// Re-export the active WASI backend crate for use only by `wstd-macro` macros.
9194
// The proc macros need to generate code that uses these definitions, but we
9295
// don't want to treat it as part of our public API with regards to semver, so

tests/http_first_byte_timeout.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use wstd::http::{Body, Client, Request, error::ErrorCode};
22

3-
#[wstd::main]
3+
#[wstd::test]
44
async fn main() -> Result<(), Box<dyn std::error::Error>> {
55
// Set first byte timeout to 1/2 second.
66
let mut client = Client::new();

tests/macro_main.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Verifies that the `main` macro is compiling.
2+
3+
#[wstd::main]
4+
async fn main() {
5+
assert_eq!(1 + 1, 2);
6+
}

tests/macro_test.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Verify that the test macro is acutally running.
2+
3+
#[wstd::test]
4+
async fn pure_computation() -> Result<(), String> {
5+
assert_eq!(1 + 1, 2);
6+
Ok(())
7+
}

0 commit comments

Comments
 (0)