jintemplify_plugin_macro/
lib.rs

1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use quote::quote;
5use syn::{parse_macro_input, parse_quote, FnArg, ItemFn, Pat, ReturnType, Stmt};
6
7fn translate_inputs<'a>(it: impl Iterator<Item = &'a mut FnArg>) -> Vec<Stmt> {
8    let preprocess_block: Stmt = parse_quote! {
9        let args: jintemplify_plugin::InputWrapper = {
10            let slice = unsafe { std::slice::from_raw_parts(ptr, len as usize) };
11            let json_str = match std::str::from_utf8(slice) {
12                Ok(s) => s,
13                Err(_) => {
14                    return jintemplify_plugin::serialize_to_return_values(&jintemplify_plugin::ErrorValue {
15                        reason: "Failed to convert byte slice to string".to_string(),
16                    })
17                }
18            };
19            let args = match serde_json::from_str(json_str) {
20                Ok(val) => val,
21                Err(err) => {
22                    return jintemplify_plugin::serialize_to_return_values(&jintemplify_plugin::ErrorValue {
23                        reason: format!("Failed to deserialize JSON: {}", err).to_string(),
24                    })
25                }
26            };
27            args
28        };
29    };
30
31    let mut out: Vec<Stmt> = vec![preprocess_block];
32
33    it.enumerate()
34        .map(|(i, arg)| {
35            let FnArg::Typed(arg) = arg else {
36                panic!("self is not allowed for plugin functions")
37            };
38            let Pat::Ident(id) = &*arg.pat else {
39                panic!("Invalid function declation")
40            };
41            (i, id.ident.clone(), &mut arg.ty)
42        })
43        .for_each(|(index, name, ty)| {
44            out.push(
45                parse_quote!(let #name: #ty = jintemplify_plugin::convert_value::<#ty>(&args.params[#index], #index).unwrap();),
46            );
47        });
48
49    out
50}
51
52fn translate_output(ret: &mut ReturnType) -> Stmt {
53    let mut out = parse_quote!(return (out.ptr, out.len););
54
55    if let ReturnType::Type(_, _ty) = ret {
56        out = parse_quote!({
57            let out = jintemplify_plugin::OutputWrapper {
58                result: serde_json::json!(out),
59            };
60            return jintemplify_plugin::serialize_to_return_values(&out);
61        });
62    }
63
64    out
65}
66
67fn make_plugin(_attr: TokenStream, item: TokenStream) -> TokenStream {
68    let mut item_fn = parse_macro_input!(item as ItemFn);
69
70    let prelude = translate_inputs(item_fn.sig.inputs.iter_mut());
71    let epilode = translate_output(&mut item_fn.sig.output);
72
73    let fn_name = &item_fn.sig.ident;
74    let fn_block = &item_fn.block;
75    let output_type = item_fn.sig.output.clone();
76
77    let expanded = quote! {
78        #[no_mangle]
79        pub unsafe extern "C" fn #fn_name (ptr: *mut u8, len: u32) -> *mut jintemplify_plugin::ReturnValues {
80            #(#prelude)*
81            let out = (move || #output_type #fn_block)();
82            #epilode
83        }
84    };
85
86    expanded.into()
87}
88
89#[proc_macro_attribute]
90pub fn plugin_filter(attr: TokenStream, item: TokenStream) -> TokenStream {
91    make_plugin(attr, item)
92}
93
94#[proc_macro_attribute]
95pub fn plugin_function(attr: TokenStream, item: TokenStream) -> TokenStream {
96    make_plugin(attr, item)
97}