#![deny(missing_debug_implementations)]
#![recursion_limit = "128"]
extern crate proc_macro;
#[macro_use]
extern crate quote;
extern crate syn;
use proc_macro::TokenStream;
use quote::{ToTokens, Tokens};
use syn::{DeriveInput, Ident, Lit, Meta};
#[proc_macro_derive(Task,
attributes(task_name, task_exchange, task_routing_key, task_timeout,
task_retries))]
pub fn task_derive(input: TokenStream) -> TokenStream {
let input: DeriveInput = syn::parse(input).unwrap();
let task_name = get_derive_name_attr(&input);
let task_exchange = get_derive_exchange_attr(&input);
let task_routing_key = get_derive_routing_key_attr(&input);
let task_timeout = get_derive_timeout_attr(&input);
let task_retries = get_derive_retries_attr(&input);
let name = &input.ident;
let expanded = quote! {
impl ::batch::Task for #name {
fn name() -> &'static str {
#task_name
}
fn exchange() -> &'static str {
#task_exchange
}
fn routing_key() -> &'static str {
#task_routing_key
}
fn timeout() -> Option<::std::time::Duration> {
#task_timeout
}
fn retries() -> u32 {
#task_retries
}
}
};
expanded.into()
}
fn get_derive_name_attr(input: &DeriveInput) -> Tokens {
let attr = {
let raw = get_str_attr_by_name(&input.attrs, "task_name");
raw.unwrap_or_else(|| input.ident.as_ref().to_string())
};
attr.into_tokens()
}
fn get_derive_exchange_attr(input: &DeriveInput) -> Tokens {
let attr = {
let raw = get_str_attr_by_name(&input.attrs, "task_exchange");
raw.unwrap_or_else(|| "".to_string())
};
attr.into_tokens()
}
fn get_derive_routing_key_attr(input: &DeriveInput) -> Tokens {
let attr = {
let raw = get_str_attr_by_name(&input.attrs, "task_routing_key");
raw.expect("task_routing_key is a mandatory attribute when deriving Task")
};
attr.into_tokens()
}
fn get_derive_timeout_attr(input: &DeriveInput) -> Tokens {
let attr = {
let raw = get_str_attr_by_name(&input.attrs, "task_timeout");
raw.unwrap_or_else(|| "900".to_string())
};
let timeout = attr.parse::<u64>()
.expect("Couldn't parse timeout as an unsigned integer");
quote! {
::std::option::Option::Some(::std::time::Duration::from_secs(#timeout))
}
}
fn get_derive_retries_attr(input: &DeriveInput) -> Tokens {
let attr = {
let raw = get_str_attr_by_name(&input.attrs, "task_retries");
raw.unwrap_or_else(|| "2".to_string())
};
let retries = attr.parse::<u32>()
.expect("Couldn't parse retries as an unsigned integer");
quote! {
#retries
}
}
fn get_str_attr_by_name(haystack: &[syn::Attribute], needle: &str) -> Option<String> {
let attr = get_raw_attr_by_name(haystack, needle);
attr.and_then(|attr| {
if let Lit::Str(literal) = attr {
Some(literal.value())
} else {
None
}
})
}
fn get_raw_attr_by_name(haystack: &[syn::Attribute], needle_raw: &str) -> Option<Lit> {
let needle = Ident::from(needle_raw);
for attr in haystack {
let meta = match attr.interpret_meta() {
Some(meta) => meta,
None => continue,
};
let nv = match meta {
Meta::NameValue(nv) => nv,
_ => continue,
};
if nv.ident != needle {
continue;
}
return Some(nv.lit.clone());
}
None
}