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