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 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 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 ::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 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 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 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 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 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}