physim_attribute/
lib.rs

1#![feature(vec_into_raw_parts)]
2use proc_macro::TokenStream;
3use quote::{format_ident, quote};
4use syn::{DeriveInput, LitStr, Token, parse::Parse, parse_macro_input};
5
6struct ElementArgs {
7    name: LitStr,
8    blurb: LitStr,
9}
10
11impl Parse for ElementArgs {
12    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
13        let mut name = None;
14        let mut blurb = None;
15
16        while !input.is_empty() {
17            let key: syn::Ident = input.parse()?;
18            input.parse::<Token![=]>()?;
19            let value: LitStr = input.parse()?;
20
21            match &*key.to_string() {
22                "name" => name = Some(value),
23                "blurb" => blurb = Some(value),
24                _ => return Err(syn::Error::new_spanned(key, "unsupported property")),
25            }
26
27            if input.peek(Token![,]) {
28                input.parse::<Token![,]>()?;
29            }
30        }
31
32        Ok(Self {
33            name: name.ok_or_else(|| input.error("missing `name`"))?,
34            blurb: blurb.ok_or_else(|| input.error("missing `blurb`"))?,
35        })
36    }
37}
38
39#[proc_macro_attribute]
40pub fn transform_element(attr: TokenStream, item: TokenStream) -> TokenStream {
41    let ast = parse_macro_input!(item as DeriveInput);
42
43    let args = parse_macro_input!(attr as ElementArgs);
44    let el_name = args.name.value();
45    let blurb = args.blurb.value();
46
47    let struct_name = &ast.ident;
48    let register_fn = format_ident!("{}_register", el_name);
49    let init_fn = format_ident!("{}_init", el_name);
50    let transform_fn = format_ident!("{}_transform", el_name);
51    let destroy_fn = format_ident!("{}_destroy", el_name);
52    let api_fn = format_ident!("{}_get_api", el_name);
53    let get_property_descriptions_fn = format_ident!("{}_get_property_descriptions", el_name);
54    let recv_message_fn = format_ident!("{}_recv_message", el_name);
55    let post_configuration_messages_fn = format_ident!("{}_post_configuration_messages", el_name);
56
57    let g = quote! {
58        #ast
59
60        #[unsafe(no_mangle)]
61        pub unsafe extern "C" fn #init_fn(config: *const u8, len: usize) -> *mut ::std::ffi::c_void {
62            if config.is_null() {
63                return ::std::ptr::null_mut();
64            }
65            let config = unsafe { ::std::str::from_raw_parts(config, len) };
66
67            let properties = match ::physim_core::plugin::deps::serde_json::from_str(config){
68                Ok(properties) => properties,
69                Err(_) => return ::std::ptr::null_mut(),
70            };
71
72            match ::std::panic::catch_unwind(::std::panic::AssertUnwindSafe( || {
73                Box::new(#struct_name::new( properties ) )
74            })) {
75                Ok(el) => {
76                    Box::into_raw(el) as *mut ::std::ffi::c_void
77                }
78                Err(_) => {
79                    eprintln!(
80                        "Problem encountered in the {} element's new method. Aborting",
81                        #el_name
82                    );
83                    ::std::process::abort();
84                }
85            }
86
87        }
88
89        #[unsafe(no_mangle)]
90        pub unsafe extern "C" fn #transform_fn(obj: *const ::std::ffi::c_void, state: *const Entity, state_len: usize, acceleration: *mut Acceleration, acceleration_len: usize) {
91            let el: & #struct_name = unsafe { &*(obj as *const #struct_name) };
92            let s =  unsafe { ::std::slice::from_raw_parts(state, state_len) };
93            let n =  unsafe {  ::std::slice::from_raw_parts_mut(acceleration, acceleration_len) };
94            if let Err(_) = ::std::panic::catch_unwind(::std::panic::AssertUnwindSafe(|| { el.transform(s, n)})) {
95                eprintln!("Problem encountered in the {} element's transform method. Aborting", #el_name);
96                ::std::process::abort();
97            }
98        }
99
100        #[unsafe(no_mangle)]
101        pub unsafe extern "C" fn #destroy_fn(obj: *mut ::std::ffi::c_void) {
102            if obj.is_null() {
103                return;
104            }
105
106            let result = ::std::panic::catch_unwind(|| {
107                drop(Box::from_raw(obj as *mut #struct_name));
108            });
109
110            if result.is_err() {
111                eprintln!("Problem encountered in the {} element's drop method. Aborting", #el_name);
112                ::std::process::abort();
113            }
114        }
115
116        #[unsafe(no_mangle)]
117        pub unsafe extern "C" fn #api_fn() -> *const ::physim_core::plugin::transform::TransformElementAPI {
118            Box::into_raw(Box::new(::physim_core::plugin::transform::TransformElementAPI {
119                init: #init_fn,
120                transform: #transform_fn,
121                destroy: #destroy_fn,
122                get_property_descriptions: #get_property_descriptions_fn,
123                recv_message: #recv_message_fn,
124                post_configuration_messages: #post_configuration_messages_fn,
125            }))
126        }
127
128        #[unsafe(no_mangle)]
129        unsafe extern "C" fn #register_fn(alloc: ::physim_core::plugin::RustStringAllocFn) -> ::physim_core::plugin::ElementMetaFFI {
130            // Create CStrings to get proper *const c_char pointers
131            let el_name = ::std::ffi::CString::new(#el_name.replace("\0", "")).expect("Failed to make CString");
132            let pkg_name = ::std::ffi::CString::new(env!("CARGO_PKG_NAME").replace("\0", "")).expect("Failed to make CString");
133            let pkg_version = ::std::ffi::CString::new(env!("CARGO_PKG_VERSION").replace("\0", "")).expect("Failed to make CString");
134            let pkg_license = ::std::ffi::CString::new(env!("CARGO_PKG_LICENSE").replace("\0", "")).expect("Failed to make CString");
135            let pkg_authors = ::std::ffi::CString::new(env!("CARGO_PKG_AUTHORS").replace("\0", "")).expect("Failed to make CString");
136            let blurb = ::std::ffi::CString::new(#blurb.replace("\0", "")).expect("Failed to make CString");
137            let pkg_repo = ::std::ffi::CString::new(env!("CARGO_PKG_REPOSITORY").replace("\0", "")).expect("Failed to make CString");
138
139            ::physim_core::plugin::ElementMetaFFI {
140                kind: ::physim_core::plugin::ElementKind::Transform,
141                name: alloc(el_name.as_ptr()),
142                plugin: alloc(pkg_name.as_ptr()),
143                version: alloc(pkg_version.as_ptr()),
144                license: alloc(pkg_license.as_ptr()),
145                author: alloc(pkg_authors.as_ptr()),
146                blurb: alloc(blurb.as_ptr()),
147                repo: alloc(pkg_repo.as_ptr()),
148            }
149        }
150
151        #[unsafe(no_mangle)]
152        pub unsafe extern "C" fn #get_property_descriptions_fn(obj: *mut ::std::ffi::c_void, alloc: ::physim_core::plugin::RustStringAllocFn) -> *mut ::std::ffi::c_char {
153            if obj.is_null() {return ::std::ptr::null_mut()};
154            let el: &mut #struct_name = unsafe { &mut *(obj as *mut #struct_name) };
155
156            match ::std::panic::catch_unwind(::std::panic::AssertUnwindSafe( || {
157                let properties = el.get_property_descriptions();
158                ::physim_core::plugin::deps::serde_json::to_string(&properties)
159            })) {
160                    Ok(Ok(s)) => {
161                    // Successful JSON serialization
162                    let c_s = ::std::ffi::CString::new(s.replace("\0", "")).expect("Failed to make CString");
163                    alloc(c_s.as_ptr())
164                }
165                Ok(Err(_)) => {
166                    // Serialization failed
167                    ::std::ptr::null_mut()
168                }
169                Err(_) => {
170                    eprintln!(
171                        "Panic encountered in the {} element's get_property_descriptions method.",
172                        #el_name
173                    );
174                    ::std::ptr::null_mut()
175                }
176            }
177        }
178
179        #[unsafe(no_mangle)]
180        pub unsafe extern "C" fn #recv_message_fn(obj: *mut ::std::ffi::c_void, msg: *const ::physim_core::messages::CMessage) {
181            if obj.is_null() {return };
182            let el: &mut #struct_name = unsafe { &mut *(obj as *mut #struct_name) };
183            let msg = unsafe{::physim_core::messages::Message::from_c_ptr(msg)};
184
185            if let Err(_) = ::std::panic::catch_unwind(::std::panic::AssertUnwindSafe(|| { el.recv_message(&msg)})) {
186                eprintln!("Problem encountered in the {} element's recv_message method. Aborting", #el_name);
187                ::std::process::abort();
188            }
189        }
190
191        #[unsafe(no_mangle)]
192        pub unsafe extern "C" fn #post_configuration_messages_fn(obj: *mut ::std::ffi::c_void) {
193            if obj.is_null() {return };
194            let el: &mut #struct_name = unsafe { &mut *(obj as *mut #struct_name) };
195            if let Err(_) = ::std::panic::catch_unwind(::std::panic::AssertUnwindSafe(|| { el.post_configuration_messages();})) {
196                eprintln!("Problem encountered in the {} element's post_configuration_messages method. Aborting", #el_name);
197                ::std::process::abort();
198            }
199        }
200    };
201    g.into()
202}
203
204#[proc_macro_attribute]
205pub fn render_element(attr: TokenStream, item: TokenStream) -> TokenStream {
206    let ast = parse_macro_input!(item as DeriveInput);
207
208    let args = parse_macro_input!(attr as ElementArgs);
209    let el_name = args.name.value();
210    let blurb = args.blurb.value();
211    let name = &ast.ident;
212    let create_element = format_ident!("{}_create_element", el_name);
213    let register_fn = format_ident!("{}_register", el_name);
214
215    let g = quote! {
216        #ast
217
218        #[unsafe(no_mangle)]
219        fn #create_element(properties: HashMap<String, Value>) -> Box<dyn ::physim_core::plugin::render::RenderElement> {
220            #name::create_element(properties)
221        }
222
223                #[unsafe(no_mangle)]
224        unsafe extern "C" fn #register_fn(alloc: extern "C" fn(*const ::std::ffi::c_char) -> *mut ::std::ffi::c_char) -> ::physim_core::plugin::ElementMetaFFI {
225            // Create CStrings to get proper *const c_char pointers
226            let el_name = ::std::ffi::CString::new(#el_name.replace("\0", "")).expect("Failed to make CString");
227            let pkg_name = ::std::ffi::CString::new(env!("CARGO_PKG_NAME").replace("\0", "")).expect("Failed to make CString");
228            let pkg_version = ::std::ffi::CString::new(env!("CARGO_PKG_VERSION").replace("\0", "")).expect("Failed to make CString");
229            let pkg_license = ::std::ffi::CString::new(env!("CARGO_PKG_LICENSE").replace("\0", "")).expect("Failed to make CString");
230            let pkg_authors = ::std::ffi::CString::new(env!("CARGO_PKG_AUTHORS").replace("\0", "")).expect("Failed to make CString");
231            let blurb = ::std::ffi::CString::new(#blurb.replace("\0", "")).expect("Failed to make CString");
232            let pkg_repo = ::std::ffi::CString::new(env!("CARGO_PKG_REPOSITORY").replace("\0", "")).expect("Failed to make CString");
233
234            ::physim_core::plugin::ElementMetaFFI {
235                kind: ::physim_core::plugin::ElementKind::Render,
236                name: alloc(el_name.as_ptr()),
237                plugin: alloc(pkg_name.as_ptr()),
238                version: alloc(pkg_version.as_ptr()),
239                license: alloc(pkg_license.as_ptr()),
240                author: alloc(pkg_authors.as_ptr()),
241                blurb: alloc(blurb.as_ptr()),
242                repo: alloc(pkg_repo.as_ptr()),
243            }
244        }
245    };
246    g.into()
247}
248
249#[proc_macro_attribute]
250pub fn initialise_state_element(attr: TokenStream, item: TokenStream) -> TokenStream {
251    let ast = parse_macro_input!(item as DeriveInput);
252
253    let args = parse_macro_input!(attr as ElementArgs);
254    let el_name = args.name.value();
255    let blurb = args.blurb.value();
256    let name = &ast.ident;
257    let create_element = format_ident!("{}_create_element", el_name);
258    let register_fn = format_ident!("{}_register", el_name);
259
260    let g = quote! {
261        #ast
262
263        #[unsafe(no_mangle)]
264        fn #create_element(properties: ::std::collections::HashMap<String, ::physim_core::plugin::deps::serde_json::Value>) -> Box<dyn ::physim_core::plugin::generator::GeneratorElement> {
265            #name::create_element(properties)
266        }
267
268        #[unsafe(no_mangle)]
269        unsafe extern "C" fn #register_fn(alloc: extern "C" fn(*const ::std::ffi::c_char) -> *mut ::std::ffi::c_char) -> ::physim_core::plugin::ElementMetaFFI {
270            // Create CStrings to get proper *const c_char pointers
271            let el_name = ::std::ffi::CString::new(#el_name.replace("\0", "")).expect("Failed to make CString");
272            let pkg_name = ::std::ffi::CString::new(env!("CARGO_PKG_NAME").replace("\0", "")).expect("Failed to make CString");
273            let pkg_version = ::std::ffi::CString::new(env!("CARGO_PKG_VERSION").replace("\0", "")).expect("Failed to make CString");
274            let pkg_license = ::std::ffi::CString::new(env!("CARGO_PKG_LICENSE").replace("\0", "")).expect("Failed to make CString");
275            let pkg_authors = ::std::ffi::CString::new(env!("CARGO_PKG_AUTHORS").replace("\0", "")).expect("Failed to make CString");
276            let blurb = ::std::ffi::CString::new(#blurb.replace("\0", "")).expect("Failed to make CString");
277            let pkg_repo = ::std::ffi::CString::new(env!("CARGO_PKG_REPOSITORY").replace("\0", "")).expect("Failed to make CString");
278
279            ::physim_core::plugin::ElementMetaFFI {
280                kind: ::physim_core::plugin::ElementKind::Initialiser,
281                name: alloc(el_name.as_ptr()),
282                plugin: alloc(pkg_name.as_ptr()),
283                version: alloc(pkg_version.as_ptr()),
284                license: alloc(pkg_license.as_ptr()),
285                author: alloc(pkg_authors.as_ptr()),
286                blurb: alloc(blurb.as_ptr()),
287                repo: alloc(pkg_repo.as_ptr()),
288            }
289        }
290    };
291    g.into()
292}
293
294#[proc_macro_attribute]
295pub fn synth_element(attr: TokenStream, item: TokenStream) -> TokenStream {
296    let ast = parse_macro_input!(item as DeriveInput);
297
298    let args = parse_macro_input!(attr as ElementArgs);
299    let el_name = args.name.value();
300    let blurb = args.blurb.value();
301    let name = &ast.ident;
302    let create_element = format_ident!("{}_create_element", el_name);
303    let register_fn = format_ident!("{}_register", el_name);
304
305    let g = quote! {
306        #ast
307
308        #[unsafe(no_mangle)]
309        fn #create_element(properties: ::std::collections::HashMap<String, ::physim_core::plugin::deps::serde_json::Value>) -> Box<dyn ::physim_core::plugin::generator::GeneratorElement> {
310            #name::create_element(properties)
311        }
312
313                #[unsafe(no_mangle)]
314        unsafe extern "C" fn #register_fn(alloc: extern "C" fn(*const ::std::ffi::c_char) -> *mut ::std::ffi::c_char) -> ::physim_core::plugin::ElementMetaFFI {
315            // Create CStrings to get proper *const c_char pointers
316            let el_name = ::std::ffi::CString::new(#el_name.replace("\0", "")).expect("Failed to make CString");
317            let pkg_name = ::std::ffi::CString::new(env!("CARGO_PKG_NAME").replace("\0", "")).expect("Failed to make CString");
318            let pkg_version = ::std::ffi::CString::new(env!("CARGO_PKG_VERSION").replace("\0", "")).expect("Failed to make CString");
319            let pkg_license = ::std::ffi::CString::new(env!("CARGO_PKG_LICENSE").replace("\0", "")).expect("Failed to make CString");
320            let pkg_authors = ::std::ffi::CString::new(env!("CARGO_PKG_AUTHORS").replace("\0", "")).expect("Failed to make CString");
321            let blurb = ::std::ffi::CString::new(#blurb.replace("\0", "")).expect("Failed to make CString");
322            let pkg_repo = ::std::ffi::CString::new(env!("CARGO_PKG_REPOSITORY").replace("\0", "")).expect("Failed to make CString");
323
324            ::physim_core::plugin::ElementMetaFFI {
325                kind: ::physim_core::plugin::ElementKind::Synth,
326                name: alloc(el_name.as_ptr()),
327                plugin: alloc(pkg_name.as_ptr()),
328                version: alloc(pkg_version.as_ptr()),
329                license: alloc(pkg_license.as_ptr()),
330                author: alloc(pkg_authors.as_ptr()),
331                blurb: alloc(blurb.as_ptr()),
332                repo: alloc(pkg_repo.as_ptr()),
333            }
334        }
335    };
336    g.into()
337}
338
339#[proc_macro_attribute]
340pub fn transmute_element(attr: TokenStream, item: TokenStream) -> TokenStream {
341    let ast = parse_macro_input!(item as DeriveInput);
342
343    let args = parse_macro_input!(attr as ElementArgs);
344    let el_name = args.name.value();
345    let blurb = args.blurb.value();
346    let name = &ast.ident;
347    let create_element = format_ident!("{}_create_element", el_name);
348    let register_fn = format_ident!("{}_register", el_name);
349
350    let g = quote! {
351        #ast
352
353        #[unsafe(no_mangle)]
354        fn #create_element(properties: ::std::collections::HashMap<String, ::physim_core::plugin::deps::serde_json::Value>) -> Box<dyn ::physim_core::plugin::transmute::TransmuteElement> {
355            #name::create_element(properties)
356        }
357
358        #[unsafe(no_mangle)]
359        unsafe extern "C" fn #register_fn(alloc: extern "C" fn(*const ::std::ffi::c_char) -> *mut ::std::ffi::c_char) -> ::physim_core::plugin::ElementMetaFFI {
360            // Create CStrings to get proper *const c_char pointers
361            let el_name = ::std::ffi::CString::new(#el_name.replace("\0", "")).expect("Failed to make CString");
362            let pkg_name = ::std::ffi::CString::new(env!("CARGO_PKG_NAME").replace("\0", "")).expect("Failed to make CString");
363            let pkg_version = ::std::ffi::CString::new(env!("CARGO_PKG_VERSION").replace("\0", "")).expect("Failed to make CString");
364            let pkg_license = ::std::ffi::CString::new(env!("CARGO_PKG_LICENSE").replace("\0", "")).expect("Failed to make CString");
365            let pkg_authors = ::std::ffi::CString::new(env!("CARGO_PKG_AUTHORS").replace("\0", "")).expect("Failed to make CString");
366            let blurb = ::std::ffi::CString::new(#blurb.replace("\0", "")).expect("Failed to make CString");
367            let pkg_repo = ::std::ffi::CString::new(env!("CARGO_PKG_REPOSITORY").replace("\0", "")).expect("Failed to make CString");
368
369            ::physim_core::plugin::ElementMetaFFI {
370                kind: ::physim_core::plugin::ElementKind::Transmute,
371                name: alloc(el_name.as_ptr()),
372                plugin: alloc(pkg_name.as_ptr()),
373                version: alloc(pkg_version.as_ptr()),
374                license: alloc(pkg_license.as_ptr()),
375                author: alloc(pkg_authors.as_ptr()),
376                blurb: alloc(blurb.as_ptr()),
377                repo: alloc(pkg_repo.as_ptr()),
378            }
379        }
380    };
381    g.into()
382}
383
384#[proc_macro_attribute]
385pub fn integrator_element(attr: TokenStream, item: TokenStream) -> TokenStream {
386    let ast = parse_macro_input!(item as DeriveInput);
387
388    let args = parse_macro_input!(attr as ElementArgs);
389    let el_name = args.name.value();
390    let blurb = args.blurb.value();
391    let name = &ast.ident;
392    let create_element = format_ident!("{}_create_element", el_name);
393    let register_fn = format_ident!("{}_register", el_name);
394
395    let g = quote! {
396        #ast
397
398        #[unsafe(no_mangle)]
399        fn #create_element(properties: ::std::collections::HashMap<String, ::physim_core::plugin::deps::serde_json::Value>) -> Box<dyn ::physim_core::plugin::integrator::IntegratorElement> {
400            #name::create_element(properties)
401        }
402
403        #[unsafe(no_mangle)]
404        unsafe extern "C" fn #register_fn(alloc: extern "C" fn(*const ::std::ffi::c_char) -> *mut ::std::ffi::c_char) -> ::physim_core::plugin::ElementMetaFFI {
405            // Create CStrings to get proper *const c_char pointers
406            let el_name = ::std::ffi::CString::new(#el_name.replace("\0", "")).expect("Failed to make CString");
407            let pkg_name = ::std::ffi::CString::new(env!("CARGO_PKG_NAME").replace("\0", "")).expect("Failed to make CString");
408            let pkg_version = ::std::ffi::CString::new(env!("CARGO_PKG_VERSION").replace("\0", "")).expect("Failed to make CString");
409            let pkg_license = ::std::ffi::CString::new(env!("CARGO_PKG_LICENSE").replace("\0", "")).expect("Failed to make CString");
410            let pkg_authors = ::std::ffi::CString::new(env!("CARGO_PKG_AUTHORS").replace("\0", "")).expect("Failed to make CString");
411            let blurb = ::std::ffi::CString::new(#blurb.replace("\0", "")).expect("Failed to make CString");
412            let pkg_repo = ::std::ffi::CString::new(env!("CARGO_PKG_REPOSITORY").replace("\0", "")).expect("Failed to make CString");
413
414            ::physim_core::plugin::ElementMetaFFI {
415                kind: ::physim_core::plugin::ElementKind::Integrator,
416                name: alloc(el_name.as_ptr()),
417                plugin: alloc(pkg_name.as_ptr()),
418                version: alloc(pkg_version.as_ptr()),
419                license: alloc(pkg_license.as_ptr()),
420                author: alloc(pkg_authors.as_ptr()),
421                blurb: alloc(blurb.as_ptr()),
422                repo: alloc(pkg_repo.as_ptr()),
423            }
424        }
425    };
426    g.into()
427}