kc_osm_proc_macros/
lib.rs1use heck::ToSnakeCase;
2use proc_macro::TokenStream;
3use proc_macro2::Span;
4use proc_macro2::TokenStream as TokenStream2;
5use quote::{ToTokens, format_ident, quote};
6use syn::{
7 Field, Fields, Ident, ItemStruct, Meta, parse::Parser, parse_macro_input,
8 punctuated::Punctuated,
9};
10
11#[proc_macro_attribute]
12pub fn dark_script(attr: TokenStream, item: TokenStream) -> TokenStream {
13 let messages =
14 parse_macro_input!(attr with Punctuated::<Meta, syn::Token![,]>::parse_terminated);
15
16 let mut registrations = TokenStream2::default();
17 for msg in messages {
18 let message = msg.to_token_stream().to_string();
19 let message_func = Ident::new(
20 &format!("on_{}", message.to_snake_case()),
21 Span::call_site(),
22 );
23 registrations.extend(quote! {
24 self.handlers.insert(#message.to_string(), Self::#message_func);
25 });
26 }
27
28 let mut item = parse_macro_input!(item as ItemStruct);
29
30 if let Fields::Named(ref mut fields) = item.fields {
31 fields.named.push(
32 Field::parse_named
33 .parse2(
34 quote! {
35 handlers: std::collections::HashMap<String, fn(&Self, &Services, &sScrMsg) -> HRESULT>
36 },
37 )
38 .unwrap(),
39 );
40 }
41
42 let name = &item.ident;
43 let script_name = name.to_string();
44 let script_impl_block = format_ident!("{}_Impl", &name);
45
46 quote! {
47 #[implement(IScript)]
48 #[derive(Default, Debug)]
49 #item
50
51 impl #name {
52 fn register_handlers(&mut self) {
53 #registrations
54 }
55 }
56
57 impl IScript_Impl for #script_impl_block {
58 unsafe fn GetClassName(&self) -> *const std::ffi::c_char {
59 std::ffi::CString::from_str(#script_name).unwrap().into_raw()
60 }
61
62 unsafe fn ReceiveMessage(&self, msg: &mut sScrMsg, _: &mut sMultiParm, _: i32) -> HRESULT {
63 let services = services();
64
65 let message_name = unsafe {
66 std::ffi::CStr::from_ptr(msg.message).to_str().unwrap()
67 };
68 if self.handlers.contains_key(message_name) {
69 return self.handlers[message_name](self, services, msg);
70 }
71
72 HRESULT(1)
73 }
74 }
75
76 impl DarkScript for #name {
77 fn get_desc(mod_name: &str) -> sScrClassDesc {
78 let mod_ = std::ffi::CString::from_str(mod_name).unwrap();
79 let name = std::ffi::CString::from_str(#script_name).unwrap();
80 sScrClassDesc {
81 mod_: mod_.into_raw(),
82 name: name.into_raw(),
83 base: std::ptr::null(),
84 factory: Self::factory,
85 }
86 }
87
88 extern "C" fn factory(
89 _name: *const std::ffi::c_char,
90 _id: std::ffi::c_int
91 ) -> *mut IScript {
92 let mut ret: *mut std::ffi::c_void = std::ptr::null_mut();
93 let mut script = Self::default();
94 script.register_handlers();
95 let script_interface: IScript = script.into();
96 let guid = IScript::IID;
97 let query_result = unsafe { script_interface.query(&raw const guid, &mut ret) };
98 if !HRESULT::is_ok(query_result) {
99 return std::ptr::null_mut();
100 }
101 ret as *mut IScript
102 }
103 }
104 }
105 .into()
106}