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::{ToTokens, format_ident, quote};
7use std::collections::{BTreeMap, BTreeSet, HashMap};
8use std::ops::Deref;
9use std::str::FromStr;
10use syn::{ImplItem, Item, Type, parse_str};
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 mut register_handlers = if uses_self && !retained_handler_registrations.is_empty() {
717                    quote! { #(#retained_handler_registrations)* }
718                } else {
719                    quote! {}
720                };
721                // C copies the pair's handler and clientd and may fire them until the client
722                // closes (close handlers fire inside that close), so the client keeps the pair
723                // and, through it, its Handler. Remove cannot release it: dependencies only grow.
724                if matches!(
725                    method.fn_name.as_str(),
726                    "aeron_add_available_counter_handler"
727                        | "aeron_add_unavailable_counter_handler"
728                        | "aeron_add_close_handler"
729                ) {
730                    register_handlers.extend(quote! {
731                        if result >= 0 {
732                            if let Some(__inner) = self.inner.as_owned() {
733                                __inner.add_dependency(pair.clone());
734                            }
735                        }
736                    });
737                }
738
739                let mut additional_methods = vec![];
740                let set_closed = quote! {};
741                Self::add_mut_string_methods_if_applicable(method, &fn_name, uses_self, &method_docs, &mut additional_methods);
742
743                // getter methods
744                Self::add_getter_instead_of_mut_arg_if_applicable(wrappers, method, &fn_name, &where_clause, &possible_self, &method_docs, &mut additional_methods, debug_fields);
745
746                // `_fn` stack-closure variant, emitted straight from the plan (sync-only:
747                // a stack closure handed to a retained callback would dangle). Skipped when
748                // aeron_custom.rs hand-writes a method of the same `<name>_fn` name, so
749                // custom code can override/deprecate a generated `_fn` variant.
750                if classified.once_capable
751                    && !self
752                        .skipped_methods
753                        .contains(&format!("{}_fn", method.struct_method_name))
754                {
755                    let once_fn_name = format_ident!("{}_fn", fn_name);
756                    let once_generics = classified.once_generics();
757                    let once_where = if once_generics.is_empty() {
758                        quote! {}
759                    } else {
760                        quote! { <#(#once_generics),*> }
761                    };
762                    let once_args = classified.once_signatures();
763                    let once_calls = classified.once_calls();
764                    let once_log_expr = Self::generate_arg_logging(&method.arguments, &once_calls);
765                    additional_methods.push(quote! {
766                        #[inline]
767                        #(#method_docs)*
768                        ///
769                        ///
770                        /// **Stack-borrowed closure** (`_fn` variant): the `FnMut` closure lives on the
771                        /// caller's stack and is borrowed for this call only — the callback fires
772                        /// synchronously inside the call, so nothing is heap-allocated, nothing is stored,
773                        /// and the closure may borrow local state. Prefer this over the retained
774                        /// [`Handler`]-based form on the hot path; only generated for callbacks the C
775                        /// client does not retain (i.e. not stored for later firing).
776                        ///
777                        /// # Panics
778                        ///
779                        /// A panic inside the closure cannot unwind across the `extern "C"` callback
780                        /// boundary and **aborts the process** (since Rust 1.81). Return early instead
781                        /// of panicking in production fragment handlers.
782                        pub fn #once_fn_name #once_where(#possible_self #(#once_args),*) -> #return_type {
783                            #set_closed
784                            unsafe {
785                                #[cfg(feature = "log-c-bindings")]
786                                log::info!(
787                                    "{}({})",
788                                    stringify!(#ffi_call),
789                                    #once_log_expr
790                                );
791
792                                let result = #ffi_call(#(#once_calls),*);
793
794                                #[cfg(feature = "log-c-bindings")]
795                                log::info!("  -> {:?}", result);
796
797                                #converter
798                            }
799                        }
800                    });
801                }
802
803                let mut_primitivies = method.arguments.iter()
804                    .filter(|a| a.is_mut_pointer() && a.is_primitive())
805                    .collect_vec();
806                let single_mut_field = method.return_type.is_c_raw_int() && mut_primitivies.len() == 1;
807
808                // in aeron some methods return error code but have &mut primitive
809                // ideally we should return that primitive instead of forcing user to pass it in
810                let __method_tokens = if single_mut_field {
811                    let mut_field = mut_primitivies.first().unwrap();
812                    // keep the qualified path (`c_int` is only valid qualified), not just the last segment
813                    let rt: Type = parse_str(mut_field.c_type.trim_start_matches("* mut").trim()).unwrap();
814                    let return_type = quote! { Result<#rt, AeronCError> };
815
816                    let fn_arguments= fn_arguments.into_iter().filter(|arg| {!arg.to_string().contains("& mut ")})
817                        .collect_vec();
818
819                    let idx = arg_names.iter().enumerate()
820                        .filter(|(_, arg)| arg.to_string().ends_with("* mut _"))
821                        .map(|(i, _)| i)
822                        .next().unwrap();
823
824                    arg_names[idx] = quote! { &mut mut_result };
825
826                    let mut first = true;
827                    let mut method_docs = method_docs.iter()
828                        .filter(|d| !d.to_string().contains("# Return"))
829                        .map(|d| {
830                            let mut string = d.to_string();
831                            string = string.replace("# Parameters", "");
832                            if string.contains("out param") {
833                                TokenStream::from_str(&string.replace("- `", "\n# Return\n`")).unwrap()
834                            } else {
835                                if string.contains("- `") && first {
836                                    first = false;
837                                    string = string.replacen("- `","# Parameters\n- `", 1);
838                                }
839                                TokenStream::from_str(&string).unwrap()
840                            }
841                        })
842                        .collect_vec();
843
844                    let filter_param_title = !method_docs.iter().any(|d| d.to_string().contains("- `"));
845
846                    if filter_param_title {
847                        method_docs = method_docs.into_iter()
848                            .map(|s| TokenStream::from_str(s.to_string().replace("# Parameters\n", "").as_str()).unwrap())
849                            .collect_vec();
850                    }
851
852
853                    quote! {
854                        #[inline]
855                        #(#method_docs)*
856                        pub fn #fn_name #where_clause(#possible_self #(#fn_arguments),*) -> #return_type {
857                            #set_closed
858                            unsafe {
859                                let mut mut_result: #rt = Default::default();
860
861                                #[cfg(feature = "log-c-bindings")]
862                                log::info!(
863                                    "{}({})",
864                                    stringify!(#ffi_call),
865                                    #args_log_expr
866                                );
867
868                                let err_code = #ffi_call(#(#arg_names),*);
869                                #register_handlers
870
871                                #[cfg(feature = "log-c-bindings")]
872                                log::info!("  -> err_code = {:?}, result = {:?}", err_code, mut_result);
873
874                                if err_code < 0 {
875                                    return Err(AeronCError::from_code(err_code));
876                                } else {
877                                    return Ok(mut_result);
878                                }
879                            }
880                        }
881
882                        #(#additional_methods)*
883                    }
884                } else {
885                    quote! {
886                        #[inline]
887                        #(#method_docs)*
888                        pub fn #fn_name #where_clause(#possible_self #(#fn_arguments),*) -> #return_type {
889                            #set_closed
890                            #handler_prelude
891                            unsafe {
892                                #[cfg(feature = "log-c-bindings")]
893                                log::info!(
894                                    "{}({})",
895                                    stringify!(#ffi_call),
896                                    #args_log_expr
897                                );
898
899                                let result = #ffi_call(#(#arg_names),*);
900                                #register_handlers
901
902                                #[cfg(feature = "log-c-bindings")]
903                                log::info!("  -> {:?}", result);
904
905                                #converter
906                            }
907                        }
908
909                        #(#additional_methods)*
910                    }
911                };
912
913                // Archive control operations surface typed errors (AeronArchiveError) so
914                // callers can match on the structured code. Apply uniformly at the token
915                // level to every method on `aeron_archive_t` — the main body plus the
916                // `additional_methods` (_fn variants, out-param getters, string getters)
917                // which all live in this same closure scope.
918                if self.type_name == "aeron_archive_t" {
919                    __method_tokens
920                        .to_string()
921                        .replace("AeronCError", "AeronArchiveError")
922                        .parse()
923                        .expect("AeronArchiveError token substitution must yield valid tokens")
924                } else {
925                    __method_tokens
926                }
927            })
928            .collect()
929    }
930
931    /// Registration statements that clone each `Handler` argument into `result`'s
932    /// dependencies. Used by constructors: the created C struct stores the callback and
933    /// clientd pointers, so the Handler must stay alive until the C resource is closed.
934    fn handler_dependency_registrations(arguments: &[Arg]) -> Vec<TokenStream> {
935        arguments
936            .iter()
937            .filter_map(|arg| {
938                if let ArgProcessing::Handler(_) = &arg.processing {
939                    if !arg.is_mut_pointer() {
940                        let name = arg.as_ident();
941                        return Some(quote! {
942                            if let Some(__handler) = #name {
943                                if let Some(__inner) = result.inner.as_owned() {
944                                    __inner.add_dependency(__handler.clone());
945                                }
946                            }
947                        });
948                    }
949                }
950                None
951            })
952            .collect()
953    }
954
955    fn add_getter_instead_of_mut_arg_if_applicable(
956        wrappers: &BTreeMap<String, CWrapper>,
957        method: &Method,
958        fn_name: &Ident,
959        where_clause: &TokenStream,
960        possible_self: &TokenStream,
961        method_docs: &Vec<TokenStream>,
962        additional_methods: &mut Vec<TokenStream>,
963        debug_fields: &mut Vec<TokenStream>,
964    ) {
965        if ["constants", "buffers", "values"]
966            .iter()
967            .any(|name| method.struct_method_name == *name)
968            && method.arguments.len() == 2
969        {
970            let rt = ReturnType::new(method.arguments[1].clone(), wrappers.clone());
971            let return_type = rt.get_new_return_type(false, false);
972            let getter_method = format_ident!("get_{}", fn_name);
973            let c_fn = format_ident!("{}", method.fn_name);
974            let method_docs = method_docs
975                .iter()
976                .cloned()
977                .take_while(|t| !t.to_string().contains(" Parameter"))
978                .collect_vec();
979            additional_methods.push(quote! {
980                #[inline]
981                #(#method_docs)*
982                pub fn #getter_method #where_clause(#possible_self) -> Result<#return_type, AeronCError> {
983                    let mut value = std::mem::MaybeUninit::uninit();
984                    // SAFETY: Aeron's constants/values functions fill every field when they
985                    // return 0 and write nothing otherwise, so no zeroing is needed
986                    let code = unsafe { #c_fn(self.get_inner(), value.as_mut_ptr()) };
987                    if code < 0 {
988                        return Err(AeronCError::from_code(code));
989                    }
990                    Ok(#return_type::from(unsafe { value.assume_init() }))
991                }
992            });
993            debug_fields.push(quote! {
994                .field(stringify!(#fn_name), &self.#getter_method() )
995            });
996        }
997    }
998
999    fn add_mut_string_methods_if_applicable(
1000        method: &Method,
1001        fn_name: &Ident,
1002        uses_self: bool,
1003        method_docs: &Vec<TokenStream>,
1004        additional_methods: &mut Vec<TokenStream>,
1005    ) {
1006        if method.arguments.len() == 3 && uses_self {
1007            let method_docs = method_docs.clone();
1008            let into_method = format_ident!("{}_into", fn_name);
1009            if method.arguments[1].is_mut_c_string() && method.arguments[2].is_usize() {
1010                let string_method = format_ident!("{}_as_string", fn_name);
1011                additional_methods.push(quote! {
1012    #[inline]
1013    #(#method_docs)*
1014    pub fn #string_method(
1015        &self,
1016        max_length: usize,
1017    ) -> Result<String, AeronCError> {
1018        let mut result = String::with_capacity(max_length);
1019        self.#into_method(&mut result)?;
1020        Ok(result)
1021    }
1022
1023    #[inline]
1024    #(#method_docs)*
1025    #[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"]
1026    pub fn #into_method(
1027        &self,
1028        dst_truncate_to_capacity: &mut String,
1029    ) -> Result<i32, AeronCError> {
1030        let capacity = dst_truncate_to_capacity.capacity();
1031        // SAFETY: the bytes are zeros (valid UTF-8) until truncated to a checked UTF-8 prefix.
1032        let vec = unsafe { dst_truncate_to_capacity.as_mut_vec() };
1033        // Zeroed so C never leaves uninitialised bytes behind, including when it fails without writing.
1034        vec.clear();
1035        vec.resize(capacity, 0);
1036        let result = self.#fn_name(&mut vec[..]);
1037        let len = match result {
1038            Ok(_) => {
1039                let len = vec.iter().position(|&b| b == 0).unwrap_or(capacity);
1040                // C truncation can split a multi-byte character.
1041                std::str::from_utf8(&vec[..len]).map_or_else(|e| e.valid_up_to(), |s| s.len())
1042            }
1043            Err(_) => 0,
1044        };
1045        vec.truncate(len);
1046        result
1047    }
1048                        });
1049            }
1050        }
1051    }
1052
1053    /// Generate the fields / getters
1054    fn generate_fields(
1055        &self,
1056        cwrappers: &BTreeMap<String, CWrapper>,
1057        debug_fields: &mut Vec<TokenStream>,
1058    ) -> Vec<TokenStream> {
1059        self.fields
1060            .iter()
1061            .filter(|arg| {
1062                !arg.name.starts_with("_") && !self.methods.iter().any(|m| m.struct_method_name.as_str() == arg.name)
1063            })
1064            .map(|arg| {
1065                let field_name = &arg.name;
1066                let fn_name = Ident::new(field_name, proc_macro2::Span::call_site());
1067
1068                let mut arg = arg.clone();
1069                // for mut strings return just &str not &mut str
1070                if arg.is_mut_c_string() {
1071                    arg.c_type = arg.c_type.replace(" mut ", " const ");
1072                }
1073                let mut rt = ReturnType::new(arg.clone(), cwrappers.clone());
1074                let mut return_type = rt.get_new_return_type(false, false);
1075                let handler = if let ArgProcessing::Handler(_) = &arg.processing {
1076                    true
1077                } else {
1078                    false
1079                };
1080                if return_type.is_empty() || handler {
1081                    rt = ReturnType::new(
1082                        Arg {
1083                            processing: ArgProcessing::Default,
1084                            ..arg.clone()
1085                        },
1086                        cwrappers.clone(),
1087                    );
1088                    return_type = rt.get_new_return_type(false, false);
1089                }
1090                let converter = rt.handle_c_to_rs_return(quote! { self.#fn_name }, false, true);
1091
1092                if rt.original.is_primitive()
1093                    || rt.original.is_c_string_any()
1094                    || rt.original.is_byte_array()
1095                    || cwrappers.contains_key(&rt.original.c_type)
1096                {
1097                    if !rt.original.is_any_pointer() || rt.original.is_c_string_any() {
1098                        debug_fields.push(quote! { .field(stringify!(#fn_name), &self.#fn_name()) });
1099                    }
1100                }
1101
1102                quote! {
1103                    #[inline]
1104                    pub fn #fn_name(&self) -> #return_type {
1105                        #converter
1106                    }
1107                }
1108            })
1109            .filter(|t| !t.is_empty())
1110            .collect()
1111    }
1112
1113    /// Generate the constructor for the struct
1114    fn generate_constructor(
1115        &self,
1116        wrappers: &BTreeMap<String, CWrapper>,
1117        constructor_fields: &mut Vec<TokenStream>,
1118        new_ref_set_none: &mut Vec<TokenStream>,
1119    ) -> Vec<TokenStream> {
1120        let constructors = self
1121            .methods
1122            .iter()
1123            .filter(|m| m.arguments.iter().any(|arg| arg.is_double_mut_pointer()))
1124            .map(|method| {
1125                let init_fn = format_ident!("{}", method.fn_name);
1126
1127                // `_add_destination`/`_remove_destination[_by_id]` are constructor-style
1128                // methods (double-mut-pointer output) that don't have a real C "close"/
1129                // "destroy" counterpart to pair with — each is a standalone async op polled
1130                // to completion via `..._destination_poll`, and (per the doc comment on
1131                // `cleanup_tokens` below) must NOT auto-invoke its sibling on drop. Treat
1132                // them as trivially "having a close method" (of themselves, unused — see
1133                // `cleanup_tokens` forcing `None` for any `_destination` method) so they are
1134                // still eligible for constructor generation without requiring a matching
1135                // pair to exist
1136                let is_destination_add_or_remove = method.fn_name.contains("_async_add_destination")
1137                    || method.fn_name.contains("_async_remove_destination");
1138                let close_method = self.find_close_method(method).or(if is_destination_add_or_remove {
1139                    Some(method)
1140                } else {
1141                    None
1142                });
1143                let found_close = is_destination_add_or_remove
1144                    || (close_method.is_some()
1145                        && close_method.unwrap().return_type.is_c_raw_int()
1146                        && close_method.unwrap() != method
1147                        && close_method
1148                            .unwrap()
1149                            .arguments
1150                            .iter()
1151                            .skip(1)
1152                            .all(|a| method.arguments.iter().any(|a2| a.name == a2.name)));
1153                if found_close {
1154                    let close_fn = format_ident!("{}", close_method.unwrap().fn_name);
1155                    let init_args: Vec<TokenStream> = method
1156                        .arguments
1157                        .iter()
1158                        .enumerate()
1159                        .map(|(idx, arg)| {
1160                            if idx == 0 {
1161                                quote! { ctx_field }
1162                            } else {
1163                                let arg_name = arg.as_ident();
1164                                quote! { #arg_name }
1165                            }
1166                        })
1167                        .filter(|t| !t.is_empty())
1168                        .collect();
1169                    let close_args: Vec<TokenStream> = close_method
1170                        .unwrap_or(method)
1171                        .arguments
1172                        .iter()
1173                        .enumerate()
1174                        .map(|(idx, arg)| {
1175                            if idx == 0 {
1176                                if arg.is_double_mut_pointer() {
1177                                    quote! { ctx_field }
1178                                } else {
1179                                    quote! { *ctx_field }
1180                                }
1181                            } else {
1182                                let arg_name = arg.as_ident();
1183                                quote! { #arg_name.into() }
1184                            }
1185                        })
1186                        .filter(|t| !t.is_empty())
1187                        .collect();
1188                    let lets: Vec<TokenStream> = Self::lets_for_copying_arguments(wrappers, &method.arguments, true);
1189
1190                    constructor_fields.clear();
1191                    constructor_fields.extend(Self::constructor_fields(wrappers, &method.arguments, &self.class_name));
1192
1193                    let new_ref_args = Self::new_args(wrappers, &method.arguments, &self.class_name, false);
1194
1195                    new_ref_set_none.clear();
1196                    new_ref_set_none.extend(Self::new_args(wrappers, &method.arguments, &self.class_name, true));
1197
1198                    let new_args: Vec<TokenStream> = method
1199                        .arguments
1200                        .iter()
1201                        .enumerate()
1202                        .filter_map(|(_idx, arg)| {
1203                            if arg.is_double_mut_pointer() {
1204                                None
1205                            } else {
1206                                let arg_name = arg.as_ident();
1207                                let arg_type =
1208                                    ReturnType::new(arg.clone(), wrappers.clone()).get_new_return_type(false, true);
1209                                if arg_type.clone().into_token_stream().is_empty() {
1210                                    None
1211                                } else {
1212                                    Some(quote! { #arg_name: #arg_type })
1213                                }
1214                            }
1215                        })
1216                        .filter(|t| !t.is_empty())
1217                        .collect();
1218
1219                    let fn_name = format_ident!(
1220                        "{}",
1221                        method
1222                            .struct_method_name
1223                            .replace("init", "new")
1224                            .replace("create", "new")
1225                    );
1226
1227                    let generic_types: Vec<TokenStream> = method
1228                        .arguments
1229                        .iter()
1230                        .flat_map(|arg| {
1231                            ReturnType::new(arg.clone(), wrappers.clone())
1232                                .method_generics_for_where(true)
1233                                .into_iter()
1234                        })
1235                        .collect_vec();
1236                    let where_clause = if generic_types.is_empty() {
1237                        quote! {}
1238                    } else {
1239                        quote! { <#(#generic_types),*> }
1240                    };
1241
1242                    let method_docs: Vec<TokenStream> = get_docs(&method.docs, wrappers, Some(&new_args));
1243
1244                    // The C struct created here stores every callback/clientd pair it is
1245                    // given (e.g. fragment assemblers keep their delegate), so clone each
1246                    // Handler into the new resource's dependencies to keep it alive until
1247                    // the C resource is closed. `lets` shadows each Handler argument with
1248                    // its C callback pointer, so take the Handler under a fresh name first.
1249                    let (handler_keeps, handler_deps): (Vec<TokenStream>, Vec<TokenStream>) = method
1250                        .arguments
1251                        .iter()
1252                        .filter(|arg| matches!(arg.processing, ArgProcessing::Handler(_)) && !arg.is_mut_pointer())
1253                        .map(|arg| {
1254                            let name = arg.as_ident();
1255                            let keep = format_ident!("__handler_{}", arg.name);
1256                            (
1257                                quote! { let #keep = #name; },
1258                                quote! {
1259                                    if let Some(__handler) = #keep {
1260                                        if let Some(__inner) = result.inner.as_owned() {
1261                                            __inner.add_dependency(Handler::clone(__handler));
1262                                        }
1263                                    }
1264                                },
1265                            )
1266                        })
1267                        .unzip();
1268
1269                    // Generate logging expression token stream (will be evaluated in closure)
1270                    let init_log_expr_tokens = Self::generate_arg_logging(&method.arguments, &init_args);
1271                    // Generate logging for close method arguments
1272                    let close_log_expr_tokens = if let Some(close_m) = close_method {
1273                        Self::generate_arg_logging(&close_m.arguments, &close_args)
1274                    } else {
1275                        quote! { "" }
1276                    };
1277
1278                    // `async_add_destination` handles must NOT run `async_remove_destination`
1279                    // on drop: removing the destination is a separate, destructive operation —
1280                    // not the destructor of the async-add handle (the C client frees the async
1281                    // struct itself once its poll completes). Pairing them made dropping the
1282                    // poll handle silently tear the destination down again, with a dangling
1283                    // uri pointer to boot.
1284                    let cleanup_tokens = if method.fn_name.contains("_destination") {
1285                        quote! { None }
1286                    } else {
1287                        quote! {
1288                            Some(Box::new(move |ctx_field| unsafe {
1289                                #[cfg(feature = "log-c-bindings")]
1290                                {
1291                                    let log_args = #close_log_expr_tokens;
1292                                    log::info!("{}({})", stringify!(#close_fn), log_args);
1293                                }
1294                                #close_fn(#(#close_args),*)
1295                            }))
1296                        }
1297                    };
1298
1299                    quote! {
1300                        #[inline]
1301                        #(#method_docs)*
1302                        pub fn #fn_name #where_clause(#(#new_args),*) -> Result<Self, AeronCError> {
1303                            #(#handler_keeps)*
1304                            #(#lets)*
1305                            // new by using constructor
1306                            let resource_constructor = ManagedCResource::new(
1307                                move |ctx_field| unsafe {
1308                                    #[cfg(feature = "log-c-bindings")]
1309                                    {
1310                                        let log_args = #init_log_expr_tokens;
1311                                        log::info!("{}({})", stringify!(#init_fn), log_args);
1312                                    }
1313                                    #init_fn(#(#init_args),*)
1314                                },
1315                                #cleanup_tokens,
1316                                false,
1317                            )?;
1318
1319                            let result = Self {
1320                                inner: CResource::OwnedOnHeap(RcOrArc::new(resource_constructor)),
1321                                #(#new_ref_args)*
1322                            };
1323                            #(#handler_deps)*
1324                            Ok(result)
1325                        }
1326                    }
1327                } else {
1328                    quote! {}
1329                }
1330            })
1331            .collect_vec();
1332
1333        let no_constructor = constructors.iter().map(|x| x.to_string()).join("").trim().is_empty();
1334        if no_constructor {
1335            let type_name = format_ident!("{}", self.type_name);
1336            let zeroed_impl = quote! {
1337                #[inline]
1338                /// creates zeroed struct where the underlying c struct is on the heap
1339                pub fn new_zeroed_on_heap() -> Self {
1340                    let resource = ManagedCResource::new(
1341                        move |ctx_field| {
1342                            #[cfg(feature = "extra-logging")]
1343                            log::info!("creating zeroed empty resource on heap {}", stringify!(#type_name));
1344                            let inst: #type_name = unsafe { std::mem::zeroed() };
1345                            let inner_ptr: *mut #type_name = Box::into_raw(Box::new(inst));
1346                            unsafe { *ctx_field = inner_ptr };
1347                            0
1348                        },
1349                        None,
1350                        true,
1351                    ).unwrap();
1352
1353                    Self {
1354                        inner: CResource::OwnedOnHeap(RcOrArc::new(resource)),
1355                    }
1356                }
1357
1358                #[inline]
1359                /// creates zeroed struct where the underlying c struct is on the stack
1360                /// _(Use with care)_
1361                pub fn new_zeroed_on_stack() -> Self {
1362                    #[cfg(feature = "extra-logging")]
1363                    log::debug!("creating zeroed empty resource on stack {}", stringify!(#type_name));
1364
1365                    Self {
1366                        inner: CResource::OwnedOnStack(std::mem::MaybeUninit::zeroed().into()),
1367                    }
1368                }
1369            };
1370            if self.has_default_method() {
1371                let type_name = format_ident!("{}", self.type_name);
1372                let new_args: Vec<TokenStream> = self
1373                    .fields
1374                    .iter()
1375                    .filter_map(|arg| {
1376                        let arg_name = arg.as_ident();
1377                        let arg_type = ReturnType::new(arg.clone(), wrappers.clone()).get_new_return_type(false, true);
1378                        if arg_type.is_empty() {
1379                            None
1380                        } else {
1381                            Some(quote! { #arg_name: #arg_type })
1382                        }
1383                    })
1384                    .filter(|t| !t.is_empty())
1385                    .collect();
1386                let init_args: Vec<TokenStream> = self
1387                    .fields
1388                    .iter()
1389                    .map(|arg| {
1390                        let arg_name = arg.as_ident();
1391                        let value = ReturnType::new(arg.clone(), wrappers.clone())
1392                            .handle_rs_to_c_return(quote! { #arg_name }, true);
1393                        quote! { #value }
1394                    })
1395                    .filter(|t| !t.is_empty())
1396                    .collect();
1397
1398                let generic_types: Vec<TokenStream> = self
1399                    .fields
1400                    .iter()
1401                    .flat_map(|arg| {
1402                        ReturnType::new(arg.clone(), wrappers.clone())
1403                            .method_generics_for_where(true)
1404                            .into_iter()
1405                    })
1406                    .collect_vec();
1407                let where_clause = if generic_types.is_empty() {
1408                    quote! {}
1409                } else {
1410                    quote! { <#(#generic_types),*> }
1411                };
1412
1413                let cloned_fields = self
1414                    .fields
1415                    .iter()
1416                    .filter(|a| a.processing == ArgProcessing::Default)
1417                    .cloned()
1418                    .collect_vec();
1419                let lets: Vec<TokenStream> = Self::lets_for_copying_arguments(wrappers, &cloned_fields, false);
1420
1421                // The C struct stores these callback/clientd fields directly, so keep the
1422                // Handler values alive alongside the resource.
1423                let field_handler_deps: Vec<TokenStream> = Self::handler_dependency_registrations(&self.fields);
1424
1425                vec![quote! {
1426                    #[inline]
1427                    pub fn new #where_clause(#(#new_args),*) -> Result<Self, AeronCError> {
1428                        #(#lets)*
1429                        // no constructor in c bindings
1430                        let r_constructor = ManagedCResource::new(
1431                            move |ctx_field| {
1432                                let inst = #type_name { #(#init_args),* };
1433                                let inner_ptr: *mut #type_name = Box::into_raw(Box::new(inst));
1434                                unsafe { *ctx_field = inner_ptr };
1435                                0
1436                            },
1437                            None,
1438                            true,
1439                        )?;
1440
1441                        let result = Self {
1442                            inner: CResource::OwnedOnHeap(RcOrArc::new(r_constructor)),
1443                        };
1444                        #(#field_handler_deps)*
1445                        Ok(result)
1446                    }
1447
1448                    #zeroed_impl
1449                }]
1450            } else {
1451                vec![zeroed_impl]
1452            }
1453        } else {
1454            constructors
1455        }
1456    }
1457
1458    fn lets_for_copying_arguments(
1459        wrappers: &BTreeMap<String, CWrapper>,
1460        arguments: &Vec<Arg>,
1461        include_let_statements: bool,
1462    ) -> Vec<TokenStream> {
1463        arguments
1464            .iter()
1465            .enumerate()
1466            .filter_map(|(_idx, arg)| {
1467                if arg.is_double_mut_pointer() {
1468                    None
1469                } else {
1470                    let arg_name = arg.as_ident();
1471                    let rtype = arg.as_type();
1472
1473                    // check if I need to make copy of object for reference counting
1474                    let fields = if arg.is_single_mut_pointer()
1475                        && wrappers.contains_key(arg.c_type.split_whitespace().last().unwrap())
1476                    {
1477                        let arg_copy = format_ident!("{}_copy", arg.name);
1478                        quote! {
1479                            let #arg_copy = #arg_name.clone();
1480                        }
1481                    } else {
1482                        quote! {}
1483                    };
1484
1485                    let return_type = ReturnType::new(arg.clone(), wrappers.clone());
1486
1487                    if let ArgProcessing::StringWithLength(_args) | ArgProcessing::ByteArrayWithLength(_args) =
1488                        &return_type.original.processing
1489                    {
1490                        return None;
1491                    }
1492                    if let ArgProcessing::Handler(args) = &return_type.original.processing {
1493                        let arg1 = args[0].as_ident();
1494                        let arg2 = args[1].as_ident();
1495                        let value = return_type.handle_rs_to_c_return(quote! { #arg_name }, false);
1496
1497                        if value.is_empty() {
1498                            return None;
1499                        }
1500
1501                        if include_let_statements {
1502                            return Some(quote! { #fields let (#arg1, #arg2)= (#value); });
1503                        } else {
1504                            return Some(fields);
1505                        }
1506                    }
1507
1508                    let value = return_type.handle_rs_to_c_return(quote! { #arg_name }, false);
1509                    if value.is_empty() {
1510                        None
1511                    } else {
1512                        if include_let_statements {
1513                            Some(quote! { #fields let #arg_name: #rtype = #value; })
1514                        } else {
1515                            return Some(fields);
1516                        }
1517                    }
1518                }
1519            })
1520            .filter(|t| !t.is_empty())
1521            .collect()
1522    }
1523
1524    fn constructor_fields(
1525        wrappers: &BTreeMap<String, CWrapper>,
1526        arguments: &Vec<Arg>,
1527        class_name: &String,
1528    ) -> Vec<TokenStream> {
1529        if class_name == "AeronAsyncDestination" {
1530            return vec![];
1531        }
1532
1533        arguments
1534            .iter()
1535            .enumerate()
1536            .filter_map(|(_idx, arg)| {
1537                if arg.is_double_mut_pointer() {
1538                    None
1539                } else {
1540                    let arg_name = arg.as_ident();
1541                    let rtype = arg.as_type();
1542                    if arg.is_single_mut_pointer()
1543                        && wrappers.contains_key(arg.c_type.split_whitespace().last().unwrap())
1544                    {
1545                        let return_type = ReturnType::new(arg.clone(), wrappers.clone());
1546                        let return_type = return_type.get_new_return_type(false, false);
1547
1548                        let arg_copy = format_ident!("_{}", arg.name);
1549                        Some(quote! {
1550                            #arg_copy: Option<#return_type>,
1551                        })
1552                    } else {
1553                        None
1554                    }
1555                }
1556            })
1557            .collect()
1558    }
1559
1560    fn new_args(
1561        wrappers: &BTreeMap<String, CWrapper>,
1562        arguments: &Vec<Arg>,
1563        class_name: &String,
1564        set_none: bool,
1565    ) -> Vec<TokenStream> {
1566        if class_name == "AeronAsyncDestination" {
1567            return vec![];
1568        }
1569
1570        arguments
1571            .iter()
1572            .enumerate()
1573            .filter_map(|(_idx, arg)| {
1574                if arg.is_double_mut_pointer() {
1575                    None
1576                } else {
1577                    let arg_name = arg.as_ident();
1578                    let rtype = arg.as_type();
1579                    if arg.is_single_mut_pointer()
1580                        && wrappers.contains_key(arg.c_type.split_whitespace().last().unwrap())
1581                    {
1582                        let arg_f = format_ident!("_{}", &arg.name);
1583                        let arg_copy = format_ident!("{}_copy", &arg.name);
1584                        if set_none {
1585                            Some(quote! {
1586                                #arg_f: None,
1587                            })
1588                        } else {
1589                            Some(quote! {
1590                                #arg_f: Some(#arg_copy),
1591                            })
1592                        }
1593                    } else {
1594                        None
1595                    }
1596                }
1597            })
1598            .collect()
1599    }
1600
1601    fn find_close_method(&self, method: &Method) -> Option<&Method> {
1602        let mut close_method = None;
1603
1604        // must have init, create, add or remove method name. `_remove` is needed so
1605        // that e.g. `aeron_publication_async_remove_destination`
1606        if ["_init", "_create", "_add", "_remove"]
1607            .iter()
1608            .all(|find| !method.fn_name.contains(find))
1609        {
1610            return None;
1611        }
1612
1613        for name in ["_destroy", "_delete"] {
1614            let mut candidate = method.fn_name.replace("_init", "_close").replace("_create", name);
1615            if method.fn_name.contains("_remove_") {
1616                // symmetric case: a `_remove_` method (e.g. `..._async_remove_destination`,
1617                // or its `..._remove_destination_by_id` sibling — which has no `_add_..._by_id`
1618                // counterpart, so drop the `_by_id` suffix before pairing) looks for its
1619                // paired `_add_` method (e.g. `..._async_add_destination`) so `found_close`
1620                // can be satisfied for it too. This is purely a "does a plausible pair
1621                // exist" heuristic gate — it does NOT wire remove_destination as
1622                // add_destination's cleanup closure or vice versa; `cleanup_tokens` below
1623                // always forces `None` for any `_destination` method regardless of what
1624                // this resolves to.
1625                candidate = candidate.trim_end_matches("_by_id").replace("_remove_", "_add_");
1626            } else {
1627                candidate = candidate.replace("_add_", "_remove_");
1628            }
1629            let close_fn = format_ident!("{}", candidate);
1630            let method = self.methods.iter().find(|m| close_fn.to_string().contains(&m.fn_name));
1631            if method.is_some() {
1632                close_method = method;
1633                break;
1634            }
1635        }
1636        close_method
1637    }
1638
1639    fn has_default_method(&self) -> bool {
1640        // AeronUriStringBuilder does not follow the normal convention so have additional check arg.is_single_mut_pointer() && m.fn_name.contains("_init_")
1641        let no_init_method = !self.methods.iter().any(|m| {
1642            m.arguments
1643                .iter()
1644                .any(|arg| arg.is_double_mut_pointer() || (arg.is_single_mut_pointer() && m.fn_name.contains("_init_")))
1645        });
1646
1647        no_init_method && !self.fields.iter().any(|arg| arg.name.starts_with("_")) && !self.fields.is_empty()
1648    }
1649
1650    fn generate_allocation_test(&self) -> TokenStream {
1651        let class_name = format_ident!("{}", self.class_name);
1652
1653        let has_c_constructor = self
1654            .methods
1655            .iter()
1656            .any(|m| m.arguments.iter().any(|arg| arg.is_double_mut_pointer()));
1657        let has_empty_new_constructor = self.methods.iter().any(|m| {
1658            m.fn_name.contains("_init_")
1659                && m.arguments.iter().any(|arg| arg.is_double_mut_pointer())
1660                && m.arguments.len() == 1
1661        });
1662
1663        let mut tests = vec![];
1664
1665        if !has_c_constructor {
1666            tests.push(quote! {
1667                #[test]
1668                #[file_serial(global)]
1669                fn test_new_on_stack() {
1670                    crate::test_alloc::assert_no_allocation(|| {
1671                        for _ in 0..100 {
1672                            let _ = #class_name::new_zeroed_on_stack();
1673                        }
1674                    });
1675                }
1676            });
1677        }
1678
1679        if has_empty_new_constructor {
1680            tests.push(quote! {
1681                #[test]
1682                #[file_serial(global)]
1683                fn test_new() {
1684                    crate::test_alloc::assert_no_allocation(|| {
1685                        for _ in 0..100 {
1686                            let _ = #class_name::new();
1687                        }
1688                    });
1689                }
1690            });
1691        }
1692
1693        if self.has_default_method() {
1694            tests.push(quote! {
1695                #[test]
1696                #[file_serial(global)]
1697                fn test_default() {
1698                    crate::test_alloc::assert_no_allocation(|| {
1699                        for _ in 0..100 {
1700                            let _ = #class_name::default();
1701                        }
1702                    });
1703                }
1704            });
1705        }
1706
1707        let mod_name = format_ident!("{}_allocation_tests", self.type_name);
1708
1709        if tests.is_empty() {
1710            quote! {}
1711        } else {
1712            quote! {
1713                #[cfg(test)]
1714                mod #mod_name {
1715                    use super::*;
1716                    use serial_test::file_serial;
1717                    #(#tests)*
1718                }
1719            }
1720        }
1721    }
1722}
1723
1724#[cfg(test)]
1725mod parse_custom_methods_tests {
1726    use super::parse_custom_methods;
1727    use std::collections::BTreeSet;
1728
1729    #[test]
1730    fn extracts_method_names_from_inherent_impls() {
1731        let src = r#"
1732            impl AeronPublication {
1733                pub fn offer_result(&self) -> i64 { 0 }
1734                pub fn status(&self) -> u8 { 0 }
1735            }
1736            impl AeronSubscription {
1737                pub fn status(&self) -> u8 { 0 }
1738            }
1739        "#;
1740        let map = parse_custom_methods(src);
1741        assert_eq!(
1742            map.get("AeronPublication"),
1743            Some(
1744                &["offer_result", "status"]
1745                    .iter()
1746                    .map(|s| s.to_string())
1747                    .collect::<BTreeSet<_>>()
1748            )
1749        );
1750        assert!(map.get("AeronSubscription").map_or(false, |s| s.contains("status")));
1751    }
1752
1753    #[test]
1754    fn ignores_trait_impls() {
1755        // `impl Display for X` and `impl<T> Trait for Y` must not be treated as
1756        // inherent method sources (they'd add bogus skip entries).
1757        let src = r#"
1758            impl std::fmt::Display for AeronCError {
1759                fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Ok(()) }
1760            }
1761        "#;
1762        let map = parse_custom_methods(src);
1763        assert!(
1764            !map.contains_key("AeronCError"),
1765            "trait impls must not appear in the skip-list"
1766        );
1767    }
1768
1769    #[test]
1770    #[should_panic(expected = "failed to parse aeron_custom.rs")]
1771    fn panics_loudly_on_malformed_input() {
1772        // An empty skip-list would let the generator emit duplicate methods.
1773        let _ = parse_custom_methods("impl { not valid rust }}}");
1774    }
1775
1776    #[test]
1777    fn empty_input_yields_empty_map() {
1778        assert!(parse_custom_methods("").is_empty());
1779    }
1780
1781    #[test]
1782    fn self_type_keyed_by_plain_token_stream() {
1783        let src = r#"
1784            impl AeronPublication { pub fn offer_result(&self) -> i64 { 0 } }
1785        "#;
1786        let map = parse_custom_methods(src);
1787        // Exact plain-name key — no surrounding whitespace, no path qualifiers.
1788        assert_eq!(map.len(), 1);
1789        assert!(map.contains_key("AeronPublication"));
1790    }
1791}
1792
1793#[cfg(test)]
1794mod arg_classification_tests {
1795    use super::{Arg, ArgProcessing};
1796
1797    fn arg(c_type: &str) -> Arg {
1798        Arg {
1799            name: "x".to_string(),
1800            c_type: c_type.to_string(),
1801            processing: ArgProcessing::Default,
1802        }
1803    }
1804
1805    #[test]
1806    fn classifies_c_strings() {
1807        assert!(arg("* const :: std :: os :: raw :: c_char").is_c_string());
1808        assert!(arg("* const :: std :: os :: raw :: c_char").is_c_string_any());
1809        assert!(arg("* mut :: std :: os :: raw :: c_char").is_mut_c_string());
1810        assert!(arg("* mut :: std :: os :: raw :: c_char").is_c_string_any());
1811        // Not a C string:
1812        assert!(!arg("* const u8").is_c_string());
1813        assert!(!arg("i32").is_c_string_any());
1814    }
1815
1816    #[test]
1817    fn classifies_byte_arrays() {
1818        assert!(arg("* const u8").is_byte_array());
1819        assert!(arg("* mut u8").is_byte_array());
1820        assert!(arg("* mut u8").is_mut_byte_array());
1821        assert!(!arg("* const u8").is_mut_byte_array());
1822        assert!(!arg("* const :: std :: os :: raw :: c_char").is_byte_array());
1823    }
1824
1825    #[test]
1826    fn classifies_raw_int_return() {
1827        assert!(arg(":: std :: os :: raw :: c_int").is_c_raw_int());
1828        assert!(!arg("i32").is_c_raw_int());
1829    }
1830
1831    #[test]
1832    fn classifies_void_pointer() {
1833        assert!(arg("* mut :: std :: os :: raw :: c_void").is_c_void());
1834        assert!(!arg("* mut u8").is_c_void());
1835    }
1836
1837    #[test]
1838    fn classifies_mut_pointers_single_vs_double() {
1839        // Single `*mut`:
1840        assert!(arg("* mut i32").is_mut_pointer());
1841        assert!(arg("* mut i32").is_single_mut_pointer());
1842        assert!(!arg("* mut i32").is_double_mut_pointer());
1843        // Double `*mut *mut` (output param / handle out):
1844        assert!(arg("* mut * mut aeron_foo_t").is_mut_pointer());
1845        assert!(arg("* mut * mut aeron_foo_t").is_double_mut_pointer());
1846        assert!(!arg("* mut * mut aeron_foo_t").is_single_mut_pointer());
1847        // `*const` is not a mut pointer:
1848        assert!(!arg("* const i32").is_mut_pointer());
1849    }
1850
1851    #[test]
1852    fn classifies_primitives() {
1853        for ty in [
1854            "i64", "u64", "f32", "f64", "i32", "i16", "u32", "u16", "bool", "usize", "isize",
1855        ] {
1856            assert!(arg(ty).is_primitive(), "{ty} should be primitive");
1857        }
1858        // NOTE: `is_primitive` is a SUFFIX match (`c_type.ends_with(primitive)`),
1859        // so a pointer-to-primitive like `* mut i32` also returns true. This is
1860        // the contract the generator relies on (it composes this with the
1861        // `is_single_mut_pointer` check elsewhere), so pin it here.
1862        assert!(arg("* mut i32").is_primitive());
1863        // But a pointer to a non-primitive (a C struct) is not primitive:
1864        assert!(!arg("* mut aeron_foo_t").is_primitive());
1865    }
1866
1867    #[test]
1868    fn classifies_any_pointer() {
1869        assert!(arg("* const i32").is_any_pointer());
1870        assert!(arg("* mut i32").is_any_pointer());
1871        assert!(!arg("i32").is_any_pointer());
1872    }
1873}
1874
1875fn get_docs(
1876    docs: &BTreeSet<String>,
1877    wrappers: &BTreeMap<String, CWrapper>,
1878    arguments: Option<&Vec<TokenStream>>,
1879) -> Vec<TokenStream> {
1880    let mut first_param = true;
1881    docs.iter()
1882        .flat_map(|d| d.lines())
1883        .filter(|s| {
1884            arguments.is_none()
1885                || !s.contains("@param")
1886                || (s.contains("@param")
1887                    && arguments
1888                        .unwrap()
1889                        .iter()
1890                        .any(|a| s.contains(format!(" {}", a.to_string().split_whitespace().next().unwrap()).as_str())))
1891        })
1892        .map(|doc| {
1893            let mut doc = doc.to_string();
1894
1895            if first_param && doc.contains("@param") {
1896                doc = format!("# Parameters\n{}", doc);
1897                first_param = false;
1898            }
1899
1900            if doc.contains("@param") {
1901                doc = regex::Regex::new("@param\\s+([^ ]+)")
1902                    .unwrap()
1903                    .replace(doc.as_str(), "\n - `$1`")
1904                    .to_string();
1905            }
1906
1907            doc = doc
1908                .replace("@return", "\n# Return\n")
1909                .replace("<p>", "\n")
1910                .replace("</p>", "\n");
1911
1912            doc = wrappers
1913                .values()
1914                .fold(doc, |acc, v| acc.replace(&v.type_name, &format!("`{}`", v.class_name)));
1915
1916            if doc.contains("@deprecated") {
1917                quote! {
1918                    #[deprecated]
1919                    #[doc = #doc]
1920                }
1921            } else {
1922                quote! {
1923                    #[doc = #doc]
1924                }
1925            }
1926        })
1927        .collect()
1928}
1929
1930fn generate_stateless_handler_code(handler: &CHandler) -> TokenStream {
1931    let type_name_ident = format_ident!("{}", handler.type_name);
1932    let callback_fn_name = format_ident!("{}_callback", handler.type_name);
1933    let closure_type_name = format_ident!("{}Callback", snake_to_pascal_case(&handler.type_name));
1934    let logger_type_name = format_ident!("{}Logger", snake_to_pascal_case(&handler.type_name));
1935    let handle_method_name = format_ident!("handle_{}", &handler.type_name[..handler.type_name.len() - 2]);
1936    let closure_return_type = handler.return_type.as_type();
1937
1938    // For stateless callbacks, the FnMut signature takes no args (other than &mut self)
1939    let (fn_mut_args, trait_args): (Vec<TokenStream>, Vec<TokenStream>) = handler
1940        .args
1941        .iter()
1942        .enumerate()
1943        .map(|(i, arg)| {
1944            let return_type = ReturnType::new(arg.clone(), BTreeMap::new());
1945            let type_name = return_type.get_new_return_type(false, false);
1946            let arg_name = format_ident!("arg_{}", i);
1947
1948            let fn_mut_arg = if arg.is_single_mut_pointer() && arg.is_primitive() {
1949                let owned_type: Type = parse_str(arg.c_type.split_whitespace().last().unwrap()).unwrap();
1950                quote! { #owned_type }
1951            } else {
1952                quote! { #type_name }
1953            };
1954
1955            let trait_arg = if type_name.is_empty() {
1956                quote! {}
1957            } else 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! { #arg_name: #owned_type }
1960            } else {
1961                quote! { #arg_name: #type_name }
1962            };
1963
1964            (fn_mut_arg, trait_arg)
1965        })
1966        .filter(|(fn_mut, trait_)| !fn_mut.is_empty() && !trait_.is_empty())
1967        .unzip();
1968
1969    // Store the FnMut signature for use in wrapper code
1970    let fn_mut_sig = quote! {
1971       FnMut(#(#fn_mut_args),*) -> #closure_return_type
1972    };
1973
1974    quote! {
1975        pub trait #closure_type_name {
1976            fn #handle_method_name(&mut self, #(#trait_args),*) -> #closure_return_type;
1977        }
1978
1979        pub struct #logger_type_name;
1980        impl #closure_type_name for #logger_type_name {
1981            fn #handle_method_name(&mut self, #(#trait_args),*) -> #closure_return_type {
1982                log::info!(
1983                    "{}({}\n)",
1984                    stringify!(#handle_method_name),
1985                    ""
1986                );
1987                unimplemented!()
1988            }
1989        }
1990
1991        // Stub callback function for stateless handlers - panics when called
1992        #[allow(dead_code)]
1993        unsafe extern "C" fn #callback_fn_name<F: #closure_type_name>(#(#trait_args),*) -> #closure_return_type {
1994            unimplemented!("Stateless handler callback called - not supported")
1995        }
1996    }
1997}
1998
1999pub fn generate_handlers(handler: &mut CHandler, bindings: &CBinding) -> TokenStream {
2000    if handler
2001        .args
2002        .iter()
2003        .any(|arg| arg.is_primitive() && arg.is_mut_pointer())
2004    {
2005        return quote! {};
2006    }
2007
2008    // Check for stateless callbacks (no c_void parameter)
2009    // These cannot use closure pattern as there's no clientd to pass the closure through
2010    // Generate only the trait and logger, not the closure infrastructure
2011    let has_c_void_param = handler.args.iter().any(|a| a.is_c_void());
2012    let is_stateless = !has_c_void_param;
2013
2014    if is_stateless {
2015        let closure_type_name = format_ident!("{}Callback", snake_to_pascal_case(&handler.type_name));
2016        // Set the FnMut signature for stateless handlers (needed by wrapper code generation)
2017        let fn_mut_args: Vec<TokenStream> = handler
2018            .args
2019            .iter()
2020            .map(|arg| {
2021                let return_type = ReturnType::new(arg.clone(), bindings.wrappers.clone());
2022                let type_name = return_type.get_new_return_type(false, false);
2023                if arg.is_single_mut_pointer() && arg.is_primitive() {
2024                    let owned_type: Type = parse_str(arg.c_type.split_whitespace().last().unwrap()).unwrap();
2025                    quote! { #owned_type }
2026                } else {
2027                    quote! { #type_name }
2028                }
2029            })
2030            .filter(|t| !t.is_empty())
2031            .collect();
2032        let closure_return_type = handler.return_type.as_type();
2033        handler.fn_mut_signature = quote! {
2034           FnMut(#(#fn_mut_args),*) -> #closure_return_type
2035        };
2036        handler.closure_type_name = quote! {
2037           #closure_type_name
2038        };
2039        return generate_stateless_handler_code(handler);
2040    }
2041
2042    let fn_name = format_ident!("{}_callback", handler.type_name);
2043    let closure_fn_name = format_ident!("{}_callback_for_once_closure", handler.type_name);
2044    let doc_comments: Vec<TokenStream> = handler
2045        .docs
2046        .iter()
2047        .flat_map(|doc| doc.lines())
2048        .map(|line| quote! { #[doc = #line] })
2049        .collect();
2050
2051    let closure = handler
2052        .args
2053        .iter()
2054        .find(|a| a.is_c_void())
2055        .map(|a| a.name.clone())
2056        .unwrap_or_else(|| "state".to_string());
2057    let closure_name = format_ident!("{}", closure);
2058    let closure_type_name = format_ident!("{}Callback", snake_to_pascal_case(&handler.type_name));
2059    let closure_return_type = handler.return_type.as_type();
2060
2061    let logger_type_name = format_ident!("{}Logger", snake_to_pascal_case(&handler.type_name));
2062
2063    let handle_method_name = format_ident!("handle_{}", &handler.type_name[..handler.type_name.len() - 2]);
2064
2065    let no_method_name = format_ident!(
2066        "no_{}_handler",
2067        &handler.type_name[..handler.type_name.len() - 2]
2068            .replace("_on_", "_")
2069            .replace("aeron_", "")
2070    );
2071
2072    let args: Vec<TokenStream> = handler
2073        .args
2074        .iter()
2075        .map(|arg| {
2076            let arg_name = arg.as_ident();
2077            // do not need to convert as its calling hour handler
2078            let arg_type: Type = arg.as_type();
2079            quote! { #arg_name: #arg_type }
2080        })
2081        .filter(|t| !t.is_empty())
2082        .collect();
2083
2084    let arg_names_for_logging: Vec<TokenStream> = handler
2085        .args
2086        .iter()
2087        .map(|arg| {
2088            let arg_name = arg.as_ident();
2089            quote! { format!("{} = {:?}", stringify!(#arg_name), #arg_name) }
2090        })
2091        .collect();
2092
2093    let converted_args: Vec<TokenStream> = handler
2094        .args
2095        .iter()
2096        .filter_map(|arg| {
2097            let name = &arg.name;
2098            let arg_name = arg.as_ident();
2099            // Skip closure argument - it's only used to get the closure reference
2100            // All other args (including other c_void args) are passed to the handler
2101            if name == &closure {
2102                None
2103            } else {
2104                let return_type = ReturnType::new(arg.clone(), bindings.wrappers.clone());
2105                Some(return_type.handle_c_to_rs_return(quote! {#arg_name}, false, false))
2106            }
2107        })
2108        .filter(|t| !t.is_empty())
2109        .collect();
2110
2111    let closure_args: Vec<TokenStream> = handler
2112        .args
2113        .iter()
2114        .filter_map(|arg| {
2115            let name = &arg.name;
2116            if name == &closure {
2117                return None;
2118            }
2119
2120            let return_type = ReturnType::new(arg.clone(), bindings.wrappers.clone());
2121            let type_name = return_type.get_new_return_type(false, false);
2122            let field_name = format_ident!("{}", name);
2123            if type_name.is_empty() {
2124                None
2125            } else {
2126                Some(quote! {
2127                    #field_name: #type_name
2128                })
2129            }
2130        })
2131        .filter(|t| !t.is_empty())
2132        .collect();
2133
2134    let mut log_field_names = vec![];
2135    let closure_args_in_logger: Vec<TokenStream> = handler
2136        .args
2137        .iter()
2138        .filter_map(|arg| {
2139            let name = &arg.name;
2140            if name == &closure {
2141                return None;
2142            }
2143
2144            let return_type = ReturnType::new(arg.clone(), bindings.wrappers.clone());
2145            let type_name = return_type.get_new_return_type(false, false);
2146            let field_name = format_ident!("{}", name);
2147            if type_name.is_empty() {
2148                None
2149            } else {
2150                log_field_names.push(Some(
2151                    quote! { format!("{} : {:?}", stringify!(#field_name), #field_name) },
2152                ));
2153
2154                Some(quote! {
2155                    #field_name: #type_name
2156                })
2157            }
2158        })
2159        .filter(|t| !t.is_empty())
2160        .collect();
2161
2162    if log_field_names.is_empty() {
2163        log_field_names.push(Some(quote! { "" }));
2164    }
2165
2166    let fn_mut_args: Vec<TokenStream> = handler
2167        .args
2168        .iter()
2169        .filter_map(|arg| {
2170            let name = &arg.name;
2171            if name == &closure {
2172                return None;
2173            }
2174
2175            let return_type = ReturnType::new(arg.clone(), bindings.wrappers.clone());
2176            let type_name = return_type.get_new_return_type(false, false);
2177            if arg.is_single_mut_pointer() && arg.is_primitive() {
2178                let owned_type: Type = parse_str(arg.c_type.split_whitespace().last().unwrap()).unwrap();
2179                return Some(quote! { #owned_type });
2180            } else {
2181                return Some(quote! {
2182                    #type_name
2183                });
2184            }
2185        })
2186        .filter(|t| !t.is_empty())
2187        .collect();
2188
2189    handler.fn_mut_signature = quote! {
2190       FnMut(#(#fn_mut_args),*) -> #closure_return_type
2191    };
2192    handler.closure_type_name = quote! {
2193       #closure_type_name
2194    };
2195
2196    let logger_return_type = if closure_return_type.to_token_stream().to_string().eq("()") {
2197        closure_return_type.clone().to_token_stream()
2198    } else {
2199        quote! {
2200            unimplemented!()
2201        }
2202    };
2203
2204    let wrapper_closure_args: Vec<TokenStream> = handler
2205        .args
2206        .iter()
2207        .filter_map(|arg| {
2208            let name = &arg.name;
2209            if name == &closure {
2210                return None;
2211            }
2212
2213            let field_name = format_ident!("{}", name);
2214            let return_type = ReturnType::new(arg.clone(), bindings.wrappers.clone()).get_new_return_type(false, false);
2215            if return_type.is_empty() {
2216                None
2217            } else {
2218                Some(quote! { #field_name })
2219            }
2220        })
2221        .filter(|t| !t.is_empty())
2222        .collect();
2223
2224    quote! {
2225        #(#doc_comments)*
2226        ///
2227        ///
2228        /// _(note you must copy any arguments that you use afterwards even those with static lifetimes)_
2229        pub trait #closure_type_name {
2230            fn #handle_method_name(&mut self, #(#closure_args),*) -> #closure_return_type;
2231        }
2232
2233        pub struct #logger_type_name;
2234        impl #closure_type_name for #logger_type_name {
2235            fn #handle_method_name(&mut self, #(#closure_args_in_logger),*) -> #closure_return_type {
2236                log::info!("{}({}\n)",
2237                    stringify!(#handle_method_name),
2238                    [#(#log_field_names),*].join(", "),
2239                );
2240                #logger_return_type
2241            }
2242        }
2243
2244        unsafe impl Send for #logger_type_name {}
2245        unsafe impl Sync for #logger_type_name {}
2246
2247        /// Any closure with the matching signature is a callback: methods that retain it
2248        /// heap-allocate it into a [`Handler`] owned by the registering resource.
2249        impl<F: FnMut(#(#fn_mut_args),*) -> #closure_return_type> #closure_type_name for F {
2250            #[inline]
2251            fn #handle_method_name(&mut self, #(#closure_args),*) -> #closure_return_type {
2252                self(#(#wrapper_closure_args),*)
2253            }
2254        }
2255
2256        /// Pass an existing [`Handler`] clone anywhere a callback value is expected, e.g.
2257        /// to share one callback instance across several registrations.
2258        impl<T: #closure_type_name> #closure_type_name for Handler<T> {
2259            #[inline]
2260            fn #handle_method_name(&mut self, #(#closure_args),*) -> #closure_return_type {
2261                let inner = unsafe { &mut *self.inner.get() };
2262                inner.#handle_method_name(#(#wrapper_closure_args),*)
2263            }
2264        }
2265
2266        // `NoHandler` implements every generated callback trait so that
2267        // `Handlers::NONE` (= `None::<&Handler<NoHandler>>`) can pin the callback
2268        // generic at any call site. Its body is unreachable: the C side receives a
2269        // null callback + null clientd, so this method can never be invoked.
2270        impl #closure_type_name for NoHandler {
2271            fn #handle_method_name(&mut self, #(#closure_args),*) -> #closure_return_type {
2272                unreachable!("NoHandler is a None sentinel; its callback must never be invoked")
2273            }
2274        }
2275
2276        // #[no_mangle]
2277        #[allow(dead_code)]
2278        #(#doc_comments)*
2279        unsafe extern "C" fn #fn_name<F: #closure_type_name>(
2280            #(#args),*
2281        ) -> #closure_return_type
2282        {
2283            #[cfg(debug_assertions)]
2284            if #closure_name.is_null() {
2285                unimplemented!("closure should not be null")
2286            }
2287            #[cfg(feature = "extra-logging")]
2288            {
2289                log::debug!("calling {}", stringify!(#handle_method_name));
2290            }
2291            #[cfg(feature = "log-c-bindings")]
2292            log::debug!(
2293                "{}({}\n)",
2294                stringify!(#fn_name),
2295                [#(#arg_names_for_logging),*].join(", ")
2296            );
2297            let closure: &mut F = unsafe { &mut *(#closure_name as *mut F) };
2298            closure.#handle_method_name(#(#converted_args),*)
2299        }
2300
2301        // #[no_mangle]
2302        #[allow(dead_code)]
2303        #(#doc_comments)*
2304        unsafe extern "C" fn #closure_fn_name<F: FnMut(#(#fn_mut_args),*) -> #closure_return_type>(
2305            #(#args),*
2306        ) -> #closure_return_type
2307        {
2308            #[cfg(debug_assertions)]
2309            if #closure_name.is_null() {
2310                unimplemented!("closure should not be null")
2311            }
2312            #[cfg(feature = "extra-logging")]
2313            {
2314                log::debug!("calling {}", stringify!(#closure_fn_name));
2315            }
2316            #[cfg(feature = "log-c-bindings")]
2317            log::debug!(
2318                "{}({}\n)",
2319                stringify!(#closure_fn_name),
2320                [#(#arg_names_for_logging),*].join(", ")
2321            );
2322            let closure: &mut F = unsafe { &mut *(#closure_name as *mut F) };
2323            closure(#(#converted_args),*)
2324        }
2325
2326    }
2327}
2328
2329pub fn generate_rust_code(
2330    wrapper: &CWrapper,
2331    wrappers: &BTreeMap<String, CWrapper>,
2332    include_common_code: bool,
2333    include_clippy: bool,
2334    include_aeron_client_registering_resource_t: bool,
2335    closure_handlers: &Vec<CHandler>,
2336) -> TokenStream {
2337    let class_name = Ident::new(&wrapper.class_name, proc_macro2::Span::call_site());
2338    let type_name = Ident::new(&wrapper.type_name, proc_macro2::Span::call_site());
2339
2340    let mut additional_outer_impls = vec![];
2341    let mut debug_fields = vec![];
2342
2343    let methods = wrapper.generate_methods(
2344        wrappers,
2345        closure_handlers,
2346        &mut additional_outer_impls,
2347        &mut debug_fields,
2348    );
2349    let tests = wrapper.generate_allocation_test();
2350    let mut constructor_fields = vec![];
2351    let mut new_ref_set_none = vec![];
2352    let constructor = wrapper.generate_constructor(wrappers, &mut constructor_fields, &mut new_ref_set_none);
2353
2354    let async_impls = if wrapper.type_name.starts_with("aeron_async_")
2355        || wrapper.type_name.starts_with("aeron_archive_async_")
2356    {
2357        let new_method = wrapper.methods.iter().find(|m| m.fn_name == wrapper.without_name);
2358
2359        if let Some(new_method) = new_method {
2360            let main_type = &wrapper.type_name.replace("_async_", "_").replace("_add_", "_");
2361            let main = get_possible_wrappers(main_type)
2362                .iter()
2363                .filter_map(|f| wrappers.get(f))
2364                .next()
2365                .expect(&format!("failed to find main type {}", main_type));
2366
2367            let poll_method = main
2368                .methods
2369                .iter()
2370                .find(|m| m.fn_name == format!("{}_poll", wrapper.without_name))
2371                .unwrap();
2372
2373            let main_class_name = format_ident!("{}", main.class_name);
2374            let async_class_name = format_ident!("{}", wrapper.class_name);
2375            let poll_method_name = format_ident!("{}_poll", wrapper.without_name);
2376            let new_method_name = format_ident!("{}", new_method.fn_name);
2377
2378            let client_class = wrappers
2379                .get(
2380                    new_method
2381                        .arguments
2382                        .iter()
2383                        .skip(1)
2384                        .next()
2385                        .unwrap()
2386                        .c_type
2387                        .split_whitespace()
2388                        .last()
2389                        .unwrap(),
2390                )
2391                .unwrap();
2392            let client_type = format_ident!("{}", client_class.class_name);
2393            let client_type_method_name = format_ident!(
2394                "{}",
2395                new_method
2396                    .fn_name
2397                    .replace(&format!("{}_", client_class.without_name), "")
2398            );
2399            let client_type_method_name_without_async = format_ident!(
2400                "{}",
2401                new_method
2402                    .fn_name
2403                    .replace(&format!("{}_", client_class.without_name), "")
2404                    .replace("async_", "")
2405            );
2406            // A context owner (archive connect) holds the client in C, set through
2407            // `set_aeron`, rather than as a Rust dependency of the poller.
2408            let owner_client_fallback = if client_class
2409                .methods
2410                .iter()
2411                .any(|m| m.fn_name == format!("{}_get_aeron", client_class.without_name))
2412            {
2413                quote! {
2414                    .or_else(|| {
2415                        self.inner
2416                            .get_dependency::<#client_type>()
2417                            .map(|owner| owner.get_aeron())
2418                            .filter(|aeron| !aeron.get_inner().is_null())
2419                    })
2420                }
2421            } else {
2422                quote! {}
2423            };
2424
2425            let init_args: Vec<TokenStream> = poll_method
2426                .arguments
2427                .iter()
2428                .enumerate()
2429                .filter_map(|(idx, arg)| {
2430                    if idx == 0 {
2431                        Some(quote! { ctx_field })
2432                    } else {
2433                        let arg_name = arg.as_ident();
2434                        let arg_name = ReturnType::new(arg.clone(), wrappers.clone())
2435                            .handle_rs_to_c_return(quote! { #arg_name }, false);
2436                        Some(quote! { #arg_name })
2437                    }
2438                })
2439                .filter(|t| !t.is_empty())
2440                .collect();
2441
2442            let new_args: Vec<TokenStream> = poll_method
2443                .arguments
2444                .iter()
2445                .enumerate()
2446                .filter_map(|(idx, arg)| {
2447                    if idx == 0 {
2448                        None
2449                    } else {
2450                        let arg_name = arg.as_ident();
2451                        let arg_type = ReturnType::new(arg.clone(), wrappers.clone()).get_new_return_type(false, true);
2452                        if arg_type.clone().into_token_stream().is_empty() {
2453                            None
2454                        } else {
2455                            Some(quote! { #arg_name: #arg_type })
2456                        }
2457                    }
2458                })
2459                .filter(|t| !t.is_empty())
2460                .collect();
2461
2462            let async_init_args: Vec<TokenStream> = new_method
2463                .arguments
2464                .iter()
2465                .enumerate()
2466                .filter_map(|(idx, arg)| {
2467                    if idx == 0 {
2468                        Some(quote! { ctx_field })
2469                    } else {
2470                        let arg_name = arg.as_ident();
2471                        let arg_name = ReturnType::new(arg.clone(), wrappers.clone())
2472                            .handle_rs_to_c_return(quote! { #arg_name }, false);
2473                        Some(quote! { #arg_name })
2474                    }
2475                })
2476                .filter(|t| !t.is_empty())
2477                .collect();
2478
2479            // Generate logging for async new method arguments (as token stream)
2480            let async_log_expr_tokens = CWrapper::generate_arg_logging(&new_method.arguments, &async_init_args);
2481
2482            let generic_types: Vec<TokenStream> = new_method
2483                .arguments
2484                .iter()
2485                .flat_map(|arg| {
2486                    ReturnType::new(arg.clone(), wrappers.clone())
2487                        .method_generics_for_where(true)
2488                        .into_iter()
2489                })
2490                .collect_vec();
2491            let where_clause_async = if generic_types.is_empty() {
2492                quote! {}
2493            } else {
2494                quote! { <#(#generic_types),*> }
2495            };
2496            let generic_types: Vec<TokenStream> = poll_method
2497                .arguments
2498                .iter()
2499                .flat_map(|arg| {
2500                    ReturnType::new(arg.clone(), wrappers.clone())
2501                        .method_generics_for_where(true)
2502                        .into_iter()
2503                })
2504                .collect_vec();
2505            let where_clause_main = if generic_types.is_empty() {
2506                quote! {}
2507            } else {
2508                quote! { <#(#generic_types),*> }
2509            };
2510            let async_new_args: Vec<TokenStream> = new_method
2511                .arguments
2512                .iter()
2513                .enumerate()
2514                .filter_map(|(idx, arg)| {
2515                    if idx == 0 {
2516                        None
2517                    } else {
2518                        let arg_name = arg.as_ident();
2519                        let arg_type = ReturnType::new(arg.clone(), wrappers.clone()).get_new_return_type(false, true);
2520                        if arg_type.clone().into_token_stream().is_empty() {
2521                            None
2522                        } else {
2523                            Some(quote! { #arg_name: #arg_type })
2524                        }
2525                    }
2526                })
2527                .filter(|t| !t.is_empty())
2528                .collect();
2529
2530            let async_dependancies = async_new_args
2531                .iter()
2532                .filter(|a| a.to_string().contains(" : Aeron") || a.to_string().contains(" : & Aeron"))
2533                .map(|e| {
2534                    let var_name = format_ident!("{}", e.to_string().split_whitespace().next().unwrap());
2535                    quote! {
2536                        result.inner.add_dependency(#var_name.clone());
2537                    }
2538                })
2539                .collect_vec();
2540
2541            // The `Aeron` client argument, if any — needed both to keep retained handlers
2542            // alive (above) and to call `..._cancel(client, async)` on drop/`.cancel()` below.
2543            let async_client_var: Option<Ident> = async_new_args
2544                .iter()
2545                .find(|a| a.to_string().contains(" : Aeron") || a.to_string().contains(" : & Aeron"))
2546                .map(|a| format_ident!("{}", a.to_string().split_whitespace().next().unwrap()));
2547
2548            let mut async_handler_deps: Vec<TokenStream> =
2549                CWrapper::handler_dependency_registrations(&new_method.arguments);
2550
2551            // The conductor thread can invoke retained callbacks (e.g. image lifecycle
2552            // handlers) for as long as the *client* lives — even if this async poller is
2553            // dropped without ever being polled. Anchor each handler to the client too, so
2554            // dropping an unpolled poller cannot free a callback C still points at.
2555            if let Some(client_arg) = async_new_args
2556                .iter()
2557                .find(|a| a.to_string().contains(" : Aeron") || a.to_string().contains(" : & Aeron"))
2558            {
2559                let client_var = format_ident!("{}", client_arg.to_string().split_whitespace().next().unwrap());
2560                let client_anchored: Vec<TokenStream> = new_method
2561                    .arguments
2562                    .iter()
2563                    .filter_map(|arg| {
2564                        if let ArgProcessing::Handler(_) = &arg.processing {
2565                            if !arg.is_mut_pointer() {
2566                                let name = arg.as_ident();
2567                                return Some(quote! {
2568                                    if let Some(__handler) = #name {
2569                                        if let Some(__client_inner) = #client_var.inner.as_owned() {
2570                                            __client_inner.add_dependency(__handler.clone());
2571                                        }
2572                                    }
2573                                });
2574                            }
2575                        }
2576                        None
2577                    })
2578                    .collect();
2579                async_handler_deps.extend(client_anchored);
2580            }
2581
2582            let async_new_args_for_client = async_new_args.iter().skip(1).cloned().collect_vec();
2583
2584            let async_new_args_name_only: Vec<TokenStream> = new_method
2585                .arguments
2586                .iter()
2587                .enumerate()
2588                .filter_map(|(idx, arg)| {
2589                    if idx < 2 {
2590                        None
2591                    } else {
2592                        let arg_name = arg.as_ident();
2593                        let arg_type = ReturnType::new(arg.clone(), wrappers.clone()).get_new_return_type(false, false);
2594                        if arg_type.clone().into_token_stream().is_empty() {
2595                            None
2596                        } else {
2597                            Some(quote! { #arg_name })
2598                        }
2599                    }
2600                })
2601                .filter(|t| !t.is_empty())
2602                .collect();
2603
2604            // Generate logging for poll method arguments (as token stream)
2605            let poll_log_expr_tokens = CWrapper::generate_arg_logging(&poll_method.arguments, &init_args);
2606
2607            let close_cleanup = if let Some(close_method) = main.get_close_method() {
2608                let close_fn = format_ident!("{}", close_method.fn_name);
2609                // Aeron C close functions now take optional notification
2610                // callbacks and a clientd pointer.  Use Default::default()
2611                // for any arg beyond the resource pointer — it resolves to
2612                // `None` / `null_mut()` via type inference at the call site.
2613                let extra_args: Vec<TokenStream> = close_method
2614                    .arguments
2615                    .iter()
2616                    .skip(1)
2617                    .map(|_| quote! { Default::default() })
2618                    .collect();
2619                quote! {
2620                    Some(Box::new(move |ptr| unsafe {
2621                        #close_fn(*ptr, #(#extra_args),*)
2622                    }))
2623                }
2624            } else {
2625                quote! { None }
2626            };
2627
2628            // Safety: `ManagedCResource::mark_resource_released()` (called by `poll()` on every
2629            // terminal outcome — success or a real error) already nulls the stored pointer, and
2630            // `close_shared()` only invokes the cleanup closure when the pointer is non-null. So
2631            // once `poll()` has ever returned `Ok(Some(_))` or `Err(_)`, this cleanup can no
2632            // longer fire — cancel() is never called on an already-resolved/consumed pointer,
2633            // whether that happens via `Drop` or an explicit `.cancel()` call afterwards.
2634            let cancel_method_name = format!("{}_cancel", new_method.fn_name);
2635            let async_cancel_cleanup = if let (Some(cancel_method), Some(client_var)) = (
2636                client_class.methods.iter().find(|m| m.fn_name == cancel_method_name),
2637                async_client_var.clone(),
2638            ) {
2639                let cancel_fn = format_ident!("{}", cancel_method.fn_name);
2640                let client_owned = format_ident!("{}_for_cancel", client_var);
2641                Some(quote! {
2642                    {
2643                        let #client_owned = #client_var.clone();
2644                        Some(Box::new(move |ptr| unsafe {
2645                            log::info!(
2646                                "auto-cancelling {} (poll() was never resolved before drop/cancel) to avoid leaking the pending Aeron registration",
2647                                stringify!(#async_class_name)
2648                            );
2649                            #cancel_fn(#client_owned.get_inner(), *ptr)
2650                        }))
2651                    }
2652                })
2653            } else {
2654                None
2655            };
2656            let async_new_cleanup_tokens = async_cancel_cleanup.clone().unwrap_or(quote! { None });
2657            let has_cancel_method = async_cancel_cleanup.is_some();
2658
2659            let cancel_method_impl = if has_cancel_method {
2660                quote! {
2661                    #[doc = r"Cancels this in-progress operation, releasing the pending Aeron"]
2662                    #[doc = r"registration instead of waiting for `poll()`/`poll_blocking()` to"]
2663                    #[doc = r"resolve it or for this value to be dropped (dropping an unresolved"]
2664                    #[doc = r"poller does this automatically — this is only needed to give up"]
2665                    #[doc = r"earlier than the drop would happen naturally)."]
2666                    #[doc = r""]
2667                    #[doc = r"A no-op if `poll()` has already returned a terminal result (`Some`"]
2668                    #[doc = r"or an `Err`) — cancelling after that point would either be invalid"]
2669                    #[doc = r"(the C struct is no longer valid) or unnecessary (nothing pending"]
2670                    #[doc = r"left to cancel)."]
2671                    #[inline]
2672                    pub fn cancel(&self) -> Result<(), AeronCError> {
2673                        if let Some(inner) = self.inner.as_owned() {
2674                            inner.close_shared()
2675                        } else {
2676                            Ok(())
2677                        }
2678                    }
2679                }
2680            } else {
2681                quote! {}
2682            };
2683
2684            quote! {
2685                    impl #main_class_name {
2686                        #[inline]
2687                        pub fn new #where_clause_main (#(#new_args),*) -> Result<Self, AeronCError> {
2688                            let resource = ManagedCResource::new(
2689                                move |ctx_field| unsafe {
2690                                    #[cfg(feature = "log-c-bindings")]
2691                                    {
2692                                        let log_args = #poll_log_expr_tokens;
2693                                        log::info!("{}({})", stringify!(#poll_method_name), log_args);
2694                                    }
2695                                    #poll_method_name(#(#init_args),*)
2696                                },
2697                                #close_cleanup,
2698                                false,
2699                            )?;
2700                            Ok(Self {
2701                                inner: CResource::OwnedOnHeap(RcOrArc::new(resource)),
2702                            })
2703                        }
2704                    }
2705
2706                    impl #client_type {
2707                        #[doc = r"# Handler lifetime and async close"]
2708                        #[doc = r""]
2709                        #[doc = r"If this call takes a [`Handler`], the C close for the resulting resource"]
2710                        #[doc = r"(e.g. `aeron_subscription_close`) is **asynchronous** — the conductor thread"]
2711                        #[doc = r"may still invoke the handler's callback (e.g. `on_available_image`) after"]
2712                        #[doc = r"`close()`/`drop` has already returned on the calling thread. Releasing the"]
2713                        #[doc = r"handler's value immediately on close would risk a use-after-free from that"]
2714                        #[doc = r"still-in-flight callback."]
2715                        #[doc = r""]
2716                        #[doc = r"To make this safe without requiring the caller to track it, this method"]
2717                        #[doc = r"stores a clone of the owning [`Aeron`] client as a *dependency* on the"]
2718                        #[doc = r"resource being created — which transitively keeps every [`Handler`] clone"]
2719                        #[doc = r"already registered as a dependency of that resource alive for as long as"]
2720                        #[doc = r"the client itself lives, regardless of when the resource closes. No manual"]
2721                        #[doc = r"`release()` call is needed."]
2722                        #[doc = r""]
2723                        #[doc = r"This differs from the Aeron C++ wrapper, which instead frees the handler"]
2724                        #[doc = r"as the final step of `on_cmd_close_subscription` on the conductor thread —"]
2725                        #[doc = r"i.e. it ties the handler's lifetime to the close completing, not to the"]
2726                        #[doc = r"client. Rusteron's approach is simpler and avoids needing a conductor-side"]
2727                        #[doc = r"hook, but it means handler dependencies accumulate on the client's"]
2728                        #[doc = r"dependency list for the client's lifetime (they are small `Arc` clones, one"]
2729                        #[doc = r"per call, and are only dropped when the client itself drops)."]
2730                        #[inline]
2731                        pub fn #client_type_method_name #where_clause_async(&self, #(#async_new_args_for_client),*) -> Result<#async_class_name, AeronCError> {
2732                            let mut result =  #async_class_name::new(self, #(#async_new_args_name_only),*);
2733                            if let Ok(result) = &mut result {
2734                                result.inner.add_dependency(self.clone());
2735                            }
2736
2737                            result
2738                        }
2739                    }
2740
2741                    impl #client_type {
2742                        #[doc = r"Blocking convenience wrapper around the async add operation."]
2743                        #[doc = r""]
2744                        #[doc = r"**Convenience for examples and tests only.** It blocks the calling thread"]
2745                        #[doc = r"in a busy-poll loop until the resource is available or `timeout` elapses. In"]
2746                        #[doc = r"production, prefer the `async_add_*` variant and drive its `poll()` from your"]
2747                        #[doc = r"own event loop rather than blocking on a single operation."]
2748                        #[doc = r""]
2749                        #[doc = r"# Production pattern (pseudo code)"]
2750                        #[doc = r"```text"]
2751                        #[doc = r"// Don't block — drive the async poller from your loop"]
2752                        #[doc = r"let poller = client.async_add_*(...)?;"]
2753                        #[doc = r"loop {"]
2754                        #[doc = r"    if let Some(resource) = poller.poll()? {"]
2755                        #[doc = r"        break; // ready"]
2756                        #[doc = r"    }"]
2757                        #[doc = r"    do_other_work(); // service other subscriptions, timers, etc."]
2758                        #[doc = r"}"]
2759                        #[doc = r"```"]
2760                        #[doc = r""]
2761                        #[doc = r"See `poll_blocking` on the async poller for the same caveat."]
2762                        #[inline]
2763                        pub fn #client_type_method_name_without_async #where_clause_async(&self #(
2764                    , #async_new_args_for_client)*,  timeout: std::time::Duration) -> Result<#main_class_name, AeronCError> {
2765                            let start = std::time::Instant::now();
2766                            loop {
2767                                if let Ok(poller) = #async_class_name::new(self, #(#async_new_args_name_only),*) {
2768                                    let invoker = poller.agent_invoker_client();
2769                                    while start.elapsed() <= timeout  {
2770                                      if let Some(aeron) = &invoker {
2771                                          aeron.main_do_work()?;
2772                                      }
2773                                      if let Some(result) = poller.poll()? {
2774                                          return Ok(result);
2775                                      }
2776                                    #[cfg(debug_assertions)]
2777                                    std::thread::sleep(std::time::Duration::from_millis(10));
2778                                  }
2779                                }
2780                            if start.elapsed() > timeout {
2781                                log::error!("failed async poll for {:?}", self);
2782                                return Err(AeronErrorType::TimedOut.into());
2783                            }
2784                            #[cfg(debug_assertions)]
2785                            std::thread::sleep(std::time::Duration::from_millis(10));
2786                          }
2787            }
2788                    }
2789
2790                    impl #async_class_name {
2791                        #[inline]
2792                        pub fn new #where_clause_async (#(#async_new_args),*) -> Result<Self, AeronCError> {
2793                            let resource_async = ManagedCResource::new(
2794                                move |ctx_field| unsafe {
2795                                    #[cfg(feature = "log-c-bindings")]
2796                                    {
2797                                        let log_args = #async_log_expr_tokens;
2798                                        log::info!("{}({})", stringify!(#new_method_name), log_args);
2799                                    }
2800                                    #new_method_name(#(#async_init_args),*)
2801                                },
2802                                #async_new_cleanup_tokens,
2803                                false,
2804                            )?;
2805                            let result = Self {
2806                                inner: CResource::OwnedOnHeap(RcOrArc::new(resource_async)),
2807                            };
2808                            #(#async_dependancies)*
2809                            // Retained callbacks (e.g. image lifecycle handlers) are stored by
2810                            // the C client; the dependency clones below are propagated to the
2811                            // final resource when poll() succeeds, keeping them alive until it
2812                            // closes.
2813                            #(#async_handler_deps)*
2814                            Ok(result)
2815                        }
2816
2817                        #[inline]
2818                        pub fn poll(&self) -> Result<Option<#main_class_name>, AeronCError> {
2819
2820                            if let Some(inner) = self.inner.as_owned() {
2821                                if inner.is_resource_released() {
2822                                    return Ok(None);
2823                                }
2824                            }
2825
2826                            let mut result = #main_class_name::new(self);
2827                            if let Ok(result) = &mut result {
2828                                unsafe {
2829                                    #[cfg(feature = "multi-threaded")]
2830                                    for d in (&mut *self.inner.as_owned().unwrap().dependencies.lock().unwrap()).iter_mut() {
2831                                      result.inner.add_dependency(d.clone());
2832                                    }
2833                                    #[cfg(not(feature = "multi-threaded"))]
2834                                    for d in (&mut *self.inner.as_owned().unwrap().dependencies.get()).iter_mut() {
2835                                      result.inner.add_dependency(d.clone());
2836                                    }
2837                                }
2838                            }
2839
2840                            match result {
2841                                Ok(result) => {
2842                                    if let Some(inner) = self.inner.as_owned() {
2843                                        inner.mark_resource_released();
2844                                    }
2845                                    Ok(Some(result))
2846                                }
2847                                Err(e) if e.code == 0 => {
2848                                  Ok(None) // try again
2849                                }
2850                                Err(e) => {
2851                                    if let Some(inner) = self.inner.as_owned() {
2852                                        inner.mark_resource_released();
2853                                    }
2854                                    Err(e)
2855                                }
2856                            }
2857                        }
2858
2859                        #cancel_method_impl
2860
2861                        #[doc = r"Polls synchronously until the async operation completes or `timeout` elapses."]
2862                        #[doc = r""]
2863                        #[doc = r"**Convenience for examples and tests only.** It blocks the calling thread"]
2864                        #[doc = r"in a busy-poll loop. In production, drive `poll()` from your own event loop"]
2865                        #[doc = r"(between other work, on a timer, or in a dedicated duty cycle) rather than"]
2866                        #[doc = r"blocking on a single operation."]
2867                        #[doc = r""]
2868                        #[doc = r"# Production pattern (pseudo code)"]
2869                        #[doc = r"```text"]
2870                        #[doc = r"// Don't block — integrate poll() into your existing loop"]
2871                        #[doc = r"loop {"]
2872                        #[doc = r"    if let Some(resource) = poller.poll()? {"]
2873                        #[doc = r"        break; // ready"]
2874                        #[doc = r"    }"]
2875                        #[doc = r"    do_other_work(); // service other subscriptions, timers, etc."]
2876                        #[doc = r"}"]
2877                        #[doc = r"```"]
2878                        #[doc = r""]
2879                        #[doc = r"See the blocking `add_*(.., timeout)` helpers for the same caveat."]
2880                        #[inline]
2881                        pub fn poll_blocking(&self, timeout: std::time::Duration) -> Result<#main_class_name, AeronCError> {
2882                            if let Some(result) = self.poll()? {
2883                                return Ok(result);
2884                            }
2885
2886                            let invoker = self.agent_invoker_client();
2887                            let time = std::time::Instant::now();
2888                            while time.elapsed() < timeout {
2889                                if let Some(aeron) = &invoker {
2890                                    aeron.main_do_work()?;
2891                                }
2892                                if let Some(result) = self.poll()? {
2893                                    return Ok(result);
2894                                }
2895                                #[cfg(debug_assertions)]
2896                                std::thread::sleep(std::time::Duration::from_millis(10));
2897                            }
2898                            log::error!("failed async poll for {:?}", self);
2899                            Err(AeronErrorType::TimedOut.into())
2900                        }
2901
2902                        #[doc = r"The client a blocking poll must drive, as nothing else runs its agent-invoker conductor."]
2903                        #[inline]
2904                        fn agent_invoker_client(&self) -> Option<Aeron> {
2905                            self.inner
2906                                .get_dependency::<Aeron>()
2907                                #owner_client_fallback
2908                                .filter(Aeron::uses_agent_invoker)
2909                        }
2910                    }
2911                                }
2912        } else {
2913            quote! {}
2914        }
2915    } else {
2916        quote! {}
2917    };
2918
2919    let mut additional_impls = vec![];
2920
2921    if let Some(close_method) = wrapper.get_close_method() {
2922        let close_deferred_if_shared = wrapper.type_name == "aeron_t" || wrapper.type_name == "aeron_archive_t";
2923        let skip_generated_close =
2924            wrapper.methods.iter().any(|m| m.fn_name.contains("_init")) && !close_deferred_if_shared;
2925        if !skip_generated_close {
2926            let close_fn = format_ident!("{}", close_method.fn_name);
2927            let close_resource_call = if close_deferred_if_shared {
2928                quote! { self.inner.close_resource_deferred_if_shared() }
2929            } else {
2930                quote! { self.inner.close_resource() }
2931            };
2932            // Consuming close(self): run the FFI close through the shared
2933            // ManagedCResource state, then drop this handle.  The cleanup
2934            // closure is taken exactly once across clones, and after
2935            // close(self) this binding is gone.
2936            if close_deferred_if_shared {
2937                additional_impls.push(quote! {
2938                    impl #class_name {
2939                        /// Releases this handle and closes the C client **once the last reference drops**.
2940                        ///
2941                        /// The C client owns and frees every child resource (publications,
2942                        /// subscriptions, counters) when it closes, so while children or clones
2943                        /// are alive this is deferred — equivalent to `drop` — and the surviving
2944                        /// handles remain fully usable. The C close runs when the final
2945                        /// reference (child or clone) is released.
2946                        pub fn close(self) -> Result<(), AeronCError> {
2947                            let result = #close_resource_call;
2948                            // Drop this handle (decrements Rc, releases deps).
2949                            drop(self);
2950                            result
2951                        }
2952
2953                        /// Closes the C client **immediately**, even if children or clones are alive.
2954                        ///
2955                        /// Clones of *this* handle are safe afterwards (their shared pointer is
2956                        /// nulled). Child handles are not:
2957                        ///
2958                        /// # Safety
2959                        /// The C client frees every child resource (publications, subscriptions,
2960                        /// counters) during this call, so surviving child handles dangle. Any use
2961                        /// is use-after-free — **including their `Drop`**, which calls the C close
2962                        /// on the freed pointer (double free). You must `std::mem::forget` every
2963                        /// surviving child, or never return (e.g. `std::process::exit`). Prefer
2964                        /// [`Self::close`], which defers until the last reference drops.
2965                        pub unsafe fn close_now(self) -> Result<(), AeronCError> {
2966                            let result = self.inner.close_resource();
2967                            drop(self);
2968                            result
2969                        }
2970                    }
2971                });
2972            } else {
2973                additional_impls.push(quote! {
2974                    impl #class_name {
2975                        /// Closes this resource in the C client immediately; all clones of this
2976                        /// handle become closed (their pointer is nulled) and must not be used.
2977                        pub fn close(self) -> Result<(), AeronCError> {
2978                            let result = #close_resource_call;
2979                            // Drop this handle (decrements Rc, releases deps).
2980                            drop(self);
2981                            result
2982                        }
2983                    }
2984                });
2985            }
2986
2987            let close_has_notification_handler = !close_deferred_if_shared
2988                && close_method
2989                    .arguments
2990                    .iter()
2991                    .any(|arg| matches!(arg.processing, ArgProcessing::Handler(_)))
2992                && close_method.arguments.iter().any(|arg| arg.is_c_void());
2993
2994            if close_has_notification_handler {
2995                additional_impls.push(quote! {
2996                    impl #class_name {
2997                        /// Like [`Self::close`], but notifies `on_close_complete` when the close
2998                        /// finishes. The handler must outlive the notification (keep your
2999                        /// `Handler` alive until it has fired).
3000                        pub fn close_with_handler<AeronNotificationHandlerImpl: AeronNotificationCallback>(
3001                            self,
3002                            on_close_complete: Option<&Handler<AeronNotificationHandlerImpl>>,
3003                        ) -> Result<(), AeronCError> {
3004                            let result = self.inner.close_resource_with(move |ptr| unsafe {
3005                                #close_fn(
3006                                    *ptr,
3007                                    {
3008                                        let callback: aeron_notification_t = if on_close_complete.is_none() {
3009                                            None
3010                                        } else {
3011                                            Some(aeron_notification_t_callback::<AeronNotificationHandlerImpl>)
3012                                        };
3013                                        callback
3014                                    },
3015                                    on_close_complete
3016                                        .map(|m| m.as_raw())
3017                                        .unwrap_or_else(|| std::ptr::null_mut()),
3018                                )
3019                            });
3020                            drop(self);
3021                            result
3022                        }
3023                    }
3024                });
3025            }
3026
3027            // Generate additional methods for specific types
3028            if wrapper.type_name == "aeron_counter_t" {
3029                additional_impls.push(quote! {
3030                    impl #class_name {
3031                        #[inline]
3032                        pub fn addr_atomic(&self) -> &std::sync::atomic::AtomicI64 {
3033                            unsafe { std::sync::atomic::AtomicI64::from_ptr(self.addr()) }
3034                        }
3035                    }
3036                });
3037            } else if wrapper.type_name == "aeron_publication_t" || wrapper.type_name == "aeron_exclusive_publication_t"
3038            {
3039                additional_impls.push(quote! {
3040                    impl #class_name {
3041                        #[inline]
3042                        pub fn is_ready(&self) -> bool {
3043                            self.is_connected() && self.position_limit() != 0
3044                        }
3045                    }
3046                });
3047            }
3048        }
3049    }
3050
3051    let common_code = if !include_common_code {
3052        quote! {}
3053    } else {
3054        TokenStream::from_str(COMMON_CODE).unwrap()
3055    };
3056    let warning_code = if !include_common_code {
3057        quote! {}
3058    } else {
3059        let mut code = String::new();
3060
3061        if include_clippy {
3062            code.push_str(
3063                "        #![allow(non_upper_case_globals)]
3064        #![allow(non_camel_case_types)]
3065        #![allow(non_snake_case)]
3066        #![allow(clippy::all)]
3067        #![allow(unused_variables)]
3068        #![allow(unused_unsafe)]
3069",
3070            );
3071        }
3072
3073        if include_aeron_client_registering_resource_t {
3074            code.push_str(
3075                "
3076                type aeron_client_registering_resource_t = aeron_client_registering_resource_stct;
3077",
3078            );
3079        }
3080
3081        TokenStream::from_str(code.as_str()).unwrap()
3082    };
3083    let class_docs: Vec<TokenStream> = wrapper
3084        .docs
3085        .iter()
3086        .map(|doc| {
3087            quote! {
3088                #[doc = #doc]
3089            }
3090        })
3091        .collect();
3092
3093    let fields = wrapper.generate_fields(&wrappers, &mut debug_fields);
3094
3095    let default_impl =
3096        if wrapper.has_default_method() && !constructor.iter().map(|x| x.to_string()).join("").trim().is_empty() {
3097            // let default_method_call = if wrapper.has_any_methods() {
3098            //     quote! {
3099            //         #class_name::new_zeroed_on_heap()
3100            //     }
3101            //  } else {
3102            //     quote! {
3103            //         #class_name::new_zeroed_on_stack()
3104            //     }
3105            // };
3106
3107            quote! {
3108                /// This will create an instance where the struct is zeroed, use with care
3109                impl Default for #class_name {
3110                    fn default() -> Self {
3111                        #class_name::new_zeroed_on_heap()
3112                    }
3113                }
3114
3115                impl #class_name {
3116                    /// Regular clone just increases the reference count of underlying count.
3117                    /// `clone_struct` shallow copies the content of the underlying struct on heap.
3118                    ///
3119                    /// NOTE: if the struct has references to other structs these will not be copied
3120                    ///
3121                    /// Must be only used on structs which has no init/clean up methods.
3122                    /// So its dangerous to use with Aeron/AeronContext/AeronPublication/AeronSubscription
3123                    /// More intended for AeronArchiveRecordingDescriptor (note strings will not work as its a shallow copy)
3124                    pub fn clone_struct(&self) -> Self {
3125                        let copy = Self::default();
3126                        // SAFETY: `copy` was created just above and is not aliased yet.
3127                        unsafe { copy.get_inner_mut().clone_from(self.deref()) };
3128                        copy
3129                    }
3130                }
3131            }
3132        } else {
3133            quote! {}
3134        };
3135
3136    quote! {
3137        #warning_code
3138
3139        #(#class_docs)*
3140        #[derive(Clone)]
3141        pub struct #class_name {
3142            inner: CResource<#type_name>,
3143            #(#constructor_fields)*
3144        }
3145
3146        impl core::fmt::Debug for  #class_name {
3147            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3148                if self.inner.get().is_null() {
3149                    f.debug_struct(stringify!(#class_name))
3150                    .field("inner", &"null")
3151                    .finish()
3152                } else {
3153                    f.debug_struct(stringify!(#class_name))
3154                      .field("inner", &self.inner)
3155                      #(#debug_fields)*
3156                      .finish()
3157                }
3158            }
3159        }
3160
3161        impl #class_name {
3162            #(#constructor)*
3163            #(#fields)*
3164            #(#methods)*
3165
3166            #[inline(always)]
3167            pub fn get_inner(&self) -> *mut #type_name {
3168                self.inner.get()
3169            }
3170
3171            /// Mutable access to the underlying C struct, minted from `&self`: nothing
3172            /// prevents two live `&mut` at once, so the caller must ensure exclusive
3173            /// access for the lifetime of the returned reference.
3174            ///
3175            /// # Safety
3176            /// No other reference (`&` or `&mut`) to the underlying struct may be
3177            /// alive while the returned `&mut` is in use.
3178            #[inline(always)]
3179            pub unsafe fn get_inner_mut(&self) -> &mut #type_name {
3180                &mut *self.inner.get()
3181            }
3182
3183            #[inline(always)]
3184            pub fn get_inner_ref(&self) -> & #type_name {
3185                unsafe { &*self.inner.get() }
3186            }
3187        }
3188
3189        impl std::ops::Deref for #class_name {
3190            type Target = #type_name;
3191
3192            fn deref(&self) -> &Self::Target {
3193                self.get_inner_ref()
3194            }
3195        }
3196
3197        impl From<*mut #type_name> for #class_name {
3198            #[inline]
3199            fn from(value: *mut #type_name) -> Self {
3200                #class_name {
3201                    inner: CResource::Borrowed(value),
3202                    #(#new_ref_set_none)*
3203                }
3204            }
3205        }
3206
3207        impl From<#class_name> for *mut #type_name {
3208            #[inline]
3209            fn from(value: #class_name) -> Self {
3210                value.get_inner()
3211            }
3212        }
3213
3214        impl From<&#class_name> for *mut #type_name {
3215            #[inline]
3216            fn from(value: &#class_name) -> Self {
3217                value.get_inner()
3218            }
3219        }
3220
3221        impl From<#class_name> for #type_name {
3222            #[inline]
3223            fn from(value: #class_name) -> Self {
3224                unsafe { *value.get_inner().clone() }
3225            }
3226        }
3227
3228        impl From<*const #type_name> for #class_name {
3229            #[inline]
3230            fn from(value: *const #type_name) -> Self {
3231                #class_name {
3232                    inner: CResource::Borrowed(value as *mut #type_name),
3233                    #(#new_ref_set_none)*
3234                }
3235            }
3236        }
3237
3238        impl From<#type_name> for #class_name {
3239            #[inline]
3240            fn from(value: #type_name) -> Self {
3241                #class_name {
3242                    inner: CResource::OwnedOnStack(MaybeUninit::new(value).into()),
3243                    #(#new_ref_set_none)*
3244                }
3245            }
3246        }
3247
3248        #(#additional_impls)*
3249
3250        #async_impls
3251        #default_impl
3252        #tests
3253        #common_code
3254    }
3255}