Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
# wasmtime is given:
# * AWS auth environment variables, for running the wstd-aws integration tests.
# * . directory is available at .
runner = "wasmtime run -Shttp --env AWS_ACCESS_KEY_ID --env AWS_SECRET_ACCESS_KEY --env AWS_SESSION_TOKEN --dir .::."
runner = "wasmtime run -Sp3 -Shttp --env AWS_ACCESS_KEY_ID --env AWS_SECRET_ACCESS_KEY --env AWS_SESSION_TOKEN --dir .::."
3 changes: 3 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ jobs:
- name: wstd tests
run: cargo test -p wstd -p wstd-axum --target wasm32-wasip2 -- --nocapture

- name: p3 wstd tests
run: cargo test -p wstd -p wstd-axum --target wasm32-wasip2 --no-default-features --features p3 -- --nocapture

- name: test-programs tests
run: cargo test -p test-programs -- --nocapture
if: steps.creds.outcome == 'success'
Expand Down
74 changes: 70 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,16 @@ repository.workspace = true
rust-version.workspace = true

[features]
default = ["json"]
default = ["json", "p2"]
json = ["dep:serde", "dep:serde_json"]

# WASI backend selection. Exactly one of these must be enabled.
p2 = ["dep:wasip2"]
p3 = ["dep:wasip3"]

[build-dependencies]
cfg_aliases.workspace = true

[dependencies]
anyhow.workspace = true
async-task.workspace = true
Expand All @@ -27,9 +34,11 @@ http.workspace = true
itoa.workspace = true
pin-project-lite.workspace = true
slab.workspace = true
wasip2.workspace = true
wstd-macro.workspace = true

wasip2 = { workspace = true, optional = true }
wasip3 = { workspace = true, optional = true }

# optional
serde = { workspace = true, optional = true }
serde_json = { workspace = true, optional = true }
Expand All @@ -43,6 +52,62 @@ humantime.workspace = true
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true

[[test]]
name = "http_first_byte_timeout"
required-features = ["p2"]

[[test]]
name = "http_get"
required-features = ["p2"]

[[test]]
name = "http_get_json"
required-features = ["p2"]

[[test]]
name = "http_handle_error_code"
required-features = ["p2"]

[[test]]
name = "http_post"
required-features = ["p2"]

[[test]]
name = "http_post_json"
required-features = ["p2"]

[[test]]
name = "http_timeout"
required-features = ["p2"]

[[test]]
name = "sleep"
required-features = ["p2"]

[[example]]
name = "complex_http_client"
required-features = ["p2"]

[[example]]
name = "http_client"
required-features = ["p2"]

[[example]]
name = "http_server"
required-features = ["p2"]

[[example]]
name = "http_server_proxy"
required-features = ["p2"]

[[example]]
name = "tcp_echo_server"
required-features = ["p2"]

[[example]]
name = "tcp_stream_client"
required-features = ["p2"]

