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