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 => {
254 ".map_err(|e| extendr_api::Error::Other(e.to_string().replace(\":\", \"_\").replace(\"/\", \"_\").replace(\"-\", \"_\").chars().take(255).collect::<String>()))"
255 }
256 _ => ".map_err(|e| e.to_string())",
257 };
258 let serde_bindings =
259 gen_serde_let_bindings(&method.params, opaque_types, cfg.core_import, err_conv, " ");
260 let serde_call_args = gen_call_args_with_let_bindings(&method.params, opaque_types);
261 let core_call = if self_needs_mutex {
262 format!("self.inner.lock().unwrap().{}({serde_call_args})", method.name)
263 } else {
264 format!("self.inner.{}({serde_call_args})", method.name)
265 };
266 if matches!(method.return_type, TypeRef::Unit) {
267 format!("{serde_bindings}{core_call}{err_conv}?;\n Ok(())")
268 } else {
269 let wrap = wrap_return_with_mutex(
270 "result",
271 &method.return_type,
272 type_name,
273 opaque_types,
274 mutex_types,
275 is_opaque,
276 method.returns_ref,
277 method.returns_cow,
278 );
279 format!("{serde_bindings}let result = {core_call}{err_conv}?;\n Ok({wrap})")
280 }
281 } else if is_functional_ref_mut {
282 let field_conversions = gen_lossy_binding_to_core_fields_mut(
291 typ,
292 cfg.core_import,
293 cfg.option_duration_on_defaults,
294 opaque_types,
295 cfg.cast_uints_to_i32,
296 cfg.cast_large_ints_to_f64,
297 cfg.lossy_skip_types,
298 );
299 let core_call = format!("core_self.{}({call_args})", method.name);
300 if method.error_type.is_some() {
301 let err_conv = match cfg.async_pattern {
302 AsyncPattern::Pyo3FutureIntoPy => {
303 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
304 }
305 AsyncPattern::NapiNativeAsync => {
306 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
307 }
308 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
309 AsyncPattern::TokioBlockOn => {
310 ".map_err(|e| extendr_api::Error::Other(e.to_string().replace(\":\", \"_\").replace(\"/\", \"_\").replace(\"-\", \"_\").chars().take(255).collect::<String>()))"
311 }
312 _ => ".map_err(|e| e.to_string())",
313 };
314 format!("{field_conversions}{core_call}{err_conv}?;\n Ok(core_self.into())")
315 } else {
316 format!("{field_conversions}{core_call};\n core_self.into()")
317 }
318 } else if !is_opaque
319 && !method.sanitized
320 && method
321 .params
322 .iter()
323 .all(|p| !p.sanitized && is_simple_non_opaque_param(&p.ty))
324 && crate::shared::is_delegatable_return(&method.return_type)
325 {
326 let is_ref_mut = matches!(method.receiver.as_ref(), Some(alef_core::ir::ReceiverKind::RefMut));
329 let field_conversions = if is_ref_mut {
330 gen_lossy_binding_to_core_fields_mut(
331 typ,
332 cfg.core_import,
333 cfg.option_duration_on_defaults,
334 opaque_types,
335 cfg.cast_uints_to_i32,
336 cfg.cast_large_ints_to_f64,
337 cfg.lossy_skip_types,
338 )
339 } else {
340 gen_lossy_binding_to_core_fields(
341 typ,
342 cfg.core_import,
343 cfg.option_duration_on_defaults,
344 opaque_types,
345 cfg.cast_uints_to_i32,
346 cfg.cast_large_ints_to_f64,
347 cfg.lossy_skip_types,
348 )
349 };
350 let core_call = format!("core_self.{}({call_args})", method.name);
351 let newtype_suffix = if method.return_newtype_wrapper.is_some() {
352 ".0"
353 } else {
354 ""
355 };
356 let result_wrap = match &method.return_type {
357 TypeRef::Named(n) if n == type_name && (method.returns_cow || method.returns_ref) => {
361 ".into_owned().into()".to_string()
362 }
363 TypeRef::Named(_) if method.returns_cow || method.returns_ref => ".into_owned().into()".to_string(),
364 TypeRef::Named(n) if n == type_name => ".into()".to_string(),
365 TypeRef::Named(_) => ".into()".to_string(),
366 TypeRef::String => {
367 if method.returns_ref {
368 ".to_owned()".to_string()
369 } else {
370 String::new()
371 }
372 }
373 TypeRef::Path => {
374 if method.returns_ref {
375 ".to_owned()".to_string()
376 } else {
377 ".to_string_lossy().to_string()".to_string()
378 }
379 }
380 TypeRef::Bytes => ".to_vec()".to_string(),
383 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::Named(_)) => {
385 if method.returns_ref {
386 ".map(|v| v.clone().into())".to_string()
387 } else {
388 ".map(Into::into)".to_string()
389 }
390 }
391 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::String | TypeRef::Bytes) => {
393 if method.returns_ref {
394 ".map(|v| v.to_owned())".to_string()
395 } else {
396 String::new()
397 }
398 }
399 TypeRef::Primitive(p) => {
402 use crate::conversions::helpers::{needs_f64_cast, needs_i32_cast};
403 if cfg.cast_uints_to_i32 && needs_i32_cast(p) {
404 " as i32".to_string()
405 } else if cfg.cast_large_ints_to_f64 && needs_f64_cast(p) {
406 " as f64".to_string()
407 } else {
408 String::new()
409 }
410 }
411 TypeRef::Optional(inner) => {
413 if let TypeRef::Primitive(p) = inner.as_ref() {
414 use crate::conversions::helpers::{needs_f64_cast, needs_i32_cast};
415 if cfg.cast_uints_to_i32 && needs_i32_cast(p) {
416 ".map(|v| v as i32)".to_string()
417 } else if cfg.cast_large_ints_to_f64 && needs_f64_cast(p) {
418 ".map(|v| v as f64)".to_string()
419 } else {
420 String::new()
421 }
422 } else {
423 String::new()
424 }
425 }
426 _ => String::new(),
427 };
428 if method.error_type.is_some() {
429 let err_conv = match cfg.async_pattern {
430 AsyncPattern::Pyo3FutureIntoPy => {
431 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
432 }
433 AsyncPattern::NapiNativeAsync => {
434 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
435 }
436 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
437 _ => ".map_err(|e| e.to_string())",
438 };
439 format!(
440 "{field_conversions}let result = {core_call}{err_conv}?;\n Ok(result{newtype_suffix}{result_wrap})"
441 )
442 } else {
443 format!("{field_conversions}{core_call}{newtype_suffix}{result_wrap}")
444 }
445 } else if is_opaque
446 && !method.sanitized
447 && (!is_ref_mut_receiver || self_needs_mutex)
448 && (!is_owned_receiver || typ.is_clone)
449 && method.error_type.is_none()
450 && method
451 .params
452 .iter()
453 .all(|p| !p.sanitized && crate::shared::is_opaque_delegatable_type(&p.ty))
454 && matches!(&method.return_type, TypeRef::Named(n) if n == type_name)
455 {
456 let core_call = if is_owned_receiver {
459 if self_needs_mutex {
460 format!("self.inner.lock().unwrap().clone().{}({call_args})", method.name)
461 } else {
462 format!("(*self.inner).clone().{}({call_args})", method.name)
463 }
464 } else if self_needs_mutex {
465 format!("self.inner.lock().unwrap().{}({call_args})", method.name)
466 } else {
467 format!("self.inner.{}({call_args})", method.name)
468 };
469 let unwrapped = apply_return_newtype_unwrap(&core_call, &method.return_newtype_wrapper);
470 let arc_expr = if self_needs_mutex {
471 format!("Arc::new(std::sync::Mutex::new({unwrapped}))")
472 } else {
473 format!("Arc::new({unwrapped})")
474 };
475 format!("Self {{ inner: {arc_expr} }}")
476 } else if !is_opaque
477 && !method.sanitized
478 && !is_ref_mut_receiver
479 && (!is_owned_receiver || typ.is_clone)
480 && method.error_type.is_none()
481 && method
482 .params
483 .iter()
484 .all(|p| !p.sanitized && is_simple_non_opaque_param(&p.ty))
485 && matches!(&method.return_type, TypeRef::Named(n) if n == type_name)
486 {
487 let is_ref_mut = matches!(method.receiver.as_ref(), Some(alef_core::ir::ReceiverKind::RefMut));
490 let field_conversions = if is_ref_mut {
491 gen_lossy_binding_to_core_fields_mut(
492 typ,
493 cfg.core_import,
494 cfg.option_duration_on_defaults,
495 opaque_types,
496 cfg.cast_uints_to_i32,
497 cfg.cast_large_ints_to_f64,
498 cfg.lossy_skip_types,
499 )
500 } else {
501 gen_lossy_binding_to_core_fields(
502 typ,
503 cfg.core_import,
504 cfg.option_duration_on_defaults,
505 opaque_types,
506 cfg.cast_uints_to_i32,
507 cfg.cast_large_ints_to_f64,
508 cfg.lossy_skip_types,
509 )
510 };
511 let core_call = format!("core_self.{}({call_args})", method.name);
512 let newtype_suffix = if method.return_newtype_wrapper.is_some() {
513 ".0"
514 } else {
515 ""
516 };
517 let result_wrap = if method.returns_cow || method.returns_ref {
518 ".into_owned().into()"
519 } else {
520 ".into()"
521 };
522 format!("{field_conversions}{core_call}{newtype_suffix}{result_wrap}")
523 } else {
524 gen_unimplemented_body(
525 &method.return_type,
526 &format!("{type_name}.{}", method.name),
527 method.error_type.is_some(),
528 cfg,
529 &method.params,
530 opaque_types,
531 )
532 }
533 } else if method.is_async {
534 let inner_clone_line = if is_opaque {
535 "let inner = self.inner.clone();\n "
536 } else {
537 ""
538 };
539 let core_call_str = make_async_core_call(&method.name);
540 gen_async_body(
541 &core_call_str,
542 cfg,
543 method.error_type.is_some(),
544 &async_result_wrap,
545 is_opaque,
546 inner_clone_line,
547 matches!(method.return_type, TypeRef::Unit),
548 Some(&return_type),
549 )
550 } else {
551 let core_call = make_core_call(&method.name);
552 if method.error_type.is_some() {
553 let err_conv = match cfg.async_pattern {
555 AsyncPattern::Pyo3FutureIntoPy => {
556 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
557 }
558 AsyncPattern::NapiNativeAsync => {
559 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
560 }
561 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
562 _ => ".map_err(|e| e.to_string())",
563 };
564 if is_opaque {
565 if matches!(method.return_type, TypeRef::Unit) {
566 format!("{core_call}{err_conv}?;\n Ok(())")
568 } else {
569 let wrap = wrap_return_with_mutex(
570 &result_expr,
571 &method.return_type,
572 type_name,
573 opaque_types,
574 mutex_types,
575 is_opaque,
576 method.returns_ref,
577 method.returns_cow,
578 );
579 format!("let result = {core_call}{err_conv}?;\n Ok({wrap})")
580 }
581 } else {
582 format!("{core_call}{err_conv}")
583 }
584 } else if is_opaque {
585 let unwrapped_call = apply_return_newtype_unwrap(&core_call, &method.return_newtype_wrapper);
586 wrap_return_with_mutex(
587 &unwrapped_call,
588 &method.return_type,
589 type_name,
590 opaque_types,
591 mutex_types,
592 is_opaque,
593 method.returns_ref,
594 method.returns_cow,
595 )
596 } else {
597 core_call
598 }
599 };
600 let adapter_key = format!("{}.{}", type_name, method.name);
601 let has_adapter = adapter_bodies.contains_key(&adapter_key);
602
603 let body = if ref_let_bindings.is_empty() || has_adapter {
609 body
610 } else {
611 format!("{ref_let_bindings}{body}")
612 };
613
614 let needs_py = method.is_async && cfg.async_pattern == AsyncPattern::Pyo3FutureIntoPy;
615
616 let body = if needs_py && !opaque_can_delegate && !has_adapter {
623 let err_msg = format!("Not implemented: {type_name}.{}", method.name);
624 let suppress = if method.params.is_empty() {
626 String::new()
627 } else {
628 let names: Vec<&str> = method.params.iter().map(|p| p.name.as_str()).collect();
629 if names.len() == 1 {
630 format!("let _ = {};\n ", names[0])
631 } else {
632 format!("let _ = ({});\n ", names.join(", "))
633 }
634 };
635 format!("{suppress}Err(pyo3::exceptions::PyNotImplementedError::new_err(\"{err_msg}\"))")
636 } else {
637 body
638 };
639 let self_param = match (needs_py, params.is_empty()) {
640 (true, true) => "&self, py: Python<'py>",
641 (true, false) => "&self, py: Python<'py>, ",
642 (false, true) => "&self",
643 (false, false) => "&self, ",
644 };
645
646 let ret = if needs_py {
651 "PyResult<Bound<'py, PyAny>>".to_string()
652 } else if is_functional_ref_mut {
653 mapper.wrap_return("Self", method.error_type.is_some())
654 } else {
655 ret
656 };
657 let method_lifetime = if needs_py { "<'py>" } else { "" };
658
659 let (sig_start, sig_params, sig_end) = if self_param.len() + params.len() > 100 {
661 let wrapped_params = method
662 .params
663 .iter()
664 .map(|p| {
665 let ty = if p.optional {
666 format!("Option<{}>", mapper.map_type(&p.ty))
667 } else {
668 mapper.map_type(&p.ty)
669 };
670 format!("{}: {}", p.name, ty)
671 })
672 .collect::<Vec<_>>()
673 .join(",\n ");
674 let py_param = if needs_py { "\n py: Python<'py>," } else { "" };
675 (
676 format!(
677 "pub fn {}{method_lifetime}(\n &self,{}\n ",
678 method.name, py_param
679 ),
680 wrapped_params,
681 "\n ) -> ".to_string(),
682 )
683 } else {
684 (
685 format!("pub fn {}{method_lifetime}({}", method.name, self_param),
686 params,
687 ") -> ".to_string(),
688 )
689 };
690
691 let mut out = String::with_capacity(1024);
692 let total_params = method.params.len() + 1 + if needs_py { 1 } else { 0 };
694 if total_params > 7 {
695 writeln!(out, " #[allow(clippy::too_many_arguments)]").ok();
696 }
697 if method.error_type.is_some() {
699 writeln!(out, " #[allow(clippy::missing_errors_doc)]").ok();
700 }
701 if is_trait_method_name(&method.name) {
703 writeln!(out, " #[allow(clippy::should_implement_trait)]").ok();
704 }
705 if cfg.needs_signature {
706 let sig = function_sig_defaults(&method.params);
707 writeln!(out, " {}{}{}", cfg.signature_prefix, sig, cfg.signature_suffix).ok();
708 }
709 write!(
710 out,
711 " {}{}{}{} {{\n \
712 {body}\n }}",
713 sig_start, sig_params, sig_end, ret,
714 )
715 .ok();
716 out
717}
718
719pub fn gen_static_method(
721 method: &MethodDef,
722 mapper: &dyn TypeMapper,
723 cfg: &RustBindingConfig,
724 typ: &TypeDef,
725 adapter_bodies: &AdapterBodies,
726 opaque_types: &AHashSet<String>,
727 mutex_types: &AHashSet<String>,
728) -> String {
729 let type_name = &typ.name;
730 let core_type_path = typ.rust_path.replace('-', "_");
732 let map_fn = |ty: &alef_core::ir::TypeRef| mapper.map_type(ty);
733 let params = function_params(&method.params, &map_fn);
734 let return_type = mapper.map_type(&method.return_type);
735 let ret = mapper.wrap_return(&return_type, method.error_type.is_some());
736
737 let core_import = cfg.core_import;
738
739 let use_let_bindings = has_named_params(&method.params, opaque_types);
742 let (call_args, ref_let_bindings) = if use_let_bindings {
743 (
744 gen_call_args_with_let_bindings(&method.params, opaque_types),
745 gen_named_let_bindings_pub(&method.params, opaque_types, core_import),
746 )
747 } else {
748 (gen_call_args(&method.params, opaque_types), String::new())
749 };
750
751 let can_delegate = crate::shared::can_auto_delegate(method, opaque_types);
752
753 let body = if !can_delegate {
754 let adapter_key = format!("{}.{}", type_name, method.name);
756 if let Some(adapter_body) = adapter_bodies.get(&adapter_key) {
757 adapter_body.clone()
758 } else {
759 gen_unimplemented_body(
760 &method.return_type,
761 &format!("{type_name}::{}", method.name),
762 method.error_type.is_some(),
763 cfg,
764 &method.params,
765 opaque_types,
766 )
767 }
768 } else if method.is_async {
769 let core_call = format!("{core_type_path}::{}({call_args})", method.name);
770 let return_wrap = format!("{return_type}::from(result)");
771 gen_async_body(
772 &core_call,
773 cfg,
774 method.error_type.is_some(),
775 &return_wrap,
776 false,
777 "",
778 matches!(method.return_type, TypeRef::Unit),
779 Some(&return_type),
780 )
781 } else {
782 let core_call = format!("{core_type_path}::{}({call_args})", method.name);
783 if method.error_type.is_some() {
784 let err_conv = match cfg.async_pattern {
786 AsyncPattern::Pyo3FutureIntoPy => {
787 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
788 }
789 AsyncPattern::NapiNativeAsync => {
790 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
791 }
792 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
793 _ => ".map_err(|e| e.to_string())",
794 };
795 let val_expr = apply_return_newtype_unwrap("val", &method.return_newtype_wrapper);
797 let wrapped = wrap_return_with_mutex(
798 &val_expr,
799 &method.return_type,
800 type_name,
801 opaque_types,
802 mutex_types,
803 typ.is_opaque,
804 method.returns_ref,
805 method.returns_cow,
806 );
807 if wrapped == val_expr {
808 format!("{core_call}{err_conv}")
809 } else if wrapped == format!("{val_expr}.into()") {
810 format!("{core_call}.map(Into::into){err_conv}")
811 } else if let Some(type_path) = wrapped.strip_suffix(&format!("::from({val_expr})")) {
812 format!("{core_call}.map({type_path}::from){err_conv}")
813 } else {
814 format!("{core_call}.map(|val| {wrapped}){err_conv}")
815 }
816 } else {
817 let unwrapped_call = apply_return_newtype_unwrap(&core_call, &method.return_newtype_wrapper);
819 wrap_return_with_mutex(
820 &unwrapped_call,
821 &method.return_type,
822 type_name,
823 opaque_types,
824 mutex_types,
825 typ.is_opaque,
826 method.returns_ref,
827 method.returns_cow,
828 )
829 }
830 };
831 let body = if ref_let_bindings.is_empty() {
833 body
834 } else {
835 format!("{ref_let_bindings}{body}")
836 };
837
838 let static_needs_py = method.is_async && cfg.async_pattern == AsyncPattern::Pyo3FutureIntoPy;
839
840 let ret = if static_needs_py {
842 "PyResult<Bound<'py, PyAny>>".to_string()
843 } else {
844 ret
845 };
846 let method_lifetime = if static_needs_py { "<'py>" } else { "" };
847
848 let (sig_start, sig_params, sig_end) = if params.len() > 100 {
850 let wrapped_params = method
851 .params
852 .iter()
853 .map(|p| {
854 let ty = if p.optional {
855 format!("Option<{}>", mapper.map_type(&p.ty))
856 } else {
857 mapper.map_type(&p.ty)
858 };
859 format!("{}: {}", p.name, ty)
860 })
861 .collect::<Vec<_>>()
862 .join(",\n ");
863 if static_needs_py {
865 (
866 format!("pub fn {}{method_lifetime}(py: Python<'py>,\n ", method.name),
867 wrapped_params,
868 "\n ) -> ".to_string(),
869 )
870 } else {
871 (
872 format!("pub fn {}(\n ", method.name),
873 wrapped_params,
874 "\n ) -> ".to_string(),
875 )
876 }
877 } else if static_needs_py {
878 (
879 format!("pub fn {}{method_lifetime}(py: Python<'py>, ", method.name),
880 params,
881 ") -> ".to_string(),
882 )
883 } else {
884 (format!("pub fn {}(", method.name), params, ") -> ".to_string())
885 };
886
887 let mut out = String::with_capacity(1024);
888 let total_params = method.params.len() + if static_needs_py { 1 } else { 0 };
890 if total_params > 7 {
891 writeln!(out, " #[allow(clippy::too_many_arguments)]").ok();
892 }
893 if method.error_type.is_some() {
895 writeln!(out, " #[allow(clippy::missing_errors_doc)]").ok();
896 }
897 if is_trait_method_name(&method.name) {
899 writeln!(out, " #[allow(clippy::should_implement_trait)]").ok();
900 }
901 if let Some(attr) = cfg.static_attr {
902 writeln!(out, " #[{attr}]").ok();
903 }
904 if cfg.needs_signature {
905 let sig = function_sig_defaults(&method.params);
906 writeln!(out, " {}{}{}", cfg.signature_prefix, sig, cfg.signature_suffix).ok();
907 }
908 write!(
909 out,
910 " {}{}{}{} {{\n \
911 {body}\n }}",
912 sig_start, sig_params, sig_end, ret,
913 )
914 .ok();
915 out
916}
917
918pub fn gen_impl_block(
920 typ: &TypeDef,
921 mapper: &dyn TypeMapper,
922 cfg: &RustBindingConfig,
923 adapter_bodies: &AdapterBodies,
924 opaque_types: &AHashSet<String>,
925) -> String {
926 gen_impl_block_with_renames(typ, mapper, cfg, adapter_bodies, opaque_types, None)
927}
928
929pub fn gen_impl_block_with_renames(
931 typ: &TypeDef,
932 mapper: &dyn TypeMapper,
933 cfg: &RustBindingConfig,
934 adapter_bodies: &AdapterBodies,
935 opaque_types: &AHashSet<String>,
936 field_renames: Option<&std::collections::HashMap<String, String>>,
937) -> String {
938 let (instance, statics) = partition_methods(&typ.methods);
939 let has_emittable_instance = instance
943 .iter()
944 .any(|m| !m.sanitized || adapter_bodies.contains_key(&format!("{}.{}", typ.name, m.name)));
945 let has_emittable_statics = statics
946 .iter()
947 .any(|m| !m.sanitized || adapter_bodies.contains_key(&format!("{}.{}", typ.name, m.name)));
948 if !has_emittable_instance && !has_emittable_statics && typ.fields.is_empty() {
949 return String::new();
950 }
951
952 let prefixed_name = format!("{}{}", cfg.type_name_prefix, typ.name);
953 let mut out = String::with_capacity(2048);
954 if let Some(block_attr) = cfg.method_block_attr {
955 writeln!(out, "#[{block_attr}]").ok();
956 }
957 writeln!(out, "impl {prefixed_name} {{").ok();
958
959 if !typ.fields.is_empty() && !cfg.skip_impl_constructor {
962 out.push_str(&gen_constructor_with_renames(typ, mapper, cfg, field_renames));
963 out.push_str("\n\n");
964 }
965
966 let empty_mutex_types: AHashSet<String> = AHashSet::new();
968 for m in &instance {
969 let adapter_key = format!("{}.{}", typ.name, m.name);
973 if m.sanitized && !adapter_bodies.contains_key(&adapter_key) {
974 continue;
975 }
976 out.push_str(&gen_method(
977 m,
978 mapper,
979 cfg,
980 typ,
981 false,
982 opaque_types,
983 &empty_mutex_types,
984 adapter_bodies,
985 ));
986 out.push_str("\n\n");
987 }
988
989 for m in &statics {
991 let adapter_key = format!("{}.{}", typ.name, m.name);
993 if m.sanitized && !adapter_bodies.contains_key(&adapter_key) {
994 continue;
995 }
996 out.push_str(&gen_static_method(
997 m,
998 mapper,
999 cfg,
1000 typ,
1001 adapter_bodies,
1002 opaque_types,
1003 &empty_mutex_types,
1004 ));
1005 out.push_str("\n\n");
1006 }
1007
1008 let trimmed = out.trim_end();
1010 let mut result = trimmed.to_string();
1011 result.push_str("\n}");
1012 result
1013}
1014
1015pub fn gen_opaque_impl_block(
1022 typ: &TypeDef,
1023 mapper: &dyn TypeMapper,
1024 cfg: &RustBindingConfig,
1025 opaque_types: &AHashSet<String>,
1026 mutex_types: &AHashSet<String>,
1027 adapter_bodies: &AdapterBodies,
1028) -> String {
1029 let (instance, statics) = partition_methods(&typ.methods);
1030 let has_emittable_instance = instance
1032 .iter()
1033 .any(|m| !m.sanitized || adapter_bodies.contains_key(&format!("{}.{}", typ.name, m.name)));
1034 let has_emittable_statics = statics
1035 .iter()
1036 .any(|m| !m.sanitized || adapter_bodies.contains_key(&format!("{}.{}", typ.name, m.name)));
1037 if !has_emittable_instance && !has_emittable_statics {
1038 return String::new();
1039 }
1040
1041 let mut out = String::with_capacity(2048);
1042 let prefixed_name = format!("{}{}", cfg.type_name_prefix, typ.name);
1043 if let Some(block_attr) = cfg.method_block_attr {
1044 writeln!(out, "#[{block_attr}]").ok();
1045 }
1046 writeln!(out, "impl {prefixed_name} {{").ok();
1047
1048 for m in &instance {
1050 let adapter_key = format!("{}.{}", typ.name, m.name);
1053 if m.sanitized && !adapter_bodies.contains_key(&adapter_key) {
1054 continue;
1055 }
1056 out.push_str(&gen_method(
1057 m,
1058 mapper,
1059 cfg,
1060 typ,
1061 true,
1062 opaque_types,
1063 mutex_types,
1064 adapter_bodies,
1065 ));
1066 out.push_str("\n\n");
1067 }
1068
1069 for m in &statics {
1071 let adapter_key = format!("{}.{}", typ.name, m.name);
1073 if m.sanitized && !adapter_bodies.contains_key(&adapter_key) {
1074 continue;
1075 }
1076 out.push_str(&gen_static_method(
1077 m,
1078 mapper,
1079 cfg,
1080 typ,
1081 adapter_bodies,
1082 opaque_types,
1083 mutex_types,
1084 ));
1085 out.push_str("\n\n");
1086 }
1087
1088 let trimmed = out.trim_end();
1089 let mut result = trimmed.to_string();
1090 result.push_str("\n}");
1091 result
1092}