1#![allow(unknown_lints)]
6#![deny(
7 clippy::await_holding_lock,
8 clippy::borrow_as_ptr,
9 clippy::branches_sharing_code,
10 clippy::cast_lossless,
11 clippy::clippy::collection_is_never_read,
12 clippy::cloned_instead_of_copied,
13 clippy::cognitive_complexity,
14 clippy::create_dir,
15 clippy::deref_by_slicing,
16 clippy::derivable_impls,
17 clippy::derive_partial_eq_without_eq,
18 clippy::equatable_if_let,
19 clippy::exhaustive_structs,
20 clippy::expect_used,
21 clippy::expl_impl_clone_on_copy,
22 clippy::explicit_deref_methods,
23 clippy::explicit_into_iter_loop,
24 clippy::explicit_iter_loop,
25 clippy::filetype_is_file,
26 clippy::flat_map_option,
27 clippy::format_push_string,
28 clippy::fn_params_excessive_bools,
29 clippy::future_not_send,
30 clippy::get_unwrap,
31 clippy::implicit_clone,
32 clippy::if_then_some_else_none,
33 clippy::impl_trait_in_params,
34 clippy::implicit_clone,
35 clippy::inefficient_to_string,
36 clippy::inherent_to_string,
37 clippy::iter_not_returning_iterator,
38 clippy::large_types_passed_by_value,
39 clippy::large_include_file,
40 clippy::let_and_return,
41 clippy::manual_assert,
42 clippy::manual_ok_or,
43 clippy::manual_split_once,
44 clippy::manual_let_else,
45 clippy::manual_string_new,
46 clippy::map_flatten,
47 clippy::map_unwrap_or,
48 clippy::missing_enforced_import_renames,
49 clippy::missing_assert_message,
50 clippy::missing_const_for_fn,
51 clippy::must_use_candidate,
52 clippy::mut_mut,
53 clippy::needless_for_each,
54 clippy::needless_option_as_deref,
55 clippy::needless_pass_by_value,
56 clippy::needless_collect,
57 clippy::needless_continue,
58 clippy::non_send_fields_in_send_ty,
59 clippy::nonstandard_macro_braces,
60 clippy::option_if_let_else,
61 clippy::option_option,
62 clippy::rc_mutex,
63 clippy::redundant_else,
64 clippy::same_name_method,
65 clippy::semicolon_if_nothing_returned,
66 clippy::str_to_string,
67 clippy::string_to_string,
68 clippy::too_many_lines,
69 clippy::trivially_copy_pass_by_ref,
70 clippy::trivial_regex,
71 clippy::try_err,
72 clippy::unnested_or_patterns,
73 clippy::unused_async,
74 clippy::unwrap_or_else_default,
75 clippy::useless_let_if_seq,
76 bad_style,
77 clashing_extern_declarations,
78 dead_code,
79 deprecated,
80 explicit_outlives_requirements,
81 improper_ctypes,
82 invalid_value,
83 missing_copy_implementations,
84 missing_debug_implementations,
85 mutable_transmutes,
86 no_mangle_generic_items,
87 non_shorthand_field_patterns,
88 overflowing_literals,
89 path_statements,
90 patterns_in_fns_without_body,
91 private_in_public,
92 trivial_bounds,
93 trivial_casts,
94 trivial_numeric_casts,
95 type_alias_bounds,
96 unconditional_recursion,
97 unreachable_pub,
98 unsafe_code,
99 unstable_features,
100 unused,
101 unused_allocation,
102 unused_comparisons,
103 unused_import_braces,
104 unused_parens,
105 unused_qualifications,
106 while_true,
107 missing_docs
108)]
109#![warn(clippy::exhaustive_enums)]
110#![allow(unused_attributes, clippy::derive_partial_eq_without_eq, clippy::box_default)]
111#![allow(missing_docs)]
114
115use proc_macro::TokenStream;
116use proc_macro2::{Ident, Span};
117use proc_macro_crate::crate_name;
118use quote::{quote, ToTokens};
119use syn::parse::Parser;
120use syn::punctuated::Punctuated;
121use syn::token::PathSep;
122use syn::{parse_macro_input, FnArg, ItemFn, Meta, PathSegment, ReturnType, Token};
123
124#[derive(Debug, Clone, Copy)]
125enum Adapter {
126 BinaryInterleavedPairs,
127 BinaryPairedRightStream,
128 UnarySimple,
129 UnaryWithOutputs,
130 GenericRaw,
131}
132
133impl Adapter {
134 fn from_segments(s: &Punctuated<PathSegment, PathSep>) -> Option<Self> {
135 let mut s = s.iter().rev();
136 let last = s.next()?.ident.to_string();
137 match last.as_str() {
138 "binary_interleaved_pairs" => Some(Adapter::BinaryInterleavedPairs),
139 "binary_paired_right_stream" => Some(Adapter::BinaryPairedRightStream),
140 "unary_simple" => Some(Adapter::UnarySimple),
141 "unary_with_outputs" => Some(Adapter::UnaryWithOutputs),
142 "generic_raw" => Some(Adapter::GenericRaw),
143 _ => None,
144 }
145 }
146
147 fn available_adapters() -> Vec<Self> {
148 vec![
149 Adapter::BinaryInterleavedPairs,
150 Adapter::BinaryPairedRightStream,
151 Adapter::UnarySimple,
152 Adapter::UnaryWithOutputs,
153 Adapter::GenericRaw,
154 ]
155 }
156}
157
158impl std::fmt::Display for Adapter {
159 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
160 match self {
161 Adapter::BinaryInterleavedPairs => write!(f, "binary_interleaved_pairs"),
162 Adapter::BinaryPairedRightStream => write!(f, "binary_paired_right_stream"),
163 Adapter::UnarySimple => write!(f, "unary_simple"),
164 Adapter::UnaryWithOutputs => write!(f, "unary_with_outputs"),
165 Adapter::GenericRaw => write!(f, "generic_raw"),
166 }
167 }
168}
169
170impl ToTokens for Adapter {
171 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
172 Ident::new(&self.to_string(), Span::call_site()).to_tokens(tokens);
173 }
174}
175
176#[proc_macro_attribute]
177pub fn operation(attr: TokenStream, item: TokenStream) -> TokenStream {
178 let parser = Punctuated::<Meta, Token![,]>::parse_terminated;
179 let args = match parser.parse2(attr.into()) {
180 Ok(args) => args,
181 Err(e) => panic!("{}", e),
182 };
183
184 let input = parse_macro_input!(item as ItemFn);
185
186 let args = args.into_iter().collect::<Vec<_>>();
187
188 let adapter = match args.as_slice() {
189 [Meta::Path(path)] => Adapter::from_segments(&path.segments),
190 _ => {
191 panic!(
192 "unsupported attributes supplied: {}, available adapters are {}",
193 quote! { args },
194 Adapter::available_adapters()
195 .iter()
196 .map(|a| a.to_string())
197 .collect::<Vec<_>>()
198 .join(", ")
199 );
200 }
201 }
202 .unwrap_or_else(|| {
203 panic!(
204 "unsupported attributes supplied: {}, available adapters are {}",
205 quote! { args },
206 Adapter::available_adapters()
207 .iter()
208 .map(|a| a.to_string())
209 .collect::<Vec<_>>()
210 .join(", ")
211 )
212 });
213 #[allow(clippy::expect_used)]
214 let _ = crate_name("wick-component").expect("wick-component needs to be added in `Cargo.toml`");
215
216 expand_wrapper(adapter, &input)
217}
218
219fn expand_wrapper(adapter: Adapter, wrappee: &ItemFn) -> TokenStream {
221 let attrs = &wrappee.attrs;
222 let async_ = &wrappee.sig.asyncness;
223 let fn_body = &wrappee.block;
224 let fn_name = &wrappee.sig.ident;
225 let fn_arg_types_outer = &wrappee
226 .sig
227 .inputs
228 .iter()
229 .cloned()
230 .map(|mut arg| {
231 if let FnArg::Typed(pat) = &mut arg {
232 #[allow(clippy::single_match)]
233 match pat.pat.as_mut() {
234 syn::Pat::Ident(id) => {
235 id.mutability = None;
236 }
237 _ => {}
238 }
239 }
240 arg
241 })
242 .collect::<Vec<_>>();
243 let fn_arg_types = &wrappee.sig.inputs.iter().collect::<Vec<_>>();
244 let fn_arg_names = &wrappee
245 .sig
246 .inputs
247 .iter()
248 .filter_map(|arg| {
249 if let FnArg::Typed(pat) = arg {
250 match pat.pat.as_ref() {
251 syn::Pat::Ident(id) => Some(&id.ident),
252 _ => None,
253 }
254 } else {
255 None
256 }
257 })
258 .collect::<Vec<_>>();
259
260 let fn_return = match &wrappee.sig.output {
261 ReturnType::Default => quote! {()},
262 ReturnType::Type(_, type_) => quote! {#type_},
263 };
264 let fn_wrapper_name = Ident::new(&format!("{}_wrapper", fn_name), Span::call_site());
265
266 let (async_, await_) = if async_.is_some() {
267 (Some(quote! {async}), Some(quote! {.await}))
268 } else {
269 (None, None)
270 };
271
272 let result = quote! {
273 wick_component::#adapter !(#fn_name => #fn_wrapper_name);
274
275 #[cfg(not(target_family = "wasm"))]
276 #(#attrs)*
277 fn #fn_wrapper_name(#(#fn_arg_types_outer),*) -> std::pin::Pin<Box<dyn std::future::Future<Output = #fn_return> + Send + Sync + 'static>> {
278 Box::pin(async move { #fn_name(#(#fn_arg_names),*)#await_ })
279 }
280
281 #[cfg(target_family = "wasm")]
282 #(#attrs)*
283 fn #fn_wrapper_name(#(#fn_arg_types_outer),*) -> std::pin::Pin<Box<dyn std::future::Future<Output = #fn_return> + 'static>> {
284 Box::pin(async move { #fn_name(#(#fn_arg_names),*)#await_ })
285 }
286
287 #async_ fn #fn_name(#(#fn_arg_types),*) -> #fn_return {
288 #fn_body
289 }
290
291 };
292
293 result.into()
294}