1extern crate proc_macro;
2use proc_macro::TokenStream;
3#[allow(unused)]
4use wakflo_common::TaskResult;
5#[allow(unused)]
6use wakflo_core::TaskContext;
7
8use quote::quote;
9use syn::{parse_macro_input, FnArg, ItemFn, ReturnType, Type};
10
11#[proc_macro_attribute]
15pub fn wakflo_plugin(_args: TokenStream, input: TokenStream) -> TokenStream {
16 let mut function = parse_macro_input!(input as ItemFn);
17 if matches!(function.vis, syn::Visibility::Public(..)) {
18 panic!("wakflo_sdk::wakflo_plugin expects a private function");
19 }
20
21 let name = &function.sig.ident;
22 let inputs = &mut function.sig.inputs;
23 let output = &mut function.sig.output;
24
25 if name != "execute" {
26 panic!("wakflo_sdk::wakflo_plugin expects the name of the function to be `execute`")
27 }
28 if inputs.is_empty() || inputs.len() > 1 {
29 panic!(
30 "wakflo_sdk::wakflo_plugin expects a function with one argument of type `PluginContext`"
31 );
32 }
33
34 let context_arg = inputs.first().expect("missing input");
35 match context_arg {
36 FnArg::Receiver(_) => {
37 panic!(
38 "wakflo_sdk::wakflo_plugin expects a function that returns wakflo_sdk::PluginContext"
39 );
40 }
41 FnArg::Typed(t) => {
42 if let Type::Path(p) = t.ty.as_ref() {
43 if let Some(t) = p.path.segments.last() {
44 if t.ident != "TaskContext" {
45 panic!("wakflo_sdk::wakflo_plugin expects a function that returns wakflo_sdk::PluginContext");
46 }
47 } else {
48 panic!("wakflo_sdk::wakflo_plugin expects a function that returns wakflo_sdk::PluginContext");
49 }
50 }
51 }
52 };
53
54 match output {
55 ReturnType::Default => {
56 panic!("wakflo_sdk::wakflo_plugin expects a return value of type `PluginResult`")
57 }
58 ReturnType::Type(_, t) => {
59 if let Type::Path(p) = t.as_ref() {
60 if let Some(t) = p.path.segments.last() {
61 if t.ident != "TaskResult" {
62 panic!("wakflo_sdk::wakflo_plugin expects a function that returns wakflo_sdk::PluginResult");
63 }
64 } else {
65 panic!("wakflo_sdk::wakflo_connector expects a function that returns wakflo_sdk::PluginResult");
66 }
67 }
68 }
69 }
70
71 let fn_name = &function.sig.ident;
72 let tokens = quote! {
73 #[repr(C)]
74 struct Slice {
75 len: usize,
76 ptr: *const u8,
77 }
78
79 #[no_mangle]
80 fn main(input_ptr: u32) -> Box<Slice> {
81 let ctx = unsafe {
83 let len = *(input_ptr as *const u32);
84 let bytes = (input_ptr + 4) as *const u8;
85 let slice = core::slice::from_raw_parts(bytes, len as usize);
86 let d = rmp_serde::from_slice::<JsonValue>(slice.clone());
87 wakflo_sdk::prelude::rmp_serde::from_slice::<TaskContext>(slice).unwrap()
88 };
89
90 let output = #fn_name(ctx).unwrap();
91 let output_bytes = wakflo_sdk::prelude::rmp_serde::to_vec_named(&output).unwrap().leak();
92
93 Box::new(Slice {
95 len: output_bytes.len(),
96 ptr: output_bytes.as_ptr(),
97 })
98 }
99
100 #function
101 };
102 tokens.into()
103}