palladium_plugin_derive/
lib.rs1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use quote::quote;
5use syn::{parse_macro_input, Attribute, DeriveInput, ItemStruct, LitStr};
6
7fn find_response_type(attrs: &[Attribute]) -> Option<syn::Type> {
10 for attr in attrs {
11 if !attr.path().is_ident("pd_message") {
12 continue;
13 }
14 let mut response: Option<syn::Type> = None;
15 let _ = attr.parse_nested_meta(|meta| {
16 if meta.path.is_ident("response") {
17 let value: syn::Type = meta.value()?.parse()?;
18 response = Some(value);
19 Ok(())
20 } else {
21 Err(meta.error("unknown pd_message attribute key"))
22 }
23 });
24 return response;
25 }
26 None
27}
28
29#[proc_macro_derive(Message, attributes(pd_message))]
48pub fn derive_message(input: TokenStream) -> TokenStream {
49 let input = parse_macro_input!(input as DeriveInput);
50 let name = &input.ident;
51
52 let response_type: syn::Type =
54 find_response_type(&input.attrs).unwrap_or_else(|| syn::parse_str("()").unwrap());
55
56 let expanded = quote! {
57 impl ::palladium_actor::Message for #name {
58 type Response = #response_type;
59 const TYPE_TAG: u64 = ::palladium_actor::fnv1a_64(concat!(module_path!(), "::", stringify!(#name)));
60 }
61 };
62
63 TokenStream::from(expanded)
64}
65
66fn to_snake_case(s: &str) -> String {
70 let mut out = String::with_capacity(s.len() + 4);
71 for (i, c) in s.chars().enumerate() {
72 if c.is_uppercase() && i > 0 {
73 out.push('_');
74 }
75 out.extend(c.to_lowercase());
76 }
77 out
78}
79
80fn parse_version(s: &str) -> (u32, u32, u32) {
82 let mut parts = s.splitn(3, '.').map(|p| p.parse::<u32>().unwrap_or(0));
83 (
84 parts.next().unwrap_or(0),
85 parts.next().unwrap_or(0),
86 parts.next().unwrap_or(0),
87 )
88}
89
90fn parse_pd_actor_args(attr: TokenStream) -> (Option<String>, Option<String>) {
94 if attr.is_empty() {
95 return (None, None);
96 }
97
98 let mut name: Option<String> = None;
99 let mut version: Option<String> = None;
100
101 let parser = syn::meta::parser(|meta| {
102 if meta.path.is_ident("name") {
103 let s: LitStr = meta.value()?.parse()?;
104 name = Some(s.value());
105 Ok(())
106 } else if meta.path.is_ident("version") {
107 let s: LitStr = meta.value()?.parse()?;
108 version = Some(s.value());
109 Ok(())
110 } else {
111 Err(meta.error("unknown palladium_actor argument; expected `name` or `version`"))
112 }
113 });
114
115 let _ = syn::parse::Parser::parse(parser, attr);
118
119 (name, version)
120}
121
122#[proc_macro_attribute]
156pub fn palladium_actor(attr: TokenStream, item: TokenStream) -> TokenStream {
157 let (name_opt, version_opt) = parse_pd_actor_args(attr);
158 let input = parse_macro_input!(item as ItemStruct);
159 let struct_name = &input.ident;
160
161 let plugin_name = name_opt.unwrap_or_else(|| to_snake_case(&struct_name.to_string()));
163 let (ver_major, ver_minor, ver_patch) =
164 parse_version(&version_opt.unwrap_or_else(|| "0.1.0".to_string()));
165
166 let plugin_name_bytes: Vec<u8> = plugin_name.bytes().collect();
168 let plugin_name_len = plugin_name.len() as u32;
169 let type_name_bytes: Vec<u8> = struct_name.to_string().bytes().collect();
170 let type_name_len = type_name_bytes.len() as u32;
171
172 let expanded = quote! {
173 #input
175
176 #[no_mangle]
183 pub unsafe extern "C" fn pd_plugin_init()
184 -> *const ::palladium_plugin_api::PdPluginInfo
185 {
186 static __PD_PLUGIN_NAME: &[u8] = &[#(#plugin_name_bytes),*];
188 static __PD_ACTOR_TYPE_NAME: &[u8] = &[#(#type_name_bytes),*];
189
190 let types = ::std::boxed::Box::leak(::std::boxed::Box::new([
191 ::palladium_plugin_api::PdActorTypeInfo {
192 type_name: __PD_ACTOR_TYPE_NAME.as_ptr(),
193 type_name_len: #type_name_len,
194 config_schema: ::core::ptr::null(),
195 config_schema_len: 0,
196 },
197 ]));
198
199 ::std::boxed::Box::leak(::std::boxed::Box::new(
200 ::palladium_plugin_api::PdPluginInfo {
201 name: __PD_PLUGIN_NAME.as_ptr(),
202 name_len: #plugin_name_len,
203 version_major: #ver_major,
204 version_minor: #ver_minor,
205 version_patch: #ver_patch,
206 abi_version: ::palladium_plugin_api::PD_ABI_VERSION,
207 actor_type_count: 1,
208 actor_types: types.as_ptr(),
209 },
210 ))
211 }
212
213 #[no_mangle]
216 pub unsafe extern "C" fn pd_actor_create(
217 _type_name: *const u8,
218 _type_name_len: u32,
219 _config: *const u8,
220 _config_len: u32,
221 ) -> *mut ::core::ffi::c_void {
222 ::std::boxed::Box::into_raw(
223 ::std::boxed::Box::new(
224 <#struct_name as ::core::default::Default>::default()
225 )
226 ) as *mut ::core::ffi::c_void
227 }
228
229 #[no_mangle]
231 pub unsafe extern "C" fn pd_actor_destroy(state: *mut ::core::ffi::c_void) {
232 drop(unsafe { ::std::boxed::Box::from_raw(state as *mut #struct_name) });
233 }
234
235 #[no_mangle]
237 pub unsafe extern "C" fn pd_actor_on_start(
238 state: *mut ::core::ffi::c_void,
239 ctx_ptr: *mut ::palladium_plugin_api::PdActorContext,
240 ) -> i32 {
241 let actor = unsafe { &mut *(state as *mut #struct_name) };
242 let mut ctx = ::palladium_plugin::make_ffi_context(ctx_ptr);
243 match ::palladium_actor::Actor::on_start(actor, &mut ctx) {
244 Ok(()) => 0,
245 Err(_) => -1,
246 }
247 }
248
249 #[no_mangle]
251 pub unsafe extern "C" fn pd_actor_on_message(
252 state: *mut ::core::ffi::c_void,
253 ctx_ptr: *mut ::palladium_plugin_api::PdActorContext,
254 envelope_bytes: *const u8,
255 payload: *const u8,
256 payload_len: u32,
257 ) -> i32 {
258 let actor = unsafe { &mut *(state as *mut #struct_name) };
259 let mut ctx = ::palladium_plugin::make_ffi_context(ctx_ptr);
260 let env_buf: [u8; 80] =
261 unsafe { *(envelope_bytes as *const [u8; 80]) };
262 let envelope = ::palladium_actor::Envelope::from_bytes(&env_buf);
263 let mp = if payload.is_null() || payload_len == 0 {
264 ::palladium_actor::MessagePayload::serialized(
265 ::std::vec::Vec::<u8>::new()
266 )
267 } else {
268 let s = unsafe {
269 ::std::slice::from_raw_parts(payload, payload_len as usize)
270 };
271 ::palladium_actor::MessagePayload::serialized(s.to_vec())
272 };
273 match ::palladium_actor::Actor::on_message(actor, &mut ctx, &envelope, mp) {
274 Ok(()) => 0,
275 Err(_) => -2,
276 }
277 }
278
279 #[no_mangle]
281 pub unsafe extern "C" fn pd_actor_on_stop(
282 state: *mut ::core::ffi::c_void,
283 ctx_ptr: *mut ::palladium_plugin_api::PdActorContext,
284 reason: i32,
285 ) {
286 let actor = unsafe { &mut *(state as *mut #struct_name) };
287 let mut ctx = ::palladium_plugin::make_ffi_context(ctx_ptr);
288 let stop_reason = match reason {
289 0 => ::palladium_actor::StopReason::Normal,
290 1 => ::palladium_actor::StopReason::Requested,
291 2 => ::palladium_actor::StopReason::Supervisor,
292 3 => ::palladium_actor::StopReason::Shutdown,
293 4 => ::palladium_actor::StopReason::Killed,
294 _ => ::palladium_actor::StopReason::Error(::palladium_actor::ActorError::Handler),
295 };
296 ::palladium_actor::Actor::on_stop(actor, &mut ctx, stop_reason);
297 }
298 };
299
300 TokenStream::from(expanded)
301}