1use proc_macro::{Span, TokenStream};
2use quote::quote;
3use syn::{parse_macro_input, parse_quote, Ident, ReturnType, Type};
4
5#[proc_macro_attribute]
13pub fn handler(_attr: TokenStream, item: TokenStream) -> TokenStream {
14 let function = parse_macro_input!(item as syn::ItemFn);
15 let fn_name = &function.sig.ident;
16
17 let data = if function.sig.inputs.len() == 2 {
18 if match function.sig.inputs.last() {
19 Some(syn::FnArg::Typed(pat)) => match &*pat.ty {
20 Type::Reference(reference) => {
21 reference.mutability.is_some()
22 }
23 _ => return quote! { compile_error!("Last argument must be either a reference to the shared data or should not be present at all.") }.into(),
24 },
25 Some(_) => return quote! { compile_error!("`self` argument, really?") }.into(),
26 None => unreachable!(),
27 } {
28 quote! {
29 ANYRUN_INTERNAL_DATA.write().unwrap().as_mut().unwrap(),
30 }
31 } else {
32 quote! {
33 ANYRUN_INTERNAL_DATA.read().unwrap().as_ref().unwrap(),
34 }
35 }
36 } else {
37 quote! {}
38 };
39
40 quote! {
41 #[::abi_stable::sabi_extern_fn]
42 fn anyrun_internal_handle_selection(
43 selection: ::anyrun_plugin::anyrun_interface::Match,
44 ) -> ::anyrun_plugin::anyrun_interface::HandleResult {
45 #function
46
47 #fn_name(
48 selection,
49 #data
50 )
51 }
52 }
53 .into()
54}
55
56#[proc_macro_attribute]
64pub fn get_matches(_attr: TokenStream, item: TokenStream) -> TokenStream {
65 let function = parse_macro_input!(item as syn::ItemFn);
66 let fn_name = &function.sig.ident;
67
68 let fn_call = if function.sig.inputs.len() == 2 {
69 let data = if match function.sig.inputs.last() {
70 Some(syn::FnArg::Typed(pat)) => match &*pat.ty {
71 Type::Reference(reference) => {
72 reference.mutability.is_some()
73 }
74 _ => return quote! { compile_error!("Last argument must be either a reference to the shared data or should not be present at all.") }.into(),
75 },
76 Some(_) => return quote! { compile_error!("`self` argument, really?") }.into(),
77 None => unreachable!(),
78 } {
79 quote! {
80 ANYRUN_INTERNAL_DATA.write().unwrap().as_mut()
81 }
82 } else {
83 quote! {
84 ANYRUN_INTERNAL_DATA.read().unwrap().as_ref()
85 }
86 };
87 quote! {
88 if let Some(data) = #data {
89 #fn_name(input, data)
90 } else {
91 ::abi_stable::std_types::RVec::new()
92 }
93 }
94 } else {
95 quote! {
96 #fn_name(input)
97 }
98 };
99
100 quote! {
101 #[::abi_stable::sabi_extern_fn]
102 fn anyrun_internal_get_matches(input: ::abi_stable::std_types::RString) -> u64 {
103 #function
104
105 let current_id =
106 ANYRUN_INTERNAL_ID_COUNTER.load(::std::sync::atomic::Ordering::Relaxed);
107 ANYRUN_INTERNAL_ID_COUNTER
108 .store(current_id + 1, ::std::sync::atomic::Ordering::Relaxed);
109
110 let handle = ::std::thread::spawn(move || {
111 #fn_call
112 });
113
114 *ANYRUN_INTERNAL_THREAD.lock().unwrap() = Some((handle, current_id));
115
116 current_id
117 }
118 }
119 .into()
120}
121
122#[proc_macro_attribute]
124pub fn info(_attr: TokenStream, item: TokenStream) -> TokenStream {
125 let function = parse_macro_input!(item as syn::ItemFn);
126 let fn_name = &function.sig.ident;
127
128 quote! {
129 #[::abi_stable::sabi_extern_fn]
130 fn anyrun_internal_info() -> ::anyrun_plugin::anyrun_interface::PluginInfo {
131 #function
132
133 #fn_name()
134 }
135 }
136 .into()
137}
138
139#[proc_macro_attribute]
142pub fn init(_attr: TokenStream, item: TokenStream) -> TokenStream {
143 let function = parse_macro_input!(item as syn::ItemFn);
144 let fn_name = &function.sig.ident;
145 let data_type = match &function.sig.output {
146 ReturnType::Default => quote! {()},
147 ReturnType::Type(_, data_type) => quote! {#data_type},
148 };
149
150 quote! {
151 static ANYRUN_INTERNAL_THREAD: ::std::sync::Mutex<
152 Option<(
153 ::std::thread::JoinHandle<
154 ::abi_stable::std_types::RVec<::anyrun_plugin::anyrun_interface::Match>,
155 >,
156 u64,
157 )>,
158 > = ::std::sync::Mutex::new(None);
159 static ANYRUN_INTERNAL_ID_COUNTER: ::std::sync::atomic::AtomicU64 =
160 ::std::sync::atomic::AtomicU64::new(0);
161 static ANYRUN_INTERNAL_DATA: ::std::sync::RwLock<Option<#data_type>> =
162 ::std::sync::RwLock::new(None);
163
164 #[::abi_stable::export_root_module]
165 fn anyrun_internal_init_root_module() -> ::anyrun_plugin::anyrun_interface::PluginRef {
166 use ::abi_stable::prefix_type::PrefixTypeTrait;
167 ::anyrun_plugin::anyrun_interface::Plugin {
168 init: anyrun_internal_init,
169 info: anyrun_internal_info,
170 get_matches: anyrun_internal_get_matches,
171 poll_matches: anyrun_internal_poll_matches,
172 handle_selection: anyrun_internal_handle_selection,
173 }
174 .leak_into_prefix()
175 }
176
177 #[::abi_stable::sabi_extern_fn]
178 fn anyrun_internal_poll_matches(id: u64) -> ::anyrun_plugin::anyrun_interface::PollResult {
179 match ANYRUN_INTERNAL_THREAD.try_lock() {
180 Ok(thread) => match thread.as_ref() {
181 Some((thread, task_id)) => {
182 if *task_id == id {
183 if !thread.is_finished() {
184 return ::anyrun_plugin::anyrun_interface::PollResult::Pending;
185 }
186 } else {
187 return ::anyrun_plugin::anyrun_interface::PollResult::Cancelled;
188 }
189 }
190 None => return ::anyrun_plugin::anyrun_interface::PollResult::Cancelled,
191 },
192 Err(_) => return ::anyrun_plugin::anyrun_interface::PollResult::Pending,
193 }
194
195 let (thread, _) = ANYRUN_INTERNAL_THREAD.lock().unwrap().take().unwrap();
196 ::anyrun_plugin::anyrun_interface::PollResult::Ready(thread.join().unwrap())
197 }
198
199 #[::abi_stable::sabi_extern_fn]
200 fn anyrun_internal_init(config_dir: ::abi_stable::std_types::RString) {
201 #function
202
203 ::std::thread::spawn(|| {
204 let mut lock = ANYRUN_INTERNAL_DATA.write().unwrap();
205 *lock = Some(#fn_name(config_dir));
206 });
207 }
208 }
209 .into()
210}
211
212#[proc_macro_attribute]
213pub fn config_args(_attr: TokenStream, item: TokenStream) -> TokenStream {
214 let item = parse_macro_input!(item as syn::ItemStruct);
215 let ident = &item.ident;
216
217 let mut opt_item = item.clone();
218
219 opt_item.attrs = vec![parse_quote!(#[derive(::clap::Args)])];
220 opt_item.ident = Ident::new(&format!("{}Args", opt_item.ident), Span::call_site().into());
221
222 let opt_ident = &opt_item.ident;
223
224 let mut operations = quote!();
225
226 for field in opt_item.fields.iter_mut() {
227 let ty = &field.ty;
228 let ident = &field.ident;
229 field.ty = Type::Verbatim(quote!(Option<#ty>));
230 field.attrs = vec![parse_quote!(#[arg(long)])];
231
232 operations = quote! {
233 #operations
234 if let Some(val) = opt.#ident {
235 self.#ident = val;
236 }
237 }
238 }
239
240 quote! {
241 #item
242
243 #opt_item
244
245 impl #ident {
246 fn merge_opt(&mut self, opt: #opt_ident) {
247 #operations
248 }
249 }
250 }
251 .into()
252}