1extern crate proc_macro;
2
3use botnet_utils::type_id;
4use proc_macro::TokenStream;
5use proc_macro_error::proc_macro_error;
6use quote::{format_ident, quote};
7use syn::{parse_macro_input, Ident, ItemFn, ItemStruct};
8
9fn process_task(attrs: TokenStream, input: TokenStream) -> TokenStream {
10 let func = parse_macro_input!(input as ItemFn);
11 let ident = format_ident!("{}Task", parse_macro_input!(attrs as Ident));
12
13 let output = quote! {
14
15 struct #ident{}
16
17 #[async_trait::async_trait]
18 impl<K, D> Task<K, D> for #ident
19 where
20 K: DatabaseKey + 'static,
21 D: Database + Send + Sync + 'static
22 {
23 #[allow(unused)]
24 #func
25 }
26
27 };
28
29 TokenStream::from(output)
30}
31
32fn process_evaluator(attrs: TokenStream, input: TokenStream) -> TokenStream {
33 let func = parse_macro_input!(input as ItemFn);
34 let ident = format_ident!("{}Evaluator", parse_macro_input!(attrs as Ident));
35
36 let output = quote! {
37
38 struct #ident{}
39
40 #[async_trait::async_trait]
41 impl Evaluator for #ident {
42 #[allow(unused)]
43 #func
44 }
45
46 };
47
48 TokenStream::from(output)
49}
50
51fn process_key(input: TokenStream) -> TokenStream {
52 let item = parse_macro_input!(input as ItemStruct);
53 let ident = &item.ident;
54 let name = ident.to_string();
55 let ty_id = type_id(&*name);
56
57 let output = quote! {
58
59 #[derive(Debug, PartialEq, Eq, Hash, Clone)]
60 #item
61
62 impl DatabaseKey for #ident {}
63
64 impl Key for #ident {
65
66 const NAME: &'static str = #name;
67 const TYPE_ID: usize = #ty_id;
68
69 type Item = Field;
70 type Metadata = KeyMetadata;
71
72 fn builder() -> Self {
73 Self {
74 fields: Vec::new(),
75 metadata: KeyMetadata::default(),
76 }
77 }
78
79 fn metadata(&mut self, meta: KeyMetadata) -> &mut Self {
80 self.metadata = KeyMetadata::with_key_code(meta, Self::TYPE_ID);
81 self
82 }
83
84 fn field(&mut self, f: Self::Item) -> &mut Self {
85 self.fields.push(f);
86 self
87 }
88
89 fn build(&self) -> Self {
90 self.clone()
91 }
92
93 fn flatten(&self) -> Bytes {
94 let fields = Bytes::from(
95 self.fields
96 .iter()
97 .map(|f| usize::to_le_bytes(f.type_id).to_vec())
98 .collect::<Vec<Vec<u8>>>()
99 .into_iter()
100 .flatten()
101 .collect::<Vec<u8>>(),
102 );
103
104 let id = Bytes::from(usize::to_le_bytes(Self::TYPE_ID).to_vec());
105 Bytes::from([id, fields].concat())
106 }
107
108 fn get_metadata(&self) -> Self::Metadata {
109 self.metadata.clone()
110 }
111
112 fn type_id(&self) -> usize {
113 Self::TYPE_ID
114 }
115
116 fn name(&self) -> &'static str {
117 Self::NAME
118 }
119 }
120
121 impl AsBytes for #ident {
122 fn as_bytes(&self) -> &[u8] {
123 <#ident as AsBytes>::as_bytes(self)
124 }
125 }
126
127 impl #ident {
128 pub fn from_input(value: Input, extractors: &Extractors, metadata: &Metadata) -> BotnetResult<Self> {
129 let meta = metadata.get(&Self::TYPE_ID);
130 let fields = extractors.items.iter().map(|e| e.1.call(&value).expect("Failed to call on input.")).collect::<Vec<Field>>();
131
132 Ok(#ident{ fields, metadata: meta.to_owned() })
134 }
135
136 pub fn from_bytes(b: Bytes, metadata: &Metadata) -> BotnetResult<#ident> {
137 let mut parts = b.chunks_exact(64);
138
139 let key_ty_id = parts.next().unwrap();
140 let meta = metadata.get(&Self::TYPE_ID);
141
142 Ok(#ident::builder())
145 }
146 }
147
148 };
149
150 TokenStream::from(output)
151}
152
153#[proc_macro_error]
154#[proc_macro_attribute]
155pub fn task(attrs: TokenStream, input: TokenStream) -> TokenStream {
156 process_task(attrs, input)
157}
158
159#[proc_macro_error]
160#[proc_macro_attribute]
161pub fn evaluator(attrs: TokenStream, input: TokenStream) -> TokenStream {
162 process_evaluator(attrs, input)
163}
164
165#[proc_macro_error]
166#[proc_macro_attribute]
167pub fn key(_attrs: TokenStream, input: TokenStream) -> TokenStream {
168 process_key(input)
169}