epics_sys/
lib.rs

1#![feature(extern_crate_item_prelude)]
2#![feature(concat_idents)]
3#![recursion_limit = "128"]
4
5// Rust std crate must be imported like this
6extern 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(&reg_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    //println!("{:#?}", rec_type);
65
66    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}