Skip to main content

rusteron_code_gen/
generator.rs

1use crate::get_possible_wrappers;
2#[allow(unused_imports)]
3use crate::snake_to_pascal_case;
4use itertools::Itertools;
5use proc_macro2::{Ident, TokenStream};
6use quote::{format_ident, quote, ToTokens};
7use std::collections::{BTreeMap, BTreeSet, HashMap};
8use std::ops::Deref;
9use std::str::FromStr;
10use syn::{parse_str, ImplItem, Item, Type};
11
12pub const COMMON_CODE: &str = include_str!("common.rs");
13pub const CLIENT_BINDINGS: &str = include_str!("../bindings/client.rs");
14pub const ARCHIVE_BINDINGS: &str = include_str!("../bindings/archive.rs");
15pub const MEDIA_DRIVER_BINDINGS: &str = include_str!("../bindings/media-driver.rs");
16
17#[derive(Debug, Clone, Default)]
18pub struct CBinding {
19    pub wrappers: BTreeMap<String, CWrapper>,
20    pub methods: Vec<Method>,
21    pub handlers: Vec<CHandler>,
22}
23
24#[derive(Debug, Clone, Eq, PartialEq)]
25pub struct Method {
26    pub fn_name: String,
27    pub struct_method_name: String,
28    pub return_type: Arg,
29    pub arguments: Vec<Arg>,
30    pub docs: BTreeSet<String>,
31}
32
33#[derive(Debug, Clone, Eq, PartialEq)]
34pub enum ArgProcessing {
35    Handler(Vec<Arg>),
36    StringWithLength(Vec<Arg>),
37    ByteArrayWithLength(Vec<Arg>),
38    Default,
39}
40
41/// Callbacks Aeron invokes only during the FFI call (e.g. `poll`'s fragment handler).
42/// The generator emits a `_fn` stack-closure variant for these.
43///
44/// Callbacks NOT listed are retained by Aeron and invoked later (conductor thread);
45/// they need a heap `Handler` registered as a dependency, and get no `_fn` variant.
46const SYNC_HANDLER_TYPES: &[&str] = &[
47    "aeron_fragment_handler_t",
48    "aeron_controlled_fragment_handler_t",
49    "aeron_block_handler_t",
50    "aeron_error_log_reader_func_t",
51    "aeron_loss_reporter_read_entry_func_t",
52    "aeron_uri_parse_callback_t",
53    "aeron_reserved_value_supplier_t",
54    "aeron_counters_reader_foreach_counter_func_t",
55    "aeron_counters_reader_foreach_metadata_func_t",
56    "aeron_str_to_ptr_hash_map_for_each_func_t",
57    "aeron_rb_handler_t",
58    "aeron_rb_controlled_handler_t",
59    "aeron_queue_drain_func_t",
60    "aeron_term_gap_scanner_on_gap_detected_func_t",
61    "aeron_archive_recording_descriptor_consumer_func_t",
62    "aeron_archive_recording_subscription_descriptor_consumer_func_t",
63];
64
65pub fn is_sync_handler_type(c_type: &str) -> bool {
66    SYNC_HANDLER_TYPES.iter().any(|t| c_type.contains(t))
67}
68
69#[derive(Debug, Clone, Eq, PartialEq)]
70pub struct Arg {
71    pub name: String,
72    pub c_type: String,
73    pub processing: ArgProcessing,
74}
75
76impl Arg {
77    pub fn is_primitive(&self) -> bool {
78        static PRIMITIVE_TYPES: &[&str] = &[
79            "i64", "u64", "f32", "f64", "i32", "i16", "u32", "u16", "bool", "usize", "isize", "c_int",
80        ];
81        PRIMITIVE_TYPES.iter().any(|&f| self.c_type.ends_with(f))
82    }
83}
84
85impl Arg {
86    const C_INT_RETURN_TYPE_STR: &'static str = ":: std :: os :: raw :: c_int";
87    const C_CHAR_STR: &'static str = "* const :: std :: os :: raw :: c_char";
88    const C_MUT_CHAR_STR: &'static str = "* mut :: std :: os :: raw :: c_char";
89    const C_BYTE_ARRAY: &'static str = "* const u8";
90    const C_BYTE_MUT_ARRAY: &'static str = "* mut u8";
91    const STAR_MUT: &'static str = "* mut";
92    const DOUBLE_STAR_MUT: &'static str = "* mut * mut";
93    const C_VOID: &'static str = "* mut :: std :: os :: raw :: c_void";
94
95    pub fn is_any_pointer(&self) -> bool {
96        self.c_type.starts_with("* const") || self.c_type.starts_with("* mut")
97    }
98
99    pub fn is_c_string(&self) -> bool {
100        self.c_type == Self::C_CHAR_STR
101    }
102
103    pub fn is_c_string_any(&self) -> bool {
104        self.is_c_string() || self.is_mut_c_string()
105    }
106
107    pub fn is_mut_c_string(&self) -> bool {
108        self.c_type == Self::C_MUT_CHAR_STR
109    }
110
111    pub fn is_usize(&self) -> bool {
112        self.c_type == "usize"
113    }
114
115    pub fn is_byte_array(&self) -> bool {
116        self.c_type == Self::C_BYTE_ARRAY || self.c_type == Self::C_BYTE_MUT_ARRAY
117    }
118
119    pub fn is_mut_byte_array(&self) -> bool {
120        self.c_type == Self::C_BYTE_MUT_ARRAY
121    }
122
123    pub fn is_c_raw_int(&self) -> bool {
124        self.c_type == Self::C_INT_RETURN_TYPE_STR
125    }
126
127    pub fn is_mut_pointer(&self) -> bool {
128        self.c_type.starts_with(Self::STAR_MUT)
129    }
130
131    pub fn is_double_mut_pointer(&self) -> bool {
132        self.c_type.starts_with(Self::DOUBLE_STAR_MUT)
133    }
134
135    pub fn is_single_mut_pointer(&self) -> bool {
136        self.is_mut_pointer() && !self.is_double_mut_pointer()
137    }
138
139    pub fn is_c_void(&self) -> bool {
140        self.c_type == Self::C_VOID
141    }
142}
143
144impl Deref for Arg {
145    type Target = str;
146
147    fn deref(&self) -> &Self::Target {
148        &self.c_type
149    }
150}
151
152impl Arg {
153    pub fn as_ident(&self) -> Ident {
154        Ident::new(&self.name, proc_macro2::Span::call_site())
155    }
156
157    pub fn as_type(&self) -> Type {
158        parse_str(&self.c_type).expect("Invalid argument type")
159    }
160}
161
162#[derive(Debug, Clone)]
163pub struct CHandler {
164    pub type_name: String,
165    pub args: Vec<Arg>,
166    pub return_type: Arg,
167    pub docs: BTreeSet<String>,
168    pub fn_mut_signature: TokenStream,
169    pub closure_type_name: TokenStream,
170}
171
172#[derive(Debug, Clone)]
173pub struct ReturnType {
174    original: Arg,
175    wrappers: BTreeMap<String, CWrapper>,
176}
177
178impl ReturnType {
179    pub fn new(original_c_type: Arg, wrappers: BTreeMap<String, CWrapper>) -> Self {
180        ReturnType {
181            original: original_c_type,
182            wrappers,
183        }
184    }
185
186    pub fn get_new_return_type(&self, convert_errors: bool, use_ref_for_cwrapper: bool) -> TokenStream {
187        if let ArgProcessing::Handler(_) = self.original.processing {
188            if self.original.name.len() > 0 {
189                if !self.original.is_mut_pointer() {
190                    let new_type =
191                        parse_str::<Type>(&format!("{}HandlerImpl", snake_to_pascal_case(&self.original.c_type)))
192                            .expect("Invalid class name in wrapper");
193                    return quote! { Option<&Handler<#new_type>> };
194                } else {
195                    return quote! {};
196                }
197            }
198        } else if let ArgProcessing::StringWithLength(_) = self.original.processing {
199            if self.original.name.len() > 0 {
200                if self.original.is_c_string() {
201                    return quote! { &str };
202                } else if self.original.is_mut_c_string() {
203                    // snprintf-style fill buffer: `&mut [u8]` as arg, `&str` as field.
204                    if use_ref_for_cwrapper {
205                        return quote! { &mut [u8] };
206                    } else {
207                        return quote! { &str };
208                    }
209                } else {
210                    return quote! {};
211                }
212            }
213        } else if let ArgProcessing::ByteArrayWithLength(_) = self.original.processing {
214            if self.original.name.len() > 0 {
215                if self.original.is_byte_array() {
216                    if self.original.is_mut_byte_array() {
217                        return quote! { &mut [u8] };
218                    } else {
219                        return quote! { &[u8] };
220                    }
221                } else {
222                    return quote! {};
223                }
224            }
225        }
226
227        if self.original.is_single_mut_pointer() {
228            let type_name = self.original.split(" ").last().unwrap();
229            if let Some(wrapper) = self.wrappers.get(type_name) {
230                let new_type = parse_str::<Type>(&wrapper.class_name).expect("Invalid class name in wrapper");
231                if use_ref_for_cwrapper {
232                    return quote! { &#new_type };
233                } else {
234                    return quote! { #new_type };
235                }
236            }
237        }
238        if let Some(wrapper) = self.wrappers.get(&self.original.c_type) {
239            let new_type = parse_str::<Type>(&wrapper.class_name).expect("Invalid class name in wrapper");
240            return quote! { #new_type };
241        }
242        if convert_errors && self.original.is_c_raw_int() {
243            return quote! { Result<i32, AeronCError> };
244        }
245        if self.original.is_c_string() {
246            // if incoming argument use &CString
247            if !convert_errors && use_ref_for_cwrapper {
248                return quote! { &std::ffi::CStr };
249            } else {
250                return quote! { &str };
251            }
252        }
253        let return_type: Type = parse_str(&self.original).expect("Invalid return type");
254        if self.original.is_single_mut_pointer() && self.original.is_primitive() {
255            let mut_type: Type =
256                parse_str(&return_type.to_token_stream().to_string().replace("* mut ", "&mut ")).unwrap();
257            return quote! { #mut_type };
258        }
259        quote! { #return_type }
260    }
261
262    pub fn handle_c_to_rs_return(&self, result: TokenStream, convert_errors: bool, use_self: bool) -> TokenStream {
263        if let ArgProcessing::StringWithLength(args) = &self.original.processing {
264            if !self.original.is_c_string_any() {
265                return quote! {};
266            }
267            // Field read: text + stored length (buffer need not be NUL-terminated).
268            let string_ptr = &args[0].as_ident();
269            let length = &args[1].as_ident();
270            let me = if use_self {
271                quote! {self.}
272            } else {
273                quote! {}
274            };
275            return quote! {
276                if #me #string_ptr.is_null() { "" } else { unsafe { std::str::from_utf8_unchecked(std::slice::from_raw_parts(#me #string_ptr as *const u8, #me #length.try_into().unwrap())) } }
277            };
278        }
279        if let ArgProcessing::ByteArrayWithLength(args) = &self.original.processing {
280            if !self.original.is_byte_array() {
281                return quote! {};
282            } else {
283                let star_const = &args[0].as_ident();
284                let length = &args[1].as_ident();
285                let me = if use_self {
286                    quote! {self.}
287                } else {
288                    quote! {}
289                };
290                if self.original.is_mut_byte_array() {
291                    return quote! {
292                        unsafe { if #me #star_const.is_null() { &mut [] as &mut [_]  } else {std::slice::from_raw_parts_mut(#me #star_const, #me #length.try_into().unwrap()) } }
293                    };
294                } else {
295                    return quote! {
296                        if #me #star_const.is_null() { &[] as &[_]  } else { std::slice::from_raw_parts(#me #star_const, #me #length.try_into().unwrap()) }
297                    };
298                }
299            }
300        }
301
302        if convert_errors && self.original.is_c_raw_int() {
303            quote! {
304                if result < 0 {
305                    return Err(AeronCError::from_code(result));
306                } else {
307                    return Ok(result)
308                }
309            }
310        } else if self.original.is_c_string() {
311            if let ArgProcessing::StringWithLength(args) = &self.original.processing {
312                let length = &args[1].as_ident();
313                return quote! { if #result.is_null() { ""} else { std::str::from_utf8_unchecked(std::slice::from_raw_parts(#result as *const u8, #length.try_into().unwrap()))}};
314            } else {
315                return quote! { if #result.is_null() { ""} else { unsafe { std::ffi::CStr::from_ptr(#result).to_str().unwrap_or("") } } };
316            }
317        } else if self.original.is_single_mut_pointer() && self.original.is_primitive() {
318            return quote! {
319                unsafe { &mut *#result }
320            };
321        } else {
322            quote! { #result.into() }
323        }
324    }
325
326    /// Generic bounds for a handler argument. `force_static` is used by constructors,
327    /// where the handler is always cloned into the created resource's dependencies
328    /// (requiring `'static` for the `dyn Any` storage). For plain methods, only
329    /// *retained* handlers (see [`SYNC_HANDLER_TYPES`]) get the `'static` bound —
330    /// synchronous handlers may borrow local state, and their `_fn` closure variants
331    /// rely on that.
332    pub fn method_generics_for_where(&self, force_static: bool) -> Option<TokenStream> {
333        if let ArgProcessing::Handler(handler_client) = &self.original.processing {
334            if !self.original.is_mut_pointer() {
335                let handler = handler_client.get(0).unwrap();
336                let new_type = parse_str::<Type>(&format!("{}HandlerImpl", snake_to_pascal_case(&handler.c_type)))
337                    .expect("Invalid class name in wrapper");
338                let new_handler = parse_str::<Type>(&format!("{}Callback", snake_to_pascal_case(&handler.c_type)))
339                    .expect("Invalid class name in wrapper");
340                let needs_static = force_static || !is_sync_handler_type(&handler.c_type);
341                return if needs_static {
342                    Some(quote! {
343                        #new_type: #new_handler + 'static
344                    })
345                } else {
346                    Some(quote! {
347                        #new_type: #new_handler
348                    })
349                };
350            }
351        }
352        None
353    }
354
355    pub fn method_generics_for_method(&self) -> Option<TokenStream> {
356        if let ArgProcessing::Handler(handler_client) = &self.original.processing {
357            if !self.original.is_mut_pointer() {
358                let handler = handler_client.get(0).unwrap();
359                let new_type = parse_str::<Type>(&format!("{}HandlerImpl", snake_to_pascal_case(&handler.c_type)))
360                    .expect("Invalid class name in wrapper");
361                return Some(quote! {
362                    #new_type
363                });
364            }
365        }
366        None
367    }
368
369    pub fn handle_rs_to_c_return(&self, result: TokenStream, include_field_name: bool) -> TokenStream {
370        if let ArgProcessing::Handler(handler_client) = &self.original.processing {
371            if !self.original.is_mut_pointer() {
372                let handler = handler_client.get(0).unwrap();
373                let handler_name = handler.as_ident();
374                let handler_type = handler.as_type();
375                let clientd_name = handler_client.get(1).unwrap().as_ident();
376                let method_name = format_ident!("{}_callback", handler.c_type);
377                let new_type =
378                    parse_str::<Type>(&format!("{}HandlerImpl", snake_to_pascal_case(&self.original.c_type)))
379                        .expect("Invalid class name in wrapper");
380                if include_field_name {
381                    return quote! {
382                        #handler_name: { let callback: #handler_type = if #handler_name.is_none() { None } else { Some(#method_name::<#new_type>) }; callback },
383                        #clientd_name: #handler_name.map(|m|m.as_raw()).unwrap_or_else(|| std::ptr::null_mut())
384                    };
385                } else {
386                    return quote! {
387                        { let callback: #handler_type = if #handler_name.is_none() { None } else { Some(#method_name::<#new_type>) }; callback },
388                        #handler_name.map(|m|m.as_raw()).unwrap_or_else(|| std::ptr::null_mut())
389                    };
390                }
391            } else {
392                return quote! {};
393            }
394        }
395        if let ArgProcessing::StringWithLength(handler_client) = &self.original.processing {
396            // Emit the (pointer, length) pair once, on the length argument.
397            if !self.original.is_c_string_any() {
398                let array = handler_client.get(0).unwrap();
399                let array_name = array.as_ident();
400                let length_name = handler_client.get(1).unwrap().as_ident();
401                // `&str` in-param for const buffers; `&mut [u8]` fill-buffer for mut ones.
402                let ptr = if array.is_mut_c_string() {
403                    quote! { #array_name.as_mut_ptr() as *mut _ }
404                } else {
405                    quote! { #array_name.as_ptr() as *const _ }
406                };
407                if include_field_name {
408                    return quote! {
409                        #array_name: #ptr,
410                        #length_name: #array_name.len()
411                    };
412                } else {
413                    return quote! {
414                        #ptr,
415                        #array_name.len()
416                    };
417                }
418            } else {
419                return quote! {};
420            }
421        }
422        if let ArgProcessing::ByteArrayWithLength(handler_client) = &self.original.processing {
423            if !self.original.is_byte_array() {
424                let array = handler_client.get(0).unwrap();
425                let array_name = array.as_ident();
426                let length_name = handler_client.get(1).unwrap().as_ident();
427                if include_field_name {
428                    return quote! {
429                        #array_name: #array_name.as_ptr() as *mut _,
430                        #length_name: #array_name.len()
431                    };
432                } else {
433                    return quote! {
434                        #array_name.as_ptr() as *mut _,
435                        #array_name.len()
436                    };
437                }
438            } else {
439                return quote! {};
440            }
441        }
442
443        if include_field_name {
444            let arg_name = self.original.as_ident();
445            return if self.original.is_c_string() {
446                quote! {
447                    #arg_name: #result.as_ptr()
448                }
449            } else {
450                if self.original.is_single_mut_pointer() && self.original.is_primitive() {
451                    return quote! {
452                        #arg_name: #result as *mut _
453                    };
454                }
455
456                quote! { #arg_name: #result.into() }
457            };
458        }
459
460        if self.original.is_single_mut_pointer() && self.original.is_primitive() {
461            return quote! {
462                #result as *mut _
463            };
464        }
465
466        if self.original.is_c_string() {
467            quote! {
468                #result.as_ptr()
469            }
470        } else {
471            quote! { #result.into() }
472        }
473    }
474}
475
476#[derive(Debug, Clone, Default, Eq, PartialEq)]
477pub struct CWrapper {
478    pub class_name: String,
479    pub type_name: String,
480    pub without_name: String,
481    pub fields: Vec<Arg>,
482    pub methods: Vec<Method>,
483    pub docs: BTreeSet<String>,
484    /// Method names manually implemented in aeron_custom.rs — the generator skips these
485    /// to avoid duplicate definitions.
486    pub skipped_methods: BTreeSet<String>,
487}
488
489/// Parse `aeron_custom.rs` source and return a map of `ClassName -> {method_names}`
490/// for every `impl ClassName { fn method_name ... }` block found.
491/// This lets the generator skip auto-generating methods that have hand-written overrides.
492pub fn parse_custom_methods(src: &str) -> HashMap<String, BTreeSet<String>> {
493    let mut result: HashMap<String, BTreeSet<String>> = HashMap::new();
494    let file = syn::parse_file(src).unwrap_or_else(|e| {
495        panic!(
496            "rusteron codegen: failed to parse aeron_custom.rs while extracting custom method \
497             names (this would silently empty the generator skip-list and emit duplicate \
498             methods): {e}"
499        )
500    });
501    for item in &file.items {
502        if let Item::Impl(impl_block) = item {
503            // Only plain `impl TypeName { ... }` — no trait impls
504            if impl_block.trait_.is_some() {
505                continue;
506            }
507            let type_name = impl_block.self_ty.to_token_stream().to_string();
508            let type_name = type_name.trim().to_string();
509            let entry = result.entry(type_name).or_default();
510            for impl_item in &impl_block.items {
511                if let ImplItem::Fn(method) = impl_item {
512                    entry.insert(method.sig.ident.to_string());
513                }
514            }
515        }
516    }
517    result
518}
519
520impl CWrapper {
521    pub fn find_methods(&self, name: &str) -> Vec<Method> {
522        self.methods
523            .iter()
524            .filter(|m| m.struct_method_name == name)
525            .cloned()
526            .collect_vec()
527    }
528
529    pub fn find_unique_method(&self, name: &str) -> Option<Method> {
530        let results = self.find_methods(name);
531        if results.len() == 1 {
532            results.into_iter().next()
533        } else {
534            None
535        }
536    }
537
538    fn get_close_method(&self) -> Option<Method> {
539        self.find_unique_method("close")
540    }
541
542    /// Generate logging expressions for method arguments
543    fn generate_arg_logging(arguments: &[Arg], arg_names: &[TokenStream]) -> TokenStream {
544        let mut arg_names_idx = 0;
545        let mut arg_names_for_logging = vec![];
546
547        for (arg_idx, arg) in arguments.iter().enumerate() {
548            if arg_names_idx >= arg_names.len() {
549                break;
550            }
551
552            let arg_name_str = &arg.name;
553            let arg_type = arg.as_type();
554            let arg_ident = arg.as_ident();
555
556            // Determine how to log this argument
557            match &arg.processing {
558                ArgProcessing::Handler(_) if !arg.is_mut_pointer() => {
559                    // Handlers - just show type
560                    arg_names_for_logging.push(quote! {
561                        concat!(#arg_name_str, ": ", stringify!(#arg_type)).to_string()
562                    });
563                    arg_names_idx += 2; // Skip BOTH expanded values (callback + clientd)
564                }
565                ArgProcessing::StringWithLength(_args) => {
566                    // Check if this is the length argument (second in pair) - skip it
567                    if arg_idx > 0 && arguments[arg_idx - 1].processing == arg.processing {
568                        continue;
569                    }
570                    // This is the string argument - show the actual string value
571                    arg_names_for_logging.push(quote! {
572                        format!("{} = {:?}", #arg_name_str, #arg_ident)
573                    });
574                    arg_names_idx += 2;
575                }
576                ArgProcessing::ByteArrayWithLength(_args) => {
577                    // Check if this is the length argument (second in pair) - skip it
578                    if arg_idx > 0 && arguments[arg_idx - 1].processing == arg.processing {
579                        continue;
580                    }
581                    // This is the byte array argument - show name, type, and length
582                    arg_names_for_logging.push(quote! {
583                        format!("{}: {} (len={})", #arg_name_str, stringify!(#arg_type), #arg_ident.len())
584                    });
585                    arg_names_idx += 2;
586                }
587                _ => {
588                    // For primitive types, show value. For pointers/structs, just show name:type
589                    if arg.is_primitive() && !arg.is_any_pointer() {
590                        arg_names_for_logging.push(quote! {
591                            format!("{} = {:?}", #arg_name_str, #arg_ident)
592                        });
593                    } else {
594                        arg_names_for_logging.push(quote! {
595                            concat!(#arg_name_str, ": ", stringify!(#arg_type)).to_string()
596                        });
597                    }
598                    arg_names_idx += 1;
599                }
600            }
601        }
602
603        // For logging - need explicit type when array is empty
604        if arg_names_for_logging.is_empty() {
605            quote! { [""; 0].join(", ") }
606        } else {
607            quote! { [#(#arg_names_for_logging),*].join(", ") }
608        }
609    }
610
611    /// Generate methods for the struct
612    fn generate_methods(
613        &self,
614        wrappers: &BTreeMap<String, CWrapper>,
615        closure_handlers: &Vec<CHandler>,
616        additional_outer_impls: &mut Vec<TokenStream>,
617        debug_fields: &mut Vec<TokenStream>,
618    ) -> Vec<TokenStream> {
619        self.methods
620            .iter()
621            .filter(|m| !m.arguments.iter().any(|arg| arg.is_double_mut_pointer()))
622            .filter(|m| !self.skipped_methods.contains(&m.struct_method_name))
623            .filter(|m| m.struct_method_name != "close")
624            .map(|method| {
625
626                let fn_name =
627                    Ident::new(&method.struct_method_name, proc_macro2::Span::call_site());
628                let return_type_helper =
629                    ReturnType::new(method.return_type.clone(), wrappers.clone());
630                let mut return_type = return_type_helper.get_new_return_type(true, false);
631                let ffi_call = Ident::new(&method.fn_name, proc_macro2::Span::call_site());
632
633                // One classification pass: every argument becomes a ClassifiedArg carrying its
634                // signature/call/generic/registration/_once fragments (see arg_classifier.rs). All
635                // emitters below consume the plan — no parallel loops to keep in sync.
636                let classified = crate::arg_classifier::classify_method_args(method, &self.type_name, wrappers, closure_handlers);
637                let uses_self = classified.uses_self;
638                let owned_retained_handler = classified.owned_retained.clone();
639
640                let generic_types: Vec<TokenStream> = classified.generics();
641                let where_clause = if generic_types.is_empty() {
642                    quote! {}
643                } else {
644                    quote! { <#(#generic_types),*> }
645                };
646
647                let fn_arguments: Vec<TokenStream> = classified.signatures();
648                let mut arg_names: Vec<TokenStream> = classified.calls();
649                let retained_handler_registrations: Vec<TokenStream> = classified.registrations();
650
651                let mut converter = return_type_helper.handle_c_to_rs_return(quote! { result }, true, false);
652
653                // Heap-allocate the owned callback and capture its raw clientd before the
654                // FFI call, so the Handler itself stays available for registration/return.
655                let handler_prelude = if let Some(arg) = &owned_retained_handler {
656                    let name = arg.as_ident();
657                    let raw_name = format_ident!("{}_raw", arg.name);
658                    quote! {
659                        let #name = #name.map(Handler::new);
660                        let #raw_name = #name
661                            .as_ref()
662                            .map(|m| m.as_raw())
663                            .unwrap_or_else(|| std::ptr::null_mut());
664                    }
665                } else {
666                    quote! {}
667                };
668
669                if let Some(arg) = &owned_retained_handler {
670                    let name = arg.as_ident();
671                    let new_type = parse_str::<Type>(&format!(
672                        "{}HandlerImpl",
673                        snake_to_pascal_case(&arg.c_type)
674                    ))
675                    .expect("Invalid class name in wrapper");
676                    return_type = quote! { Result<Option<Handler<#new_type>>, AeronCError> };
677                    converter = quote! {
678                        if result < 0 {
679                            return Err(AeronCError::from_code(result));
680                        } else {
681                            return Ok(#name);
682                        }
683                    };
684                }
685
686                let mut method_docs: Vec<TokenStream> = get_docs(&method.docs, wrappers, Some(&fn_arguments) );
687
688                if owned_retained_handler.is_some() {
689                    method_docs.push(quote! { #[doc = ""] });
690                    method_docs.push(quote! { #[doc = " The callback is retained by the C client; this resource keeps it alive automatically."] });
691                    method_docs.push(quote! { #[doc = " Returns the created [`Handler`] for optional state access — safe to ignore. Closures with a matching signature are accepted directly."] });
692                }
693
694                // Generate logging expression for arguments
695                let args_log_expr = Self::generate_arg_logging(&method.arguments, &arg_names);
696
697                if uses_self && method.return_type.is_c_string_any() && method.arguments.len() == 1 {
698                    let name = format_ident!("{}", method.struct_method_name);
699                    debug_fields.push( quote! {
700                            .field(stringify!(#name), &self.#name() )
701                        } );
702                }
703
704                let possible_self = if uses_self  {
705                    quote! { &self, }
706                } else {
707                    if return_type.to_string().eq("& str") {
708                        return_type = quote! { &'static str  };
709                        method_docs.push(quote! {#[doc = "SAFETY: this is static for performance reasons, so you should not store this without copying it!!"]});
710                    }
711                    quote! {}
712                };
713
714
715
716                let register_handlers = if uses_self && !retained_handler_registrations.is_empty() {
717                    quote! { #(#retained_handler_registrations)* }
718                } else {
719                    quote! {}
720                };
721
722                let mut additional_methods = vec![];
723                let set_closed = quote! {};
724
725                Self::add_mut_string_methods_if_applicable(method, &fn_name, uses_self, &method_docs, &mut additional_methods);
726
727                // getter methods
728                Self::add_getter_instead_of_mut_arg_if_applicable(wrappers, method, &fn_name, &where_clause, &possible_self, &method_docs, &mut additional_methods, debug_fields);
729
730                // `_fn` stack-closure variant, emitted straight from the plan (sync-only:
731                // a stack closure handed to a retained callback would dangle). Skipped when
732                // aeron_custom.rs hand-writes a method of the same `<name>_fn` name, so
733                // custom code can override/deprecate a generated `_fn` variant.
734                if classified.once_capable
735                    && !self
736                        .skipped_methods
737                        .contains(&format!("{}_fn", method.struct_method_name))
738                {
739                    let once_fn_name = format_ident!("{}_fn", fn_name);
740                    let once_generics = classified.once_generics();
741                    let once_where = if once_generics.is_empty() {
742                        quote! {}
743                    } else {
744                        quote! { <#(#once_generics),*> }
745                    };
746                    let once_args = classified.once_signatures();
747                    let once_calls = classified.once_calls();
748                    let once_log_expr = Self::generate_arg_logging(&method.arguments, &once_calls);
749                    additional_methods.push(quote! {
750                        #[inline]
751                        #(#method_docs)*
752                        ///
753                        ///
754                        /// **Stack-borrowed closure** (`_fn` variant): the `FnMut` closure lives on the
755                        /// caller's stack and is borrowed for this call only — the callback fires
756                        /// synchronously inside the call, so nothing is heap-allocated, nothing is stored,
757                        /// and the closure may borrow local state. Prefer this over the retained
758                        /// [`Handler`]-based form on the hot path; only generated for callbacks the C
759                        /// client does not retain (i.e. not stored for later firing).
760                        ///
761                        /// # Panics
762                        ///
763                        /// A panic inside the closure cannot unwind across the `extern "C"` callback
764                        /// boundary and **aborts the process** (since Rust 1.81). Return early instead
765                        /// of panicking in production fragment handlers.
766                        pub fn #once_fn_name #once_where(#possible_self #(#once_args),*) -> #return_type {
767                            #set_closed
768                            unsafe {
769                                #[cfg(feature = "log-c-bindings")]
770                                log::info!(
771                                    "{}({})",
772                                    stringify!(#ffi_call),
773                                    #once_log_expr
774                                );
775
776                                let result = #ffi_call(#(#once_calls),*);
777
778                                #[cfg(feature = "log-c-bindings")]
779                                log::info!("  -> {:?}", result);
780
781                                #converter
782                            }
783                        }
784                    });
785                }
786
787                let mut_primitivies = method.arguments.iter()
788                    .filter(|a| a.is_mut_pointer() && a.is_primitive())
789                    .collect_vec();
790                let single_mut_field = method.return_type.is_c_raw_int() && mut_primitivies.len() == 1;
791
792                // in aeron some methods return error code but have &mut primitive
793                // ideally we should return that primitive instead of forcing user to pass it in
794                let __method_tokens = if single_mut_field {
795                    let mut_field = mut_primitivies.first().unwrap();
796                    // keep the qualified path (`c_int` is only valid qualified), not just the last segment
797                    let rt: Type = parse_str(mut_field.c_type.trim_start_matches("* mut").trim()).unwrap();
798                    let return_type = quote! { Result<#rt, AeronCError> };
799
800                    let fn_arguments= fn_arguments.into_iter().filter(|arg| {!arg.to_string().contains("& mut ")})
801                        .collect_vec();
802
803                    let idx = arg_names.iter().enumerate()
804                        .filter(|(_, arg)| arg.to_string().ends_with("* mut _"))
805                        .map(|(i, _)| i)
806                        .next().unwrap();
807
808                    arg_names[idx] = quote! { &mut mut_result };
809
810                    let mut first = true;
811                    let mut method_docs = method_docs.iter()
812                        .filter(|d| !d.to_string().contains("# Return"))
813                        .map(|d| {
814                            let mut string = d.to_string();
815                            string = string.replace("# Parameters", "");
816                            if string.contains("out param") {
817                                TokenStream::from_str(&string.replace("- `", "\n# Return\n`")).unwrap()
818                            } else {
819                                if string.contains("- `") && first {
820                                    first = false;
821                                    string = string.replacen("- `","# Parameters\n- `", 1);
822                                }
823                                TokenStream::from_str(&string).unwrap()
824                            }
825                        })
826                        .collect_vec();
827
828                    let filter_param_title = !method_docs.iter().any(|d| d.to_string().contains("- `"));
829
830                    if filter_param_title {
831                        method_docs = method_docs.into_iter()
832                            .map(|s| TokenStream::from_str(s.to_string().replace("# Parameters\n", "").as_str()).unwrap())
833                            .collect_vec();
834                    }
835
836
837                    quote! {
838                        #[inline]
839                        #(#method_docs)*
840                        pub fn #fn_name #where_clause(#possible_self #(#fn_arguments),*) -> #return_type {
841                            #set_closed
842                            unsafe {
843                                let mut mut_result: #rt = Default::default();
844
845                                #[cfg(feature = "log-c-bindings")]
846                                log::info!(
847                                    "{}({})",
848                                    stringify!(#ffi_call),
849                                    #args_log_expr
850                                );
851
852                                let err_code = #ffi_call(#(#arg_names),*);
853                                #register_handlers
854
855                                #[cfg(feature = "log-c-bindings")]
856                                log::info!("  -> err_code = {:?}, result = {:?}", err_code, mut_result);
857
858                                if err_code < 0 {
859                                    return Err(AeronCError::from_code(err_code));
860                                } else {
861                                    return Ok(mut_result);
862                                }
863                            }
864                        }
865
866                        #(#additional_methods)*
867                    }
868                } else {
869                    quote! {
870                        #[inline]
871                        #(#method_docs)*
872                        pub fn #fn_name #where_clause(#possible_self #(#fn_arguments),*) -> #return_type {
873                            #set_closed
874                            #handler_prelude
875                            unsafe {
876                                #[cfg(feature = "log-c-bindings")]
877                                log::info!(
878                                    "{}({})",
879                                    stringify!(#ffi_call),
880                                    #args_log_expr
881                                );
882
883                                let result = #ffi_call(#(#arg_names),*);
884                                #register_handlers
885
886                                #[cfg(feature = "log-c-bindings")]
887                                log::info!("  -> {:?}", result);
888
889                                #converter
890                            }
891                        }
892
893                        #(#additional_methods)*
894                    }
895                };
896
897                // Archive control operations surface typed errors (AeronArchiveError) so
898                // callers can match on the structured code. Apply uniformly at the token
899                // level to every method on `aeron_archive_t` — the main body plus the
900                // `additional_methods` (_fn variants, out-param getters, string getters)
901                // which all live in this same closure scope.
902                if self.type_name == "aeron_archive_t" {
903                    __method_tokens
904                        .to_string()
905                        .replace("AeronCError", "AeronArchiveError")
906                        .parse()
907                        .expect("AeronArchiveError token substitution must yield valid tokens")
908                } else {
909                    __method_tokens
910                }
911            })
912            .collect()
913    }
914
915    /// Registration statements that clone each `Handler` argument into `result`'s
916    /// dependencies. Used by constructors: the created C struct stores the callback and
917    /// clientd pointers, so the Handler must stay alive until the C resource is closed.
918    fn handler_dependency_registrations(arguments: &[Arg]) -> Vec<TokenStream> {
919        arguments
920            .iter()
921            .filter_map(|arg| {
922                if let ArgProcessing::Handler(_) = &arg.processing {
923                    if !arg.is_mut_pointer() {
924                        let name = arg.as_ident();
925                        return Some(quote! {
926                            if let Some(__handler) = #name {
927                                if let Some(__inner) = result.inner.as_owned() {
928                                    __inner.add_dependency(__handler.clone());
929                                }
930                            }
931                        });
932                    }
933                }
934                None
935            })
936            .collect()
937    }
938
939    fn add_getter_instead_of_mut_arg_if_applicable(
940        wrappers: &BTreeMap<String, CWrapper>,
941        method: &Method,
942        fn_name: &Ident,
943        where_clause: &TokenStream,
944        possible_self: &TokenStream,
945        method_docs: &Vec<TokenStream>,
946        additional_methods: &mut Vec<TokenStream>,
947        debug_fields: &mut Vec<TokenStream>,
948    ) {
949        if ["constants", "buffers", "values"]
950            .iter()
951            .any(|name| method.struct_method_name == *name)
952            && method.arguments.len() == 2
953        {
954            let rt = ReturnType::new(method.arguments[1].clone(), wrappers.clone());
955            let return_type = rt.get_new_return_type(false, false);
956            let getter_method = format_ident!("get_{}", fn_name);
957            let method_docs = method_docs
958                .iter()
959                .cloned()
960                .take_while(|t| !t.to_string().contains(" Parameter"))
961                .collect_vec();
962            additional_methods.push(quote! {
963                #[inline]
964                #(#method_docs)*
965                pub fn #getter_method #where_clause(#possible_self) -> Result<#return_type, AeronCError> {
966                    let result = #return_type::new_zeroed_on_stack();
967                    self.#fn_name(&result)?;
968                    Ok(result)
969                }
970            });
971            debug_fields.push(quote! {
972                .field(stringify!(#fn_name), &self.#getter_method() )
973            });
974        }
975    }
976
977    fn add_mut_string_methods_if_applicable(
978        method: &Method,
979        fn_name: &Ident,
980        uses_self: bool,
981        method_docs: &Vec<TokenStream>,
982        additional_methods: &mut Vec<TokenStream>,
983    ) {
984        if method.arguments.len() == 3 && uses_self {
985            let method_docs = method_docs.clone();
986            let into_method = format_ident!("{}_into", fn_name);
987            if method.arguments[1].is_mut_c_string() && method.arguments[2].is_usize() {
988                let string_method = format_ident!("{}_as_string", fn_name);
989                additional_methods.push(quote! {
990    #[inline]
991    #(#method_docs)*
992    pub fn #string_method(
993        &self,
994        max_length: usize,
995    ) -> Result<String, AeronCError> {
996        let mut result = String::with_capacity(max_length);
997        self.#into_method(&mut result)?;
998        Ok(result)
999    }
1000
1001    #[inline]
1002    #(#method_docs)*
1003    #[doc = "NOTE: allocation friendly method, the string capacity must be set as it will truncate string to capacity it will never grow the string. So if you pass String::new() it will write 0 chars"]
1004    pub fn #into_method(
1005        &self,
1006        dst_truncate_to_capacity: &mut String,
1007    ) -> Result<i32, AeronCError> {
1008        unsafe {
1009            let capacity = dst_truncate_to_capacity.capacity();
1010            let vec = dst_truncate_to_capacity.as_mut_vec();
1011            vec.set_len(capacity);
1012            let result = self.#fn_name(&mut vec[..])?;
1013            let mut len = 0;
1014            loop {
1015                if len == capacity {
1016                    break;
1017                }
1018                let val = vec[len];
1019                if val == 0 {
1020                    break;
1021                }
1022                len += 1;
1023            }
1024            vec.set_len(len);
1025            Ok(result)
1026        }
1027    }
1028                        });
1029            }
1030        }
1031    }
1032
1033    /// Generate the fields / getters
1034    fn generate_fields(
1035        &self,
1036        cwrappers: &BTreeMap<String, CWrapper>,
1037        debug_fields: &mut Vec<TokenStream>,
1038    ) -> Vec<TokenStream> {
1039        self.fields
1040            .iter()
1041            .filter(|arg| {
1042                !arg.name.starts_with("_") && !self.methods.iter().any(|m| m.struct_method_name.as_str() == arg.name)
1043            })
1044            .map(|arg| {
1045                let field_name = &arg.name;
1046                let fn_name = Ident::new(field_name, proc_macro2::Span::call_site());
1047
1048                let mut arg = arg.clone();
1049                // for mut strings return just &str not &mut str
1050                if arg.is_mut_c_string() {
1051                    arg.c_type = arg.c_type.replace(" mut ", " const ");
1052                }
1053                let mut rt = ReturnType::new(arg.clone(), cwrappers.clone());
1054                let mut return_type = rt.get_new_return_type(false, false);
1055                let handler = if let ArgProcessing::Handler(_) = &arg.processing {
1056                    true
1057                } else {
1058                    false
1059                };
1060                if return_type.is_empty() || handler {
1061                    rt = ReturnType::new(
1062                        Arg {
1063                            processing: ArgProcessing::Default,
1064                            ..arg.clone()
1065                        },
1066                        cwrappers.clone(),
1067                    );
1068                    return_type = rt.get_new_return_type(false, false);
1069                }
1070                let converter = rt.handle_c_to_rs_return(quote! { self.#fn_name }, false, true);
1071
1072                if rt.original.is_primitive()
1073                    || rt.original.is_c_string_any()
1074                    || rt.original.is_byte_array()
1075                    || cwrappers.contains_key(&rt.original.c_type)
1076                {
1077                    if !rt.original.is_any_pointer() || rt.original.is_c_string_any() {
1078                        debug_fields.push(quote! { .field(stringify!(#fn_name), &self.#fn_name()) });
1079                    }
1080                }
1081
1082                quote! {
1083                    #[inline]
1084                    pub fn #fn_name(&self) -> #return_type {
1085                        #converter
1086                    }
1087                }
1088            })
1089            .filter(|t| !t.is_empty())
1090            .collect()
1091    }
1092
1093    /// Generate the constructor for the struct
1094    fn generate_constructor(
1095        &self,
1096        wrappers: &BTreeMap<String, CWrapper>,
1097        constructor_fields: &mut Vec<TokenStream>,
1098        new_ref_set_none: &mut Vec<TokenStream>,
1099    ) -> Vec<TokenStream> {
1100        let constructors = self
1101            .methods
1102            .iter()
1103            .filter(|m| m.arguments.iter().any(|arg| arg.is_double_mut_pointer()))
1104            .map(|method| {
1105                let init_fn = format_ident!("{}", method.fn_name);
1106                let close_method = self.find_close_method(method);
1107                let found_close = close_method.is_some()
1108                    && close_method.unwrap().return_type.is_c_raw_int()
1109                    && close_method.unwrap() != method
1110                    && close_method
1111                        .unwrap()
1112                        .arguments
1113                        .iter()
1114                        .skip(1)
1115                        .all(|a| method.arguments.iter().any(|a2| a.name == a2.name));
1116                if found_close {
1117                    let close_fn = format_ident!("{}", close_method.unwrap().fn_name);
1118                    let init_args: Vec<TokenStream> = method
1119                        .arguments
1120                        .iter()
1121                        .enumerate()
1122                        .map(|(idx, arg)| {
1123                            if idx == 0 {
1124                                quote! { ctx_field }
1125                            } else {
1126                                let arg_name = arg.as_ident();
1127                                quote! { #arg_name }
1128                            }
1129                        })
1130                        .filter(|t| !t.is_empty())
1131                        .collect();
1132                    let close_args: Vec<TokenStream> = close_method
1133                        .unwrap_or(method)
1134                        .arguments
1135                        .iter()
1136                        .enumerate()
1137                        .map(|(idx, arg)| {
1138                            if idx == 0 {
1139                                if arg.is_double_mut_pointer() {
1140                                    quote! { ctx_field }
1141                                } else {
1142                                    quote! { *ctx_field }
1143                                }
1144                            } else {
1145                                let arg_name = arg.as_ident();
1146                                quote! { #arg_name.into() }
1147                            }
1148                        })
1149                        .filter(|t| !t.is_empty())
1150                        .collect();
1151                    let lets: Vec<TokenStream> = Self::lets_for_copying_arguments(wrappers, &method.arguments, true);
1152
1153                    constructor_fields.clear();
1154                    constructor_fields.extend(Self::constructor_fields(wrappers, &method.arguments, &self.class_name));
1155
1156                    let new_ref_args = Self::new_args(wrappers, &method.arguments, &self.class_name, false);
1157
1158                    new_ref_set_none.clear();
1159                    new_ref_set_none.extend(Self::new_args(wrappers, &method.arguments, &self.class_name, true));
1160
1161                    let new_args: Vec<TokenStream> = method
1162                        .arguments
1163                        .iter()
1164                        .enumerate()
1165                        .filter_map(|(_idx, arg)| {
1166                            if arg.is_double_mut_pointer() {
1167                                None
1168                            } else {
1169                                let arg_name = arg.as_ident();
1170                                let arg_type =
1171                                    ReturnType::new(arg.clone(), wrappers.clone()).get_new_return_type(false, true);
1172                                if arg_type.clone().into_token_stream().is_empty() {
1173                                    None
1174                                } else {
1175                                    Some(quote! { #arg_name: #arg_type })
1176                                }
1177                            }
1178                        })
1179                        .filter(|t| !t.is_empty())
1180                        .collect();
1181
1182                    let fn_name = format_ident!(
1183                        "{}",
1184                        method
1185                            .struct_method_name
1186                            .replace("init", "new")
1187                            .replace("create", "new")
1188                    );
1189
1190                    let generic_types: Vec<TokenStream> = method
1191                        .arguments
1192                        .iter()
1193                        .flat_map(|arg| {
1194                            ReturnType::new(arg.clone(), wrappers.clone())
1195                                .method_generics_for_where(true)
1196                                .into_iter()
1197                        })
1198                        .collect_vec();
1199                    let where_clause = if generic_types.is_empty() {
1200                        quote! {}
1201                    } else {
1202                        quote! { <#(#generic_types),*> }
1203                    };
1204
1205                    let method_docs: Vec<TokenStream> = get_docs(&method.docs, wrappers, Some(&new_args));
1206
1207                    // The C struct created here stores every callback/clientd pair it is
1208                    // given (e.g. fragment assemblers keep their delegate), so clone each
1209                    // Handler into the new resource's dependencies to keep it alive until
1210                    // the C resource is closed.
1211                    let handler_deps: Vec<TokenStream> = Self::handler_dependency_registrations(&method.arguments);
1212
1213                    // Generate logging expression token stream (will be evaluated in closure)
1214                    let init_log_expr_tokens = Self::generate_arg_logging(&method.arguments, &init_args);
1215                    // Generate logging for close method arguments
1216                    let close_log_expr_tokens = if let Some(close_m) = close_method {
1217                        Self::generate_arg_logging(&close_m.arguments, &close_args)
1218                    } else {
1219                        quote! { "" }
1220                    };
1221
1222                    // `async_add_destination` handles must NOT run `async_remove_destination`
1223                    // on drop: removing the destination is a separate, destructive operation —
1224                    // not the destructor of the async-add handle (the C client frees the async
1225                    // struct itself once its poll completes). Pairing them made dropping the
1226                    // poll handle silently tear the destination down again, with a dangling
1227                    // uri pointer to boot.
1228                    let cleanup_tokens = if method.fn_name.contains("_destination") {
1229                        quote! { None }
1230                    } else {
1231                        quote! {
1232                            Some(Box::new(move |ctx_field| unsafe {
1233                                #[cfg(feature = "log-c-bindings")]
1234                                {
1235                                    let log_args = #close_log_expr_tokens;
1236                                    log::info!("{}({})", stringify!(#close_fn), log_args);
1237                                }
1238                                #close_fn(#(#close_args),*)
1239                            }))
1240                        }
1241                    };
1242
1243                    quote! {
1244                        #[inline]
1245                        #(#method_docs)*
1246                        pub fn #fn_name #where_clause(#(#new_args),*) -> Result<Self, AeronCError> {
1247                            #(#lets)*
1248                            // new by using constructor
1249                            let resource_constructor = ManagedCResource::new(
1250                                move |ctx_field| unsafe {
1251                                    #[cfg(feature = "log-c-bindings")]
1252                                    {
1253                                        let log_args = #init_log_expr_tokens;
1254                                        log::info!("{}({})", stringify!(#init_fn), log_args);
1255                                    }
1256                                    #init_fn(#(#init_args),*)
1257                                },
1258                                #cleanup_tokens,
1259                                false,
1260                            )?;
1261
1262                            let result = Self {
1263                                inner: CResource::OwnedOnHeap(RcOrArc::new(resource_constructor)),
1264                                #(#new_ref_args)*
1265                            };
1266                            #(#handler_deps)*
1267                            Ok(result)
1268                        }
1269                    }
1270                } else {
1271                    quote! {}
1272                }
1273            })
1274            .collect_vec();
1275
1276        let no_constructor = constructors.iter().map(|x| x.to_string()).join("").trim().is_empty();
1277        if no_constructor {
1278            let type_name = format_ident!("{}", self.type_name);
1279            let zeroed_impl = quote! {
1280                #[inline]
1281                /// creates zeroed struct where the underlying c struct is on the heap
1282                pub fn new_zeroed_on_heap() -> Self {
1283                    let resource = ManagedCResource::new(
1284                        move |ctx_field| {
1285                            #[cfg(feature = "extra-logging")]
1286                            log::info!("creating zeroed empty resource on heap {}", stringify!(#type_name));
1287                            let inst: #type_name = unsafe { std::mem::zeroed() };
1288                            let inner_ptr: *mut #type_name = Box::into_raw(Box::new(inst));
1289                            unsafe { *ctx_field = inner_ptr };
1290                            0
1291                        },
1292                        None,
1293                        true,
1294                    ).unwrap();
1295
1296                    Self {
1297                        inner: CResource::OwnedOnHeap(RcOrArc::new(resource)),
1298                    }
1299                }
1300
1301                #[inline]
1302                /// creates zeroed struct where the underlying c struct is on the stack
1303                /// _(Use with care)_
1304                pub fn new_zeroed_on_stack() -> Self {
1305                    #[cfg(feature = "extra-logging")]
1306                    log::debug!("creating zeroed empty resource on stack {}", stringify!(#type_name));
1307
1308                    Self {
1309                        inner: CResource::OwnedOnStack(std::mem::MaybeUninit::zeroed()),
1310                    }
1311                }
1312            };
1313            if self.has_default_method() {
1314                let type_name = format_ident!("{}", self.type_name);
1315                let new_args: Vec<TokenStream> = self
1316                    .fields
1317                    .iter()
1318                    .filter_map(|arg| {
1319                        let arg_name = arg.as_ident();
1320                        let arg_type = ReturnType::new(arg.clone(), wrappers.clone()).get_new_return_type(false, true);
1321                        if arg_type.is_empty() {
1322                            None
1323                        } else {
1324                            Some(quote! { #arg_name: #arg_type })
1325                        }
1326                    })
1327                    .filter(|t| !t.is_empty())
1328                    .collect();
1329                let init_args: Vec<TokenStream> = self
1330                    .fields
1331                    .iter()
1332                    .map(|arg| {
1333                        let arg_name = arg.as_ident();
1334                        let value = ReturnType::new(arg.clone(), wrappers.clone())
1335                            .handle_rs_to_c_return(quote! { #arg_name }, true);
1336                        quote! { #value }
1337                    })
1338                    .filter(|t| !t.is_empty())
1339                    .collect();
1340
1341                let generic_types: Vec<TokenStream> = self
1342                    .fields
1343                    .iter()
1344                    .flat_map(|arg| {
1345                        ReturnType::new(arg.clone(), wrappers.clone())
1346                            .method_generics_for_where(true)
1347                            .into_iter()
1348                    })
1349                    .collect_vec();
1350                let where_clause = if generic_types.is_empty() {
1351                    quote! {}
1352                } else {
1353                    quote! { <#(#generic_types),*> }
1354                };
1355
1356                let cloned_fields = self
1357                    .fields
1358                    .iter()
1359                    .filter(|a| a.processing == ArgProcessing::Default)
1360                    .cloned()
1361                    .collect_vec();
1362                let lets: Vec<TokenStream> = Self::lets_for_copying_arguments(wrappers, &cloned_fields, false);
1363
1364                // The C struct stores these callback/clientd fields directly, so keep the
1365                // Handler values alive alongside the resource.
1366                let field_handler_deps: Vec<TokenStream> = Self::handler_dependency_registrations(&self.fields);
1367
1368                vec![quote! {
1369                    #[inline]
1370                    pub fn new #where_clause(#(#new_args),*) -> Result<Self, AeronCError> {
1371                        #(#lets)*
1372                        // no constructor in c bindings
1373                        let r_constructor = ManagedCResource::new(
1374                            move |ctx_field| {
1375                                let inst = #type_name { #(#init_args),* };
1376                                let inner_ptr: *mut #type_name = Box::into_raw(Box::new(inst));
1377                                unsafe { *ctx_field = inner_ptr };
1378                                0
1379                            },
1380                            None,
1381                            true,
1382                        )?;
1383
1384                        let result = Self {
1385                            inner: CResource::OwnedOnHeap(RcOrArc::new(r_constructor)),
1386                        };
1387                        #(#field_handler_deps)*
1388                        Ok(result)
1389                    }
1390
1391                    #zeroed_impl
1392                }]
1393            } else {
1394                vec![zeroed_impl]
1395            }
1396        } else {
1397            constructors
1398        }
1399    }
1400
1401    fn lets_for_copying_arguments(
1402        wrappers: &BTreeMap<String, CWrapper>,
1403        arguments: &Vec<Arg>,
1404        include_let_statements: bool,
1405    ) -> Vec<TokenStream> {
1406        arguments
1407            .iter()
1408            .enumerate()
1409            .filter_map(|(_idx, arg)| {
1410                if arg.is_double_mut_pointer() {
1411                    None
1412                } else {
1413                    let arg_name = arg.as_ident();
1414                    let rtype = arg.as_type();
1415
1416                    // check if I need to make copy of object for reference counting
1417                    let fields = if arg.is_single_mut_pointer()
1418                        && wrappers.contains_key(arg.c_type.split_whitespace().last().unwrap())
1419                    {
1420                        let arg_copy = format_ident!("{}_copy", arg.name);
1421                        quote! {
1422                            let #arg_copy = #arg_name.clone();
1423                        }
1424                    } else {
1425                        quote! {}
1426                    };
1427
1428                    let return_type = ReturnType::new(arg.clone(), wrappers.clone());
1429
1430                    if let ArgProcessing::StringWithLength(_args) | ArgProcessing::ByteArrayWithLength(_args) =
1431                        &return_type.original.processing
1432                    {
1433                        return None;
1434                    }
1435                    if let ArgProcessing::Handler(args) = &return_type.original.processing {
1436                        let arg1 = args[0].as_ident();
1437                        let arg2 = args[1].as_ident();
1438                        let value = return_type.handle_rs_to_c_return(quote! { #arg_name }, false);
1439
1440                        if value.is_empty() {
1441                            return None;
1442                        }
1443
1444                        if include_let_statements {
1445                            return Some(quote! { #fields let (#arg1, #arg2)= (#value); });
1446                        } else {
1447                            return Some(fields);
1448                        }
1449                    }
1450
1451                    let value = return_type.handle_rs_to_c_return(quote! { #arg_name }, false);
1452                    if value.is_empty() {
1453                        None
1454                    } else {
1455                        if include_let_statements {
1456                            Some(quote! { #fields let #arg_name: #rtype = #value; })
1457                        } else {
1458                            return Some(fields);
1459                        }
1460                    }
1461                }
1462            })
1463            .filter(|t| !t.is_empty())
1464            .collect()
1465    }
1466
1467    fn constructor_fields(
1468        wrappers: &BTreeMap<String, CWrapper>,
1469        arguments: &Vec<Arg>,
1470        class_name: &String,
1471    ) -> Vec<TokenStream> {
1472        if class_name == "AeronAsyncDestination" {
1473            return vec![];
1474        }
1475
1476        arguments
1477            .iter()
1478            .enumerate()
1479            .filter_map(|(_idx, arg)| {
1480                if arg.is_double_mut_pointer() {
1481                    None
1482                } else {
1483                    let arg_name = arg.as_ident();
1484                    let rtype = arg.as_type();
1485                    if arg.is_single_mut_pointer()
1486                        && wrappers.contains_key(arg.c_type.split_whitespace().last().unwrap())
1487                    {
1488                        let return_type = ReturnType::new(arg.clone(), wrappers.clone());
1489                        let return_type = return_type.get_new_return_type(false, false);
1490
1491                        let arg_copy = format_ident!("_{}", arg.name);
1492                        Some(quote! {
1493                            #arg_copy: Option<#return_type>,
1494                        })
1495                    } else {
1496                        None
1497                    }
1498                }
1499            })
1500            .collect()
1501    }
1502
1503    fn new_args(
1504        wrappers: &BTreeMap<String, CWrapper>,
1505        arguments: &Vec<Arg>,
1506        class_name: &String,
1507        set_none: bool,
1508    ) -> Vec<TokenStream> {
1509        if class_name == "AeronAsyncDestination" {
1510            return vec![];
1511        }
1512
1513        arguments
1514            .iter()
1515            .enumerate()
1516            .filter_map(|(_idx, arg)| {
1517                if arg.is_double_mut_pointer() {
1518                    None
1519                } else {
1520                    let arg_name = arg.as_ident();
1521                    let rtype = arg.as_type();
1522                    if arg.is_single_mut_pointer()
1523                        && wrappers.contains_key(arg.c_type.split_whitespace().last().unwrap())
1524                    {
1525                        let arg_f = format_ident!("_{}", &arg.name);
1526                        let arg_copy = format_ident!("{}_copy", &arg.name);
1527                        if set_none {
1528                            Some(quote! {
1529                                #arg_f: None,
1530                            })
1531                        } else {
1532                            Some(quote! {
1533                                #arg_f: Some(#arg_copy),
1534                            })
1535                        }
1536                    } else {
1537                        None
1538                    }
1539                }
1540            })
1541            .collect()
1542    }
1543
1544    fn find_close_method(&self, method: &Method) -> Option<&Method> {
1545        let mut close_method = None;
1546
1547        // must have init, create or add method name
1548        if ["_init", "_create", "_add"]
1549            .iter()
1550            .all(|find| !method.fn_name.contains(find))
1551        {
1552            return None;
1553        }
1554
1555        for name in ["_destroy", "_delete"] {
1556            let close_fn = format_ident!(
1557                "{}",
1558                method
1559                    .fn_name
1560                    .replace("_init", "_close")
1561                    .replace("_create", name)
1562                    .replace("_add_", "_remove_")
1563            );
1564            let method = self.methods.iter().find(|m| close_fn.to_string().contains(&m.fn_name));
1565            if method.is_some() {
1566                close_method = method;
1567                break;
1568            }
1569        }
1570        close_method
1571    }
1572
1573    fn has_default_method(&self) -> bool {
1574        // AeronUriStringBuilder does not follow the normal convention so have additional check arg.is_single_mut_pointer() && m.fn_name.contains("_init_")
1575        let no_init_method = !self.methods.iter().any(|m| {
1576            m.arguments
1577                .iter()
1578                .any(|arg| arg.is_double_mut_pointer() || (arg.is_single_mut_pointer() && m.fn_name.contains("_init_")))
1579        });
1580
1581        no_init_method && !self.fields.iter().any(|arg| arg.name.starts_with("_")) && !self.fields.is_empty()
1582    }
1583
1584    fn generate_allocation_test(&self) -> TokenStream {
1585        let class_name = format_ident!("{}", self.class_name);
1586
1587        let has_c_constructor = self
1588            .methods
1589            .iter()
1590            .any(|m| m.arguments.iter().any(|arg| arg.is_double_mut_pointer()));
1591        let has_empty_new_constructor = self.methods.iter().any(|m| {
1592            m.fn_name.contains("_init_")
1593                && m.arguments.iter().any(|arg| arg.is_double_mut_pointer())
1594                && m.arguments.len() == 1
1595        });
1596
1597        let mut tests = vec![];
1598
1599        if !has_c_constructor {
1600            tests.push(quote! {
1601                #[test]
1602                #[file_serial(global)]
1603                fn test_new_on_stack() {
1604                    crate::test_alloc::assert_no_allocation(|| {
1605                        for _ in 0..100 {
1606                            let _ = #class_name::new_zeroed_on_stack();
1607                        }
1608                    });
1609                }
1610            });
1611        }
1612
1613        if has_empty_new_constructor {
1614            tests.push(quote! {
1615                #[test]
1616                #[file_serial(global)]
1617                fn test_new() {
1618                    crate::test_alloc::assert_no_allocation(|| {
1619                        for _ in 0..100 {
1620                            let _ = #class_name::new();
1621                        }
1622                    });
1623                }
1624            });
1625        }
1626
1627        if self.has_default_method() {
1628            tests.push(quote! {
1629                #[test]
1630                #[file_serial(global)]
1631                fn test_default() {
1632                    crate::test_alloc::assert_no_allocation(|| {
1633                        for _ in 0..100 {
1634                            let _ = #class_name::default();
1635                        }
1636                    });
1637                }
1638            });
1639        }
1640
1641        let mod_name = format_ident!("{}_allocation_tests", self.type_name);
1642
1643        if tests.is_empty() {
1644            quote! {}
1645        } else {
1646            quote! {
1647                #[cfg(test)]
1648                mod #mod_name {
1649                    use super::*;
1650                    use serial_test::file_serial;
1651                    #(#tests)*
1652                }
1653            }
1654        }
1655    }
1656}
1657
1658#[cfg(test)]
1659mod parse_custom_methods_tests {
1660    use super::parse_custom_methods;
1661    use std::collections::BTreeSet;
1662
1663    #[test]
1664    fn extracts_method_names_from_inherent_impls() {
1665        let src = r#"
1666            impl AeronPublication {
1667                pub fn offer_result(&self) -> i64 { 0 }
1668                pub fn status(&self) -> u8 { 0 }
1669            }
1670            impl AeronSubscription {
1671                pub fn status(&self) -> u8 { 0 }
1672            }
1673        "#;
1674        let map = parse_custom_methods(src);
1675        assert_eq!(
1676            map.get("AeronPublication"),
1677            Some(
1678                &["offer_result", "status"]
1679                    .iter()
1680                    .map(|s| s.to_string())
1681                    .collect::<BTreeSet<_>>()
1682            )
1683        );
1684        assert!(map.get("AeronSubscription").map_or(false, |s| s.contains("status")));
1685    }
1686
1687    #[test]
1688    fn ignores_trait_impls() {
1689        // `impl Display for X` and `impl<T> Trait for Y` must not be treated as
1690        // inherent method sources (they'd add bogus skip entries).
1691        let src = r#"
1692            impl std::fmt::Display for AeronCError {
1693                fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Ok(()) }
1694            }
1695        "#;
1696        let map = parse_custom_methods(src);
1697        assert!(
1698            !map.contains_key("AeronCError"),
1699            "trait impls must not appear in the skip-list"
1700        );
1701    }
1702
1703    #[test]
1704    #[should_panic(expected = "failed to parse aeron_custom.rs")]
1705    fn panics_loudly_on_malformed_input() {
1706        // Previously this returned an empty map → duplicate-method emission.
1707        let _ = parse_custom_methods("impl { not valid rust }}}");
1708    }
1709
1710    #[test]
1711    fn empty_input_yields_empty_map() {
1712        assert!(parse_custom_methods("").is_empty());
1713    }
1714
1715    #[test]
1716    fn self_type_keyed_by_plain_token_stream() {
1717        let src = r#"
1718            impl AeronPublication { pub fn offer_result(&self) -> i64 { 0 } }
1719        "#;
1720        let map = parse_custom_methods(src);
1721        // Exact plain-name key — no surrounding whitespace, no path qualifiers.
1722        assert_eq!(map.len(), 1);
1723        assert!(map.contains_key("AeronPublication"));
1724    }
1725}
1726
1727#[cfg(test)]
1728mod arg_classification_tests {
1729    use super::{Arg, ArgProcessing};
1730
1731    fn arg(c_type: &str) -> Arg {
1732        Arg {
1733            name: "x".to_string(),
1734            c_type: c_type.to_string(),
1735            processing: ArgProcessing::Default,
1736        }
1737    }
1738
1739    #[test]
1740    fn classifies_c_strings() {
1741        assert!(arg("* const :: std :: os :: raw :: c_char").is_c_string());
1742        assert!(arg("* const :: std :: os :: raw :: c_char").is_c_string_any());
1743        assert!(arg("* mut :: std :: os :: raw :: c_char").is_mut_c_string());
1744        assert!(arg("* mut :: std :: os :: raw :: c_char").is_c_string_any());
1745        // Not a C string:
1746        assert!(!arg("* const u8").is_c_string());
1747        assert!(!arg("i32").is_c_string_any());
1748    }
1749
1750    #[test]
1751    fn classifies_byte_arrays() {
1752        assert!(arg("* const u8").is_byte_array());
1753        assert!(arg("* mut u8").is_byte_array());
1754        assert!(arg("* mut u8").is_mut_byte_array());
1755        assert!(!arg("* const u8").is_mut_byte_array());
1756        assert!(!arg("* const :: std :: os :: raw :: c_char").is_byte_array());
1757    }
1758
1759    #[test]
1760    fn classifies_raw_int_return() {
1761        assert!(arg(":: std :: os :: raw :: c_int").is_c_raw_int());
1762        assert!(!arg("i32").is_c_raw_int());
1763    }
1764
1765    #[test]
1766    fn classifies_void_pointer() {
1767        assert!(arg("* mut :: std :: os :: raw :: c_void").is_c_void());
1768        assert!(!arg("* mut u8").is_c_void());
1769    }
1770
1771    #[test]
1772    fn classifies_mut_pointers_single_vs_double() {
1773        // Single `*mut`:
1774        assert!(arg("* mut i32").is_mut_pointer());
1775        assert!(arg("* mut i32").is_single_mut_pointer());
1776        assert!(!arg("* mut i32").is_double_mut_pointer());
1777        // Double `*mut *mut` (output param / handle out):
1778        assert!(arg("* mut * mut aeron_foo_t").is_mut_pointer());
1779        assert!(arg("* mut * mut aeron_foo_t").is_double_mut_pointer());
1780        assert!(!arg("* mut * mut aeron_foo_t").is_single_mut_pointer());
1781        // `*const` is not a mut pointer:
1782        assert!(!arg("* const i32").is_mut_pointer());
1783    }
1784
1785    #[test]
1786    fn classifies_primitives() {
1787        for ty in [
1788            "i64", "u64", "f32", "f64", "i32", "i16", "u32", "u16", "bool", "usize", "isize",
1789        ] {
1790            assert!(arg(ty).is_primitive(), "{ty} should be primitive");
1791        }
1792        // NOTE: `is_primitive` is a SUFFIX match (`c_type.ends_with(primitive)`),
1793        // so a pointer-to-primitive like `* mut i32` also returns true. This is
1794        // the contract the generator relies on (it composes this with the
1795        // `is_single_mut_pointer` check elsewhere), so pin it here.
1796        assert!(arg("* mut i32").is_primitive());
1797        // But a pointer to a non-primitive (a C struct) is not primitive:
1798        assert!(!arg("* mut aeron_foo_t").is_primitive());
1799    }
1800
1801    #[test]
1802    fn classifies_any_pointer() {
1803        assert!(arg("* const i32").is_any_pointer());
1804        assert!(arg("* mut i32").is_any_pointer());
1805        assert!(!arg("i32").is_any_pointer());
1806    }
1807}
1808
1809fn get_docs(
1810    docs: &BTreeSet<String>,
1811    wrappers: &BTreeMap<String, CWrapper>,
1812    arguments: Option<&Vec<TokenStream>>,
1813) -> Vec<TokenStream> {
1814    let mut first_param = true;
1815    docs.iter()
1816        .flat_map(|d| d.lines())
1817        .filter(|s| {
1818            arguments.is_none()
1819                || !s.contains("@param")
1820                || (s.contains("@param")
1821                    && arguments
1822                        .unwrap()
1823                        .iter()
1824                        .any(|a| s.contains(format!(" {}", a.to_string().split_whitespace().next().unwrap()).as_str())))
1825        })
1826        .map(|doc| {
1827            let mut doc = doc.to_string();
1828
1829            if first_param && doc.contains("@param") {
1830                doc = format!("# Parameters\n{}", doc);
1831                first_param = false;
1832            }
1833
1834            if doc.contains("@param") {
1835                doc = regex::Regex::new("@param\\s+([^ ]+)")
1836                    .unwrap()
1837                    .replace(doc.as_str(), "\n - `$1`")
1838                    .to_string();
1839            }
1840
1841            doc = doc
1842                .replace("@return", "\n# Return\n")
1843                .replace("<p>", "\n")
1844                .replace("</p>", "\n");
1845
1846            doc = wrappers
1847                .values()
1848                .fold(doc, |acc, v| acc.replace(&v.type_name, &format!("`{}`", v.class_name)));
1849
1850            if doc.contains("@deprecated") {
1851                quote! {
1852                    #[deprecated]
1853                    #[doc = #doc]
1854                }
1855            } else {
1856                quote! {
1857                    #[doc = #doc]
1858                }
1859            }
1860        })
1861        .collect()
1862}
1863
1864fn generate_stateless_handler_code(handler: &CHandler) -> TokenStream {
1865    let type_name_ident = format_ident!("{}", handler.type_name);
1866    let callback_fn_name = format_ident!("{}_callback", handler.type_name);
1867    let closure_type_name = format_ident!("{}Callback", snake_to_pascal_case(&handler.type_name));
1868    let logger_type_name = format_ident!("{}Logger", snake_to_pascal_case(&handler.type_name));
1869    let handle_method_name = format_ident!("handle_{}", &handler.type_name[..handler.type_name.len() - 2]);
1870    let closure_return_type = handler.return_type.as_type();
1871
1872    // For stateless callbacks, the FnMut signature takes no args (other than &mut self)
1873    let (fn_mut_args, trait_args): (Vec<TokenStream>, Vec<TokenStream>) = handler
1874        .args
1875        .iter()
1876        .enumerate()
1877        .map(|(i, arg)| {
1878            let return_type = ReturnType::new(arg.clone(), BTreeMap::new());
1879            let type_name = return_type.get_new_return_type(false, false);
1880            let arg_name = format_ident!("arg_{}", i);
1881
1882            let fn_mut_arg = if arg.is_single_mut_pointer() && arg.is_primitive() {
1883                let owned_type: Type = parse_str(arg.c_type.split_whitespace().last().unwrap()).unwrap();
1884                quote! { #owned_type }
1885            } else {
1886                quote! { #type_name }
1887            };
1888
1889            let trait_arg = if type_name.is_empty() {
1890                quote! {}
1891            } else if arg.is_single_mut_pointer() && arg.is_primitive() {
1892                let owned_type: Type = parse_str(arg.c_type.split_whitespace().last().unwrap()).unwrap();
1893                quote! { #arg_name: #owned_type }
1894            } else {
1895                quote! { #arg_name: #type_name }
1896            };
1897
1898            (fn_mut_arg, trait_arg)
1899        })
1900        .filter(|(fn_mut, trait_)| !fn_mut.is_empty() && !trait_.is_empty())
1901        .unzip();
1902
1903    // Store the FnMut signature for use in wrapper code
1904    let fn_mut_sig = quote! {
1905       FnMut(#(#fn_mut_args),*) -> #closure_return_type
1906    };
1907
1908    quote! {
1909        pub trait #closure_type_name {
1910            fn #handle_method_name(&mut self, #(#trait_args),*) -> #closure_return_type;
1911        }
1912
1913        pub struct #logger_type_name;
1914        impl #closure_type_name for #logger_type_name {
1915            fn #handle_method_name(&mut self, #(#trait_args),*) -> #closure_return_type {
1916                log::info!(
1917                    "{}({}\n)",
1918                    stringify!(#handle_method_name),
1919                    ""
1920                );
1921                unimplemented!()
1922            }
1923        }
1924
1925        // Stub callback function for stateless handlers - panics when called
1926        #[allow(dead_code)]
1927        unsafe extern "C" fn #callback_fn_name<F: #closure_type_name>(#(#trait_args),*) -> #closure_return_type {
1928            unimplemented!("Stateless handler callback called - not supported")
1929        }
1930    }
1931}
1932
1933pub fn generate_handlers(handler: &mut CHandler, bindings: &CBinding) -> TokenStream {
1934    if handler
1935        .args
1936        .iter()
1937        .any(|arg| arg.is_primitive() && arg.is_mut_pointer())
1938    {
1939        return quote! {};
1940    }
1941
1942    // Check for stateless callbacks (no c_void parameter)
1943    // These cannot use closure pattern as there's no clientd to pass the closure through
1944    // Generate only the trait and logger, not the closure infrastructure
1945    let has_c_void_param = handler.args.iter().any(|a| a.is_c_void());
1946    let is_stateless = !has_c_void_param;
1947
1948    if is_stateless {
1949        let closure_type_name = format_ident!("{}Callback", snake_to_pascal_case(&handler.type_name));
1950        // Set the FnMut signature for stateless handlers (needed by wrapper code generation)
1951        let fn_mut_args: Vec<TokenStream> = handler
1952            .args
1953            .iter()
1954            .map(|arg| {
1955                let return_type = ReturnType::new(arg.clone(), bindings.wrappers.clone());
1956                let type_name = return_type.get_new_return_type(false, false);
1957                if arg.is_single_mut_pointer() && arg.is_primitive() {
1958                    let owned_type: Type = parse_str(arg.c_type.split_whitespace().last().unwrap()).unwrap();
1959                    quote! { #owned_type }
1960                } else {
1961                    quote! { #type_name }
1962                }
1963            })
1964            .filter(|t| !t.is_empty())
1965            .collect();
1966        let closure_return_type = handler.return_type.as_type();
1967        handler.fn_mut_signature = quote! {
1968           FnMut(#(#fn_mut_args),*) -> #closure_return_type
1969        };
1970        handler.closure_type_name = quote! {
1971           #closure_type_name
1972        };
1973        return generate_stateless_handler_code(handler);
1974    }
1975
1976    let fn_name = format_ident!("{}_callback", handler.type_name);
1977    let closure_fn_name = format_ident!("{}_callback_for_once_closure", handler.type_name);
1978    let doc_comments: Vec<TokenStream> = handler
1979        .docs
1980        .iter()
1981        .flat_map(|doc| doc.lines())
1982        .map(|line| quote! { #[doc = #line] })
1983        .collect();
1984
1985    let closure = handler
1986        .args
1987        .iter()
1988        .find(|a| a.is_c_void())
1989        .map(|a| a.name.clone())
1990        .unwrap_or_else(|| "state".to_string());
1991    let closure_name = format_ident!("{}", closure);
1992    let closure_type_name = format_ident!("{}Callback", snake_to_pascal_case(&handler.type_name));
1993    let closure_return_type = handler.return_type.as_type();
1994
1995    let logger_type_name = format_ident!("{}Logger", snake_to_pascal_case(&handler.type_name));
1996
1997    let handle_method_name = format_ident!("handle_{}", &handler.type_name[..handler.type_name.len() - 2]);
1998
1999    let no_method_name = format_ident!(
2000        "no_{}_handler",
2001        &handler.type_name[..handler.type_name.len() - 2]
2002            .replace("_on_", "_")
2003            .replace("aeron_", "")
2004    );
2005
2006    let args: Vec<TokenStream> = handler
2007        .args
2008        .iter()
2009        .map(|arg| {
2010            let arg_name = arg.as_ident();
2011            // do not need to convert as its calling hour handler
2012            let arg_type: Type = arg.as_type();
2013            quote! { #arg_name: #arg_type }
2014        })
2015        .filter(|t| !t.is_empty())
2016        .collect();
2017
2018    let arg_names_for_logging: Vec<TokenStream> = handler
2019        .args
2020        .iter()
2021        .map(|arg| {
2022            let arg_name = arg.as_ident();
2023            quote! { format!("{} = {:?}", stringify!(#arg_name), #arg_name) }
2024        })
2025        .collect();
2026
2027    let converted_args: Vec<TokenStream> = handler
2028        .args
2029        .iter()
2030        .filter_map(|arg| {
2031            let name = &arg.name;
2032            let arg_name = arg.as_ident();
2033            // Skip closure argument - it's only used to get the closure reference
2034            // All other args (including other c_void args) are passed to the handler
2035            if name == &closure {
2036                None
2037            } else {
2038                let return_type = ReturnType::new(arg.clone(), bindings.wrappers.clone());
2039                Some(return_type.handle_c_to_rs_return(quote! {#arg_name}, false, false))
2040            }
2041        })
2042        .filter(|t| !t.is_empty())
2043        .collect();
2044
2045    let closure_args: Vec<TokenStream> = handler
2046        .args
2047        .iter()
2048        .filter_map(|arg| {
2049            let name = &arg.name;
2050            if name == &closure {
2051                return None;
2052            }
2053
2054            let return_type = ReturnType::new(arg.clone(), bindings.wrappers.clone());
2055            let type_name = return_type.get_new_return_type(false, false);
2056            let field_name = format_ident!("{}", name);
2057            if type_name.is_empty() {
2058                None
2059            } else {
2060                Some(quote! {
2061                    #field_name: #type_name
2062                })
2063            }
2064        })
2065        .filter(|t| !t.is_empty())
2066        .collect();
2067
2068    let mut log_field_names = vec![];
2069    let closure_args_in_logger: Vec<TokenStream> = handler
2070        .args
2071        .iter()
2072        .filter_map(|arg| {
2073            let name = &arg.name;
2074            if name == &closure {
2075                return None;
2076            }
2077
2078            let return_type = ReturnType::new(arg.clone(), bindings.wrappers.clone());
2079            let type_name = return_type.get_new_return_type(false, false);
2080            let field_name = format_ident!("{}", name);
2081            if type_name.is_empty() {
2082                None
2083            } else {
2084                log_field_names.push(Some(
2085                    quote! { format!("{} : {:?}", stringify!(#field_name), #field_name) },
2086                ));
2087
2088                Some(quote! {
2089                    #field_name: #type_name
2090                })
2091            }
2092        })
2093        .filter(|t| !t.is_empty())
2094        .collect();
2095
2096    if log_field_names.is_empty() {
2097        log_field_names.push(Some(quote! { "" }));
2098    }
2099
2100    let fn_mut_args: Vec<TokenStream> = handler
2101        .args
2102        .iter()
2103        .filter_map(|arg| {
2104            let name = &arg.name;
2105            if name == &closure {
2106                return None;
2107            }
2108
2109            let return_type = ReturnType::new(arg.clone(), bindings.wrappers.clone());
2110            let type_name = return_type.get_new_return_type(false, false);
2111            if arg.is_single_mut_pointer() && arg.is_primitive() {
2112                let owned_type: Type = parse_str(arg.c_type.split_whitespace().last().unwrap()).unwrap();
2113                return Some(quote! { #owned_type });
2114            } else {
2115                return Some(quote! {
2116                    #type_name
2117                });
2118            }
2119        })
2120        .filter(|t| !t.is_empty())
2121        .collect();
2122
2123    handler.fn_mut_signature = quote! {
2124       FnMut(#(#fn_mut_args),*) -> #closure_return_type
2125    };
2126    handler.closure_type_name = quote! {
2127       #closure_type_name
2128    };
2129
2130    let logger_return_type = if closure_return_type.to_token_stream().to_string().eq("()") {
2131        closure_return_type.clone().to_token_stream()
2132    } else {
2133        quote! {
2134            unimplemented!()
2135        }
2136    };
2137
2138    let wrapper_closure_args: Vec<TokenStream> = handler
2139        .args
2140        .iter()
2141        .filter_map(|arg| {
2142            let name = &arg.name;
2143            if name == &closure {
2144                return None;
2145            }
2146
2147            let field_name = format_ident!("{}", name);
2148            let return_type = ReturnType::new(arg.clone(), bindings.wrappers.clone()).get_new_return_type(false, false);
2149            if return_type.is_empty() {
2150                None
2151            } else {
2152                Some(quote! { #field_name })
2153            }
2154        })
2155        .filter(|t| !t.is_empty())
2156        .collect();
2157
2158    quote! {
2159        #(#doc_comments)*
2160        ///
2161        ///
2162        /// _(note you must copy any arguments that you use afterwards even those with static lifetimes)_
2163        pub trait #closure_type_name {
2164            fn #handle_method_name(&mut self, #(#closure_args),*) -> #closure_return_type;
2165        }
2166
2167        pub struct #logger_type_name;
2168        impl #closure_type_name for #logger_type_name {
2169            fn #handle_method_name(&mut self, #(#closure_args_in_logger),*) -> #closure_return_type {
2170                log::info!("{}({}\n)",
2171                    stringify!(#handle_method_name),
2172                    [#(#log_field_names),*].join(", "),
2173                );
2174                #logger_return_type
2175            }
2176        }
2177
2178        unsafe impl Send for #logger_type_name {}
2179        unsafe impl Sync for #logger_type_name {}
2180
2181        /// Any closure with the matching signature is a callback: methods that retain it
2182        /// heap-allocate it into a [`Handler`] owned by the registering resource.
2183        impl<F: FnMut(#(#fn_mut_args),*) -> #closure_return_type> #closure_type_name for F {
2184            #[inline]
2185            fn #handle_method_name(&mut self, #(#closure_args),*) -> #closure_return_type {
2186                self(#(#wrapper_closure_args),*)
2187            }
2188        }
2189
2190        /// Pass an existing [`Handler`] clone anywhere a callback value is expected, e.g.
2191        /// to share one callback instance across several registrations.
2192        impl<T: #closure_type_name> #closure_type_name for Handler<T> {
2193            #[inline]
2194            fn #handle_method_name(&mut self, #(#closure_args),*) -> #closure_return_type {
2195                let inner = unsafe { &mut *self.inner.get() };
2196                inner.#handle_method_name(#(#wrapper_closure_args),*)
2197            }
2198        }
2199
2200        // `NoHandler` implements every generated callback trait so that
2201        // `Handlers::NONE` (= `None::<&Handler<NoHandler>>`) can pin the callback
2202        // generic at any call site. Its body is unreachable: the C side receives a
2203        // null callback + null clientd, so this method can never be invoked.
2204        impl #closure_type_name for NoHandler {
2205            fn #handle_method_name(&mut self, #(#closure_args),*) -> #closure_return_type {
2206                unreachable!("NoHandler is a None sentinel; its callback must never be invoked")
2207            }
2208        }
2209
2210        // #[no_mangle]
2211        #[allow(dead_code)]
2212        #(#doc_comments)*
2213        unsafe extern "C" fn #fn_name<F: #closure_type_name>(
2214            #(#args),*
2215        ) -> #closure_return_type
2216        {
2217            #[cfg(debug_assertions)]
2218            if #closure_name.is_null() {
2219                unimplemented!("closure should not be null")
2220            }
2221            #[cfg(feature = "extra-logging")]
2222            {
2223                log::debug!("calling {}", stringify!(#handle_method_name));
2224            }
2225            #[cfg(feature = "log-c-bindings")]
2226            log::debug!(
2227                "{}({}\n)",
2228                stringify!(#fn_name),
2229                [#(#arg_names_for_logging),*].join(", ")
2230            );
2231            let closure: &mut F = unsafe { &mut *(#closure_name as *mut F) };
2232            closure.#handle_method_name(#(#converted_args),*)
2233        }
2234
2235        // #[no_mangle]
2236        #[allow(dead_code)]
2237        #(#doc_comments)*
2238        unsafe extern "C" fn #closure_fn_name<F: FnMut(#(#fn_mut_args),*) -> #closure_return_type>(
2239            #(#args),*
2240        ) -> #closure_return_type
2241        {
2242            #[cfg(debug_assertions)]
2243            if #closure_name.is_null() {
2244                unimplemented!("closure should not be null")
2245            }
2246            #[cfg(feature = "extra-logging")]
2247            {
2248                log::debug!("calling {}", stringify!(#closure_fn_name));
2249            }
2250            #[cfg(feature = "log-c-bindings")]
2251            log::debug!(
2252                "{}({}\n)",
2253                stringify!(#closure_fn_name),
2254                [#(#arg_names_for_logging),*].join(", ")
2255            );
2256            let closure: &mut F = unsafe { &mut *(#closure_name as *mut F) };
2257            closure(#(#converted_args),*)
2258        }
2259
2260    }
2261}
2262
2263pub fn generate_rust_code(
2264    wrapper: &CWrapper,
2265    wrappers: &BTreeMap<String, CWrapper>,
2266    include_common_code: bool,
2267    include_clippy: bool,
2268    include_aeron_client_registering_resource_t: bool,
2269    closure_handlers: &Vec<CHandler>,
2270) -> TokenStream {
2271    let class_name = Ident::new(&wrapper.class_name, proc_macro2::Span::call_site());
2272    let type_name = Ident::new(&wrapper.type_name, proc_macro2::Span::call_site());
2273
2274    let mut additional_outer_impls = vec![];
2275    let mut debug_fields = vec![];
2276
2277    let methods = wrapper.generate_methods(
2278        wrappers,
2279        closure_handlers,
2280        &mut additional_outer_impls,
2281        &mut debug_fields,
2282    );
2283    let tests = wrapper.generate_allocation_test();
2284    let mut constructor_fields = vec![];
2285    let mut new_ref_set_none = vec![];
2286    let constructor = wrapper.generate_constructor(wrappers, &mut constructor_fields, &mut new_ref_set_none);
2287
2288    let async_impls = if wrapper.type_name.starts_with("aeron_async_")
2289        || wrapper.type_name.starts_with("aeron_archive_async_")
2290    {
2291        let new_method = wrapper.methods.iter().find(|m| m.fn_name == wrapper.without_name);
2292
2293        if let Some(new_method) = new_method {
2294            let main_type = &wrapper.type_name.replace("_async_", "_").replace("_add_", "_");
2295            let main = get_possible_wrappers(main_type)
2296                .iter()
2297                .filter_map(|f| wrappers.get(f))
2298                .next()
2299                .expect(&format!("failed to find main type {}", main_type));
2300
2301            let poll_method = main
2302                .methods
2303                .iter()
2304                .find(|m| m.fn_name == format!("{}_poll", wrapper.without_name))
2305                .unwrap();
2306
2307            let main_class_name = format_ident!("{}", main.class_name);
2308            let async_class_name = format_ident!("{}", wrapper.class_name);
2309            let poll_method_name = format_ident!("{}_poll", wrapper.without_name);
2310            let new_method_name = format_ident!("{}", new_method.fn_name);
2311
2312            let client_class = wrappers
2313                .get(
2314                    new_method
2315                        .arguments
2316                        .iter()
2317                        .skip(1)
2318                        .next()
2319                        .unwrap()
2320                        .c_type
2321                        .split_whitespace()
2322                        .last()
2323                        .unwrap(),
2324                )
2325                .unwrap();
2326            let client_type = format_ident!("{}", client_class.class_name);
2327            let client_type_method_name = format_ident!(
2328                "{}",
2329                new_method
2330                    .fn_name
2331                    .replace(&format!("{}_", client_class.without_name), "")
2332            );
2333            let client_type_method_name_without_async = format_ident!(
2334                "{}",
2335                new_method
2336                    .fn_name
2337                    .replace(&format!("{}_", client_class.without_name), "")
2338                    .replace("async_", "")
2339            );
2340
2341            let init_args: Vec<TokenStream> = poll_method
2342                .arguments
2343                .iter()
2344                .enumerate()
2345                .filter_map(|(idx, arg)| {
2346                    if idx == 0 {
2347                        Some(quote! { ctx_field })
2348                    } else {
2349                        let arg_name = arg.as_ident();
2350                        let arg_name = ReturnType::new(arg.clone(), wrappers.clone())
2351                            .handle_rs_to_c_return(quote! { #arg_name }, false);
2352                        Some(quote! { #arg_name })
2353                    }
2354                })
2355                .filter(|t| !t.is_empty())
2356                .collect();
2357
2358            let new_args: Vec<TokenStream> = poll_method
2359                .arguments
2360                .iter()
2361                .enumerate()
2362                .filter_map(|(idx, arg)| {
2363                    if idx == 0 {
2364                        None
2365                    } else {
2366                        let arg_name = arg.as_ident();
2367                        let arg_type = ReturnType::new(arg.clone(), wrappers.clone()).get_new_return_type(false, true);
2368                        if arg_type.clone().into_token_stream().is_empty() {
2369                            None
2370                        } else {
2371                            Some(quote! { #arg_name: #arg_type })
2372                        }
2373                    }
2374                })
2375                .filter(|t| !t.is_empty())
2376                .collect();
2377
2378            let async_init_args: Vec<TokenStream> = new_method
2379                .arguments
2380                .iter()
2381                .enumerate()
2382                .filter_map(|(idx, arg)| {
2383                    if idx == 0 {
2384                        Some(quote! { ctx_field })
2385                    } else {
2386                        let arg_name = arg.as_ident();
2387                        let arg_name = ReturnType::new(arg.clone(), wrappers.clone())
2388                            .handle_rs_to_c_return(quote! { #arg_name }, false);
2389                        Some(quote! { #arg_name })
2390                    }
2391                })
2392                .filter(|t| !t.is_empty())
2393                .collect();
2394
2395            // Generate logging for async new method arguments (as token stream)
2396            let async_log_expr_tokens = CWrapper::generate_arg_logging(&new_method.arguments, &async_init_args);
2397
2398            let generic_types: Vec<TokenStream> = new_method
2399                .arguments
2400                .iter()
2401                .flat_map(|arg| {
2402                    ReturnType::new(arg.clone(), wrappers.clone())
2403                        .method_generics_for_where(true)
2404                        .into_iter()
2405                })
2406                .collect_vec();
2407            let where_clause_async = if generic_types.is_empty() {
2408                quote! {}
2409            } else {
2410                quote! { <#(#generic_types),*> }
2411            };
2412            let generic_types: Vec<TokenStream> = poll_method
2413                .arguments
2414                .iter()
2415                .flat_map(|arg| {
2416                    ReturnType::new(arg.clone(), wrappers.clone())
2417                        .method_generics_for_where(true)
2418                        .into_iter()
2419                })
2420                .collect_vec();
2421            let where_clause_main = if generic_types.is_empty() {
2422                quote! {}
2423            } else {
2424                quote! { <#(#generic_types),*> }
2425            };
2426            let async_new_args: Vec<TokenStream> = new_method
2427                .arguments
2428                .iter()
2429                .enumerate()
2430                .filter_map(|(idx, arg)| {
2431                    if idx == 0 {
2432                        None
2433                    } else {
2434                        let arg_name = arg.as_ident();
2435                        let arg_type = ReturnType::new(arg.clone(), wrappers.clone()).get_new_return_type(false, true);
2436                        if arg_type.clone().into_token_stream().is_empty() {
2437                            None
2438                        } else {
2439                            Some(quote! { #arg_name: #arg_type })
2440                        }
2441                    }
2442                })
2443                .filter(|t| !t.is_empty())
2444                .collect();
2445
2446            let async_dependancies = async_new_args
2447                .iter()
2448                .filter(|a| a.to_string().contains(" : Aeron") || a.to_string().contains(" : & Aeron"))
2449                .map(|e| {
2450                    let var_name = format_ident!("{}", e.to_string().split_whitespace().next().unwrap());
2451                    quote! {
2452                        result.inner.add_dependency(#var_name.clone());
2453                    }
2454                })
2455                .collect_vec();
2456
2457            let mut async_handler_deps: Vec<TokenStream> =
2458                CWrapper::handler_dependency_registrations(&new_method.arguments);
2459
2460            // The conductor thread can invoke retained callbacks (e.g. image lifecycle
2461            // handlers) for as long as the *client* lives — even if this async poller is
2462            // dropped without ever being polled. Anchor each handler to the client too, so
2463            // dropping an unpolled poller cannot free a callback C still points at.
2464            if let Some(client_arg) = async_new_args
2465                .iter()
2466                .find(|a| a.to_string().contains(" : Aeron") || a.to_string().contains(" : & Aeron"))
2467            {
2468                let client_var = format_ident!("{}", client_arg.to_string().split_whitespace().next().unwrap());
2469                let client_anchored: Vec<TokenStream> = new_method
2470                    .arguments
2471                    .iter()
2472                    .filter_map(|arg| {
2473                        if let ArgProcessing::Handler(_) = &arg.processing {
2474                            if !arg.is_mut_pointer() {
2475                                let name = arg.as_ident();
2476                                return Some(quote! {
2477                                    if let Some(__handler) = #name {
2478                                        if let Some(__client_inner) = #client_var.inner.as_owned() {
2479                                            __client_inner.add_dependency(__handler.clone());
2480                                        }
2481                                    }
2482                                });
2483                            }
2484                        }
2485                        None
2486                    })
2487                    .collect();
2488                async_handler_deps.extend(client_anchored);
2489            }
2490
2491            let async_new_args_for_client = async_new_args.iter().skip(1).cloned().collect_vec();
2492
2493            let async_new_args_name_only: Vec<TokenStream> = new_method
2494                .arguments
2495                .iter()
2496                .enumerate()
2497                .filter_map(|(idx, arg)| {
2498                    if idx < 2 {
2499                        None
2500                    } else {
2501                        let arg_name = arg.as_ident();
2502                        let arg_type = ReturnType::new(arg.clone(), wrappers.clone()).get_new_return_type(false, false);
2503                        if arg_type.clone().into_token_stream().is_empty() {
2504                            None
2505                        } else {
2506                            Some(quote! { #arg_name })
2507                        }
2508                    }
2509                })
2510                .filter(|t| !t.is_empty())
2511                .collect();
2512
2513            // Generate logging for poll method arguments (as token stream)
2514            let poll_log_expr_tokens = CWrapper::generate_arg_logging(&poll_method.arguments, &init_args);
2515
2516            let close_cleanup = if let Some(close_method) = main.get_close_method() {
2517                let close_fn = format_ident!("{}", close_method.fn_name);
2518                // Aeron C close functions now take optional notification
2519                // callbacks and a clientd pointer.  Use Default::default()
2520                // for any arg beyond the resource pointer — it resolves to
2521                // `None` / `null_mut()` via type inference at the call site.
2522                let extra_args: Vec<TokenStream> = close_method
2523                    .arguments
2524                    .iter()
2525                    .skip(1)
2526                    .map(|_| quote! { Default::default() })
2527                    .collect();
2528                quote! {
2529                    Some(Box::new(move |ptr| unsafe {
2530                        #close_fn(*ptr, #(#extra_args),*)
2531                    }))
2532                }
2533            } else {
2534                quote! { None }
2535            };
2536
2537            quote! {
2538                    impl #main_class_name {
2539                        #[inline]
2540                        pub fn new #where_clause_main (#(#new_args),*) -> Result<Self, AeronCError> {
2541                            let resource = ManagedCResource::new(
2542                                move |ctx_field| unsafe {
2543                                    #[cfg(feature = "log-c-bindings")]
2544                                    {
2545                                        let log_args = #poll_log_expr_tokens;
2546                                        log::info!("{}({})", stringify!(#poll_method_name), log_args);
2547                                    }
2548                                    #poll_method_name(#(#init_args),*)
2549                                },
2550                                #close_cleanup,
2551                                false,
2552                            )?;
2553                            Ok(Self {
2554                                inner: CResource::OwnedOnHeap(RcOrArc::new(resource)),
2555                            })
2556                        }
2557                    }
2558
2559                    impl #client_type {
2560                        #[inline]
2561                        pub fn #client_type_method_name #where_clause_async(&self, #(#async_new_args_for_client),*) -> Result<#async_class_name, AeronCError> {
2562                            let mut result =  #async_class_name::new(self, #(#async_new_args_name_only),*);
2563                            if let Ok(result) = &mut result {
2564                                result.inner.add_dependency(self.clone());
2565                            }
2566
2567                            result
2568                        }
2569                    }
2570
2571                    impl #client_type {
2572                        #[doc = r"Blocking convenience wrapper around the async add operation."]
2573                        #[doc = r""]
2574                        #[doc = r"**Convenience for examples and tests only.** It blocks the calling thread"]
2575                        #[doc = r"in a busy-poll loop until the resource is available or `timeout` elapses. In"]
2576                        #[doc = r"production, prefer the `async_add_*` variant and drive its `poll()` from your"]
2577                        #[doc = r"own event loop rather than blocking on a single operation."]
2578                        #[doc = r""]
2579                        #[doc = r"# Production pattern (pseudo code)"]
2580                        #[doc = r"```text"]
2581                        #[doc = r"// Don't block — drive the async poller from your loop"]
2582                        #[doc = r"let poller = client.async_add_*(...)?;"]
2583                        #[doc = r"loop {"]
2584                        #[doc = r"    if let Some(resource) = poller.poll()? {"]
2585                        #[doc = r"        break; // ready"]
2586                        #[doc = r"    }"]
2587                        #[doc = r"    do_other_work(); // service other subscriptions, timers, etc."]
2588                        #[doc = r"}"]
2589                        #[doc = r"```"]
2590                        #[doc = r""]
2591                        #[doc = r"See `poll_blocking` on the async poller for the same caveat."]
2592                        #[inline]
2593                        pub fn #client_type_method_name_without_async #where_clause_async(&self #(
2594                    , #async_new_args_for_client)*,  timeout: std::time::Duration) -> Result<#main_class_name, AeronCError> {
2595                            let start = std::time::Instant::now();
2596                            loop {
2597                                if let Ok(poller) = #async_class_name::new(self, #(#async_new_args_name_only),*) {
2598                                    while start.elapsed() <= timeout  {
2599                                      if let Some(result) = poller.poll()? {
2600                                          return Ok(result);
2601                                      }
2602                                    #[cfg(debug_assertions)]
2603                                    std::thread::sleep(std::time::Duration::from_millis(10));
2604                                  }
2605                                }
2606                            if start.elapsed() > timeout {
2607                                log::error!("failed async poll for {:?}", self);
2608                                return Err(AeronErrorType::TimedOut.into());
2609                            }
2610                            #[cfg(debug_assertions)]
2611                            std::thread::sleep(std::time::Duration::from_millis(10));
2612                          }
2613            }
2614                    }
2615
2616                    impl #async_class_name {
2617                        #[inline]
2618                        pub fn new #where_clause_async (#(#async_new_args),*) -> Result<Self, AeronCError> {
2619                            let resource_async = ManagedCResource::new(
2620                                move |ctx_field| unsafe {
2621                                    #[cfg(feature = "log-c-bindings")]
2622                                    {
2623                                        let log_args = #async_log_expr_tokens;
2624                                        log::info!("{}({})", stringify!(#new_method_name), log_args);
2625                                    }
2626                                    #new_method_name(#(#async_init_args),*)
2627                                },
2628                                None,
2629                                false,
2630                            )?;
2631                            let result = Self {
2632                                inner: CResource::OwnedOnHeap(RcOrArc::new(resource_async)),
2633                            };
2634                            #(#async_dependancies)*
2635                            // Retained callbacks (e.g. image lifecycle handlers) are stored by
2636                            // the C client; the dependency clones below are propagated to the
2637                            // final resource when poll() succeeds, keeping them alive until it
2638                            // closes.
2639                            #(#async_handler_deps)*
2640                            Ok(result)
2641                        }
2642
2643                        #[inline]
2644                        pub fn poll(&self) -> Result<Option<#main_class_name>, AeronCError> {
2645
2646                            if let Some(inner) = self.inner.as_owned() {
2647                                if inner.is_resource_released() {
2648                                    return Ok(None);
2649                                }
2650                            }
2651
2652                            let mut result = #main_class_name::new(self);
2653                            if let Ok(result) = &mut result {
2654                                unsafe {
2655                                    #[cfg(feature = "multi-threaded")]
2656                                    for d in (&mut *self.inner.as_owned().unwrap().dependencies.lock().unwrap()).iter_mut() {
2657                                      result.inner.add_dependency(d.clone());
2658                                    }
2659                                    #[cfg(not(feature = "multi-threaded"))]
2660                                    for d in (&mut *self.inner.as_owned().unwrap().dependencies.get()).iter_mut() {
2661                                      result.inner.add_dependency(d.clone());
2662                                    }
2663                                }
2664                            }
2665
2666                            match result {
2667                                Ok(result) => {
2668                                    if let Some(inner) = self.inner.as_owned() {
2669                                        inner.mark_resource_released();
2670                                    }
2671                                    Ok(Some(result))
2672                                }
2673                                Err(e) if e.code == 0 => {
2674                                  Ok(None) // try again
2675                                }
2676                                Err(e) => {
2677                                    if let Some(inner) = self.inner.as_owned() {
2678                                        inner.mark_resource_released();
2679                                    }
2680                                    Err(e)
2681                                }
2682                            }
2683                        }
2684
2685                        #[doc = r"Polls synchronously until the async operation completes or `timeout` elapses."]
2686                        #[doc = r""]
2687                        #[doc = r"**Convenience for examples and tests only.** It blocks the calling thread"]
2688                        #[doc = r"in a busy-poll loop. In production, drive `poll()` from your own event loop"]
2689                        #[doc = r"(between other work, on a timer, or in a dedicated duty cycle) rather than"]
2690                        #[doc = r"blocking on a single operation."]
2691                        #[doc = r""]
2692                        #[doc = r"# Production pattern (pseudo code)"]
2693                        #[doc = r"```text"]
2694                        #[doc = r"// Don't block — integrate poll() into your existing loop"]
2695                        #[doc = r"loop {"]
2696                        #[doc = r"    if let Some(resource) = poller.poll()? {"]
2697                        #[doc = r"        break; // ready"]
2698                        #[doc = r"    }"]
2699                        #[doc = r"    do_other_work(); // service other subscriptions, timers, etc."]
2700                        #[doc = r"}"]
2701                        #[doc = r"```"]
2702                        #[doc = r""]
2703                        #[doc = r"See the blocking `add_*(.., timeout)` helpers for the same caveat."]
2704                        #[inline]
2705                        pub fn poll_blocking(&self, timeout: std::time::Duration) -> Result<#main_class_name, AeronCError> {
2706                            if let Some(result) = self.poll()? {
2707                                return Ok(result);
2708                            }
2709
2710                            let time = std::time::Instant::now();
2711                            while time.elapsed() < timeout {
2712                                if let Some(result) = self.poll()? {
2713                                    return Ok(result);
2714                                }
2715                                #[cfg(debug_assertions)]
2716                                std::thread::sleep(std::time::Duration::from_millis(10));
2717                            }
2718                            log::error!("failed async poll for {:?}", self);
2719                            Err(AeronErrorType::TimedOut.into())
2720                        }
2721                    }
2722                                }
2723        } else {
2724            quote! {}
2725        }
2726    } else {
2727        quote! {}
2728    };
2729
2730    let mut additional_impls = vec![];
2731
2732    if let Some(close_method) = wrapper.get_close_method() {
2733        let close_deferred_if_shared = wrapper.type_name == "aeron_t" || wrapper.type_name == "aeron_archive_t";
2734        let skip_generated_close =
2735            wrapper.methods.iter().any(|m| m.fn_name.contains("_init")) && !close_deferred_if_shared;
2736        if !skip_generated_close {
2737            let close_fn = format_ident!("{}", close_method.fn_name);
2738            let close_resource_call = if close_deferred_if_shared {
2739                quote! { self.inner.close_resource_deferred_if_shared() }
2740            } else {
2741                quote! { self.inner.close_resource() }
2742            };
2743            // Consuming close(self): run the FFI close through the shared
2744            // ManagedCResource state, then drop this handle.  The cleanup
2745            // closure is taken exactly once across clones, and after
2746            // close(self) this binding is gone.
2747            if close_deferred_if_shared {
2748                additional_impls.push(quote! {
2749                    impl #class_name {
2750                        /// Releases this handle and closes the C client **once the last reference drops**.
2751                        ///
2752                        /// The C client owns and frees every child resource (publications,
2753                        /// subscriptions, counters) when it closes, so while children or clones
2754                        /// are alive this is deferred — equivalent to `drop` — and the surviving
2755                        /// handles remain fully usable. The C close runs when the final
2756                        /// reference (child or clone) is released.
2757                        pub fn close(self) -> Result<(), AeronCError> {
2758                            let result = #close_resource_call;
2759                            // Drop this handle (decrements Rc, releases deps).
2760                            drop(self);
2761                            result
2762                        }
2763
2764                        /// Closes the C client **immediately**, even if children or clones are alive.
2765                        ///
2766                        /// Clones of *this* handle are safe afterwards (their shared pointer is
2767                        /// nulled). Child handles are not:
2768                        ///
2769                        /// # Safety
2770                        /// The C client frees every child resource (publications, subscriptions,
2771                        /// counters) during this call, so surviving child handles dangle. Any use
2772                        /// is use-after-free — **including their `Drop`**, which calls the C close
2773                        /// on the freed pointer (double free). You must `std::mem::forget` every
2774                        /// surviving child, or never return (e.g. `std::process::exit`). Prefer
2775                        /// [`Self::close`], which defers until the last reference drops.
2776                        pub unsafe fn close_now(self) -> Result<(), AeronCError> {
2777                            let result = self.inner.close_resource();
2778                            drop(self);
2779                            result
2780                        }
2781                    }
2782                });
2783            } else {
2784                additional_impls.push(quote! {
2785                    impl #class_name {
2786                        /// Closes this resource in the C client immediately; all clones of this
2787                        /// handle become closed (their pointer is nulled) and must not be used.
2788                        pub fn close(self) -> Result<(), AeronCError> {
2789                            let result = #close_resource_call;
2790                            // Drop this handle (decrements Rc, releases deps).
2791                            drop(self);
2792                            result
2793                        }
2794                    }
2795                });
2796            }
2797
2798            let close_has_notification_handler = !close_deferred_if_shared
2799                && close_method
2800                    .arguments
2801                    .iter()
2802                    .any(|arg| matches!(arg.processing, ArgProcessing::Handler(_)))
2803                && close_method.arguments.iter().any(|arg| arg.is_c_void());
2804
2805            if close_has_notification_handler {
2806                additional_impls.push(quote! {
2807                    impl #class_name {
2808                        /// Like [`Self::close`], but notifies `on_close_complete` when the close
2809                        /// finishes. The handler must outlive the notification (keep your
2810                        /// `Handler` alive until it has fired).
2811                        pub fn close_with_handler<AeronNotificationHandlerImpl: AeronNotificationCallback>(
2812                            self,
2813                            on_close_complete: Option<&Handler<AeronNotificationHandlerImpl>>,
2814                        ) -> Result<(), AeronCError> {
2815                            let result = self.inner.close_resource_with(move |ptr| unsafe {
2816                                #close_fn(
2817                                    *ptr,
2818                                    {
2819                                        let callback: aeron_notification_t = if on_close_complete.is_none() {
2820                                            None
2821                                        } else {
2822                                            Some(aeron_notification_t_callback::<AeronNotificationHandlerImpl>)
2823                                        };
2824                                        callback
2825                                    },
2826                                    on_close_complete
2827                                        .map(|m| m.as_raw())
2828                                        .unwrap_or_else(|| std::ptr::null_mut()),
2829                                )
2830                            });
2831                            drop(self);
2832                            result
2833                        }
2834                    }
2835                });
2836            }
2837
2838            // Generate additional methods for specific types
2839            if wrapper.type_name == "aeron_counter_t" {
2840                additional_impls.push(quote! {
2841                    impl #class_name {
2842                        #[inline]
2843                        pub fn addr_atomic(&self) -> &std::sync::atomic::AtomicI64 {
2844                            unsafe { std::sync::atomic::AtomicI64::from_ptr(self.addr()) }
2845                        }
2846                    }
2847                });
2848            } else if wrapper.type_name == "aeron_publication_t" || wrapper.type_name == "aeron_exclusive_publication_t"
2849            {
2850                additional_impls.push(quote! {
2851                    impl #class_name {
2852                        #[inline]
2853                        pub fn is_ready(&self) -> bool {
2854                            self.is_connected() && self.position_limit() != 0
2855                        }
2856                    }
2857                });
2858            }
2859        }
2860    }
2861
2862    let common_code = if !include_common_code {
2863        quote! {}
2864    } else {
2865        TokenStream::from_str(COMMON_CODE).unwrap()
2866    };
2867    let warning_code = if !include_common_code {
2868        quote! {}
2869    } else {
2870        let mut code = String::new();
2871
2872        if include_clippy {
2873            code.push_str(
2874                "        #![allow(non_upper_case_globals)]
2875        #![allow(non_camel_case_types)]
2876        #![allow(non_snake_case)]
2877        #![allow(clippy::all)]
2878        #![allow(unused_variables)]
2879        #![allow(unused_unsafe)]
2880",
2881            );
2882        }
2883
2884        if include_aeron_client_registering_resource_t {
2885            code.push_str(
2886                "
2887                type aeron_client_registering_resource_t = aeron_client_registering_resource_stct;
2888",
2889            );
2890        }
2891
2892        TokenStream::from_str(code.as_str()).unwrap()
2893    };
2894    let class_docs: Vec<TokenStream> = wrapper
2895        .docs
2896        .iter()
2897        .map(|doc| {
2898            quote! {
2899                #[doc = #doc]
2900            }
2901        })
2902        .collect();
2903
2904    let fields = wrapper.generate_fields(&wrappers, &mut debug_fields);
2905
2906    let default_impl =
2907        if wrapper.has_default_method() && !constructor.iter().map(|x| x.to_string()).join("").trim().is_empty() {
2908            // let default_method_call = if wrapper.has_any_methods() {
2909            //     quote! {
2910            //         #class_name::new_zeroed_on_heap()
2911            //     }
2912            //  } else {
2913            //     quote! {
2914            //         #class_name::new_zeroed_on_stack()
2915            //     }
2916            // };
2917
2918            quote! {
2919                /// This will create an instance where the struct is zeroed, use with care
2920                impl Default for #class_name {
2921                    fn default() -> Self {
2922                        #class_name::new_zeroed_on_heap()
2923                    }
2924                }
2925
2926                impl #class_name {
2927                    /// Regular clone just increases the reference count of underlying count.
2928                    /// `clone_struct` shallow copies the content of the underlying struct on heap.
2929                    ///
2930                    /// NOTE: if the struct has references to other structs these will not be copied
2931                    ///
2932                    /// Must be only used on structs which has no init/clean up methods.
2933                    /// So its dangerous to use with Aeron/AeronContext/AeronPublication/AeronSubscription
2934                    /// More intended for AeronArchiveRecordingDescriptor (note strings will not work as its a shallow copy)
2935                    pub fn clone_struct(&self) -> Self {
2936                        let copy = Self::default();
2937                        // SAFETY: `copy` was created just above and is not aliased yet.
2938                        unsafe { copy.get_inner_mut().clone_from(self.deref()) };
2939                        copy
2940                    }
2941                }
2942            }
2943        } else {
2944            quote! {}
2945        };
2946
2947    quote! {
2948        #warning_code
2949
2950        #(#class_docs)*
2951        #[derive(Clone)]
2952        pub struct #class_name {
2953            inner: CResource<#type_name>,
2954            #(#constructor_fields)*
2955        }
2956
2957        impl core::fmt::Debug for  #class_name {
2958            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2959                if self.inner.get().is_null() {
2960                    f.debug_struct(stringify!(#class_name))
2961                    .field("inner", &"null")
2962                    .finish()
2963                } else {
2964                    f.debug_struct(stringify!(#class_name))
2965                      .field("inner", &self.inner)
2966                      #(#debug_fields)*
2967                      .finish()
2968                }
2969            }
2970        }
2971
2972        impl #class_name {
2973            #(#constructor)*
2974            #(#fields)*
2975            #(#methods)*
2976
2977            #[inline(always)]
2978            pub fn get_inner(&self) -> *mut #type_name {
2979                self.inner.get()
2980            }
2981
2982            /// Mutable access to the underlying C struct, minted from `&self`: nothing
2983            /// prevents two live `&mut` at once, so the caller must ensure exclusive
2984            /// access for the lifetime of the returned reference.
2985            ///
2986            /// # Safety
2987            /// No other reference (`&` or `&mut`) to the underlying struct may be
2988            /// alive while the returned `&mut` is in use.
2989            #[inline(always)]
2990            pub unsafe fn get_inner_mut(&self) -> &mut #type_name {
2991                &mut *self.inner.get()
2992            }
2993
2994            #[inline(always)]
2995            pub fn get_inner_ref(&self) -> & #type_name {
2996                unsafe { &*self.inner.get() }
2997            }
2998        }
2999
3000        impl std::ops::Deref for #class_name {
3001            type Target = #type_name;
3002
3003            fn deref(&self) -> &Self::Target {
3004                self.get_inner_ref()
3005            }
3006        }
3007
3008        impl From<*mut #type_name> for #class_name {
3009            #[inline]
3010            fn from(value: *mut #type_name) -> Self {
3011                #class_name {
3012                    inner: CResource::Borrowed(value),
3013                    #(#new_ref_set_none)*
3014                }
3015            }
3016        }
3017
3018        impl From<#class_name> for *mut #type_name {
3019            #[inline]
3020            fn from(value: #class_name) -> Self {
3021                value.get_inner()
3022            }
3023        }
3024
3025        impl From<&#class_name> for *mut #type_name {
3026            #[inline]
3027            fn from(value: &#class_name) -> Self {
3028                value.get_inner()
3029            }
3030        }
3031
3032        impl From<#class_name> for #type_name {
3033            #[inline]
3034            fn from(value: #class_name) -> Self {
3035                unsafe { *value.get_inner().clone() }
3036            }
3037        }
3038
3039        impl From<*const #type_name> for #class_name {
3040            #[inline]
3041            fn from(value: *const #type_name) -> Self {
3042                #class_name {
3043                    inner: CResource::Borrowed(value as *mut #type_name),
3044                    #(#new_ref_set_none)*
3045                }
3046            }
3047        }
3048
3049        impl From<#type_name> for #class_name {
3050            #[inline]
3051            fn from(value: #type_name) -> Self {
3052                #class_name {
3053                    inner: CResource::OwnedOnStack(MaybeUninit::new(value)),
3054                    #(#new_ref_set_none)*
3055                }
3056            }
3057        }
3058
3059        #(#additional_impls)*
3060
3061        #async_impls
3062        #default_impl
3063        #tests
3064        #common_code
3065    }
3066}