1#![forbid(unsafe_code)]
7
8use autozig_parser::{
9 AutoZigConfig,
10 IncludeZigConfig,
11};
12use proc_macro::TokenStream;
13use proc_macro_error::proc_macro_error;
14use quote::quote;
15use syn::parse_macro_input;
16
17#[proc_macro_error]
42#[proc_macro]
43pub fn autozig(input: TokenStream) -> TokenStream {
44 let config = parse_macro_input!(input as AutoZigConfig);
45
46 let mod_name = syn::Ident::new(config.get_mod_name(), proc_macro2::Span::call_site());
49
50 let output = if config.has_rust_signatures()
51 || !config.rust_structs.is_empty()
52 || !config.rust_enums.is_empty()
53 || !config.rust_trait_impls.is_empty()
54 {
55 let enum_defs = generate_enum_definitions(&config);
57
58 let struct_defs = generate_struct_definitions(&config);
60
61 let trait_impl_types = generate_trait_impl_types(&config);
63
64 let (ffi_decls, wrappers) = generate_with_monomorphization(&config);
67
68 let trait_ffi_decls = generate_trait_ffi_declarations(&config);
70
71 let trait_impls = generate_trait_implementations(&config);
73
74 quote! {
75 #enum_defs
77
78 #struct_defs
80
81 #trait_impl_types
83
84 mod #mod_name {
86 use super::*; #ffi_decls
88 #trait_ffi_decls
89 }
90
91 #wrappers
93
94 #trait_impls
96 }
97 } else {
98 quote! {
100 compile_error!("autozig! macro requires Rust function signatures after --- separator");
103 }
104 };
105
106 TokenStream::from(output)
107}
108
109fn generate_enum_definitions(config: &AutoZigConfig) -> proc_macro2::TokenStream {
111 let enums: Vec<_> = config.rust_enums.iter().map(|e| &e.item).collect();
112
113 quote! {
114 #(#enums)*
115 }
116}
117
118fn generate_struct_definitions(config: &AutoZigConfig) -> proc_macro2::TokenStream {
120 let structs: Vec<_> = config.rust_structs.iter().map(|s| &s.item).collect();
121
122 quote! {
123 #(#structs)*
124 }
125}
126
127fn is_slice_or_str_ref(ty: &syn::Type) -> Option<(bool, Option<syn::Type>)> {
129 if let syn::Type::Reference(type_ref) = ty {
130 let is_mut = type_ref.mutability.is_some();
131
132 if let syn::Type::Path(type_path) = &*type_ref.elem {
134 if type_path.path.is_ident("str") {
135 return Some((is_mut, None)); }
137 }
138
139 if let syn::Type::Slice(type_slice) = &*type_ref.elem {
141 return Some((is_mut, Some((*type_slice.elem).clone())));
142 }
143 }
144 None
145}
146
147
148fn generate_trait_impl_types(config: &AutoZigConfig) -> proc_macro2::TokenStream {
152 let mut type_defs = Vec::new();
153 let mut generated_types = std::collections::HashSet::new();
154
155 for trait_impl in &config.rust_trait_impls {
156 if generated_types.contains(&trait_impl.target_type) {
158 continue;
159 }
160 generated_types.insert(trait_impl.target_type.clone());
161
162 let type_name = syn::Ident::new(&trait_impl.target_type, proc_macro2::Span::call_site());
163
164 if trait_impl.is_opaque {
165 type_defs.push(generate_opaque_struct(&type_name));
167 } else if trait_impl.is_zst {
168 type_defs.push(quote! {
170 #[derive(Default, Debug, Clone, Copy)]
171 pub struct #type_name;
172 });
173 }
174 }
175
176 quote! {
177 #(#type_defs)*
178 }
179}
180
181fn generate_opaque_struct(type_name: &syn::Ident) -> proc_macro2::TokenStream {
183 quote! {
184 pub struct #type_name {
185 inner: std::ptr::NonNull<std::ffi::c_void>,
186 _marker: std::marker::PhantomData<*mut ()>,
187 }
188
189 impl Default for #type_name {
194 fn default() -> Self {
195 Self::new()
196 }
197 }
198 }
199}
200
201fn generate_trait_implementations(config: &AutoZigConfig) -> proc_macro2::TokenStream {
203 let mut impls = Vec::new();
204 let mod_name = syn::Ident::new(config.get_mod_name(), proc_macro2::Span::call_site());
205
206 for trait_impl in &config.rust_trait_impls {
207 let type_name = syn::Ident::new(&trait_impl.target_type, proc_macro2::Span::call_site());
208
209 if let Some(constructor) = &trait_impl.constructor {
211 impls.push(generate_constructor(&type_name, constructor, &mod_name));
212 }
213
214 if let Some(destructor) = &trait_impl.destructor {
216 impls.push(generate_drop_impl(&type_name, destructor, &mod_name));
217 }
218
219 if trait_impl.trait_name.is_empty() {
221 continue;
222 }
223
224 let trait_name = syn::Ident::new(&trait_impl.trait_name, proc_macro2::Span::call_site());
225
226 let mut methods = Vec::new();
228 for method in &trait_impl.methods {
229 let method_sig = &method.sig;
230 let method_name = &method_sig.ident;
231 let inputs = &method_sig.inputs;
232 let return_type = &method_sig.output;
233
234 let should_generate_ffi_call = trait_impl.is_opaque || method.body.is_none();
238
239 if !should_generate_ffi_call {
240 if let Some(original_body) = &method.body {
243 methods.push(quote! {
244 fn #method_name(#inputs) #return_type {
245 unsafe #original_body
246 }
247 });
248 }
249 } else {
250 let zig_fn = syn::Ident::new(&method.zig_function, proc_macro2::Span::call_site());
252
253 let mut ffi_args = Vec::new();
254
255 if trait_impl.is_opaque {
257 ffi_args.push(inject_self_pointer(method_sig));
258 }
259
260 for input in &method_sig.inputs {
261 if let syn::FnArg::Receiver(_) = input {
262 continue;
264 }
265
266 if let syn::FnArg::Typed(pat_type) = input {
267 if let syn::Pat::Ident(ident) = &*pat_type.pat {
268 let param_name = &ident.ident;
269
270 if let Some((is_mut, _elem_type)) = is_slice_or_str_ref(&pat_type.ty) {
271 if is_mut {
272 ffi_args.push(quote! { #param_name.as_mut_ptr() });
273 } else {
274 ffi_args.push(quote! { #param_name.as_ptr() });
275 }
276 ffi_args.push(quote! { #param_name.len() });
277 } else {
278 ffi_args.push(quote! { #param_name });
279 }
280 }
281 }
282 }
283
284 methods.push(quote! {
285 fn #method_name(#inputs) #return_type {
286 unsafe {
287 #mod_name::#zig_fn(#(#ffi_args),*)
288 }
289 }
290 });
291 }
292 }
293
294 impls.push(quote! {
296 impl #trait_name for #type_name {
297 #(#methods)*
298 }
299 });
300 }
301
302 quote! {
303 #(#impls)*
304 }
305}
306
307fn generate_constructor(
309 type_name: &syn::Ident,
310 constructor: &autozig_parser::TraitMethod,
311 mod_name: &syn::Ident,
312) -> proc_macro2::TokenStream {
313 let zig_fn = syn::Ident::new(&constructor.zig_function, proc_macro2::Span::call_site());
314 let method_name = syn::Ident::new(&constructor.name, proc_macro2::Span::call_site());
315
316 let params: Vec<_> = constructor
318 .sig
319 .inputs
320 .iter()
321 .filter_map(|input| {
322 if let syn::FnArg::Typed(pat_type) = input {
323 Some(pat_type)
324 } else {
325 None
326 }
327 })
328 .collect();
329
330 let param_names: Vec<_> = params
331 .iter()
332 .filter_map(|pat_type| {
333 if let syn::Pat::Ident(ident) = &*pat_type.pat {
334 Some(&ident.ident)
335 } else {
336 None
337 }
338 })
339 .collect();
340
341 let inputs = &constructor.sig.inputs;
342
343 quote! {
344 impl #type_name {
345 pub fn #method_name(#inputs) -> Self {
346 unsafe {
347 let ptr = #mod_name::#zig_fn(#(#param_names),*);
348 std::ptr::NonNull::new(ptr as *mut std::ffi::c_void)
349 .map(|inner| Self {
350 inner,
351 _marker: std::marker::PhantomData,
352 })
353 .expect("Zig allocation failed (OOM)")
354 }
355 }
356 }
357 }
358}
359
360fn generate_drop_impl(
362 type_name: &syn::Ident,
363 destructor: &autozig_parser::TraitMethod,
364 mod_name: &syn::Ident,
365) -> proc_macro2::TokenStream {
366 let zig_fn = syn::Ident::new(&destructor.zig_function, proc_macro2::Span::call_site());
367
368 quote! {
369 impl Drop for #type_name {
370 fn drop(&mut self) {
371 unsafe {
372 #mod_name::#zig_fn(self.inner.as_ptr());
373 }
374 }
375 }
376 }
377}
378
379fn inject_self_pointer(sig: &syn::Signature) -> proc_macro2::TokenStream {
381 for input in &sig.inputs {
383 if let syn::FnArg::Receiver(receiver) = input {
384 if receiver.mutability.is_some() {
385 return quote! { self.inner.as_ptr() };
387 } else {
388 return quote! { self.inner.as_ptr() as *const std::ffi::c_void };
390 }
391 }
392 }
393
394 quote! {}
396}
397
398fn generate_trait_ffi_declarations(config: &AutoZigConfig) -> proc_macro2::TokenStream {
401 let mut decls = Vec::new();
402
403 for trait_impl in &config.rust_trait_impls {
404 if let Some(constructor) = &trait_impl.constructor {
406 let zig_fn = syn::Ident::new(&constructor.zig_function, proc_macro2::Span::call_site());
407 let params: Vec<_> = constructor
408 .sig
409 .inputs
410 .iter()
411 .filter_map(|input| {
412 if let syn::FnArg::Typed(pat_type) = input {
413 let param_name = &pat_type.pat;
414 let param_type = &pat_type.ty;
415 Some(quote! { #param_name: #param_type })
416 } else {
417 None
418 }
419 })
420 .collect();
421
422 decls.push(quote! {
423 extern "C" {
424 pub fn #zig_fn(#(#params),*) -> *mut std::ffi::c_void;
425 }
426 });
427 }
428
429 if let Some(destructor) = &trait_impl.destructor {
431 let zig_fn = syn::Ident::new(&destructor.zig_function, proc_macro2::Span::call_site());
432
433 decls.push(quote! {
434 extern "C" {
435 pub fn #zig_fn(ptr: *mut std::ffi::c_void);
436 }
437 });
438 }
439
440 for method in &trait_impl.methods {
441 let zig_fn = syn::Ident::new(&method.zig_function, proc_macro2::Span::call_site());
442 let method_sig = &method.sig;
443
444 let mut ffi_params = Vec::new();
446
447 if trait_impl.is_opaque {
449 let self_param = handle_receiver_type(method_sig);
450 if !self_param.is_empty() {
451 ffi_params.push(self_param);
452 }
453 }
454
455 for input in &method_sig.inputs {
456 if let syn::FnArg::Receiver(_) = input {
457 continue;
460 }
461
462 if let syn::FnArg::Typed(pat_type) = input {
463 let param_name = &pat_type.pat;
464 let param_type = &pat_type.ty;
465
466 if let Some((is_mut, elem_type)) = is_slice_or_str_ref(param_type) {
468 let param_name_str = if let syn::Pat::Ident(ident) = &*pat_type.pat {
470 ident.ident.to_string()
471 } else {
472 continue;
473 };
474
475 let ptr_type = if let Some(elem) = elem_type {
477 if is_mut {
478 quote! { *mut #elem }
479 } else {
480 quote! { *const #elem }
481 }
482 } else if is_mut {
483 quote! { *mut u8 }
484 } else {
485 quote! { *const u8 }
486 };
487
488 let ptr_name = quote::format_ident!("{}_ptr", param_name_str);
489 let len_name = quote::format_ident!("{}_len", param_name_str);
490
491 ffi_params.push(quote! { #ptr_name: #ptr_type });
492 ffi_params.push(quote! { #len_name: usize });
493 } else {
494 ffi_params.push(quote! { #param_name: #param_type });
495 }
496 }
497 }
498
499 let zig_return_type = extract_zig_return_type(&config.zig_code, &method.zig_function);
501 let return_type = if let Some(zig_ret) = zig_return_type {
502 zig_ret
503 } else {
504 method_sig.output.clone()
508 };
509
510 decls.push(quote! {
511 extern "C" {
512 pub fn #zig_fn(#(#ffi_params),*) #return_type;
513 }
514 });
515 }
516 }
517
518 quote! {
519 #(#decls)*
520 }
521}
522
523fn extract_zig_return_type(zig_code: &str, fn_name: &str) -> Option<syn::ReturnType> {
526 let search_pattern1 = format!("export fn {}", fn_name);
529 let search_pattern2 = format!("export fn\n{}", fn_name);
530
531 let start_pos = if let Some(pos) = zig_code.find(&search_pattern1) {
532 pos
533 } else if let Some(pos) = zig_code.find(&search_pattern2) {
534 pos
535 } else {
536 return None;
537 };
538
539 let after_fn = &zig_code[start_pos..];
541 let paren_start = after_fn.find('(')?;
542 let mut paren_count = 1;
543 let mut paren_end = paren_start + 1;
544
545 for (i, ch) in after_fn[paren_start + 1..].chars().enumerate() {
546 match ch {
547 '(' => paren_count += 1,
548 ')' => {
549 paren_count -= 1;
550 if paren_count == 0 {
551 paren_end = paren_start + 1 + i;
552 break;
553 }
554 },
555 _ => {},
556 }
557 }
558
559 let after_paren = &after_fn[paren_end + 1..];
561 let brace_pos = after_paren.find('{')?;
562 let return_type_str = after_paren[..brace_pos].trim();
563
564 let rust_type = match return_type_str {
566 "i32" => quote! { -> i32 },
567 "u32" => quote! { -> u32 },
568 "i64" => quote! { -> i64 },
569 "u64" => quote! { -> u64 },
570 "f32" => quote! { -> f32 },
571 "f64" => quote! { -> f64 },
572 "bool" => quote! { -> bool },
573 "void" => quote! {},
574 _ => return None, };
576
577 syn::parse2(rust_type).ok()
578}
579
580fn handle_receiver_type(sig: &syn::Signature) -> proc_macro2::TokenStream {
583 for input in &sig.inputs {
584 if let syn::FnArg::Receiver(receiver) = input {
585 if receiver.mutability.is_some() {
586 return quote! { self_ptr: *mut std::ffi::c_void };
588 } else {
589 return quote! { self_ptr: *const std::ffi::c_void };
591 }
592 }
593 }
594 quote! {}
595}
596
597#[proc_macro_error]
610#[proc_macro]
611pub fn include_zig(input: TokenStream) -> TokenStream {
612 let config = parse_macro_input!(input as IncludeZigConfig);
613
614 let mod_name = config.get_unique_mod_name();
617 let mod_name_ident = syn::Ident::new(&mod_name, proc_macro2::Span::call_site());
618 let file_path = &config.file_path;
619
620 let marker_code = format!("// @autozig:include:{}", file_path);
623
624 let output = if config.has_rust_signatures()
625 || !config.rust_structs.is_empty()
626 || !config.rust_enums.is_empty()
627 {
628 let enum_defs = generate_enum_definitions_for_include(&config);
630
631 let struct_defs = generate_struct_definitions_for_include(&config);
633
634 let (ffi_decls, wrappers) = generate_with_monomorphization_for_include(&config);
636
637 quote! {
638 #[doc = #marker_code]
640
641 #enum_defs
643
644 #struct_defs
646
647 mod #mod_name_ident {
649 use super::*;
650 #ffi_decls
651 }
652
653 #wrappers
655 }
656 } else {
657 quote! {
658 #[doc = #marker_code]
659 compile_error!("include_zig! macro requires Rust function signatures");
660 }
661 };
662
663 TokenStream::from(output)
664}
665
666fn generate_enum_definitions_for_include(config: &IncludeZigConfig) -> proc_macro2::TokenStream {
668 let enums: Vec<_> = config.rust_enums.iter().map(|e| &e.item).collect();
669 quote! {
670 #(#enums)*
671 }
672}
673
674fn generate_struct_definitions_for_include(config: &IncludeZigConfig) -> proc_macro2::TokenStream {
675 let structs: Vec<_> = config.rust_structs.iter().map(|s| &s.item).collect();
676 quote! {
677 #(#structs)*
678 }
679}
680
681#[allow(dead_code)]
682fn generate_ffi_declarations_for_include(config: &IncludeZigConfig) -> proc_macro2::TokenStream {
683 let mut decls = Vec::new();
684
685 for rust_sig in &config.rust_signatures {
686 let sig = &rust_sig.sig;
687 let fn_name = &sig.ident;
688 let output = &sig.output;
689
690 let mut ffi_params = Vec::new();
691
692 for input in &sig.inputs {
693 if let syn::FnArg::Typed(pat_type) = input {
694 let param_type = &pat_type.ty;
695 let param_name_str = if let syn::Pat::Ident(ident) = &*pat_type.pat {
696 ident.ident.to_string()
697 } else {
698 continue;
699 };
700
701 if let Some((is_mut, elem_type)) = is_slice_or_str_ref(param_type) {
702 let ptr_type = if let Some(elem) = elem_type {
703 if is_mut {
704 quote! { *mut #elem }
705 } else {
706 quote! { *const #elem }
707 }
708 } else if is_mut {
709 quote! { *mut u8 }
710 } else {
711 quote! { *const u8 }
712 };
713
714 let ptr_name = quote::format_ident!("{}_ptr", param_name_str);
715 let len_name = quote::format_ident!("{}_len", param_name_str);
716
717 ffi_params.push(quote! { #ptr_name: #ptr_type });
718 ffi_params.push(quote! { #len_name: usize });
719 } else {
720 let param_name = &pat_type.pat;
721 ffi_params.push(quote! { #param_name: #param_type });
722 }
723 }
724 }
725
726 decls.push(quote! {
727 extern "C" {
728 pub fn #fn_name(#(#ffi_params),*) #output;
729 }
730 });
731 }
732
733 quote! {
734 #(#decls)*
735 }
736}
737
738#[allow(dead_code)]
739fn generate_safe_wrappers_for_include(config: &IncludeZigConfig) -> proc_macro2::TokenStream {
740 let mut wrappers = Vec::new();
741 let mod_name_str = config.get_unique_mod_name();
742 let mod_name = syn::Ident::new(&mod_name_str, proc_macro2::Span::call_site());
743
744 for rust_sig in &config.rust_signatures {
745 let sig = &rust_sig.sig;
746 let fn_name = &sig.ident;
747 let inputs = &sig.inputs;
748 let output = &sig.output;
749
750 let mut ffi_args = Vec::new();
751
752 for input in &sig.inputs {
753 if let syn::FnArg::Typed(pat_type) = input {
754 if let syn::Pat::Ident(ident) = &*pat_type.pat {
755 let param_name = &ident.ident;
756 let param_type = &pat_type.ty;
757
758 if let Some((is_mut, _elem_type)) = is_slice_or_str_ref(param_type) {
759 if is_mut {
760 ffi_args.push(quote! { #param_name.as_mut_ptr() });
761 } else {
762 ffi_args.push(quote! { #param_name.as_ptr() });
763 }
764 ffi_args.push(quote! { #param_name.len() });
765 } else {
766 ffi_args.push(quote! { #param_name });
767 }
768 }
769 }
770 }
771
772 let wrapper = quote! {
773 pub fn #fn_name(#inputs) #output {
774 unsafe {
775 #mod_name::#fn_name(#(#ffi_args),*)
776 }
777 }
778 };
779
780 wrappers.push(wrapper);
781 }
782
783 quote! {
784 #(#wrappers)*
785 }
786}
787
788#[allow(dead_code)]
789fn generate_trait_impl_types_for_include(config: &IncludeZigConfig) -> proc_macro2::TokenStream {
790 let mut type_defs = Vec::new();
791
792 for trait_impl in &config.rust_trait_impls {
793 if trait_impl.is_zst {
794 let type_name =
795 syn::Ident::new(&trait_impl.target_type, proc_macro2::Span::call_site());
796
797 type_defs.push(quote! {
798 #[derive(Default, Debug, Clone, Copy)]
799 pub struct #type_name;
800 });
801 }
802 }
803
804 quote! {
805 #(#type_defs)*
806 }
807}
808
809#[allow(dead_code)]
810fn generate_trait_implementations_for_include(
811 config: &IncludeZigConfig,
812) -> proc_macro2::TokenStream {
813 let mut impls = Vec::new();
814 let mod_name_str = config.get_unique_mod_name();
815 let mod_name = syn::Ident::new(&mod_name_str, proc_macro2::Span::call_site());
816
817 for trait_impl in &config.rust_trait_impls {
818 let trait_name = syn::Ident::new(&trait_impl.trait_name, proc_macro2::Span::call_site());
819 let type_name = syn::Ident::new(&trait_impl.target_type, proc_macro2::Span::call_site());
820
821 let mut methods = Vec::new();
822 for method in &trait_impl.methods {
823 let method_sig = &method.sig;
824 let method_name = &method_sig.ident;
825 let zig_fn = syn::Ident::new(&method.zig_function, proc_macro2::Span::call_site());
826
827 let mut ffi_args = Vec::new();
828 for input in &method_sig.inputs {
829 if let syn::FnArg::Typed(pat_type) = input {
830 if let syn::Pat::Ident(ident) = &*pat_type.pat {
831 let param_name = &ident.ident;
832
833 if let Some((is_mut, _elem_type)) = is_slice_or_str_ref(&pat_type.ty) {
834 if is_mut {
835 ffi_args.push(quote! { #param_name.as_mut_ptr() });
836 } else {
837 ffi_args.push(quote! { #param_name.as_ptr() });
838 }
839 ffi_args.push(quote! { #param_name.len() });
840 } else {
841 ffi_args.push(quote! { #param_name });
842 }
843 }
844 }
845 }
846
847 let return_type = &method_sig.output;
848
849 methods.push(quote! {
850 fn #method_name(#method_sig) #return_type {
851 unsafe {
852 #mod_name::#zig_fn(#(#ffi_args),*)
853 }
854 }
855 });
856 }
857
858 impls.push(quote! {
859 impl #trait_name for #type_name {
860 #(#methods)*
861 }
862 });
863 }
864
865 quote! {
866 #(#impls)*
867 }
868}
869
870fn generate_with_monomorphization(
877 config: &AutoZigConfig,
878) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
879 let mut all_ffi_decls = Vec::new();
880 let mut all_wrappers = Vec::new();
881
882 for rust_sig in &config.rust_signatures {
883 if !rust_sig.generic_params.is_empty() && !rust_sig.monomorphize_types.is_empty() {
884 let (mono_ffi, mono_wrappers) =
886 generate_monomorphized_versions(rust_sig, config.get_mod_name());
887 all_ffi_decls.push(mono_ffi);
888 all_wrappers.push(mono_wrappers);
889 } else if rust_sig.is_async {
890 let (async_ffi, async_wrapper) =
892 generate_async_ffi_and_wrapper(rust_sig, config.get_mod_name());
893 all_ffi_decls.push(async_ffi);
894 all_wrappers.push(async_wrapper);
895 } else {
896 let ffi_decl = generate_single_ffi_declaration(rust_sig);
898 let wrapper = generate_single_safe_wrapper(rust_sig, config.get_mod_name());
899 all_ffi_decls.push(ffi_decl);
900 all_wrappers.push(wrapper);
901 }
902 }
903
904 let ffi_decls = quote! { #(#all_ffi_decls)* };
905 let wrappers = quote! { #(#all_wrappers)* };
906
907 (ffi_decls, wrappers)
908}
909
910fn generate_single_ffi_declaration(
912 rust_sig: &autozig_parser::RustFunctionSignature,
913) -> proc_macro2::TokenStream {
914 let sig = &rust_sig.sig;
915 let fn_name = &sig.ident;
916 let output = &sig.output;
917
918 let mut ffi_params = Vec::new();
919
920 for input in &sig.inputs {
921 if let syn::FnArg::Typed(pat_type) = input {
922 let param_type = &pat_type.ty;
923 let param_name_str = if let syn::Pat::Ident(ident) = &*pat_type.pat {
924 ident.ident.to_string()
925 } else {
926 continue;
927 };
928
929 if let Some((is_mut, elem_type)) = is_slice_or_str_ref(param_type) {
930 let ptr_type = if let Some(elem) = elem_type {
931 if is_mut {
932 quote! { *mut #elem }
933 } else {
934 quote! { *const #elem }
935 }
936 } else if is_mut {
937 quote! { *mut u8 }
938 } else {
939 quote! { *const u8 }
940 };
941
942 let ptr_name = quote::format_ident!("{}_ptr", param_name_str);
943 let len_name = quote::format_ident!("{}_len", param_name_str);
944
945 ffi_params.push(quote! { #ptr_name: #ptr_type });
946 ffi_params.push(quote! { #len_name: usize });
947 } else {
948 let param_name = &pat_type.pat;
949 ffi_params.push(quote! { #param_name: #param_type });
950 }
951 }
952 }
953
954 quote! {
955 extern "C" {
956 pub fn #fn_name(#(#ffi_params),*) #output;
957 }
958 }
959}
960
961fn generate_single_safe_wrapper(
963 rust_sig: &autozig_parser::RustFunctionSignature,
964 mod_name: &str,
965) -> proc_macro2::TokenStream {
966 let sig = &rust_sig.sig;
967 let fn_name = &sig.ident;
968 let inputs = &sig.inputs;
969 let output = &sig.output;
970 let mod_ident = syn::Ident::new(mod_name, proc_macro2::Span::call_site());
971
972 let mut ffi_args = Vec::new();
973
974 for input in &sig.inputs {
975 if let syn::FnArg::Typed(pat_type) = input {
976 if let syn::Pat::Ident(ident) = &*pat_type.pat {
977 let param_name = &ident.ident;
978 let param_type = &pat_type.ty;
979
980 if let Some((is_mut, _elem_type)) = is_slice_or_str_ref(param_type) {
981 if is_mut {
982 ffi_args.push(quote! { #param_name.as_mut_ptr() });
983 } else {
984 ffi_args.push(quote! { #param_name.as_ptr() });
985 }
986 ffi_args.push(quote! { #param_name.len() });
987 } else {
988 ffi_args.push(quote! { #param_name });
989 }
990 }
991 }
992 }
993
994 quote! {
995 pub fn #fn_name(#inputs) #output {
996 unsafe {
997 #mod_ident::#fn_name(#(#ffi_args),*)
998 }
999 }
1000 }
1001}
1002
1003fn generate_monomorphized_versions(
1005 rust_sig: &autozig_parser::RustFunctionSignature,
1006 mod_name: &str,
1007) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
1008 let mut ffi_decls = Vec::new();
1009 let mut wrappers = Vec::new();
1010
1011 let base_name = &rust_sig.sig.ident;
1012
1013 for mono_type in &rust_sig.monomorphize_types {
1014 let mono_name = syn::Ident::new(
1016 &format!("{}_{}", base_name, mono_type.replace("::", "_")),
1017 proc_macro2::Span::call_site(),
1018 );
1019
1020 let mono_sig = substitute_generic_type(&rust_sig.sig, mono_type);
1022
1023 let ffi_decl = generate_ffi_declaration_from_sig(&mono_name, &mono_sig);
1025 ffi_decls.push(ffi_decl);
1026
1027 let wrapper = generate_wrapper_from_sig(&mono_name, &mono_sig, mod_name);
1029 wrappers.push(wrapper);
1030 }
1031
1032 let ffi_output = quote! { #(#ffi_decls)* };
1033 let wrapper_output = quote! { #(#wrappers)* };
1034
1035 (ffi_output, wrapper_output)
1036}
1037
1038fn substitute_generic_type(sig: &syn::Signature, concrete_type: &str) -> syn::Signature {
1040 let mut new_sig = sig.clone();
1041
1042 let concrete_ty: syn::Type =
1044 syn::parse_str(concrete_type).unwrap_or_else(|_| panic!("Invalid type: {}", concrete_type));
1045
1046 let generic_name =
1048 if let Some(syn::GenericParam::Type(type_param)) = sig.generics.params.first() {
1049 type_param.ident.to_string()
1050 } else {
1051 return new_sig; };
1053
1054 new_sig.generics = syn::Generics::default();
1056
1057 for input in &mut new_sig.inputs {
1059 if let syn::FnArg::Typed(pat_type) = input {
1060 *pat_type.ty = substitute_type_recursive(&pat_type.ty, &generic_name, &concrete_ty);
1061 }
1062 }
1063
1064 if let syn::ReturnType::Type(_, ret_ty) = &mut new_sig.output {
1066 **ret_ty = substitute_type_recursive(ret_ty, &generic_name, &concrete_ty);
1067 }
1068
1069 new_sig
1070}
1071
1072fn substitute_type_recursive(
1074 ty: &syn::Type,
1075 generic_name: &str,
1076 concrete_ty: &syn::Type,
1077) -> syn::Type {
1078 match ty {
1079 syn::Type::Path(type_path) => {
1080 if type_path.path.is_ident(generic_name) {
1082 concrete_ty.clone()
1083 } else {
1084 ty.clone()
1085 }
1086 },
1087 syn::Type::Reference(type_ref) => {
1088 let mut new_ref = type_ref.clone();
1089 *new_ref.elem = substitute_type_recursive(&type_ref.elem, generic_name, concrete_ty);
1090 syn::Type::Reference(new_ref)
1091 },
1092 syn::Type::Slice(type_slice) => {
1093 let mut new_slice = type_slice.clone();
1094 *new_slice.elem =
1095 substitute_type_recursive(&type_slice.elem, generic_name, concrete_ty);
1096 syn::Type::Slice(new_slice)
1097 },
1098 _ => ty.clone(),
1099 }
1100}
1101
1102fn generate_ffi_declaration_from_sig(
1104 fn_name: &syn::Ident,
1105 sig: &syn::Signature,
1106) -> proc_macro2::TokenStream {
1107 let output = &sig.output;
1108
1109 let mut ffi_params = Vec::new();
1110
1111 for input in &sig.inputs {
1112 if let syn::FnArg::Typed(pat_type) = input {
1113 let param_type = &pat_type.ty;
1114 let param_name_str = if let syn::Pat::Ident(ident) = &*pat_type.pat {
1115 ident.ident.to_string()
1116 } else {
1117 continue;
1118 };
1119
1120 if let Some((is_mut, elem_type)) = is_slice_or_str_ref(param_type) {
1121 let ptr_type = if let Some(elem) = elem_type {
1122 if is_mut {
1123 quote! { *mut #elem }
1124 } else {
1125 quote! { *const #elem }
1126 }
1127 } else if is_mut {
1128 quote! { *mut u8 }
1129 } else {
1130 quote! { *const u8 }
1131 };
1132
1133 let ptr_name = quote::format_ident!("{}_ptr", param_name_str);
1134 let len_name = quote::format_ident!("{}_len", param_name_str);
1135
1136 ffi_params.push(quote! { #ptr_name: #ptr_type });
1137 ffi_params.push(quote! { #len_name: usize });
1138 } else {
1139 let param_name = &pat_type.pat;
1140 ffi_params.push(quote! { #param_name: #param_type });
1141 }
1142 }
1143 }
1144
1145 quote! {
1146 extern "C" {
1147 pub fn #fn_name(#(#ffi_params),*) #output;
1148 }
1149 }
1150}
1151
1152fn generate_wrapper_from_sig(
1154 fn_name: &syn::Ident,
1155 sig: &syn::Signature,
1156 mod_name: &str,
1157) -> proc_macro2::TokenStream {
1158 let mod_ident = syn::Ident::new(mod_name, proc_macro2::Span::call_site());
1159 let inputs = &sig.inputs;
1160 let output = &sig.output;
1161
1162 let mut ffi_args = Vec::new();
1163
1164 for input in &sig.inputs {
1165 if let syn::FnArg::Typed(pat_type) = input {
1166 if let syn::Pat::Ident(ident) = &*pat_type.pat {
1167 let param_name = &ident.ident;
1168 let param_type = &pat_type.ty;
1169
1170 if let Some((is_mut, _elem_type)) = is_slice_or_str_ref(param_type) {
1171 if is_mut {
1172 ffi_args.push(quote! { #param_name.as_mut_ptr() });
1173 } else {
1174 ffi_args.push(quote! { #param_name.as_ptr() });
1175 }
1176 ffi_args.push(quote! { #param_name.len() });
1177 } else {
1178 ffi_args.push(quote! { #param_name });
1179 }
1180 }
1181 }
1182 }
1183
1184 quote! {
1185 pub fn #fn_name(#inputs) #output {
1187 unsafe {
1188 #mod_ident::#fn_name(#(#ffi_args),*)
1189 }
1190 }
1191 }
1192}
1193
1194fn generate_async_ffi_and_wrapper(
1200 rust_sig: &autozig_parser::RustFunctionSignature,
1201 mod_name: &str,
1202) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
1203 let fn_name = &rust_sig.sig.ident;
1204 let sig = &rust_sig.sig;
1205
1206 let ffi_decl = generate_ffi_declaration_from_sig(fn_name, sig);
1209
1210 let inputs = &sig.inputs;
1212 let output = &sig.output;
1213 let mod_ident = syn::Ident::new(mod_name, proc_macro2::Span::call_site());
1214
1215 let mut ffi_args = Vec::new();
1216 let mut param_captures = Vec::new();
1217
1218 for input in &sig.inputs {
1219 if let syn::FnArg::Typed(pat_type) = input {
1220 if let syn::Pat::Ident(ident) = &*pat_type.pat {
1221 let param_name = &ident.ident;
1222 let param_type = &pat_type.ty;
1223
1224 if let Some((_is_mut, _elem_type)) = is_slice_or_str_ref(param_type) {
1227 param_captures.push(quote! {
1229 let #param_name = #param_name.to_vec();
1230 });
1231
1232 ffi_args.push(quote! { #param_name.as_ptr() });
1233 ffi_args.push(quote! { #param_name.len() });
1234 } else {
1235 ffi_args.push(quote! { #param_name });
1237 }
1238 }
1239 }
1240 }
1241
1242 let wrapper = quote! {
1244 pub async fn #fn_name(#inputs) #output {
1252 #(#param_captures)*
1254
1255 tokio::task::spawn_blocking(move || {
1257 unsafe {
1258 #mod_ident::#fn_name(#(#ffi_args),*)
1259 }
1260 })
1261 .await
1262 .expect("Zig task panicked or was cancelled")
1263 }
1264 };
1265
1266 (ffi_decl, wrapper)
1267}
1268
1269fn generate_with_monomorphization_for_include(
1272 config: &IncludeZigConfig,
1273) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
1274 let mut all_ffi_decls = Vec::new();
1275 let mut all_wrappers = Vec::new();
1276 let mod_name = config.get_unique_mod_name();
1277
1278 for rust_sig in &config.rust_signatures {
1279 if !rust_sig.generic_params.is_empty() && !rust_sig.monomorphize_types.is_empty() {
1280 let (mono_ffi, mono_wrappers) = generate_monomorphized_versions(rust_sig, &mod_name);
1282 all_ffi_decls.push(mono_ffi);
1283 all_wrappers.push(mono_wrappers);
1284 } else if rust_sig.is_async {
1285 let (async_ffi, async_wrapper) = generate_async_ffi_and_wrapper(rust_sig, &mod_name);
1287 all_ffi_decls.push(async_ffi);
1288 all_wrappers.push(async_wrapper);
1289 } else {
1290 let ffi_decl = generate_single_ffi_declaration(rust_sig);
1292 let wrapper = generate_single_safe_wrapper(rust_sig, &mod_name);
1293 all_ffi_decls.push(ffi_decl);
1294 all_wrappers.push(wrapper);
1295 }
1296 }
1297
1298 let ffi_decls = quote! { #(#all_ffi_decls)* };
1299 let wrappers = quote! { #(#all_wrappers)* };
1300
1301 (ffi_decls, wrappers)
1302}
1303
1304#[cfg(test)]
1305mod tests {
1306 }