1use proc_macro::TokenStream;
2use quote::{quote, quote_spanned};
3use syn::{parse_macro_input, spanned::Spanned, ItemFn};
4
5#[proc_macro_attribute]
6pub 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}
47
48#[proc_macro_attribute]
49pub fn attr_macro_test(_attr: TokenStream, item: TokenStream) -> TokenStream {
50 let input = parse_macro_input!(item as ItemFn);
51
52 if input.sig.asyncness.is_none() {
53 return quote_spanned! { input.sig.fn_token.span()=>
54 compile_error!("fn must be `async fn`");
55 }
56 .into();
57 }
58
59 let name = input.sig.ident;
60
61 if !input.sig.inputs.is_empty() {
62 return quote_spanned! { input.sig.inputs.span()=>
63 compile_error!("arguments to main are not supported");
64 }
65 .into();
66 }
67 let attrs = input.attrs;
68 let output = input.sig.output;
69 let block = input.block;
70 quote! {
71 #[::core::prelude::v1::test]
72 pub fn #name() #output {
73
74 #(#attrs)*
75 async fn __run() #output {
76 #block
77 }
78
79 ::wstd::runtime::block_on(async {
80 __run().await
81 })
82 }
83 }
84 .into()
85}
86
87#[proc_macro_attribute]
102pub fn attr_macro_http_server(_attr: TokenStream, item: TokenStream) -> TokenStream {
103 let input = parse_macro_input!(item as ItemFn);
104
105 if input.sig.asyncness.is_none() {
106 return quote_spanned! { input.sig.fn_token.span()=>
107 compile_error!("fn must be `async fn`");
108 }
109 .into();
110 }
111
112 let output = &input.sig.output;
113 let inputs = &input.sig.inputs;
114 let name = &input.sig.ident;
115 let body = &input.block;
116 let attrs = &input.attrs;
117 let vis = &input.vis;
118
119 if name != "main" {
120 return quote_spanned! { input.sig.ident.span()=>
121 compile_error!("only `async fn main` can be used for #[wstd::http_server]");
122 }
123 .into();
124 }
125
126 quote! {
127 struct TheServer;
128
129 impl ::wstd::wasi::exports::http::incoming_handler::Guest for TheServer {
130 fn handle(
131 request: ::wstd::wasi::http::types::IncomingRequest,
132 response_out: ::wstd::wasi::http::types::ResponseOutparam
133 ) {
134 #(#attrs)*
135 #vis async fn __run(#inputs) #output {
136 #body
137 }
138
139 let responder = ::wstd::http::server::Responder::new(response_out);
140 let _finished: ::wstd::http::server::Finished =
141 match ::wstd::http::request::try_from_incoming(request)
142 {
143 Ok(request) => ::wstd::runtime::block_on(async { __run(request, responder).await }),
144 Err(err) => responder.fail(err),
145 };
146 }
147 }
148
149 ::wstd::wasi::http::proxy::export!(TheServer with_types_in ::wstd::wasi);
150
151 fn main() {
181 unreachable!("HTTP server components should be run with `handle` rather than `run`")
182 }
183 }
184 .into()
185}