jintemplify_plugin_macro/
lib.rs1extern 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}