[workspace]
members = [
"axum",
Expand Down Expand Up @@ -72,6 +137,7 @@ async-task = "4.7"
axum = { version = "0.8.6", default-features = false }
bytes = "1.10.1"
cargo_metadata = "0.22"
cfg_aliases = "0.2"
clap = { version = "4.5.26", features = ["derive"] }
futures-core = "0.3.19"
futures-lite = "1.12.0"
Expand All @@ -95,13 +161,13 @@ test-programs = { path = "test-programs" }
tower-service = "0.3.3"
ureq = { version = "3.1", default-features = false, features = ["json"] }
wasip2 = "1.0"
wstd = { path = ".", version = "=0.6.8" }
wasip3 = "0.8"
wstd = { path = ".", version = "=0.6.8", default-features = false }
wstd-axum = { path = "./axum", version = "=0.6.8" }
wstd-axum-macro = { path = "./axum/macro", version = "=0.6.8" }
wstd-macro = { path = "./macro", version = "=0.6.8" }

[package.metadata.docs.rs]
all-features = true
targets = [
"wasm32-wasip2"
]
22 changes: 21 additions & 1 deletion axum/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,32 @@ rust-version.workspace = true
[dependencies]
axum.workspace = true
tower-service.workspace = true
wstd.workspace = true
wstd = { workspace = true, features = ["json"] }
wstd-axum-macro.workspace = true

[features]
default = ["p2"]
p2 = ["wstd/p2"]
p3 = ["wstd/p3"]

[build-dependencies]
cfg_aliases.workspace = true

[dev-dependencies]
anyhow.workspace = true
futures-concurrency.workspace = true
serde = { workspace = true, features = ["derive"] }
serde_qs.workspace = true
axum = { workspace = true, features = ["query", "json", "macros"] }

[[example]]
name = "hello_world"
required-features = ["p2"]

[[example]]
name = "hello_world_nomacro"
required-features = ["p2"]

[[example]]
name = "weather"
required-features = ["p2"]
10 changes: 10 additions & 0 deletions axum/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use cfg_aliases::cfg_aliases;

fn main() {
cfg_aliases! {
// TODO https://github.com/bytecodealliance/wstd/issues/147: Swap these
// to use `target_env` instead.
p2: { feature = "p2" },
p3: { feature = "p3" },
}
}
1 change: 1 addition & 0 deletions axum/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#![cfg(p2)]
//! Support for the [`axum`] web server framework in wasi-http components, via
//! [`wstd`].
//!
Expand Down
10 changes: 10 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use cfg_aliases::cfg_aliases;

fn main() {
cfg_aliases! {
// TODO https://github.com/bytecodealliance/wstd/issues/147: Swap these
// to use `target_env` instead.
p2: { feature = "p2" },
p3: { feature = "p3" },
}
}
6 changes: 4 additions & 2 deletions examples/tcp_echo_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use wstd::net::TcpListener;

#[wstd::main]
async fn main() -> io::Result<()> {
let listener = TcpListener::bind("127.0.0.1:8080").await?;
let mut listener = TcpListener::bind("127.0.0.1:8080").await?;
println!("Listening on {}", listener.local_addr()?);
println!("type `nc localhost 8080` to create a TCP client");

Expand All @@ -14,7 +14,9 @@ async fn main() -> io::Result<()> {
println!("Accepted from: {}", stream.peer_addr()?);
wstd::runtime::spawn(async move {
// If echo copy fails, we can ignore it.
let _ = io::copy(&stream, &stream).await;
let mut stream = stream;
let (mut read_half, mut write_half) = stream.split();
let _ = io::copy(&mut read_half, &mut write_half).await;
})
.detach();
}
Expand Down
152 changes: 152 additions & 0 deletions macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@ use proc_macro::TokenStream;
use quote::{quote, quote_spanned};
use syn::{ItemFn, parse_macro_input, spanned::Spanned};

fn returns_unit(output: &syn::ReturnType) -> bool {
match output {
syn::ReturnType::Default => true,
syn::ReturnType::Type(_, ty) => {
matches!(&**ty, syn::Type::Tuple(tuple) if tuple.elems.is_empty())
}
}
}

#[proc_macro_attribute]
pub fn attr_macro_main(_attr: TokenStream, item: TokenStream) -> TokenStream {
let input = parse_macro_input!(item as ItemFn);
Expand Down Expand Up @@ -84,6 +93,149 @@ pub fn attr_macro_test(_attr: TokenStream, item: TokenStream) -> TokenStream {
.into()
}

#[proc_macro_attribute]
pub fn attr_macro_main_p3(_attr: TokenStream, item: TokenStream) -> TokenStream {
let input = parse_macro_input!(item as ItemFn);

if input.sig.asyncness.is_none() {
return quote_spanned! { input.sig.fn_token.span()=>
compile_error!("fn must be `async fn`");
}
.into();
}

if input.sig.ident != "main" {
return quote_spanned! { input.sig.ident.span()=>
compile_error!("only `async fn main` can be used for #[wstd::main]");
}
.into();
}

if !input.sig.inputs.is_empty() {
return quote_spanned! { input.sig.inputs.span()=>
compile_error!("arguments to main are not supported");
}
.into();
}
let attrs = input.attrs;
let output = input.sig.output;
let block = input.block;
let run_result = if returns_unit(&output) {
quote! {
__run().await;
::core::result::Result::Ok(())
}
} else {
quote! {
::core::result::Result::map_err(__run().await, |_| ())
}
};
quote! {
struct __WstdCliRunner;

impl ::wstd::__internal::wasip3::exports::cli::run::Guest for __WstdCliRunner {
async fn run() -> ::core::result::Result<(), ()> {
#(#attrs)*
async fn __run() #output {
#block
}

#run_result
}
}

::wstd::__internal::wasip3::cli::command::export!(
__WstdCliRunner with_types_in ::wstd::__internal::wasip3
);

// Provide a `main` so users don't have to write `#![no_main]`.
fn main() {
::core::unreachable!(
"wstd p3 components run via the wasi:cli/run@0.3.0 export, not `main`"
)
}
}
.into()
}

#[proc_macro_attribute]
pub fn attr_macro_test_p3(_attr: TokenStream, item: TokenStream) -> TokenStream {
let input = parse_macro_input!(item as ItemFn);

if input.sig.asyncness.is_none() {
return quote_spanned! { input.sig.fn_token.span()=>
compile_error!("fn must be `async fn`");
}
.into();
}

if !input.sig.inputs.is_empty() {
return quote_spanned! { input.sig.inputs.span()=>
compile_error!("arguments to a test are not supported");
}
.into();
}
let attrs = input.attrs;
let output = input.sig.output;
let block = input.block;
let name = input.sig.ident;
let message = format!("test {name} ... ");
let run_result = if returns_unit(&output) {
quote! {
::std::println!("");
::std::print!(#message);
__run().await;
::std::println!("ok");
::std::println!("");
::core::result::Result::Ok(())
}
} else {
quote! {
::std::println!("");
::std::print!(#message);
match __run().await {
::core::result::Result::Ok(_) => {
::std::println!("ok");
::std::println!("");
::core::result::Result::Ok(())
}
::core::result::Result::Err(err) => {
::std::println!("failed");
::std::println!("Error {:?}", err);
::std::println!("");
::core::result::Result::Err(())
}
}
}
};
quote! {
struct __WstdCliRunner;

impl ::wstd::__internal::wasip3::exports::cli::run::Guest for __WstdCliRunner {
async fn run() -> ::core::result::Result<(), ()> {
#(#attrs)*
async fn __run() #output {
#block
}

#run_result
}
}

::wstd::__internal::wasip3::cli::command::export!(
__WstdCliRunner with_types_in ::wstd::__internal::wasip3
);

// Provide a `main` so users don't have to write `#![no_main]`.
fn main() {
::core::unreachable!(
"wstd p3 components run via the wasi:cli/run@0.3.0 export, not `main`"
)
}
}
.into()
}

/// Enables a HTTP server main function, for creating [HTTP servers].
///
/// [HTTP servers]: https://docs.rs/wstd/latest/wstd/http/server/index.html
Expand Down
Loading
Loading