1use proc_macro::TokenStream;
2use quote::quote;
3
4const WIT_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/wit");
5
6#[proc_macro_attribute]
28pub fn redis_component(_attr: TokenStream, item: TokenStream) -> TokenStream {
29 let func = syn::parse_macro_input!(item as syn::ItemFn);
30 let func_name = &func.sig.ident;
31 let await_postfix = func.sig.asyncness.map(|_| quote!(.await));
32 let preamble = preamble(Export::Redis);
33
34 quote!(
35 #func
36 mod __spin_redis {
37 mod preamble {
38 #preamble
39 }
40 impl self::preamble::exports::fermyon::spin::inbound_redis::Guest for preamble::Spin {
41 fn handle_message(msg: self::preamble::exports::fermyon::spin::inbound_redis::Payload) -> Result<(), self::preamble::fermyon::spin::redis_types::Error> {
42 ::spin_sdk::http::run(async move {
43 match super::#func_name(msg.try_into().expect("cannot convert from Spin Redis payload"))#await_postfix {
44 Ok(()) => Ok(()),
45 Err(e) => {
46 eprintln!("{}", e);
47 Err(self::preamble::fermyon::spin::redis_types::Error::Error)
48 },
49 }
50 })
51 }
52 }
53 }
54 )
55 .into()
56}
57
58#[proc_macro_attribute]
114pub fn http_component(_attr: TokenStream, item: TokenStream) -> TokenStream {
115 let func = syn::parse_macro_input!(item as syn::ItemFn);
116 let func_name = &func.sig.ident;
117 let preamble = preamble(Export::WasiHttp);
118 let is_native_wasi_http_handler = func.sig.inputs.len() == 2;
119 let await_postfix = func.sig.asyncness.map(|_| quote!(.await));
120 let handler = if is_native_wasi_http_handler {
121 quote! { super::#func_name(req, response_out)#await_postfix }
122 } else {
123 quote! { handle_response(response_out, super::#func_name(req)#await_postfix).await }
124 };
125
126 quote!(
127 #func
128 mod __spin_wasi_http {
129 mod preamble {
130 #preamble
131 }
132 impl self::preamble::exports::wasi::http::incoming_handler::Guest for self::preamble::Spin {
133 fn handle(request: self::preamble::wasi::http::types::IncomingRequest, response_out: self::preamble::wasi::http::types::ResponseOutparam) {
134 let request: ::spin_sdk::http::IncomingRequest = ::std::convert::Into::into(request);
135 let response_out: ::spin_sdk::http::ResponseOutparam = ::std::convert::Into::into(response_out);
136 ::spin_sdk::http::run(async move {
137 match ::spin_sdk::http::conversions::TryFromIncomingRequest::try_from_incoming_request(request).await {
138 ::std::result::Result::Ok(req) => #handler,
139 ::std::result::Result::Err(e) => handle_response(response_out, e).await,
140 }
141 });
142 }
143 }
144
145 async fn handle_response<R: ::spin_sdk::http::IntoResponse>(response_out: ::spin_sdk::http::ResponseOutparam, resp: R) {
146 let mut response = ::spin_sdk::http::IntoResponse::into_response(resp);
147 let body = ::std::mem::take(response.body_mut());
148 match ::std::convert::TryInto::try_into(response) {
149 ::std::result::Result::Ok(response) => {
150 if let Err(e) = ::spin_sdk::http::ResponseOutparam::set_with_body(response_out, response, body).await {
151 ::std::eprintln!("Could not set `ResponseOutparam`: {e}");
152 }
153 }
154 ::std::result::Result::Err(e) => {
155 ::std::eprintln!("Could not convert response: {e}");
156 }
157 }
158 }
159
160 impl From<self::preamble::wasi::http::types::IncomingRequest> for ::spin_sdk::http::IncomingRequest {
161 fn from(req: self::preamble::wasi::http::types::IncomingRequest) -> Self {
162 unsafe { Self::from_handle(req.take_handle()) }
163 }
164 }
165
166 impl From<::spin_sdk::http::OutgoingResponse> for self::preamble::wasi::http::types::OutgoingResponse {
167 fn from(resp: ::spin_sdk::http::OutgoingResponse) -> Self {
168 unsafe { Self::from_handle(resp.take_handle()) }
169 }
170 }
171
172 impl From<self::preamble::wasi::http::types::ResponseOutparam> for ::spin_sdk::http::ResponseOutparam {
173 fn from(resp: self::preamble::wasi::http::types::ResponseOutparam) -> Self {
174 unsafe { Self::from_handle(resp.take_handle()) }
175 }
176 }
177 }
178
179 )
180 .into()
181}
182
183#[derive(Copy, Clone)]
184enum Export {
185 WasiHttp,
186 Redis,
187}
188
189fn preamble(export: Export) -> proc_macro2::TokenStream {
190 let world = match export {
191 Export::WasiHttp => quote!("wasi-http-trigger"),
192 Export::Redis => quote!("redis-trigger"),
193 };
194 quote! {
195 #![allow(missing_docs)]
196 ::spin_sdk::wit_bindgen::generate!({
197 world: #world,
198 path: #WIT_PATH,
199 runtime_path: "::spin_sdk::wit_bindgen::rt",
200 generate_all,
201 });
202 pub struct Spin;
203 export!(Spin);
204 }
205}