1use crate::generators::binding_helpers::{
2 apply_return_newtype_unwrap, gen_async_body, gen_call_args, gen_call_args_cfg, gen_call_args_with_let_bindings,
3 gen_lossy_binding_to_core_fields, gen_lossy_binding_to_core_fields_mut, gen_named_let_bindings_pub,
4 gen_serde_let_bindings, gen_unimplemented_body, has_named_params, is_simple_non_opaque_param,
5 wrap_return_with_mutex,
6};
7use crate::generators::{AdapterBodies, AsyncPattern, RustBindingConfig};
8use crate::shared::{function_params, function_sig_defaults, partition_methods};
9use crate::type_mapper::TypeMapper;
10use ahash::AHashSet;
11use alef_core::ir::{MethodDef, TypeDef, TypeRef};
12use std::fmt::Write;
13
14pub fn is_trait_method_name(name: &str) -> bool {
17 crate::generators::TRAIT_METHOD_NAMES.contains(&name)
18}
19
20pub fn gen_constructor(typ: &TypeDef, mapper: &dyn TypeMapper, cfg: &RustBindingConfig) -> String {
22 gen_constructor_with_renames(typ, mapper, cfg, None)
23}
24
25pub fn gen_constructor_with_renames(
27 typ: &TypeDef,
28 mapper: &dyn TypeMapper,
29 cfg: &RustBindingConfig,
30 field_renames: Option<&std::collections::HashMap<String, String>>,
31) -> String {
32 let map_fn = |ty: &alef_core::ir::TypeRef| mapper.map_type(ty);
33
34 let (param_list, sig_defaults, assignments) = if typ.has_default {
36 crate::shared::config_constructor_parts_with_renames(
37 &typ.fields,
38 &map_fn,
39 cfg.option_duration_on_defaults,
40 field_renames,
41 )
42 } else {
43 crate::shared::constructor_parts_with_renames(&typ.fields, &map_fn, field_renames)
44 };
45
46 let mut out = String::with_capacity(512);
47 if typ.fields.len() > 7 {
49 writeln!(out, " #[allow(clippy::too_many_arguments)]").ok();
50 }
51 writeln!(out, " #[must_use]").ok();
52 if cfg.needs_signature {
53 writeln!(
54 out,
55 " {}{}{}",
56 cfg.signature_prefix, sig_defaults, cfg.signature_suffix
57 )
58 .ok();
59 }
60 write!(
61 out,
62 " {}\n pub fn new({param_list}) -> Self {{\n Self {{ {assignments} }}\n }}",
63 cfg.constructor_attr
64 )
65 .ok();
66 out
67}
68
69#[allow(clippy::too_many_arguments)]
78pub fn gen_method(
79 method: &MethodDef,
80 mapper: &dyn TypeMapper,
81 cfg: &RustBindingConfig,
82 typ: &TypeDef,
83 is_opaque: bool,
84 opaque_types: &AHashSet<String>,
85 mutex_types: &AHashSet<String>,
86 adapter_bodies: &AdapterBodies,
87) -> String {
88 let type_name = &typ.name;
89 let core_type_path = typ.rust_path.replace('-', "_");
91
92 let map_fn = |ty: &alef_core::ir::TypeRef| mapper.map_type(ty);
93 let params = function_params(&method.params, &map_fn);
94 let return_type = mapper.map_type(&method.return_type);
95 let ret = mapper.wrap_return(&return_type, method.error_type.is_some());
96
97 let core_import = cfg.core_import;
98
99 let has_ref_named_params = has_named_params(&method.params, opaque_types);
103 let (call_args, ref_let_bindings) = if has_ref_named_params {
104 (
105 gen_call_args_with_let_bindings(&method.params, opaque_types),
106 gen_named_let_bindings_pub(&method.params, opaque_types, core_import),
107 )
108 } else if cfg.cast_uints_to_i32 || cfg.cast_large_ints_to_f64 {
109 (
111 gen_call_args_cfg(
112 &method.params,
113 opaque_types,
114 cfg.cast_uints_to_i32,
115 cfg.cast_large_ints_to_f64,
116 ),
117 String::new(),
118 )
119 } else {
120 (gen_call_args(&method.params, opaque_types), String::new())
121 };
122
123 let is_owned_receiver = matches!(method.receiver.as_ref(), Some(alef_core::ir::ReceiverKind::Owned));
124 let is_ref_mut_receiver = matches!(method.receiver.as_ref(), Some(alef_core::ir::ReceiverKind::RefMut));
125
126 let is_functional_ref_mut = !is_opaque
133 && is_ref_mut_receiver
134 && !method.sanitized
135 && method.trait_source.is_none()
136 && method
137 .params
138 .iter()
139 .all(|p| !p.sanitized && crate::shared::is_delegatable_param(&p.ty, opaque_types));
140
141 let is_trait_method = method.trait_source.is_some();
144
145 let self_needs_mutex = is_opaque && mutex_types.contains(type_name.as_str());
147
148 let opaque_can_delegate = is_opaque
155 && !method.sanitized
156 && (!is_ref_mut_receiver || self_needs_mutex)
157 && !is_trait_method
158 && (!is_owned_receiver || typ.is_clone)
159 && method
160 .params
161 .iter()
162 .all(|p| !p.sanitized && crate::shared::is_opaque_delegatable_type(&p.ty))
163 && crate::shared::is_opaque_delegatable_type(&method.return_type);
164
165 let make_core_call = |method_name: &str| -> String {
169 if is_opaque {
170 if is_owned_receiver {
171 if self_needs_mutex {
174 format!("self.inner.lock().unwrap().clone().{method_name}({call_args})")
175 } else {
176 format!("(*self.inner).clone().{method_name}({call_args})")
177 }
178 } else if self_needs_mutex {
179 format!("self.inner.lock().unwrap().{method_name}({call_args})")
181 } else {
182 format!("self.inner.{method_name}({call_args})")
183 }
184 } else {
185 format!("{core_type_path}::from(self.clone()).{method_name}({call_args})")
186 }
187 };
188
189 let make_async_core_call = |method_name: &str| -> String {
192 if is_opaque {
193 if self_needs_mutex {
194 format!("inner.lock().unwrap().{method_name}({call_args})")
195 } else {
196 format!("inner.{method_name}({call_args})")
197 }
198 } else {
199 format!("{core_type_path}::from(self.clone()).{method_name}({call_args})")
200 }
201 };
202
203 let result_expr = apply_return_newtype_unwrap("result", &method.return_newtype_wrapper);
210 let async_result_wrap = if is_opaque {
211 wrap_return_with_mutex(
212 &result_expr,
213 &method.return_type,
214 type_name,
215 opaque_types,
216 mutex_types,
217 is_opaque,
218 method.returns_ref,
219 method.returns_cow,
220 )
221 } else {
222 match &method.return_type {
225 TypeRef::Named(_) | TypeRef::Json => format!("{result_expr}.into()"),
226 _ => result_expr.clone(),
227 }
228 };
229
230 let body = if !opaque_can_delegate {
231 let adapter_key_inner = format!("{}.{}", type_name, method.name);
233 if let Some(adapter_body) = adapter_bodies.get(&adapter_key_inner) {
234 adapter_body.clone()
235 } else if cfg.has_serde
236 && is_opaque
237 && !method.sanitized
238 && !is_trait_method
239 && has_named_params(&method.params, opaque_types)
240 && method.error_type.is_some()
241 && crate::shared::is_opaque_delegatable_type(&method.return_type)
242 {
243 let err_conv = match cfg.async_pattern {
246 AsyncPattern::Pyo3FutureIntoPy => {
247 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
248 }
249 AsyncPattern::NapiNativeAsync => {
250 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
251 }
252 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
253 AsyncPattern::TokioBlockOn => ".map_err(|e| extendr_api::Error::Other(e.to_string()))",
254 _ => ".map_err(|e| e.to_string())",
255 };
256 let serde_bindings =
257 gen_serde_let_bindings(&method.params, opaque_types, cfg.core_import, err_conv, " ");
258 let serde_call_args = gen_call_args_with_let_bindings(&method.params, opaque_types);
259 let core_call = if self_needs_mutex {
260 format!("self.inner.lock().unwrap().{}({serde_call_args})", method.name)
261 } else {
262 format!("self.inner.{}({serde_call_args})", method.name)
263 };
264 if matches!(method.return_type, TypeRef::Unit) {
265 format!("{serde_bindings}{core_call}{err_conv}?;\n Ok(())")
266 } else {
267 let wrap = wrap_return_with_mutex(
268 "result",
269 &method.return_type,
270 type_name,
271 opaque_types,
272 mutex_types,
273 is_opaque,
274 method.returns_ref,
275 method.returns_cow,
276 );
277 format!("{serde_bindings}let result = {core_call}{err_conv}?;\n Ok({wrap})")
278 }
279 } else if is_functional_ref_mut {
280 let field_conversions = gen_lossy_binding_to_core_fields_mut(
289 typ,
290 cfg.core_import,
291 cfg.option_duration_on_defaults,
292 opaque_types,
293 cfg.cast_uints_to_i32,
294 cfg.cast_large_ints_to_f64,
295 cfg.lossy_skip_types,
296 );
297 let core_call = format!("core_self.{}({call_args})", method.name);
298 if method.error_type.is_some() {
299 let err_conv = match cfg.async_pattern {
300 AsyncPattern::Pyo3FutureIntoPy => {
301 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
302 }
303 AsyncPattern::NapiNativeAsync => {
304 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
305 }
306 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
307 AsyncPattern::TokioBlockOn => ".map_err(|e| extendr_api::Error::Other(e.to_string()))",
308 _ => ".map_err(|e| e.to_string())",
309 };
310 format!("{field_conversions}{core_call}{err_conv}?;\n Ok(core_self.into())")
311 } else {
312 format!("{field_conversions}{core_call};\n core_self.into()")
313 }
314 } else if !is_opaque
315 && !method.sanitized
316 && method
317 .params
318 .iter()
319 .all(|p| !p.sanitized && is_simple_non_opaque_param(&p.ty))
320 && crate::shared::is_delegatable_return(&method.return_type)
321 {
322 let is_ref_mut = matches!(method.receiver.as_ref(), Some(alef_core::ir::ReceiverKind::RefMut));
325 let field_conversions = if is_ref_mut {
326 gen_lossy_binding_to_core_fields_mut(
327 typ,
328 cfg.core_import,
329 cfg.option_duration_on_defaults,
330 opaque_types,
331 cfg.cast_uints_to_i32,
332 cfg.cast_large_ints_to_f64,
333 cfg.lossy_skip_types,
334 )
335 } else {
336 gen_lossy_binding_to_core_fields(
337 typ,
338 cfg.core_import,
339 cfg.option_duration_on_defaults,
340 opaque_types,
341 cfg.cast_uints_to_i32,
342 cfg.cast_large_ints_to_f64,
343 cfg.lossy_skip_types,
344 )
345 };
346 let core_call = format!("core_self.{}({call_args})", method.name);
347 let newtype_suffix = if method.return_newtype_wrapper.is_some() {
348 ".0"
349 } else {
350 ""
351 };
352 let result_wrap = match &method.return_type {
353 TypeRef::Named(n) if n == type_name && (method.returns_cow || method.returns_ref) => {
357 ".into_owned().into()".to_string()
358 }
359 TypeRef::Named(_) if method.returns_cow || method.returns_ref => ".into_owned().into()".to_string(),
360 TypeRef::Named(n) if n == type_name => ".into()".to_string(),
361 TypeRef::Named(_) => ".into()".to_string(),
362 TypeRef::String => {
363 if method.returns_ref {
364 ".to_owned()".to_string()
365 } else {
366 String::new()
367 }
368 }
369 TypeRef::Path => {
370 if method.returns_ref {
371 ".to_owned()".to_string()
372 } else {
373 ".to_string_lossy().to_string()".to_string()
374 }
375 }
376 TypeRef::Bytes => ".to_vec()".to_string(),
379 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::Named(_)) => {
381 if method.returns_ref {
382 ".map(|v| v.clone().into())".to_string()
383 } else {
384 ".map(Into::into)".to_string()
385 }
386 }
387 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::String | TypeRef::Bytes) => {
389 if method.returns_ref {
390 ".map(|v| v.to_owned())".to_string()
391 } else {
392 String::new()
393 }
394 }
395 TypeRef::Primitive(p) => {
398 use crate::conversions::helpers::{needs_f64_cast, needs_i32_cast};
399 if cfg.cast_uints_to_i32 && needs_i32_cast(p) {
400 " as i32".to_string()
401 } else if cfg.cast_large_ints_to_f64 && needs_f64_cast(p) {
402 " as f64".to_string()
403 } else {
404 String::new()
405 }
406 }
407 TypeRef::Optional(inner) => {
409 if let TypeRef::Primitive(p) = inner.as_ref() {
410 use crate::conversions::helpers::{needs_f64_cast, needs_i32_cast};
411 if cfg.cast_uints_to_i32 && needs_i32_cast(p) {
412 ".map(|v| v as i32)".to_string()
413 } else if cfg.cast_large_ints_to_f64 && needs_f64_cast(p) {
414 ".map(|v| v as f64)".to_string()
415 } else {
416 String::new()
417 }
418 } else {
419 String::new()
420 }
421 }
422 _ => String::new(),
423 };
424 if method.error_type.is_some() {
425 let err_conv = match cfg.async_pattern {
426 AsyncPattern::Pyo3FutureIntoPy => {
427 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
428 }
429 AsyncPattern::NapiNativeAsync => {
430 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
431 }
432 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
433 _ => ".map_err(|e| e.to_string())",
434 };
435 format!(
436 "{field_conversions}let result = {core_call}{err_conv}?;\n Ok(result{newtype_suffix}{result_wrap})"
437 )
438 } else {
439 format!("{field_conversions}{core_call}{newtype_suffix}{result_wrap}")
440 }
441 } else if is_opaque
442 && !method.sanitized
443 && (!is_ref_mut_receiver || self_needs_mutex)
444 && (!is_owned_receiver || typ.is_clone)
445 && method.error_type.is_none()
446 && method
447 .params
448 .iter()
449 .all(|p| !p.sanitized && crate::shared::is_opaque_delegatable_type(&p.ty))
450 && matches!(&method.return_type, TypeRef::Named(n) if n == type_name)
451 {
452 let core_call = if is_owned_receiver {
455 if self_needs_mutex {
456 format!("self.inner.lock().unwrap().clone().{}({call_args})", method.name)
457 } else {
458 format!("(*self.inner).clone().{}({call_args})", method.name)
459 }
460 } else if self_needs_mutex {
461 format!("self.inner.lock().unwrap().{}({call_args})", method.name)
462 } else {
463 format!("self.inner.{}({call_args})", method.name)
464 };
465 let unwrapped = apply_return_newtype_unwrap(&core_call, &method.return_newtype_wrapper);
466 let arc_expr = if self_needs_mutex {
467 format!("Arc::new(std::sync::Mutex::new({unwrapped}))")
468 } else {
469 format!("Arc::new({unwrapped})")
470 };
471 format!("Self {{ inner: {arc_expr} }}")
472 } else if !is_opaque
473 && !method.sanitized
474 && !is_ref_mut_receiver
475 && (!is_owned_receiver || typ.is_clone)
476 && method.error_type.is_none()
477 && method
478 .params
479 .iter()
480 .all(|p| !p.sanitized && is_simple_non_opaque_param(&p.ty))
481 && matches!(&method.return_type, TypeRef::Named(n) if n == type_name)
482 {
483 let is_ref_mut = matches!(method.receiver.as_ref(), Some(alef_core::ir::ReceiverKind::RefMut));
486 let field_conversions = if is_ref_mut {
487 gen_lossy_binding_to_core_fields_mut(
488 typ,
489 cfg.core_import,
490 cfg.option_duration_on_defaults,
491 opaque_types,
492 cfg.cast_uints_to_i32,
493 cfg.cast_large_ints_to_f64,
494 cfg.lossy_skip_types,
495 )
496 } else {
497 gen_lossy_binding_to_core_fields(
498 typ,
499 cfg.core_import,
500 cfg.option_duration_on_defaults,
501 opaque_types,
502 cfg.cast_uints_to_i32,
503 cfg.cast_large_ints_to_f64,
504 cfg.lossy_skip_types,
505 )
506 };
507 let core_call = format!("core_self.{}({call_args})", method.name);
508 let newtype_suffix = if method.return_newtype_wrapper.is_some() {
509 ".0"
510 } else {
511 ""
512 };
513 let result_wrap = if method.returns_cow || method.returns_ref {
514 ".into_owned().into()"
515 } else {
516 ".into()"
517 };
518 format!("{field_conversions}{core_call}{newtype_suffix}{result_wrap}")
519 } else {
520 gen_unimplemented_body(
521 &method.return_type,
522 &format!("{type_name}.{}", method.name),
523 method.error_type.is_some(),
524 cfg,
525 &method.params,
526 opaque_types,
527 )
528 }
529 } else if method.is_async {
530 let inner_clone_line = if is_opaque {
531 "let inner = self.inner.clone();\n "
532 } else {
533 ""
534 };
535 let core_call_str = make_async_core_call(&method.name);
536 gen_async_body(
537 &core_call_str,
538 cfg,
539 method.error_type.is_some(),
540 &async_result_wrap,
541 is_opaque,
542 inner_clone_line,
543 matches!(method.return_type, TypeRef::Unit),
544 Some(&return_type),
545 )
546 } else {
547 let core_call = make_core_call(&method.name);
548 if method.error_type.is_some() {
549 let err_conv = match cfg.async_pattern {
551 AsyncPattern::Pyo3FutureIntoPy => {
552 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
553 }
554 AsyncPattern::NapiNativeAsync => {
555 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
556 }
557 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
558 _ => ".map_err(|e| e.to_string())",
559 };
560 if is_opaque {
561 if matches!(method.return_type, TypeRef::Unit) {
562 format!("{core_call}{err_conv}?;\n Ok(())")
564 } else {
565 let wrap = wrap_return_with_mutex(
566 &result_expr,
567 &method.return_type,
568 type_name,
569 opaque_types,
570 mutex_types,
571 is_opaque,
572 method.returns_ref,
573 method.returns_cow,
574 );
575 format!("let result = {core_call}{err_conv}?;\n Ok({wrap})")
576 }
577 } else {
578 format!("{core_call}{err_conv}")
579 }
580 } else if is_opaque {
581 let unwrapped_call = apply_return_newtype_unwrap(&core_call, &method.return_newtype_wrapper);
582 wrap_return_with_mutex(
583 &unwrapped_call,
584 &method.return_type,
585 type_name,
586 opaque_types,
587 mutex_types,
588 is_opaque,
589 method.returns_ref,
590 method.returns_cow,
591 )
592 } else {
593 core_call
594 }
595 };
596 let adapter_key = format!("{}.{}", type_name, method.name);
597 let has_adapter = adapter_bodies.contains_key(&adapter_key);
598
599 let body = if ref_let_bindings.is_empty() || has_adapter {
605 body
606 } else {
607 format!("{ref_let_bindings}{body}")
608 };
609
610 let needs_py = method.is_async && cfg.async_pattern == AsyncPattern::Pyo3FutureIntoPy;
611
612 let body = if needs_py && !opaque_can_delegate && !has_adapter {
619 let err_msg = format!("Not implemented: {type_name}.{}", method.name);
620 let suppress = if method.params.is_empty() {
622 String::new()
623 } else {
624 let names: Vec<&str> = method.params.iter().map(|p| p.name.as_str()).collect();
625 if names.len() == 1 {
626 format!("let _ = {};\n ", names[0])
627 } else {
628 format!("let _ = ({});\n ", names.join(", "))
629 }
630 };
631 format!("{suppress}Err(pyo3::exceptions::PyNotImplementedError::new_err(\"{err_msg}\"))")
632 } else {
633 body
634 };
635 let self_param = match (needs_py, params.is_empty()) {
636 (true, true) => "&self, py: Python<'py>",
637 (true, false) => "&self, py: Python<'py>, ",
638 (false, true) => "&self",
639 (false, false) => "&self, ",
640 };
641
642 let ret = if needs_py {
647 "PyResult<Bound<'py, PyAny>>".to_string()
648 } else if is_functional_ref_mut {
649 mapper.wrap_return("Self", method.error_type.is_some())
650 } else {
651 ret
652 };
653 let method_lifetime = if needs_py { "<'py>" } else { "" };
654
655 let (sig_start, sig_params, sig_end) = if self_param.len() + params.len() > 100 {
657 let wrapped_params = method
658 .params
659 .iter()
660 .map(|p| {
661 let ty = if p.optional {
662 format!("Option<{}>", mapper.map_type(&p.ty))
663 } else {
664 mapper.map_type(&p.ty)
665 };
666 format!("{}: {}", p.name, ty)
667 })
668 .collect::<Vec<_>>()
669 .join(",\n ");
670 let py_param = if needs_py { "\n py: Python<'py>," } else { "" };
671 (
672 format!(
673 "pub fn {}{method_lifetime}(\n &self,{}\n ",
674 method.name, py_param
675 ),
676 wrapped_params,
677 "\n ) -> ".to_string(),
678 )
679 } else {
680 (
681 format!("pub fn {}{method_lifetime}({}", method.name, self_param),
682 params,
683 ") -> ".to_string(),
684 )
685 };
686
687 let mut out = String::with_capacity(1024);
688 let total_params = method.params.len() + 1 + if needs_py { 1 } else { 0 };
690 if total_params > 7 {
691 writeln!(out, " #[allow(clippy::too_many_arguments)]").ok();
692 }
693 if method.error_type.is_some() {
695 writeln!(out, " #[allow(clippy::missing_errors_doc)]").ok();
696 }
697 if is_trait_method_name(&method.name) {
699 writeln!(out, " #[allow(clippy::should_implement_trait)]").ok();
700 }
701 if cfg.needs_signature {
702 let sig = function_sig_defaults(&method.params);
703 writeln!(out, " {}{}{}", cfg.signature_prefix, sig, cfg.signature_suffix).ok();
704 }
705 write!(
706 out,
707 " {}{}{}{} {{\n \
708 {body}\n }}",
709 sig_start, sig_params, sig_end, ret,
710 )
711 .ok();
712 out
713}
714
715pub fn gen_static_method(
717 method: &MethodDef,
718 mapper: &dyn TypeMapper,
719 cfg: &RustBindingConfig,
720 typ: &TypeDef,
721 adapter_bodies: &AdapterBodies,
722 opaque_types: &AHashSet<String>,
723 mutex_types: &AHashSet<String>,
724) -> String {
725 let type_name = &typ.name;
726 let core_type_path = typ.rust_path.replace('-', "_");
728 let map_fn = |ty: &alef_core::ir::TypeRef| mapper.map_type(ty);
729 let params = function_params(&method.params, &map_fn);
730 let return_type = mapper.map_type(&method.return_type);
731 let ret = mapper.wrap_return(&return_type, method.error_type.is_some());
732
733 let core_import = cfg.core_import;
734
735 let use_let_bindings = has_named_params(&method.params, opaque_types);
738 let (call_args, ref_let_bindings) = if use_let_bindings {
739 (
740 gen_call_args_with_let_bindings(&method.params, opaque_types),
741 gen_named_let_bindings_pub(&method.params, opaque_types, core_import),
742 )
743 } else {
744 (gen_call_args(&method.params, opaque_types), String::new())
745 };
746
747 let can_delegate = crate::shared::can_auto_delegate(method, opaque_types);
748
749 let body = if !can_delegate {
750 let adapter_key = format!("{}.{}", type_name, method.name);
752 if let Some(adapter_body) = adapter_bodies.get(&adapter_key) {
753 adapter_body.clone()
754 } else {
755 gen_unimplemented_body(
756 &method.return_type,
757 &format!("{type_name}::{}", method.name),
758 method.error_type.is_some(),
759 cfg,
760 &method.params,
761 opaque_types,
762 )
763 }
764 } else if method.is_async {
765 let core_call = format!("{core_type_path}::{}({call_args})", method.name);
766 let return_wrap = format!("{return_type}::from(result)");
767 gen_async_body(
768 &core_call,
769 cfg,
770 method.error_type.is_some(),
771 &return_wrap,
772 false,
773 "",
774 matches!(method.return_type, TypeRef::Unit),
775 Some(&return_type),
776 )
777 } else {
778 let core_call = format!("{core_type_path}::{}({call_args})", method.name);
779 if method.error_type.is_some() {
780 let err_conv = match cfg.async_pattern {
782 AsyncPattern::Pyo3FutureIntoPy => {
783 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
784 }
785 AsyncPattern::NapiNativeAsync => {
786 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
787 }
788 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
789 _ => ".map_err(|e| e.to_string())",
790 };
791 let val_expr = apply_return_newtype_unwrap("val", &method.return_newtype_wrapper);
793 let wrapped = wrap_return_with_mutex(
794 &val_expr,
795 &method.return_type,
796 type_name,
797 opaque_types,
798 mutex_types,
799 typ.is_opaque,
800 method.returns_ref,
801 method.returns_cow,
802 );
803 if wrapped == val_expr {
804 format!("{core_call}{err_conv}")
805 } else if wrapped == format!("{val_expr}.into()") {
806 format!("{core_call}.map(Into::into){err_conv}")
807 } else if let Some(type_path) = wrapped.strip_suffix(&format!("::from({val_expr})")) {
808 format!("{core_call}.map({type_path}::from){err_conv}")
809 } else {
810 format!("{core_call}.map(|val| {wrapped}){err_conv}")
811 }
812 } else {
813 let unwrapped_call = apply_return_newtype_unwrap(&core_call, &method.return_newtype_wrapper);
815 wrap_return_with_mutex(
816 &unwrapped_call,
817 &method.return_type,
818 type_name,
819 opaque_types,
820 mutex_types,
821 typ.is_opaque,
822 method.returns_ref,
823 method.returns_cow,
824 )
825 }
826 };
827 let body = if ref_let_bindings.is_empty() {
829 body
830 } else {
831 format!("{ref_let_bindings}{body}")
832 };
833
834 let static_needs_py = method.is_async && cfg.async_pattern == AsyncPattern::Pyo3FutureIntoPy;
835
836 let ret = if static_needs_py {
838 "PyResult<Bound<'py, PyAny>>".to_string()
839 } else {
840 ret
841 };
842 let method_lifetime = if static_needs_py { "<'py>" } else { "" };
843
844 let (sig_start, sig_params, sig_end) = if params.len() > 100 {
846 let wrapped_params = method
847 .params
848 .iter()
849 .map(|p| {
850 let ty = if p.optional {
851 format!("Option<{}>", mapper.map_type(&p.ty))
852 } else {
853 mapper.map_type(&p.ty)
854 };
855 format!("{}: {}", p.name, ty)
856 })
857 .collect::<Vec<_>>()
858 .join(",\n ");
859 if static_needs_py {
861 (
862 format!("pub fn {}{method_lifetime}(py: Python<'py>,\n ", method.name),
863 wrapped_params,
864 "\n ) -> ".to_string(),
865 )
866 } else {
867 (
868 format!("pub fn {}(\n ", method.name),
869 wrapped_params,
870 "\n ) -> ".to_string(),
871 )
872 }
873 } else if static_needs_py {
874 (
875 format!("pub fn {}{method_lifetime}(py: Python<'py>, ", method.name),
876 params,
877 ") -> ".to_string(),
878 )
879 } else {
880 (format!("pub fn {}(", method.name), params, ") -> ".to_string())
881 };
882
883 let mut out = String::with_capacity(1024);
884 let total_params = method.params.len() + if static_needs_py { 1 } else { 0 };
886 if total_params > 7 {
887 writeln!(out, " #[allow(clippy::too_many_arguments)]").ok();
888 }
889 if method.error_type.is_some() {
891 writeln!(out, " #[allow(clippy::missing_errors_doc)]").ok();
892 }
893 if is_trait_method_name(&method.name) {
895 writeln!(out, " #[allow(clippy::should_implement_trait)]").ok();
896 }
897 if let Some(attr) = cfg.static_attr {
898 writeln!(out, " #[{attr}]").ok();
899 }
900 if cfg.needs_signature {
901 let sig = function_sig_defaults(&method.params);
902 writeln!(out, " {}{}{}", cfg.signature_prefix, sig, cfg.signature_suffix).ok();
903 }
904 write!(
905 out,
906 " {}{}{}{} {{\n \
907 {body}\n }}",
908 sig_start, sig_params, sig_end, ret,
909 )
910 .ok();
911 out
912}
913
914pub fn gen_impl_block(
916 typ: &TypeDef,
917 mapper: &dyn TypeMapper,
918 cfg: &RustBindingConfig,
919 adapter_bodies: &AdapterBodies,
920 opaque_types: &AHashSet<String>,
921) -> String {
922 gen_impl_block_with_renames(typ, mapper, cfg, adapter_bodies, opaque_types, None)
923}
924
925pub fn gen_impl_block_with_renames(
927 typ: &TypeDef,
928 mapper: &dyn TypeMapper,
929 cfg: &RustBindingConfig,
930 adapter_bodies: &AdapterBodies,
931 opaque_types: &AHashSet<String>,
932 field_renames: Option<&std::collections::HashMap<String, String>>,
933) -> String {
934 let (instance, statics) = partition_methods(&typ.methods);
935 let has_emittable_instance = instance
939 .iter()
940 .any(|m| !m.sanitized || adapter_bodies.contains_key(&format!("{}.{}", typ.name, m.name)));
941 let has_emittable_statics = statics
942 .iter()
943 .any(|m| !m.sanitized || adapter_bodies.contains_key(&format!("{}.{}", typ.name, m.name)));
944 if !has_emittable_instance && !has_emittable_statics && typ.fields.is_empty() {
945 return String::new();
946 }
947
948 let prefixed_name = format!("{}{}", cfg.type_name_prefix, typ.name);
949 let mut out = String::with_capacity(2048);
950 if let Some(block_attr) = cfg.method_block_attr {
951 writeln!(out, "#[{block_attr}]").ok();
952 }
953 writeln!(out, "impl {prefixed_name} {{").ok();
954
955 if !typ.fields.is_empty() && !cfg.skip_impl_constructor {
958 out.push_str(&gen_constructor_with_renames(typ, mapper, cfg, field_renames));
959 out.push_str("\n\n");
960 }
961
962 let empty_mutex_types: AHashSet<String> = AHashSet::new();
964 for m in &instance {
965 let adapter_key = format!("{}.{}", typ.name, m.name);
969 if m.sanitized && !adapter_bodies.contains_key(&adapter_key) {
970 continue;
971 }
972 out.push_str(&gen_method(
973 m,
974 mapper,
975 cfg,
976 typ,
977 false,
978 opaque_types,
979 &empty_mutex_types,
980 adapter_bodies,
981 ));
982 out.push_str("\n\n");
983 }
984
985 for m in &statics {
987 let adapter_key = format!("{}.{}", typ.name, m.name);
989 if m.sanitized && !adapter_bodies.contains_key(&adapter_key) {
990 continue;
991 }
992 out.push_str(&gen_static_method(
993 m,
994 mapper,
995 cfg,
996 typ,
997 adapter_bodies,
998 opaque_types,
999 &empty_mutex_types,
1000 ));
1001 out.push_str("\n\n");
1002 }
1003
1004 let trimmed = out.trim_end();
1006 let mut result = trimmed.to_string();
1007 result.push_str("\n}");
1008 result
1009}
1010
1011pub fn gen_opaque_impl_block(
1018 typ: &TypeDef,
1019 mapper: &dyn TypeMapper,
1020 cfg: &RustBindingConfig,
1021 opaque_types: &AHashSet<String>,
1022 mutex_types: &AHashSet<String>,
1023 adapter_bodies: &AdapterBodies,
1024) -> String {
1025 let (instance, statics) = partition_methods(&typ.methods);
1026 let has_emittable_instance = instance
1028 .iter()
1029 .any(|m| !m.sanitized || adapter_bodies.contains_key(&format!("{}.{}", typ.name, m.name)));
1030 let has_emittable_statics = statics
1031 .iter()
1032 .any(|m| !m.sanitized || adapter_bodies.contains_key(&format!("{}.{}", typ.name, m.name)));
1033 if !has_emittable_instance && !has_emittable_statics {
1034 return String::new();
1035 }
1036
1037 let mut out = String::with_capacity(2048);
1038 let prefixed_name = format!("{}{}", cfg.type_name_prefix, typ.name);
1039 if let Some(block_attr) = cfg.method_block_attr {
1040 writeln!(out, "#[{block_attr}]").ok();
1041 }
1042 writeln!(out, "impl {prefixed_name} {{").ok();
1043
1044 for m in &instance {
1046 let adapter_key = format!("{}.{}", typ.name, m.name);
1049 if m.sanitized && !adapter_bodies.contains_key(&adapter_key) {
1050 continue;
1051 }
1052 out.push_str(&gen_method(
1053 m,
1054 mapper,
1055 cfg,
1056 typ,
1057 true,
1058 opaque_types,
1059 mutex_types,
1060 adapter_bodies,
1061 ));
1062 out.push_str("\n\n");
1063 }
1064
1065 for m in &statics {
1067 let adapter_key = format!("{}.{}", typ.name, m.name);
1069 if m.sanitized && !adapter_bodies.contains_key(&adapter_key) {
1070 continue;
1071 }
1072 out.push_str(&gen_static_method(
1073 m,
1074 mapper,
1075 cfg,
1076 typ,
1077 adapter_bodies,
1078 opaque_types,
1079 mutex_types,
1080 ));
1081 out.push_str("\n\n");
1082 }
1083
1084 let trimmed = out.trim_end();
1085 let mut result = trimmed.to_string();
1086 result.push_str("\n}");
1087 result
1088}