Skip to content

Commit 45eaa3b

Browse files
authored
Merge pull request #18 from yoshuawuyts/pch/attr_macro_main
Attribute macro for main
2 parents 799dd08 + 97120d6 commit 45eaa3b

File tree

6 files changed

+105
-43
lines changed

6 files changed

+105
-43
lines changed

Cargo.toml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "wstd"
3-
version = "0.4.0"
3+
version.workspace = true
44
license.workspace = true
55
repository = "https://github.com/yoshuawuyts/wstd"
66
documentation = "https://docs.rs/wstd"
@@ -19,6 +19,7 @@ authors = [
1919
slab.workspace = true
2020
url.workspace = true
2121
wasi.workspace = true
22+
wstd-macro.workspace = true
2223

2324
[dev-dependencies]
2425
anyhow.workspace = true
@@ -30,26 +31,31 @@ wasmtime-wasi-http.workspace = true
3031

3132
[workspace]
3233
members = [
34+
"macro",
3335
"test-programs",
3436
"test-programs/artifacts",
3537
]
3638
resolver = "2"
3739

3840
[workspace.package]
41+
version = "0.4.0"
3942
edition = "2021"
4043
license = "MIT OR Apache-2.0 OR Apache-2.0 WITH LLVM-exception"
4144

4245
[workspace.dependencies]
4346
anyhow = "1"
4447
cargo_metadata = "0.18.1"
4548
heck = "0.5"
49+
quote = "1.0"
4650
serde_json = "1"
4751
slab = "0.4.9"
52+
syn = "2.0"
4853
test-programs = { path = "test-programs" }
4954
test-programs-artifacts = { path = "test-programs/artifacts" }
5055
url = "2.5.0"
5156
wasi = "0.13.1"
52-
wstd = { path = "." }
5357
wasmtime = "26"
5458
wasmtime-wasi = "26"
5559
wasmtime-wasi-http = "26"
60+
wstd = { path = "." }
61+
wstd-macro = { path = "macro" }

examples/http_get.rs

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,34 @@
11
use std::error::Error;
22
use wstd::http::{Client, Method, Request, Url};
33
use wstd::io::AsyncRead;
4-
use wstd::runtime::block_on;
54

6-
fn main() -> Result<(), Box<dyn Error>> {
7-
block_on(async move {
8-
let request = Request::new(Method::Get, Url::parse("https://postman-echo.com/get")?);
9-
let mut response = Client::new().send(request).await?;
5+
#[wstd::main]
6+
async fn main() -> Result<(), Box<dyn Error>> {
7+
let request = Request::new(Method::Get, Url::parse("https://postman-echo.com/get")?);
8+
let mut response = Client::new().send(request).await?;
109

11-
let content_type = response
12-
.headers()
13-
.get(&"content-type".into())
14-
.ok_or_else(|| "response expected to have content-type header")?;
15-
assert_eq!(content_type.len(), 1, "one header value for content-type");
16-
assert_eq!(content_type[0], b"application/json; charset=utf-8");
10+
let content_type = response
11+
.headers()
12+
.get(&"content-type".into())
13+
.ok_or_else(|| "response expected to have content-type header")?;
14+
assert_eq!(content_type.len(), 1, "one header value for content-type");
15+
assert_eq!(content_type[0], b"application/json; charset=utf-8");
1716

18-
// Would much prefer read_to_end here:
19-
let mut body_buf = vec![0; 4096];
20-
let body_len = response.body().read(&mut body_buf).await?;
21-
body_buf.truncate(body_len);
17+
// Would much prefer read_to_end here:
18+
let mut body_buf = vec![0; 4096];
19+
let body_len = response.body().read(&mut body_buf).await?;
20+
body_buf.truncate(body_len);
2221

23-
let val: serde_json::Value = serde_json::from_slice(&body_buf)?;
24-
let body_url = val
25-
.get("url")
26-
.ok_or_else(|| "body json has url")?
27-
.as_str()
28-
.ok_or_else(|| "body json url is str")?;
29-
assert!(
30-
body_url.contains("postman-echo.com/get"),
31-
"expected body url to contain the authority and path, got: {body_url}"
32-
);
22+
let val: serde_json::Value = serde_json::from_slice(&body_buf)?;
23+
let body_url = val
24+
.get("url")
25+
.ok_or_else(|| "body json has url")?
26+
.as_str()
27+
.ok_or_else(|| "body json url is str")?;
28+
assert!(
29+
body_url.contains("postman-echo.com/get"),
30+
"expected body url to contain the authority and path, got: {body_url}"
31+
);
3332

34-
Ok(())
35-
})
33+
Ok(())
3634
}

examples/tcp_echo_server.rs

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
11
use wstd::io;
22
use wstd::iter::AsyncIterator;
33
use wstd::net::TcpListener;
4-
use wstd::runtime::block_on;
54

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

12-
let mut incoming = listener.incoming();
13-
while let Some(stream) = incoming.next().await {
14-
let stream = stream?;
15-
println!("Accepted from: {}", stream.peer_addr()?);
16-
io::copy(&stream, &stream).await?;
17-
}
18-
Ok(())
19-
})
11+
let mut incoming = listener.incoming();
12+
while let Some(stream) = incoming.next().await {
13+
let stream = stream?;
14+
println!("Accepted from: {}", stream.peer_addr()?);
15+
io::copy(&stream, &stream).await?;
16+
}
17+
Ok(())
2018
}

macro/Cargo.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "wstd-macro"
3+
version.workspace = true
4+
edition.workspace = true
5+
license.workspace = true
6+
7+
[lib]
8+
proc-macro = true
9+
10+
[dependencies]
11+
syn = { workspace = true, features = ["full"] }
12+
quote.workspace = true

macro/src/lib.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
use proc_macro::TokenStream;
2+
use quote::{quote, quote_spanned};
3+
use syn::{parse_macro_input, spanned::Spanned, ItemFn};
4+
5+
#[proc_macro_attribute]
6+
pub fn attr_macro_main(_attr: TokenStream, item: TokenStream) -> TokenStream {
7+
let input = parse_macro_input!(item as ItemFn);
8+
9+
if input.sig.asyncness.is_none() {
10+
return quote_spanned! { input.sig.fn_token.span()=>
11+
compile_error!("fn must be `async fn`");
12+
}
13+
.into();
14+
}
15+
16+
if input.sig.ident != "main" {
17+
return quote_spanned! { input.sig.ident.span()=>
18+
compile_error!("only `async fn main` can be used for #[wstd::main]");
19+
}
20+
.into();
21+
}
22+
23+
if !input.sig.inputs.is_empty() {
24+
return quote_spanned! { input.sig.inputs.span()=>
25+
compile_error!("arguments to main are not supported");
26+
}
27+
.into();
28+
}
29+
let attrs = input.attrs;
30+
let output = input.sig.output;
31+
let block = input.block;
32+
quote! {
33+
pub fn main() #output {
34+
35+
#(#attrs)*
36+
async fn __run() #output {
37+
#block
38+
}
39+
40+
::wstd::runtime::block_on(async {
41+
__run().await
42+
})
43+
}
44+
}
45+
.into()
46+
}

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,5 @@ pub mod net;
5151
pub mod rand;
5252
pub mod runtime;
5353
pub mod time;
54+
55+
pub use wstd_macro::attr_macro_main as main;

0 commit comments

Comments
 (0)