1#![feature(extern_crate_item_prelude)]
2#![feature(concat_idents)]
3#![recursion_limit = "128"]
4
5extern crate proc_macro;
7
8use quote::quote;
9use syn::spanned::Spanned;
10
11#[proc_macro_attribute]
12pub fn epics_register(
13 _attr: proc_macro::TokenStream,
14 item: proc_macro::TokenStream,
15) -> proc_macro::TokenStream {
16 let ast = syn::parse(item).unwrap();
17
18 impl_epics_register(&ast).into()
19}
20
21fn impl_epics_register(ast: &syn::ItemFn) -> proc_macro2::TokenStream {
22 let name = &ast.ident;
23 let name_str = name.to_string();
24 if !name_str.ends_with("_impl") {
25 return syn::parse::Error::new(name.span(), "expected name to end with `_impl`")
26 .to_compile_error();
27 }
28 let name_export = name_str.trim_end_matches(&"_impl");
29 let name_ident = syn::Ident::new(name_export, name.span());
30 let pvar_name = String::from("pvar_func_register_func_") + &name_export;
31 let pvar_ident = syn::Ident::new(&pvar_name, name.span());
32 let reg_func_name = String::from("register_func_") + &name_export;
33 let reg_func_ident = syn::Ident::new(®_func_name, name.span());
34
35 if ast.decl.inputs.len() != 1 {
36 return syn::parse::Error::new(
37 ast.decl.paren_token.span,
38 "expected function to have 1 argument",
39 )
40 .to_compile_error();
41 }
42 let rec_type = &ast.decl.inputs.first().unwrap().into_value();
43 let rec_type = match rec_type {
44 syn::FnArg::Captured(arg) => Some(&arg.ty),
45 _ => {
46 return syn::parse::Error::new(rec_type.span(), "unknown argument").to_compile_error();
47 }
48 }
49 .unwrap();
50 let rec_type = match rec_type {
51 syn::Type::Reference(ty) => Some(ty.elem.as_ref()),
52 _ => {
53 return syn::parse::Error::new(rec_type.span(), "expected reference").to_compile_error();
54 }
55 }
56 .unwrap();
57 let rec_type = match rec_type {
58 syn::Type::Path(ty) => Some(ty),
59 _ => {
60 return syn::parse::Error::new(rec_type.span(), "expected path/type").to_compile_error();
61 }
62 }
63 .unwrap();
64 let gen = quote! {
67 #[no_mangle]
68 pub extern "C" fn #name_ident(precord: *mut #rec_type)
69 -> ::std::os::raw::c_long {
70 match #name(unsafe {&mut *precord}) {
71 Ok(()) => 0,
72 Err(()) => 1,
73 }
74 }
75
76 #[no_mangle]
77 pub fn #reg_func_ident() {
78 use std::mem;
79 let fnname = format!("{}\0", stringify!(#name_ident));
80 unsafe {
81 registryFunctionAdd(
82 fnname.as_ptr() as *const _,
83 Some(mem::transmute::<
84 extern "C" fn(*mut #rec_type)
85 -> ::std::os::raw::c_long, unsafe extern "C" fn()
86 >(#name_ident)));
87 }
88 }
89
90 #[no_mangle]
91 pub static mut #pvar_ident: *const ::std::os::raw::c_void =
92 #reg_func_ident as *const ::std::os::raw::c_void;
93
94 #ast
95 };
96 gen.into()
97}