1#![cfg_attr(feature = "nightly", feature(proc_macro_span))]
2
3use std::{env::current_dir, fs};
4
5use quote::{format_ident, quote, ToTokens, TokenStreamExt};
6use syn::{parse_macro_input, spanned::Spanned, Item};
7
8#[proc_macro]
10pub fn define_k3_write_inputs(_input: proc_macro::TokenStream) -> proc_macro::TokenStream {
11 let expanded = quote! {
12 #[no_mangle]
13 pub static mut ARG_PTR: *const u8 = std::ptr::null();
14
15 #[no_mangle]
16 pub static mut ARG_LEN: usize = 0;
17
18 #[no_mangle]
19 extern "C" fn __k3_write_inputs(ptr: *const u8, len: usize) -> *const u8 {
20 unsafe {
21 ARG_PTR = ptr;
22 ARG_LEN = len;
23 ARG_PTR
24 }
25 }
26 };
27
28 proc_macro::TokenStream::from(expanded)
29}
30
31#[proc_macro]
32pub fn init(_item: proc_macro::TokenStream) -> proc_macro::TokenStream {
33 let mut tokens = quote! {
34 #[no_mangle]
35 extern "C" fn __k3_alloc(size: u32) -> *mut u8 {
36 unsafe { std::alloc::alloc(std::alloc::Layout::array::<u8>(size as usize).unwrap()) }
37 }
38
39
40 #[no_mangle]
41 extern "C" fn __k3_init_env(ptr: *const u8, len: usize) -> *const u8 {
42 unsafe {
43 k3_wasm_sdk::ENV_VAR_PTR = ptr;
44 k3_wasm_sdk::ENV_VAR_LEN = len;
45 k3_wasm_sdk::ENV_VAR_PTR
46 }
47 }
48
49 #[no_mangle]
50 extern "C" fn __k3_results() -> *const u8 {
51 unsafe {
52 k3_wasm_sdk::RES_PTR
53 }
54 }
55
56 #[no_mangle]
57 extern "C" fn __k3_dealloc(ptr: *mut u8, size: u32) {
58 unsafe {
59 std::alloc::dealloc(ptr, std::alloc::Layout::array::<u8>(size as usize).unwrap())
60 }
61 }
62 };
63
64 let mut added_main = false;
65 let env_path = current_dir().unwrap().join(".env");
66 if env_path.exists() {
67 let env_string = fs::read_to_string(env_path).unwrap();
68 let env_string = syn::LitStr::new(&env_string, tokens.span());
69 tokens.append_all(quote! {
70 #[no_mangle]
71 pub static mut ENV_STRING_PTR: u32 = 0;
72
73 #[no_mangle]
74 pub static mut PROJECT_ID_PTR: u32 = 0;
75
76 fn main() {
77 k3_wasm_sdk::dotenvy::from_read(#env_string.as_bytes()).unwrap();
80 unsafe {
81 let mut env_string_buf = #env_string.len().to_le_bytes().to_vec();
82 env_string_buf.append(&mut #env_string.as_bytes().to_vec());
83 PROJECT_ID_PTR = env_string_buf.as_ptr() as u32;
84 std::mem::forget(env_string_buf);
85 };
87 }
89 });
90 added_main = true;
91 }
92
93 if !added_main {
94 tokens.append_all(quote! {fn main() {}});
95 }
96
97 tokens.into()
98}
99
100#[proc_macro_attribute]
101pub fn http_handler(
102 attr: proc_macro::TokenStream,
103 input: proc_macro::TokenStream,
104) -> proc_macro::TokenStream {
105 let input = parse_macro_input!(input as Item);
106
107 let attr_str = attr.to_string();
108
109 let has_env = attr_str.contains("Env");
110
111 if let Item::Fn(mut fn_decl) = input {
112 let name = fn_decl.sig.ident;
113
114
115 #[cfg(feature = "nightly")]
116 let name = {
117 let prefix = if attr.is_empty() || has_env {
118 let file = proc_macro::Span::call_site().source_file().path();
119
120 let file_name = file.file_name().unwrap().to_str().unwrap();
121 let segments = file
122 .components()
123 .map(|comp| comp.as_os_str().to_str().unwrap())
124 .collect::<Vec<_>>();
125 if *segments.last().unwrap() == "main.rs" {
126 "".to_string()
127 } else {
128 let mut relevant_segments = segments
129 .into_iter()
130 .skip_while(|seg| *seg != "src")
131 .skip(1)
132 .map(|s| s.to_string())
133 .collect::<Vec<_>>();
134 if file_name == "mod.rs" {
135 relevant_segments.pop();
136 } else {
137 *relevant_segments.last_mut().unwrap() =
138 file_name[0..file_name.len() - 3].to_string();
139 }
140 format!("_{}", relevant_segments.join("_"))
141 }
142 } else {
143 syn::parse::<syn::Ident>(attr)
145 .expect("Expected identifier as argument to http_handler")
146 .to_string()
147 };
148 format_ident!("__k3_handler_{}_{}", prefix, name.to_string())
149 };
150
151 #[cfg(not(feature = "nightly"))]
152 let name = {
153 let prefix = syn::parse::<syn::Ident>(attr)
154 .expect("Expected identifier as argument to http_handler")
155 .to_string();
156 format_ident!("__k3_handler_{}_{}", prefix, name.to_string())
157 };
158
159 let impl_fn_name = format_ident!("__k3_{}_impl", name);
160 fn_decl.sig.ident = impl_fn_name.clone();
161 let fn_tokens = fn_decl.to_token_stream();
162
163 quote! {
164 #[no_mangle]
165 pub extern "C" fn #name(req_ptr: u32, req_len: u32) -> u32 {
166 #fn_tokens
167 if #has_env {
168 k3_wasm_sdk::set_env_context();
169 };
170 let req = k3_wasm_sdk::request_from_raw_parts(req_ptr, req_len);
171 let res = #impl_fn_name(req);
172 let r = k3_wasm_sdk::encode_response(res);
173 unsafe {k3_wasm_sdk::RES_PTR = r as *const u8;};
174 r
175
176 }
177 }
178 .into()
179 } else {
180 panic!("Macro can only be used on functions")
181 }
182}