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