-
Notifications
You must be signed in to change notification settings - Fork 0
/
lib.rs
118 lines (105 loc) · 3.22 KB
/
lib.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#![allow(clippy::eval_order_dependence)]
extern crate proc_macro;
use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::quote;
use syn::{
bracketed, parenthesized,
parse::{Parse, ParseStream},
parse_macro_input,
punctuated::Punctuated,
token, Ident, Result, Token, Type,
};
struct MessageInner {
name: Ident,
#[allow(dead_code)]
first_comma: token::Comma,
reply: Type,
}
impl Parse for MessageInner {
fn parse(input: ParseStream) -> Result<Self> {
Ok(MessageInner {
name: input.parse()?,
first_comma: input.parse()?,
reply: input.parse()?,
})
}
}
struct Message {
name: Ident,
reply: Type,
}
impl Parse for Message {
fn parse(input: ParseStream) -> Result<Self> {
let content;
parenthesized!(content in input);
for parsed in content.parse_terminated::<MessageInner, token::Comma>(MessageInner::parse)? {
return Ok(Message {
name: parsed.name,
reply: parsed.reply,
});
}
unreachable!();
}
}
struct Configuration {
resource: Ident,
#[allow(dead_code)]
first_comma: token::Comma,
#[allow(dead_code)]
first_bracket: token::Bracket,
messages: Punctuated<Message, Token![,]>,
}
impl Parse for Configuration {
fn parse(input: ParseStream) -> Result<Self> {
let messages;
Ok(Self {
resource: input.parse()?,
first_comma: input.parse()?,
first_bracket: bracketed!(messages in input),
messages: messages.parse_terminated(Message::parse)?,
})
}
}
#[proc_macro]
pub fn generate(input: TokenStream) -> TokenStream {
let Configuration {
resource, messages, ..
} = parse_macro_input!(input as Configuration);
let actor_name = resource.to_string();
let actor_lock = Ident::new(&actor_name.to_ascii_uppercase(), Span::call_site());
let actor_msg = Ident::new(format!("{}Msg", actor_name).as_str(), Span::call_site());
let msg_structs = messages.iter().map(|m| {
let name_str = m.name.to_string();
let msg = Ident::new(format!("{}{}Msg", actor_name, name_str).as_str(), Span::call_site());
let payload = Ident::new(format!("{}{}Payload", actor_name, name_str).as_str(), Span::call_site());
let reply = &m.reply;
quote! {
#[derive(Debug)]
pub struct #msg {
pub reply: crossbeam_channel::Sender<Option<crate::resources::ResponseData<#reply>>>,
pub payload: entities::#payload,
pub db_pool: &'static sqlx::PgPool,
}
}
});
let msg_variants = messages.iter().map(|m| {
let name = &m.name;
let msg = Ident::new(
format!("{}{}Msg", actor_name, name).as_str(),
Span::call_site(),
);
quote! {
#name(#msg)
}
});
(quote! {
#(#msg_structs)*
#[derive(Debug)]
pub enum #actor_msg {
#(#msg_variants),*
}
pub static #actor_lock: once_cell::sync::OnceCell<&'static parking_lot::RwLock<Option<crossbeam_channel::Sender<#actor_msg>>>> = once_cell::sync::OnceCell::new();
})
.into()
}