Skip to main content

rusteron_code_gen/
generator.rs

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