autozig_macro/
lib.rs

1//! Procedural macro implementation for autozig!
2//!
3//! This macro processes mixed Zig/Rust code and generates safe bindings
4//! using IDL-driven FFI generation (no bindgen required)
5
6#![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/// Main autozig! procedural macro
18///
19/// # Syntax
20///
21/// ```rust,ignore
22/// autozig! {
23///     // Zig code section
24///     const std = @import("std");
25///     export fn my_function(a: i32) i32 {
26///         return a * 2;
27///     }
28///     
29///     ---
30///     
31///     // Rust signatures for safe wrappers (optional)
32///     fn my_function(a: i32) -> i32;
33/// }
34/// ```
35///
36/// The macro will:
37/// 1. Extract Zig code to be compiled by build.rs (via Scanner)
38/// 2. Generate extern "C" FFI bindings directly from Rust signatures
39///    (IDL-driven)
40/// 3. Generate safe Rust wrappers
41#[proc_macro_error]
42#[proc_macro]
43pub fn autozig(input: TokenStream) -> TokenStream {
44    let config = parse_macro_input!(input as AutoZigConfig);
45
46    // Generate code with IDL-driven FFI
47    // No bindgen needed - we generate extern "C" directly from user signatures
48    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        // Generate enum definitions (must come before struct definitions)
56        let enum_defs = generate_enum_definitions(&config);
57
58        // Generate struct definitions (must come before FFI declarations that use them)
59        let struct_defs = generate_struct_definitions(&config);
60
61        // Generate trait impl target types (ZST structs for Phase 1)
62        let trait_impl_types = generate_trait_impl_types(&config);
63
64        // Phase 3: Generate FFI declarations and wrappers with monomorphization and
65        // async support
66        let (ffi_decls, wrappers) = generate_with_monomorphization(&config);
67
68        // Generate trait FFI declarations
69        let trait_ffi_decls = generate_trait_ffi_declarations(&config);
70
71        // Generate trait implementations
72        let trait_impls = generate_trait_implementations(&config);
73
74        quote! {
75            // Enum definitions (visible at module level)
76            #enum_defs
77
78            // Struct definitions (visible at module level)
79            #struct_defs
80
81            // Trait impl target types (ZST structs)
82            #trait_impl_types
83
84            // Raw FFI module with extern "C" declarations
85            mod #mod_name {
86                use super::*;  // Import enums and structs from parent scope
87                #ffi_decls
88                #trait_ffi_decls
89            }
90
91            // Safe wrappers
92            #wrappers
93
94            // Trait implementations
95            #trait_impls
96        }
97    } else {
98        // No signatures provided - user must write their own FFI declarations
99        quote! {
100            // Note: No Rust signatures provided in autozig! macro
101            // You must manually declare extern "C" functions or provide signatures after ---
102            compile_error!("autozig! macro requires Rust function signatures after --- separator");
103        }
104    };
105
106    TokenStream::from(output)
107}
108
109/// Generate enum definitions from IDL
110fn 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
118/// Generate struct definitions from IDL
119fn 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
127/// Check if a type is a reference to a slice or str
128fn 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        // Check for &str or &mut str
133        if let syn::Type::Path(type_path) = &*type_ref.elem {
134            if type_path.path.is_ident("str") {
135                return Some((is_mut, None)); // str has no element type
136            }
137        }
138
139        // Check for &[T] or &mut [T]
140        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
148/// Generate ZST struct types for trait implementations (Phase 1)
149/// Generate Opaque Pointer struct types for stateful trait implementations
150/// (Phase 2)
151fn 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        // Skip if we've already generated this type
157        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            // Phase 2: Generate opaque pointer struct
166            type_defs.push(generate_opaque_struct(&type_name));
167        } else if trait_impl.is_zst {
168            // Phase 1: Generate zero-sized type with Default derive
169            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
181/// Generate an opaque pointer struct (Phase 2)
182fn 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        // Opaque types are !Send and !Sync by default (via PhantomData<*mut ()>)
190        // Users can manually implement Send/Sync if their Zig code is thread-safe
191
192        // Implement Default by calling the constructor (if available)
193        impl Default for #type_name {
194            fn default() -> Self {
195                Self::new()
196            }
197        }
198    }
199}
200
201/// Generate trait implementations (Phase 1 & 2)
202fn 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        // Phase 2: Generate constructor if present
210        if let Some(constructor) = &trait_impl.constructor {
211            impls.push(generate_constructor(&type_name, constructor, &mod_name));
212        }
213
214        // Phase 2: Generate Drop implementation if destructor present
215        if let Some(destructor) = &trait_impl.destructor {
216            impls.push(generate_drop_impl(&type_name, destructor, &mod_name));
217        }
218
219        // Skip trait impl generation if this is an inherent impl (empty trait name)
220        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        // Generate methods for the trait implementation
227        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            // Phase 2: For opaque types, always generate FFI call (ignore user's simplified
235            // body) Phase 1: Use original method body if available (preserves
236            // user logic like Option wrapping)
237            let should_generate_ffi_call = trait_impl.is_opaque || method.body.is_none();
238
239            if !should_generate_ffi_call {
240                // Phase 1: Use the original body with unsafe wrapper (for ZST with complex
241                // logic)
242                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                // Fallback: generate simple FFI call
251                let zig_fn = syn::Ident::new(&method.zig_function, proc_macro2::Span::call_site());
252
253                let mut ffi_args = Vec::new();
254
255                // Phase 2: Inject self pointer for opaque types
256                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                        // Skip self/&self/&mut self - already handled above
263                        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        // Generate the complete impl block
295        impls.push(quote! {
296            impl #trait_name for #type_name {
297                #(#methods)*
298            }
299        });
300    }
301
302    quote! {
303        #(#impls)*
304    }
305}
306
307/// Generate constructor for opaque types (Phase 2)
308fn 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    // Get parameters (excluding self)
317    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
360/// Generate Drop implementation for opaque types (Phase 2)
361fn 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
379/// Inject self pointer as first argument for opaque types (Phase 2)
380fn inject_self_pointer(sig: &syn::Signature) -> proc_macro2::TokenStream {
381    // Check receiver type: &self or &mut self
382    for input in &sig.inputs {
383        if let syn::FnArg::Receiver(receiver) = input {
384            if receiver.mutability.is_some() {
385                // &mut self -> *mut c_void
386                return quote! { self.inner.as_ptr() };
387            } else {
388                // &self -> *const c_void
389                return quote! { self.inner.as_ptr() as *const std::ffi::c_void };
390            }
391        }
392    }
393
394    // No receiver, shouldn't happen for trait methods
395    quote! {}
396}
397
398/// Generate FFI declarations for Zig functions used in trait implementations
399/// (Phase 1 & 2)
400fn 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        // Phase 2: Generate constructor FFI declaration
405        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        // Phase 2: Generate destructor FFI declaration
430        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            // Build FFI parameter list
445            let mut ffi_params = Vec::new();
446
447            // Phase 2: Add self pointer parameter for opaque types
448            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                    // Skip &self / &mut self for ZST (Phase 1)
458                    // For opaque types, already handled above
459                    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                    // Check if this is a slice or str reference
467                    if let Some((is_mut, elem_type)) = is_slice_or_str_ref(param_type) {
468                        // Extract parameter name as string
469                        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                        // Lower to ptr + len
476                        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            // Extract Zig function return type from Zig code
500            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                // Fallback: use method signature for most cases, but we need special handling
505                // If the method returns Option but doesn't have the Option in its body,
506                // it likely means the Zig function returns the unwrapped type
507                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
523/// Extract return type from Zig function definition
524/// Looks for patterns like: `export fn function_name(...) TYPE {`
525fn extract_zig_return_type(zig_code: &str, fn_name: &str) -> Option<syn::ReturnType> {
526    // Simple string-based extraction
527    // Find "export fn function_name" - handle possible newline before function name
528    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    // Find the closing parenthesis of parameters
540    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    // Extract return type between ')' and '{'
560    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    // Map Zig types to Rust types
565    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, // Unknown type, fall back to method signature
575    };
576
577    syn::parse2(rust_type).ok()
578}
579
580/// Handle receiver type for FFI parameter list (Phase 2)
581/// Returns the self pointer parameter for opaque types
582fn 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                // &mut self -> *mut c_void
587                return quote! { self_ptr: *mut std::ffi::c_void };
588            } else {
589                // &self -> *const c_void
590                return quote! { self_ptr: *const std::ffi::c_void };
591            }
592        }
593    }
594    quote! {}
595}
596
597/// include_zig! macro for referencing external Zig files
598///
599/// # Syntax
600///
601/// ```rust,ignore
602/// include_zig!("path/to/file.zig", {
603///     // Rust function signatures
604///     fn my_function(a: i32) -> i32;
605/// });
606/// ```
607///
608/// The path is relative to the Cargo manifest directory.
609#[proc_macro_error]
610#[proc_macro]
611pub fn include_zig(input: TokenStream) -> TokenStream {
612    let config = parse_macro_input!(input as IncludeZigConfig);
613
614    // Generate unique module name based on file path
615    // Convert "zig/math.zig" to "ffi_zig_math"
616    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    // Generate a marker that build.rs can detect
621    // We use a const string that scanner will find
622    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        // Generate enum definitions
629        let enum_defs = generate_enum_definitions_for_include(&config);
630
631        // Generate struct definitions
632        let struct_defs = generate_struct_definitions_for_include(&config);
633
634        // Phase 3: Use monomorphization-aware generation for include_zig! too
635        let (ffi_decls, wrappers) = generate_with_monomorphization_for_include(&config);
636
637        quote! {
638            // Marker for scanner (will be removed in final output)
639            #[doc = #marker_code]
640
641            // Enum definitions (visible at module level)
642            #enum_defs
643
644            // Struct definitions (visible at module level)
645            #struct_defs
646
647            // Raw FFI module with extern "C" declarations (unique name per file)
648            mod #mod_name_ident {
649                use super::*;
650                #ffi_decls
651            }
652
653            // Safe wrappers
654            #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
666/// Helper functions for include_zig! - reuse the same logic as autozig!
667fn 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
870// ============================================================================
871// Phase 3: Generics and Async Support
872// ============================================================================
873
874/// Phase 3: Generate FFI declarations and wrappers with monomorphization
875/// support
876fn 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            // Generic function with monomorphization attribute
885            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            // Async function
891            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            // Regular function (non-generic, non-async)
897            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
910/// Generate single FFI declaration for regular (non-generic) function
911fn 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
961/// Generate single safe wrapper for regular (non-generic) function
962fn 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
1003/// Phase 3: Generate monomorphized versions for a generic function
1004fn 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        // Generate mangled name: process<T> + i32 -> process_i32
1015        let mono_name = syn::Ident::new(
1016            &format!("{}_{}", base_name, mono_type.replace("::", "_")),
1017            proc_macro2::Span::call_site(),
1018        );
1019
1020        // Substitute generic type T with concrete type
1021        let mono_sig = substitute_generic_type(&rust_sig.sig, mono_type);
1022
1023        // Generate FFI declaration for this monomorphized version
1024        let ffi_decl = generate_ffi_declaration_from_sig(&mono_name, &mono_sig);
1025        ffi_decls.push(ffi_decl);
1026
1027        // Generate safe wrapper for this monomorphized version
1028        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
1038/// Substitute generic type parameter with concrete type
1039fn substitute_generic_type(sig: &syn::Signature, concrete_type: &str) -> syn::Signature {
1040    let mut new_sig = sig.clone();
1041
1042    // Parse concrete type
1043    let concrete_ty: syn::Type =
1044        syn::parse_str(concrete_type).unwrap_or_else(|_| panic!("Invalid type: {}", concrete_type));
1045
1046    // Get generic parameter name (e.g., "T")
1047    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; // No generics
1052        };
1053
1054    // Remove generics from signature
1055    new_sig.generics = syn::Generics::default();
1056
1057    // Substitute type in parameters
1058    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    // Substitute type in return type
1065    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
1072/// Recursively substitute generic type in a type expression
1073fn 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            // Check if this is the generic parameter
1081            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
1102/// Generate FFI declaration from signature with specific name
1103fn 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
1152/// Generate safe wrapper from signature with specific name
1153fn 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        /// Monomorphized wrapper (generated by autozig)
1186        pub fn #fn_name(#inputs) #output {
1187            unsafe {
1188                #mod_ident::#fn_name(#(#ffi_args),*)
1189            }
1190        }
1191    }
1192}
1193
1194/// Phase 3.2: Generate async FFI and wrapper using spawn_blocking pattern
1195/// Architecture: "Rust Async Wrapper, Zig Sync Execution"
1196/// - Zig writes normal synchronous code (no async/await needed in Zig)
1197/// - Rust async fn automatically uses tokio::task::spawn_blocking
1198/// - This prevents blocking the async runtime while maintaining async interface
1199fn 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    // Generate standard synchronous FFI declaration
1207    // Zig side is always synchronous - no async/await needed!
1208    let ffi_decl = generate_ffi_declaration_from_sig(fn_name, sig);
1209
1210    // Build wrapper parameters and FFI call arguments
1211    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                // For async, we need to move parameters into the closure
1225                // For slices/strings, we need to convert to owned data
1226                if let Some((_is_mut, _elem_type)) = is_slice_or_str_ref(param_type) {
1227                    // Convert slice to Vec to own the data
1228                    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                    // For Copy types, just capture them
1236                    ffi_args.push(quote! { #param_name });
1237                }
1238            }
1239        }
1240    }
1241
1242    // Generate async wrapper using spawn_blocking
1243    let wrapper = quote! {
1244        /// Async wrapper (auto-generated by AutoZig Phase 3.2)
1245        ///
1246        /// This function uses tokio::task::spawn_blocking to offload the
1247        /// synchronous Zig FFI call to a dedicated thread pool, preventing
1248        /// blocking of the async runtime.
1249        ///
1250        /// Zig side: Write normal synchronous code, no async/await needed!
1251        pub async fn #fn_name(#inputs) #output {
1252            // Capture parameters (convert slices to owned Vec)
1253            #(#param_captures)*
1254
1255            // Offload to blocking thread pool
1256            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
1269/// Phase 3: Generate FFI declarations and wrappers with monomorphization
1270/// support for include_zig!
1271fn 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            // Generic function with monomorphization attribute
1281            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            // Async function
1286            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            // Regular function (non-generic, non-async)
1291            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    // Proc macro tests would go here
1307    // Testing proc macros is tricky, usually done with integration tests
1308}