#![forbid(unsafe_code)]
#![deny(
clippy::dbg_macro,
missing_copy_implementations,
rustdoc::missing_crate_level_docs,
missing_debug_implementations,
nonstandard_style,
unused_qualifications
)]
#![warn(missing_docs, clippy::nursery, clippy::cargo)]
#![allow(clippy::must_use_candidate, clippy::module_name_repetitions)]
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Data, DeriveInput, Index};
#[proc_macro_derive(Handler, attributes(handler))]
pub fn derive_handler(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let name = input.ident;
let handler = match input.data {
Data::Struct(ds) => {
if ds.fields.len() == 1 {
ds.fields
.into_iter()
.next()
.expect("len == 1 should have only one element")
.ident
.map_or_else(|| quote!(self.0), |field| quote!(self.#field))
} else {
ds
.fields
.into_iter()
.enumerate()
.find_map(|(n, f)| {
if f.attrs.iter().any(|attr| attr.path.is_ident("handler")) {
Some(f.ident.map_or_else(|| {
let n = Index::from(n);
quote!(self.#n)
}, |ident| quote!(self.#ident)))
} else {
None
}
})
.expect("for structs with more than one field, please annotate one of them with #[handler]")
}
}
_ => panic!("Only structs are currently supported by derive(Handler). Enums coming soon!"),
};
let name_string = name.to_string();
quote! {
#[trillium::async_trait]
impl #impl_generics trillium::Handler for #name #ty_generics #where_clause {
async fn run(&self, conn: trillium::Conn) -> trillium::Conn {
trillium::Handler::run(&#handler, conn).await
}
async fn init(&mut self, info: &mut trillium::Info) {
trillium::Handler::init(&mut #handler, info).await;
}
async fn before_send(&self, conn: trillium::Conn) -> trillium::Conn {
trillium::Handler::before_send(&#handler, conn).await
}
fn name(&self) -> std::borrow::Cow<'static, str> {
format!("{} ({})", #name_string, trillium::Handler::name(&#handler)).into()
}
fn has_upgrade(&self, upgrade: &trillium::Upgrade) -> bool {
trillium::Handler::has_upgrade(&#handler, upgrade)
}
async fn upgrade(&self, upgrade: trillium::Upgrade) {
trillium::Handler::upgrade(&#handler, upgrade).await;
}
}
}
.into()
}