use proc_macro::TokenStream; use proc_macro2::{ Ident, Span as Span2, TokenStream as TokenStream2, TokenTree as TokenTree2 }; use quote::quote; pub fn hyper_express(attr: TokenStream, item: TokenStream) -> TokenStream { if let syn::Item::Fn(function_item) = syn::parse(item.clone()).unwrap() { let name = function_item.ident.clone(); let express_name = Ident::new(&format!("express_{}", name.clone()), Span2::call_site()); let visibility = function_item.vis.clone(); let context_generator = match TokenStream2::from(attr).into_iter().next() { Some(TokenTree2::Ident(i)) => i, _ => panic!("Oops! You should include a context generator function in the attribute, something like #[express(generate_context)] Otherwise this macro doesn't know how to make a context.") }; let gen = quote! { #function_item #visibility fn #express_name(mut cx: FunctionContext) -> JsResult { use thruster::thruster_context::basic_hyper_context::{HyperRequest}; use thruster::{MiddlewareFn, MiddlewareNext, MiddlewareReturnValue, MiddlewareResult}; use thruster::thruster_proc::{middleware_fn}; use hyper::{Body, Request}; use std::collections::HashMap; use tokio::runtime::Builder; use tokio::stream::StreamExt; // Convenience method to unwrap JS types fn downcast_unwrap(cx: &mut FunctionContext, req: &JsObject, key: &str) -> JsType { *req.get(cx, key) .unwrap() .downcast::() .unwrap() } // Converts express req -> context fn convert_request_to_context(cx: &mut FunctionContext, req: JsObject) -> Ctx { let path = downcast_unwrap::(cx, &req, "url"); let method = downcast_unwrap::(cx, &req, "method"); let host = downcast_unwrap::(cx, &req, "host"); let protocol = downcast_unwrap::(cx, &req, "protocol"); let headers = downcast_unwrap::(cx, &req, "headers"); let params = downcast_unwrap::(cx, &req, "params"); let mut builder = Request::builder() .uri(format!("{}://{}{}", protocol.value(), host.value(), path.value())) .method(method.value().as_str()); let headers_vec: Vec> = headers.get_own_property_names(cx) .unwrap() .to_vec(cx) .unwrap(); for val in headers_vec { let header = val.downcast::().unwrap().value(); builder = builder.header(header.as_str(), downcast_unwrap::( cx, &headers, header.as_str()).value().as_str()); } let mut param_map = HashMap::new(); let params_vec: Vec> = params.get_own_property_names(cx) .unwrap() .to_vec(cx) .unwrap(); for val in params_vec { let param = val.downcast::().unwrap().value(); let key = param.as_str(); let value = downcast_unwrap::( cx, ¶ms, key).value(); param_map.insert(param.clone(), value); } // let body_handle = req.get(cx, "body") // .unwrap() // .downcast::() // .unwrap(); // let body = cx.borrow(&body_handle, |data| data.as_slice::()); let body = vec![]; let request = builder .body(Body::from(body.to_vec())) .unwrap(); let hyper_request = HyperRequest { request, parts: None, body: None, params: HashMap::new(), // Will get thrown out by generator }; #context_generator(hyper_request) } let req_handle: Handle = cx.argument(0)?; let res_handle: Handle = cx.argument(1)?; let mutable_cx = &mut cx; let req = *req_handle .downcast::() .unwrap_or(JsObject::new(mutable_cx)); let res = res_handle .downcast::() .unwrap_or(JsObject::new(mutable_cx)); let send = downcast_unwrap::(mutable_cx, &res, "send"); let status = downcast_unwrap::(mutable_cx, &res, "status"); let set = downcast_unwrap::(mutable_cx, &res, "set"); let ctx = convert_request_to_context(mutable_cx, req); // TODO(trezm): Optimize this let mut rt = Builder::new() .threaded_scheduler() .build() .unwrap(); rt.block_on(async move { let results = #name(ctx, Box::new(|_| panic!("End of chain"))) .await; if let Ok(result) = results { let mut body_string = "".to_string(); let mut unwrapped_body = result.body; while let Some(chunk) = unwrapped_body.next().await { // TODO(trezm): Dollars to donuts this is pretty slow -- could make it faster with a // mutable byte buffer. body_string = format!("{}{}", body_string, String::from_utf8_lossy(chunk.unwrap().as_ref())); } let body = mutable_cx.string(body_string); let status_code = mutable_cx.number(result.status); let _ = status.call(mutable_cx, res, vec![status_code]); for (key, value) in result.headers { let header_key = mutable_cx.string(key); let header_value = mutable_cx.string(value); let _ = set.call(mutable_cx, res, vec![header_key, header_value]); } let _ = send.call(mutable_cx, res, vec![body]); } else { println!("Something in the chain failed"); } }); Ok(cx.null()) } }; gen.into() } else { item } }