#![doc(test(attr(deny(warnings))))]
#![doc(test(attr(allow(dead_code))))]
#![doc(test(attr(allow(unused_variables))))]
#![warn(missing_docs)]
#![deny(rustdoc::broken_intra_doc_links)]
#![deny(rustdoc::invalid_codeblock_attributes)]
extern crate proc_macro;
use {
proc_macro::TokenStream,
proc_macro2::Span,
quote::quote_spanned,
syn::{
parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned, Attribute, Ident,
ItemFn, ReturnType, Signature, Visibility,
},
};
#[proc_macro_attribute]
pub fn main(_: TokenStream, input: TokenStream) -> TokenStream {
let raw_main = parse_macro_input!(input as ItemFn);
if !check_impl_signature(&raw_main.sig) {
return syn::Error::new(
raw_main.sig.span(),
"`fastly::main` expects a function such as:
#[fastly::main]
fn main (request: Request) -> Result<Response, Error> {
...
}
",
)
.to_compile_error()
.into();
}
let (attrs, vis, sig) = outer_main_info(&raw_main);
let (name, inner_fn) = inner_fn_info(raw_main);
let output = quote_spanned! {inner_fn.span() =>
#(#attrs)*
#vis
#sig {
#[inline(always)]
#inner_fn
fastly::init();
let ds_req = fastly::Request::from_client();
match #name(ds_req) {
Ok(ds_resp) => ds_resp.send_to_client(),
Err(e) => {
fastly::Response::from_body(e.to_string())
.with_status(fastly::http::StatusCode::INTERNAL_SERVER_ERROR)
.send_to_client()
}
};
Ok(())
}
};
output.into()
}
fn check_impl_signature(sig: &Signature) -> bool {
if sig.inputs.iter().len() != 1 {
false } else if let ReturnType::Default = sig.output {
false } else {
true
}
}
fn outer_main_info(inner_main: &ItemFn) -> (Vec<Attribute>, Visibility, Signature) {
let attrs = inner_main.attrs.clone();
let vis = Visibility::Inherited;
let sig = {
let mut sig = inner_main.sig.clone();
sig.ident = Ident::new("main", Span::call_site());
sig.inputs = Punctuated::new();
sig.output = parse_quote!(-> ::std::result::Result<(), fastly::Error>);
sig
};
(attrs, vis, sig)
}
fn inner_fn_info(mut inner_main: ItemFn) -> (Ident, ItemFn) {
let name = inner_main.sig.ident.clone();
inner_main.vis = Visibility::Inherited;
inner_main
.attrs
.retain(|attr| !attr.path.is_ident("no_mangle"));
(name, inner_main)
}