1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{parse_macro_input, Data, DeriveInput, Fields, Ident, LitStr};
4
5#[proc_macro_derive(ActorMsgHandle, attributes(actor))]
6pub fn actor_msg_handle_derive(input: TokenStream) -> TokenStream {
7 let input = parse_macro_input!(input as DeriveInput);
8 let enum_name = input.ident; let mut actor_idents = Vec::new();
11
12 for attr in input.attrs {
14 if attr.path().is_ident("actor") {
15 attr.parse_nested_meta(|meta| {
16 if meta.path.is_ident("kind") {
17 let value = meta.value()?; let lit_str: LitStr = value.parse()?; actor_idents.push(Ident::new(&lit_str.value(), lit_str.span()));
21 Ok(())
22 } else {
23 Err(meta.error("no kind attribute"))
24 }
25 })
26 .unwrap_or_else(|err| {
27 panic!("Failed to parse actor attribute: {}", err);
28 });
29 }
30 }
31
32 let expanded_list: Vec<_> = actor_idents
33 .into_iter()
34 .map(|actor_ident| {
35 let variants = if let Data::Enum(data_enum) = &input.data {
37 data_enum.variants.iter().map(|variant| {
38 let variant_name = &variant.ident;
39 match &variant.fields {
40 Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
41 quote! {
43 #enum_name::#variant_name(val) => pupactor::AsyncHandle::async_handle(self, val).await.into(),
44 }
45 }
46 _ => quote! {
47 _ => panic!("Unsupported enum variant or structure"),
48 },
49 }
50 }).collect::<Vec<_>>()
51 } else {
52 panic!("ActorMsgHandle can only be derived for enums");
53 };
54
55 quote! {
57 impl pupactor::AsyncHandle<#enum_name> for #actor_ident {
58 #[inline(always)]
59 async fn async_handle(&mut self, value: #enum_name) -> pupactor::ActorCmdRes<Self::Cmd> {
60 match value {
61 #(#variants)*
62 }
63 }
64 }
65 }
66 })
67 .collect();
68
69 TokenStream::from(quote! {
70 #(#expanded_list)*
71 })
72}
73
74#[proc_macro_derive(Pupactor, attributes(actor, listener))]
75pub fn pupactor_derive(input: TokenStream) -> TokenStream {
76 let input = parse_macro_input!(input as DeriveInput);
77 let struct_name = input.ident;
78
79 let mut cmd_ident = None;
81 for attr in input.attrs {
82 if attr.path().is_ident("actor") {
83 attr.parse_nested_meta(|meta| {
84 if meta.path.is_ident("cmd") {
85 let value = meta.value()?; let lit_str: LitStr = value.parse()?; cmd_ident = Some(Ident::new(&lit_str.value(), lit_str.span()));
89 Ok(())
90 } else {
91 Err(meta.error("no kind attribute"))
92 }
93 })
94 .unwrap_or_else(|err| {
95 panic!("Failed to parse actor attribute: {}", err);
96 });
97 }
98 }
99
100 let cmd_ident = match cmd_ident {
102 Some(cmd_ident) => {
103 quote! { #cmd_ident }
104 }
105 None => quote! { std::convert::Infallible }
106 };
107
108 let listeners = if let Data::Struct(data_struct) = input.data {
110 data_struct
111 .fields
112 .iter()
113 .filter_map(|field| {
114 let field_name = field.ident.clone();
115 field
116 .attrs
117 .iter()
118 .find(|attr| attr.path().is_ident("listener"))
119 .map(|_| field_name)
120 })
121 .collect::<Vec<_>>()
122 } else {
123 panic!("`Pupactor` can only be derived for structs");
124 };
125
126 let match_msg_inside_loop = quote! {
128 match msg {
129 pupactor::ActorMsg::Msg(msg) => {
130 let cmd: pupactor::ActorCmdRes<Self::Cmd> = <Self as pupactor::AsyncHandle<_>>::async_handle(self, msg).await.into();
131 if let Err(err) = cmd.0 {
132 if err.is_ok() {
133 return err;
134 } else {
135 break;
136 }
137 } else {
138 continue;
139 }
140 }
141 pupactor::ActorMsg::Cmd(cmd) => {
142 return Ok(Self::Cmd::from(cmd));
143 }
144 }
145 };
146
147 let internal_loop = match listeners.len() {
148 0 => quote! { },
149 1 => {
150 let field_name = listeners.first().unwrap();
151 quote! {
152 while let Some(msg) = Listener::next_msg(&mut self.#field_name).await {
153 #match_msg_inside_loop
154 }
155 }
156 }
157 _ => {
158 let listener_branches = listeners.iter().map(|field_name| {
159 quote! {
160 msg = Listener::next_msg(&mut self.#field_name) => {
161 if let Some(msg) = msg {
162 #match_msg_inside_loop
163 } else {
164 break;
165 }
166 }
167 }
168 });
169 quote! {
170 loop {
171 tokio::select! {
172 #(#listener_branches)*
173 }
174 }
175 }
176 }
177 };
178
179 let expanded = quote! {
180 impl pupactor::Actor for #struct_name {
181 type Cmd = #cmd_ident;
182
183 async fn infinite_loop(&mut self) -> Result<Self::Cmd, pupactor::Break> {
184 #internal_loop
185 Err(pupactor::Break)
186 }
187 }
188 };
189 TokenStream::from(expanded)
190}
191
192#[proc_macro_derive(ActorCmd)]
194pub fn actor_cmd_derive(input: TokenStream) -> TokenStream {
195 let input = parse_macro_input!(input as DeriveInput);
196 let struct_name = input.ident;
197
198 let expanded = quote! {
199 impl From<std::convert::Infallible> for #struct_name {
200 #[inline(always)]
201 fn from(_: std::convert::Infallible) -> Self {
202 unreachable!()
203 }
204 }
205 };
206
207 TokenStream::from(expanded)
208}