1mod generics_processor;
2mod handler;
3mod utils;
4
5extern crate proc_macro;
6
7use crate::generics_processor::{ArgOrder, Extract, GenericBuilder, ProcessedGenerics};
8use crate::utils::get_segment;
9use proc_macro::TokenStream;
10use proc_macro2::Ident;
11use quote::{format_ident, quote, ToTokens, TokenStreamExt};
12use syn::{parse_macro_input, Error, ItemImpl, Type};
13
14macro_rules! maybe_compiler_error {
15 ($res:expr) => {{
16 match $res {
17 Err(e) => return e.into_compile_error().into(),
18 Ok(r) => r,
19 }
20 }};
21}
22
23#[proc_macro_attribute]
24pub fn puppet_actor(_attr: TokenStream, item: TokenStream) -> TokenStream {
62 let info = parse_macro_input!(item as ItemImpl);
63 generate(info)
64}
65
66fn generate(mut info: ItemImpl) -> TokenStream {
67 let actor_name = if let Type::Path(path) = &*info.self_ty {
68 get_segment(path).expect("Get struct name").ident
69 } else {
70 return Error::new_spanned(info, "Expected macro to be placed on a struct impl.")
71 .into_compile_error()
72 .into();
73 };
74
75 let extracted_generics = maybe_compiler_error!(generics_processor::process_impl_generics(
76 info.generics.clone()
77 ));
78 let extract_handlers = maybe_compiler_error!(handler::create_message_handlers(
79 &mut info.items,
80 &extracted_generics
81 ));
82
83 let types = extract_handlers.handlers
84 .iter()
85 .map(|(_, msg)| {
86 let ty = msg.ty();
87 let ident = msg.ident();
88 quote! {
89 #ident {
90 msg: #ty,
91 tx: puppet::__private::futures::channel::oneshot::Sender<<#ty as puppet::Message>::Output>,
92 }
93 }
94 });
95
96 let enum_name = format_ident!("{}Op", actor_name);
97 let enum_traits = extract_handlers
98 .handlers
99 .iter()
100 .map(|(_handler, msg)| EnumFrom {
101 name: msg.ident(),
102 message: msg.ty(),
103 parent: enum_name.clone(),
104 generics: extract_handlers.generics.clone(),
105 });
106 let mut enum_match_statements = Vec::new();
107 for (method, message) in extract_handlers.handlers.iter() {
108 let callback_name = &method.sig.ident;
109 let tokens = match message {
110 Extract::Message(msg) => {
111 let ident = &msg.ident;
112 quote! {
113 Self::#ident { msg, tx } => {
114 let res = actor.#callback_name(msg).await;
115 let _ = tx.send(res);
116 }
117 }
118 }
119 Extract::MessageWithReply(msg, ArgOrder::First) => {
120 let ident = &msg.ident;
121 quote! {
122 Self::#ident { msg, tx } => {
123 let reply = puppet::Reply::from(tx);
124 let () = actor.#callback_name(reply, msg).await;
126 }
127 }
128 }
129 Extract::MessageWithReply(msg, ArgOrder::Second) => {
130 let ident = &msg.ident;
131 quote! {
132 Self::#ident { msg, tx } => {
133 let reply = puppet::Reply::from(tx);
134 let () = actor.#callback_name(msg, reply).await;
136 }
137 }
138 }
139 };
140
141 enum_match_statements.push(tokens);
142 }
143
144 let remaining_generics = extract_handlers.generics.remaining_generics();
145 let remaining_where = extract_handlers.generics.remaining_where();
146 let enum_where = extract_handlers.generics.enum_where();
147 let generic_builder = extract_handlers.generics.clone();
148 let actor_generics = extracted_generics.types;
149 let actor_where = extracted_generics.where_clause;
150
151 let enum_tokens = quote! {
152 pub enum #enum_name #generic_builder
153 #enum_where
154 {
155 #(#types),*
156 }
157
158 impl #generic_builder #enum_name #generic_builder
159 #enum_where
160 {
161 async fn __run #remaining_generics (self, actor: &mut #actor_name #actor_generics)
162 #remaining_where
163 {
164 match self {
165 #(#enum_match_statements),*
166 }
167 }
168 }
169
170 #(#enum_traits)*
171 };
172
173 #[cfg(not(feature = "helper-methods"))]
174 let helper_methods = quote! {};
175
176 #[cfg(feature = "helper-methods")]
177 let helper_methods = quote! {
178 pub async fn spawn_actor(mut self) -> puppet::ActorMailbox<#actor_name #actor_generics> {
179 self.spawn_actor_with_queue_size(100).await
180 }
181
182 pub async fn spawn_actor_with_name(mut self, name: impl AsRef<str>) -> puppet::ActorMailbox<#actor_name #actor_generics> {
183 self.spawn_actor_with_name_and_size(name, 100).await
184 }
185
186 pub async fn spawn_actor_with_queue_size(mut self, n: usize) -> puppet::ActorMailbox<#actor_name #actor_generics> {
187 self.spawn_actor_with_name_and_size(stringify!(#actor_name), 100).await
188 }
189
190 pub async fn spawn_actor_with_name_and_size(mut self, name: impl AsRef<str>, n: usize) -> puppet::ActorMailbox<#actor_name #actor_generics> {
191 let (tx, rx) = puppet::__private::flume::bounded::<#enum_name #generic_builder>(n);
192
193 puppet::__private::tokio::spawn(async move {
194 while let Ok(op) = rx.recv_async().await {
195 op.__run(&mut self).await;
196 }
197 });
198
199 let name = std::borrow::Cow::Owned(name.as_ref().to_string());
200 puppet::ActorMailbox::new(tx, name)
201 }
202 };
203
204 #[cfg(not(feature = "custom-executor"))]
205 let custom_executor = quote! {};
206
207 #[cfg(feature = "custom-executor")]
208 let custom_executor = quote! {
209 pub async fn spawn_actor_with(mut self, name: impl AsRef<str>, n: usize, executor: impl puppet::Executor) -> puppet::ActorMailbox<#actor_name #actor_generics> {
210 use puppet::Executor;
211
212 let (tx, rx) = puppet::__private::flume::bounded::<#enum_name #generic_builder>(n);
213
214 executor.spawn(async move {
215 while let Ok(op) = rx.recv_async().await {
216 op.__run(&mut self).await;
217 }
218 });
219
220 let name = std::borrow::Cow::Owned(name.as_ref().to_string());
221 puppet::ActorMailbox::new(tx, name)
222 }
223 };
224
225 let tokens = quote! {
226 #info
227
228 impl #actor_generics puppet::Actor for #actor_name #actor_generics #actor_where {
229 type Messages = #enum_name #generic_builder;
230 }
231
232 impl #actor_generics #actor_name #actor_generics #actor_where {
233 pub async fn run_actor(mut self, messages: puppet::__private::flume::Receiver<#enum_name #generic_builder>) {
234 while let Ok(op) = messages.recv_async().await {
235 op.__run(&mut self).await;
236 }
237 }
238
239 #custom_executor
240
241 #helper_methods
242 }
243
244 #enum_tokens
245 };
246
247 tokens.into()
248}
249
250struct EnumFrom {
251 parent: Ident,
252 name: Ident,
253 message: Type,
254 generics: GenericBuilder,
255}
256
257impl ToTokens for EnumFrom {
258 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
259 let parent = self.parent.clone();
260 let name = self.name.clone();
261 let message = self.message.clone();
262 let generics = self.generics.clone();
263 let where_clause = self.generics.enum_where();
264 let variant = quote! {
265 impl #generics puppet::MessageHandler<#message> for #parent #generics
266 #where_clause
267 {
268 fn create(msg: #message) -> (Self, puppet::__private::futures::channel::oneshot::Receiver<<#message as puppet::Message>::Output>) {
269 let (tx, rx) = puppet::__private::futures::channel::oneshot::channel();
270
271 let slf = Self::#name { msg, tx };
272
273 (slf, rx)
274 }
275 }
276 };
277 tokens.append_all(variant);
278 }
279}