1#![deny(missing_debug_implementations)]
8#![recursion_limit = "128"]
9
10extern crate proc_macro;
11#[macro_use]
12extern crate quote;
13extern crate syn;
14
15use proc_macro::TokenStream;
16use quote::{ToTokens, Tokens};
17use syn::{DeriveInput, Ident, Lit, Meta};
18
19#[proc_macro_derive(Task,
39 attributes(task_name, task_exchange, task_routing_key, task_timeout,
40 task_retries))]
41pub fn task_derive(input: TokenStream) -> TokenStream {
42 let input: DeriveInput = syn::parse(input).unwrap();
43 let task_name = get_derive_name_attr(&input);
44 let task_exchange = get_derive_exchange_attr(&input);
45 let task_routing_key = get_derive_routing_key_attr(&input);
46 let task_timeout = get_derive_timeout_attr(&input);
47 let task_retries = get_derive_retries_attr(&input);
48 let name = &input.ident;
49
50 let expanded = quote! {
51 impl ::batch::Task for #name {
52
53 fn name() -> &'static str {
54 #task_name
55 }
56
57 fn exchange() -> &'static str {
58 #task_exchange
59 }
60
61 fn routing_key() -> &'static str {
62 #task_routing_key
63 }
64
65 fn timeout() -> Option<::std::time::Duration> {
66 #task_timeout
67 }
68
69 fn retries() -> u32 {
70 #task_retries
71 }
72 }
73 };
74 expanded.into()
75}
76
77fn get_derive_name_attr(input: &DeriveInput) -> Tokens {
78 let attr = {
79 let raw = get_str_attr_by_name(&input.attrs, "task_name");
80 raw.unwrap_or_else(|| input.ident.as_ref().to_string())
81 };
82 attr.into_tokens()
83}
84
85fn get_derive_exchange_attr(input: &DeriveInput) -> Tokens {
86 let attr = {
87 let raw = get_str_attr_by_name(&input.attrs, "task_exchange");
88 raw.unwrap_or_else(|| "".to_string())
89 };
90 attr.into_tokens()
91}
92
93fn get_derive_routing_key_attr(input: &DeriveInput) -> Tokens {
94 let attr = {
95 let raw = get_str_attr_by_name(&input.attrs, "task_routing_key");
96 raw.expect("task_routing_key is a mandatory attribute when deriving Task")
97 };
98 attr.into_tokens()
99}
100
101fn get_derive_timeout_attr(input: &DeriveInput) -> Tokens {
102 let attr = {
103 let raw = get_str_attr_by_name(&input.attrs, "task_timeout");
104 raw.unwrap_or_else(|| "900".to_string())
105 };
106 let timeout = attr.parse::<u64>()
107 .expect("Couldn't parse timeout as an unsigned integer");
108 quote! {
109 ::std::option::Option::Some(::std::time::Duration::from_secs(#timeout))
110 }
111}
112
113fn get_derive_retries_attr(input: &DeriveInput) -> Tokens {
114 let attr = {
115 let raw = get_str_attr_by_name(&input.attrs, "task_retries");
116 raw.unwrap_or_else(|| "2".to_string())
117 };
118 let retries = attr.parse::<u32>()
119 .expect("Couldn't parse retries as an unsigned integer");
120 quote! {
121 #retries
122 }
123}
124
125fn get_str_attr_by_name(haystack: &[syn::Attribute], needle: &str) -> Option<String> {
127 let attr = get_raw_attr_by_name(haystack, needle);
128 attr.and_then(|attr| {
129 if let Lit::Str(literal) = attr {
130 Some(literal.value())
131 } else {
132 None
133 }
134 })
135}
136
137fn get_raw_attr_by_name(haystack: &[syn::Attribute], needle_raw: &str) -> Option<Lit> {
139 let needle = Ident::from(needle_raw);
140 for attr in haystack {
141 let meta = match attr.interpret_meta() {
142 Some(meta) => meta,
143 None => continue,
144 };
145 let nv = match meta {
146 Meta::NameValue(nv) => nv,
147 _ => continue,
148 };
149 if nv.ident != needle {
150 continue;
151 }
152 return Some(nv.lit.clone());
153 }
154 None
155}