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_mapped,
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};
12
13pub fn is_trait_method_name(name: &str) -> bool {
16 crate::generators::TRAIT_METHOD_NAMES.contains(&name)
17}
18
19pub fn gen_constructor(typ: &TypeDef, mapper: &dyn TypeMapper, cfg: &RustBindingConfig) -> String {
21 gen_constructor_with_renames(typ, mapper, cfg, None)
22}
23
24pub fn gen_constructor_with_renames(
26 typ: &TypeDef,
27 mapper: &dyn TypeMapper,
28 cfg: &RustBindingConfig,
29 field_renames: Option<&std::collections::HashMap<String, String>>,
30) -> String {
31 let map_fn = |ty: &alef_core::ir::TypeRef| mapper.map_type(ty);
32
33 let (param_list, sig_defaults, assignments) = if typ.has_default {
35 crate::shared::config_constructor_parts_with_renames_and_cfg_restore(
36 &typ.fields,
37 &map_fn,
38 cfg.option_duration_on_defaults,
39 field_renames,
40 cfg.never_skip_cfg_field_names,
41 )
42 } else {
43 crate::shared::constructor_parts_with_renames_and_cfg_restore(
44 &typ.fields,
45 &map_fn,
46 field_renames,
47 cfg.never_skip_cfg_field_names,
48 )
49 };
50
51 crate::template_env::render(
52 "generators/methods/constructor.jinja",
53 minijinja::context! {
54 has_too_many_args => typ.fields.len() > 7,
55 needs_signature => cfg.needs_signature,
56 signature_prefix => cfg.signature_prefix,
57 sig_defaults => sig_defaults,
58 signature_suffix => cfg.signature_suffix,
59 constructor_attr => cfg.constructor_attr,
60 param_list => param_list,
61 assignments => assignments,
62 },
63 )
64}
65
66#[allow(clippy::too_many_arguments)]
75pub fn gen_method(
76 method: &MethodDef,
77 mapper: &dyn TypeMapper,
78 cfg: &RustBindingConfig,
79 typ: &TypeDef,
80 is_opaque: bool,
81 opaque_types: &AHashSet<String>,
82 mutex_types: &AHashSet<String>,
83 adapter_bodies: &AdapterBodies,
84) -> String {
85 let type_name = &typ.name;
86 let core_type_path = typ.rust_path.replace('-', "_");
88
89 let map_fn = |ty: &alef_core::ir::TypeRef| mapper.map_type(ty);
90 let params = function_params(&method.params, &map_fn);
91 let return_type = mapper.map_type(&method.return_type);
92 let ret = mapper.wrap_return(&return_type, method.error_type.is_some());
93
94 let core_import = cfg.core_import;
95
96 let has_ref_named_params = has_named_params(&method.params, opaque_types);
100 let (call_args, ref_let_bindings) = if has_ref_named_params {
101 (
102 gen_call_args_with_let_bindings(&method.params, opaque_types),
103 gen_named_let_bindings_pub(&method.params, opaque_types, core_import),
104 )
105 } else if cfg.cast_uints_to_i32 || cfg.cast_large_ints_to_f64 {
106 (
108 gen_call_args_cfg(
109 &method.params,
110 opaque_types,
111 cfg.cast_uints_to_i32,
112 cfg.cast_large_ints_to_f64,
113 ),
114 String::new(),
115 )
116 } else {
117 (gen_call_args(&method.params, opaque_types), String::new())
118 };
119
120 let is_owned_receiver = matches!(method.receiver.as_ref(), Some(alef_core::ir::ReceiverKind::Owned));
121 let is_ref_mut_receiver = matches!(method.receiver.as_ref(), Some(alef_core::ir::ReceiverKind::RefMut));
122
123 let is_functional_ref_mut = !is_opaque
130 && is_ref_mut_receiver
131 && !method.sanitized
132 && method.trait_source.is_none()
133 && method
134 .params
135 .iter()
136 .all(|p| !p.sanitized && crate::shared::is_delegatable_param(&p.ty, opaque_types));
137
138 let is_trait_method = method.trait_source.is_some();
141
142 let self_needs_mutex = is_opaque && mutex_types.contains(type_name.as_str());
144
145 let opaque_can_delegate = is_opaque
152 && !method.sanitized
153 && (!is_ref_mut_receiver || self_needs_mutex)
154 && !is_trait_method
155 && (!is_owned_receiver || typ.is_clone)
156 && method
157 .params
158 .iter()
159 .all(|p| !p.sanitized && crate::shared::is_opaque_delegatable_type(&p.ty))
160 && crate::shared::is_opaque_delegatable_type(&method.return_type);
161
162 let make_core_call = |method_name: &str| -> String {
166 if is_opaque {
167 if is_owned_receiver {
168 if self_needs_mutex {
171 format!("self.inner.lock().unwrap().clone().{method_name}({call_args})")
172 } else {
173 format!("(*self.inner).clone().{method_name}({call_args})")
174 }
175 } else if self_needs_mutex {
176 format!("self.inner.lock().unwrap().{method_name}({call_args})")
178 } else {
179 format!("self.inner.{method_name}({call_args})")
180 }
181 } else {
182 format!("{core_type_path}::from(self.clone()).{method_name}({call_args})")
183 }
184 };
185
186 let make_async_core_call = |method_name: &str| -> String {
189 if is_opaque {
190 if self_needs_mutex {
191 format!("inner.lock().unwrap().{method_name}({call_args})")
192 } else {
193 format!("inner.{method_name}({call_args})")
194 }
195 } else {
196 format!("{core_type_path}::from(self.clone()).{method_name}({call_args})")
197 }
198 };
199
200 let result_expr = apply_return_newtype_unwrap("result", &method.return_newtype_wrapper);
207 let async_result_wrap = if is_opaque {
208 wrap_return_with_mutex_mapped(
209 &result_expr,
210 &method.return_type,
211 type_name,
212 opaque_types,
213 mutex_types,
214 is_opaque,
215 method.returns_ref,
216 method.returns_cow,
217 mapper,
218 )
219 } else {
220 match &method.return_type {
223 TypeRef::Named(_) | TypeRef::Json => format!("{result_expr}.into()"),
224 _ => result_expr.clone(),
225 }
226 };
227
228 let body = if !opaque_can_delegate {
229 let adapter_key_inner = format!("{}.{}", type_name, method.name);
231 if let Some(adapter_body) = adapter_bodies.get(&adapter_key_inner) {
232 adapter_body.clone()
233 } else if cfg.has_serde
234 && is_opaque
235 && !method.sanitized
236 && !is_trait_method
237 && has_named_params(&method.params, opaque_types)
238 && method.error_type.is_some()
239 && crate::shared::is_opaque_delegatable_type(&method.return_type)
240 {
241 let err_conv = match cfg.async_pattern {
244 AsyncPattern::Pyo3FutureIntoPy => {
245 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
246 }
247 AsyncPattern::NapiNativeAsync => {
248 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
249 }
250 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
251 AsyncPattern::TokioBlockOn => {
252 ".map_err(|e| extendr_api::Error::Other(e.to_string().replace(\":\", \"_\").replace(\"/\", \"_\").replace(\"-\", \"_\").chars().take(255).collect::<String>()))"
253 }
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_mapped(
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 mapper,
277 );
278 format!("{serde_bindings}let result = {core_call}{err_conv}?;\n Ok({wrap})")
279 }
280 } else if is_functional_ref_mut {
281 let field_conversions = gen_lossy_binding_to_core_fields_mut(
290 typ,
291 cfg.core_import,
292 cfg.option_duration_on_defaults,
293 opaque_types,
294 cfg.cast_uints_to_i32,
295 cfg.cast_large_ints_to_f64,
296 cfg.lossy_skip_types,
297 );
298 let core_call = format!("core_self.{}({call_args})", method.name);
299 if method.error_type.is_some() {
300 let err_conv = match cfg.async_pattern {
301 AsyncPattern::Pyo3FutureIntoPy => {
302 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
303 }
304 AsyncPattern::NapiNativeAsync => {
305 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
306 }
307 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
308 AsyncPattern::TokioBlockOn => {
309 ".map_err(|e| extendr_api::Error::Other(e.to_string().replace(\":\", \"_\").replace(\"/\", \"_\").replace(\"-\", \"_\").chars().take(255).collect::<String>()))"
310 }
311 _ => ".map_err(|e| e.to_string())",
312 };
313 format!("{field_conversions}{core_call}{err_conv}?;\n Ok(core_self.into())")
314 } else {
315 format!("{field_conversions}{core_call};\n core_self.into()")
316 }
317 } else if !is_opaque
318 && !method.sanitized
319 && method
320 .params
321 .iter()
322 .all(|p| !p.sanitized && is_simple_non_opaque_param(&p.ty))
323 && crate::shared::is_delegatable_return(&method.return_type)
324 {
325 let is_ref_mut = matches!(method.receiver.as_ref(), Some(alef_core::ir::ReceiverKind::RefMut));
328 let field_conversions = if is_ref_mut {
329 gen_lossy_binding_to_core_fields_mut(
330 typ,
331 cfg.core_import,
332 cfg.option_duration_on_defaults,
333 opaque_types,
334 cfg.cast_uints_to_i32,
335 cfg.cast_large_ints_to_f64,
336 cfg.lossy_skip_types,
337 )
338 } else {
339 gen_lossy_binding_to_core_fields(
340 typ,
341 cfg.core_import,
342 cfg.option_duration_on_defaults,
343 opaque_types,
344 cfg.cast_uints_to_i32,
345 cfg.cast_large_ints_to_f64,
346 cfg.lossy_skip_types,
347 )
348 };
349 let core_call = format!("core_self.{}({call_args})", method.name);
350 let newtype_suffix = if method.return_newtype_wrapper.is_some() {
351 ".0"
352 } else {
353 ""
354 };
355 let result_wrap = match &method.return_type {
356 TypeRef::Named(n) if n == type_name && (method.returns_cow || method.returns_ref) => {
360 ".into_owned().into()".to_string()
361 }
362 TypeRef::Named(_) if method.returns_cow || method.returns_ref => ".into_owned().into()".to_string(),
363 TypeRef::Named(n) if n == type_name => ".into()".to_string(),
364 TypeRef::Named(_) => ".into()".to_string(),
365 TypeRef::String => {
366 if method.returns_ref {
367 ".to_owned()".to_string()
368 } else {
369 String::new()
370 }
371 }
372 TypeRef::Path => {
373 if method.returns_ref {
374 ".to_owned()".to_string()
375 } else {
376 ".to_string_lossy().to_string()".to_string()
377 }
378 }
379 TypeRef::Bytes => ".to_vec()".to_string(),
382 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::Named(_)) => {
384 if method.returns_ref {
385 ".map(|v| v.clone().into())".to_string()
386 } else {
387 ".map(Into::into)".to_string()
388 }
389 }
390 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::String | TypeRef::Bytes) => {
392 if method.returns_ref {
393 ".map(|v| v.to_owned())".to_string()
394 } else {
395 String::new()
396 }
397 }
398 TypeRef::Primitive(p) => {
401 use crate::conversions::helpers::{needs_f64_cast, needs_i32_cast};
402 if cfg.cast_uints_to_i32 && needs_i32_cast(p) {
403 " as i32".to_string()
404 } else if cfg.cast_large_ints_to_f64 && needs_f64_cast(p) {
405 " as f64".to_string()
406 } else {
407 String::new()
408 }
409 }
410 TypeRef::Optional(inner) => {
412 if let TypeRef::Primitive(p) = inner.as_ref() {
413 use crate::conversions::helpers::{needs_f64_cast, needs_i32_cast};
414 if cfg.cast_uints_to_i32 && needs_i32_cast(p) {
415 ".map(|v| v as i32)".to_string()
416 } else if cfg.cast_large_ints_to_f64 && needs_f64_cast(p) {
417 ".map(|v| v as f64)".to_string()
418 } else {
419 String::new()
420 }
421 } else {
422 String::new()
423 }
424 }
425 _ => String::new(),
426 };
427 if method.error_type.is_some() {
428 let err_conv = match cfg.async_pattern {
429 AsyncPattern::Pyo3FutureIntoPy => {
430 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
431 }
432 AsyncPattern::NapiNativeAsync => {
433 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
434 }
435 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
436 _ => ".map_err(|e| e.to_string())",
437 };
438 format!(
439 "{field_conversions}let result = {core_call}{err_conv}?;\n Ok(result{newtype_suffix}{result_wrap})"
440 )
441 } else {
442 format!("{field_conversions}{core_call}{newtype_suffix}{result_wrap}")
443 }
444 } else if is_opaque
445 && !method.sanitized
446 && (!is_ref_mut_receiver || self_needs_mutex)
447 && (!is_owned_receiver || typ.is_clone)
448 && method.error_type.is_none()
449 && method
450 .params
451 .iter()
452 .all(|p| !p.sanitized && crate::shared::is_opaque_delegatable_type(&p.ty))
453 && matches!(&method.return_type, TypeRef::Named(n) if n == type_name)
454 {
455 let core_call = if is_owned_receiver {
458 if self_needs_mutex {
459 format!("self.inner.lock().unwrap().clone().{}({call_args})", method.name)
460 } else {
461 format!("(*self.inner).clone().{}({call_args})", method.name)
462 }
463 } else if self_needs_mutex {
464 format!("self.inner.lock().unwrap().{}({call_args})", method.name)
465 } else {
466 format!("self.inner.{}({call_args})", method.name)
467 };
468 let unwrapped = apply_return_newtype_unwrap(&core_call, &method.return_newtype_wrapper);
469 let arc_expr = if self_needs_mutex {
470 format!("Arc::new(std::sync::Mutex::new({unwrapped}))")
471 } else {
472 format!("Arc::new({unwrapped})")
473 };
474 format!("Self {{ inner: {arc_expr} }}")
475 } else if !is_opaque
476 && !method.sanitized
477 && !is_ref_mut_receiver
478 && (!is_owned_receiver || typ.is_clone)
479 && method.error_type.is_none()
480 && method
481 .params
482 .iter()
483 .all(|p| !p.sanitized && is_simple_non_opaque_param(&p.ty))
484 && matches!(&method.return_type, TypeRef::Named(n) if n == type_name)
485 {
486 let is_ref_mut = matches!(method.receiver.as_ref(), Some(alef_core::ir::ReceiverKind::RefMut));
489 let field_conversions = if is_ref_mut {
490 gen_lossy_binding_to_core_fields_mut(
491 typ,
492 cfg.core_import,
493 cfg.option_duration_on_defaults,
494 opaque_types,
495 cfg.cast_uints_to_i32,
496 cfg.cast_large_ints_to_f64,
497 cfg.lossy_skip_types,
498 )
499 } else {
500 gen_lossy_binding_to_core_fields(
501 typ,
502 cfg.core_import,
503 cfg.option_duration_on_defaults,
504 opaque_types,
505 cfg.cast_uints_to_i32,
506 cfg.cast_large_ints_to_f64,
507 cfg.lossy_skip_types,
508 )
509 };
510 let core_call = format!("core_self.{}({call_args})", method.name);
511 let newtype_suffix = if method.return_newtype_wrapper.is_some() {
512 ".0"
513 } else {
514 ""
515 };
516 let result_wrap = if method.returns_cow || method.returns_ref {
517 ".into_owned().into()"
518 } else {
519 ".into()"
520 };
521 format!("{field_conversions}{core_call}{newtype_suffix}{result_wrap}")
522 } else {
523 gen_unimplemented_body(
524 &method.return_type,
525 &format!("{type_name}.{}", method.name),
526 method.error_type.is_some(),
527 cfg,
528 &method.params,
529 opaque_types,
530 )
531 }
532 } else if method.is_async {
533 let inner_clone_line = if is_opaque {
534 "let inner = self.inner.clone();\n "
535 } else {
536 ""
537 };
538 let core_call_str = make_async_core_call(&method.name);
539 gen_async_body(
540 &core_call_str,
541 cfg,
542 method.error_type.is_some(),
543 &async_result_wrap,
544 is_opaque,
545 inner_clone_line,
546 matches!(method.return_type, TypeRef::Unit),
547 Some(&return_type),
548 )
549 } else {
550 let core_call = make_core_call(&method.name);
551 if method.error_type.is_some() {
552 let err_conv = match cfg.async_pattern {
554 AsyncPattern::Pyo3FutureIntoPy => {
555 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
556 }
557 AsyncPattern::NapiNativeAsync => {
558 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
559 }
560 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
561 _ => ".map_err(|e| e.to_string())",
562 };
563 if is_opaque {
564 if matches!(method.return_type, TypeRef::Unit) {
565 format!("{core_call}{err_conv}?;\n Ok(())")
567 } else {
568 let wrap = wrap_return_with_mutex_mapped(
569 &result_expr,
570 &method.return_type,
571 type_name,
572 opaque_types,
573 mutex_types,
574 is_opaque,
575 method.returns_ref,
576 method.returns_cow,
577 mapper,
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_mapped(
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 mapper,
596 )
597 } else {
598 core_call
599 }
600 };
601 let adapter_key = format!("{}.{}", type_name, method.name);
602 let has_adapter = adapter_bodies.contains_key(&adapter_key);
603
604 let body = if ref_let_bindings.is_empty() || has_adapter {
610 body
611 } else {
612 format!("{ref_let_bindings}{body}")
613 };
614
615 let needs_py = method.is_async && cfg.async_pattern == AsyncPattern::Pyo3FutureIntoPy;
616
617 let body = if needs_py && !opaque_can_delegate && !has_adapter {
624 let err_msg = format!("Not implemented: {type_name}.{}", method.name);
625 let suppress = if method.params.is_empty() {
627 String::new()
628 } else {
629 let names: Vec<&str> = method.params.iter().map(|p| p.name.as_str()).collect();
630 if names.len() == 1 {
631 format!("let _ = {};\n ", names[0])
632 } else {
633 format!("let _ = ({});\n ", names.join(", "))
634 }
635 };
636 format!("{suppress}Err(pyo3::exceptions::PyNotImplementedError::new_err(\"{err_msg}\"))")
637 } else {
638 body
639 };
640 let self_param = match (needs_py, params.is_empty()) {
641 (true, true) => "&self, py: Python<'py>",
642 (true, false) => "&self, py: Python<'py>, ",
643 (false, true) => "&self",
644 (false, false) => "&self, ",
645 };
646
647 let ret = if needs_py {
652 "PyResult<Bound<'py, PyAny>>".to_string()
653 } else if is_functional_ref_mut {
654 mapper.wrap_return("Self", method.error_type.is_some())
655 } else {
656 ret
657 };
658 let method_lifetime = if needs_py { "<'py>" } else { "" };
659
660 let (sig_start, sig_params, sig_end) = if self_param.len() + params.len() > 100 {
662 let wrapped_params = method
663 .params
664 .iter()
665 .map(|p| {
666 let ty = if p.optional {
667 format!("Option<{}>", mapper.map_type(&p.ty))
668 } else {
669 mapper.map_type(&p.ty)
670 };
671 format!("{}: {}", p.name, ty)
672 })
673 .collect::<Vec<_>>()
674 .join(",\n ");
675 let py_param = if needs_py { "\n py: Python<'py>," } else { "" };
676 (
677 format!(
678 "pub fn {}{method_lifetime}(\n &self,{}\n ",
679 method.name, py_param
680 ),
681 wrapped_params,
682 "\n ) -> ".to_string(),
683 )
684 } else {
685 (
686 format!("pub fn {}{method_lifetime}({}", method.name, self_param),
687 params,
688 ") -> ".to_string(),
689 )
690 };
691
692 let total_params = method.params.len() + 1 + if needs_py { 1 } else { 0 };
693 let sig_defaults = if cfg.needs_signature {
694 function_sig_defaults(&method.params)
695 } else {
696 String::new()
697 };
698
699 crate::template_env::render(
700 "generators/methods/method_signature.jinja",
701 minijinja::context! {
702 has_too_many_arguments => total_params > 7,
703 has_missing_errors_doc => method.error_type.is_some(),
704 has_should_implement_trait => is_trait_method_name(&method.name),
705 needs_signature => cfg.needs_signature,
706 signature_prefix => cfg.signature_prefix,
707 sig_defaults => sig_defaults,
708 signature_suffix => cfg.signature_suffix,
709 sig_start => sig_start,
710 sig_params => sig_params,
711 sig_end => sig_end,
712 ret => ret,
713 body => body,
714 },
715 )
716}
717
718pub fn gen_static_method(
720 method: &MethodDef,
721 mapper: &dyn TypeMapper,
722 cfg: &RustBindingConfig,
723 typ: &TypeDef,
724 adapter_bodies: &AdapterBodies,
725 opaque_types: &AHashSet<String>,
726 mutex_types: &AHashSet<String>,
727) -> String {
728 let type_name = &typ.name;
729 let core_type_path = typ.rust_path.replace('-', "_");
731 let map_fn = |ty: &alef_core::ir::TypeRef| mapper.map_type(ty);
732 let params = function_params(&method.params, &map_fn);
733 let return_type = mapper.map_type(&method.return_type);
734 let ret = mapper.wrap_return(&return_type, method.error_type.is_some());
735
736 let core_import = cfg.core_import;
737
738 let use_let_bindings = has_named_params(&method.params, opaque_types);
741 let (call_args, ref_let_bindings) = if use_let_bindings {
742 (
743 gen_call_args_with_let_bindings(&method.params, opaque_types),
744 gen_named_let_bindings_pub(&method.params, opaque_types, core_import),
745 )
746 } else {
747 (gen_call_args(&method.params, opaque_types), String::new())
748 };
749
750 let can_delegate = crate::shared::can_auto_delegate(method, opaque_types);
751
752 let body = if !can_delegate {
753 let adapter_key = format!("{}.{}", type_name, method.name);
755 if let Some(adapter_body) = adapter_bodies.get(&adapter_key) {
756 adapter_body.clone()
757 } else {
758 gen_unimplemented_body(
759 &method.return_type,
760 &format!("{type_name}::{}", method.name),
761 method.error_type.is_some(),
762 cfg,
763 &method.params,
764 opaque_types,
765 )
766 }
767 } else if method.is_async {
768 let core_call = format!("{core_type_path}::{}({call_args})", method.name);
769 let return_wrap = format!("{return_type}::from(result)");
770 gen_async_body(
771 &core_call,
772 cfg,
773 method.error_type.is_some(),
774 &return_wrap,
775 false,
776 "",
777 matches!(method.return_type, TypeRef::Unit),
778 Some(&return_type),
779 )
780 } else {
781 let core_call = format!("{core_type_path}::{}({call_args})", method.name);
782 if method.error_type.is_some() {
783 let err_conv = match cfg.async_pattern {
785 AsyncPattern::Pyo3FutureIntoPy => {
786 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
787 }
788 AsyncPattern::NapiNativeAsync => {
789 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
790 }
791 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
792 _ => ".map_err(|e| e.to_string())",
793 };
794 let val_expr = apply_return_newtype_unwrap("val", &method.return_newtype_wrapper);
796 let wrapped = wrap_return_with_mutex_mapped(
797 &val_expr,
798 &method.return_type,
799 type_name,
800 opaque_types,
801 mutex_types,
802 typ.is_opaque,
803 method.returns_ref,
804 method.returns_cow,
805 mapper,
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_mapped(
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 mapper,
829 )
830 }
831 };
832 let body = if ref_let_bindings.is_empty() {
834 body
835 } else {
836 format!("{ref_let_bindings}{body}")
837 };
838
839 let static_needs_py = method.is_async && cfg.async_pattern == AsyncPattern::Pyo3FutureIntoPy;
840
841 let ret = if static_needs_py {
843 "PyResult<Bound<'py, PyAny>>".to_string()
844 } else {
845 ret
846 };
847 let method_lifetime = if static_needs_py { "<'py>" } else { "" };
848
849 let (sig_start, sig_params, sig_end) = if params.len() > 100 {
851 let wrapped_params = method
852 .params
853 .iter()
854 .map(|p| {
855 let ty = if p.optional {
856 format!("Option<{}>", mapper.map_type(&p.ty))
857 } else {
858 mapper.map_type(&p.ty)
859 };
860 format!("{}: {}", p.name, ty)
861 })
862 .collect::<Vec<_>>()
863 .join(",\n ");
864 if static_needs_py {
866 (
867 format!("pub fn {}{method_lifetime}(py: Python<'py>,\n ", method.name),
868 wrapped_params,
869 "\n ) -> ".to_string(),
870 )
871 } else {
872 (
873 format!("pub fn {}(\n ", method.name),
874 wrapped_params,
875 "\n ) -> ".to_string(),
876 )
877 }
878 } else if static_needs_py {
879 (
880 format!("pub fn {}{method_lifetime}(py: Python<'py>, ", method.name),
881 params,
882 ") -> ".to_string(),
883 )
884 } else {
885 (format!("pub fn {}(", method.name), params, ") -> ".to_string())
886 };
887
888 let total_params = method.params.len() + if static_needs_py { 1 } else { 0 };
889 let sig_defaults = if cfg.needs_signature {
890 function_sig_defaults(&method.params)
891 } else {
892 String::new()
893 };
894 let static_attr_str = if let Some(attr) = cfg.static_attr {
895 format!("#[{attr}]")
896 } else {
897 String::new()
898 };
899
900 let mut out = String::with_capacity(1024);
903 if total_params > 7 {
904 out.push_str(" #[allow(clippy::too_many_arguments)]\n");
905 }
906 if method.error_type.is_some() {
907 out.push_str(" #[allow(clippy::missing_errors_doc)]\n");
908 }
909 if is_trait_method_name(&method.name) {
910 out.push_str(" #[allow(clippy::should_implement_trait)]\n");
911 }
912 if !static_attr_str.is_empty() {
913 out.push_str(&crate::template_env::render(
914 "generators/methods/static_attr.jinja",
915 minijinja::context! {
916 static_attr_str => static_attr_str,
917 },
918 ));
919 }
920 if cfg.needs_signature {
921 out.push_str(&crate::template_env::render(
922 "generators/methods/signature_attr.jinja",
923 minijinja::context! {
924 signature_prefix => &cfg.signature_prefix,
925 sig_defaults => sig_defaults,
926 signature_suffix => &cfg.signature_suffix,
927 },
928 ));
929 }
930 out.push_str(&crate::template_env::render(
931 "generators/methods/method_body.jinja",
932 minijinja::context! {
933 sig_start => sig_start,
934 sig_params => sig_params,
935 sig_end => sig_end,
936 ret => ret,
937 body => body,
938 },
939 ));
940 out
941}
942
943pub fn gen_impl_block(
945 typ: &TypeDef,
946 mapper: &dyn TypeMapper,
947 cfg: &RustBindingConfig,
948 adapter_bodies: &AdapterBodies,
949 opaque_types: &AHashSet<String>,
950) -> String {
951 gen_impl_block_with_renames(typ, mapper, cfg, adapter_bodies, opaque_types, None)
952}
953
954pub fn gen_impl_block_with_renames(
956 typ: &TypeDef,
957 mapper: &dyn TypeMapper,
958 cfg: &RustBindingConfig,
959 adapter_bodies: &AdapterBodies,
960 opaque_types: &AHashSet<String>,
961 field_renames: Option<&std::collections::HashMap<String, String>>,
962) -> String {
963 let (instance, statics) = partition_methods(&typ.methods);
964 let has_emittable_instance = instance
968 .iter()
969 .any(|m| !m.sanitized || adapter_bodies.contains_key(&format!("{}.{}", typ.name, m.name)));
970 let has_emittable_statics = statics
971 .iter()
972 .any(|m| !m.sanitized || adapter_bodies.contains_key(&format!("{}.{}", typ.name, m.name)));
973 if !has_emittable_instance && !has_emittable_statics && typ.fields.is_empty() {
974 return String::new();
975 }
976
977 let prefixed_name = format!("{}{}", cfg.type_name_prefix, typ.name);
978 let mut out = String::with_capacity(2048);
979
980 if !typ.fields.is_empty() && !cfg.skip_impl_constructor {
983 out.push_str(&gen_constructor_with_renames(typ, mapper, cfg, field_renames));
984 out.push_str("\n\n");
985 }
986
987 let empty_mutex_types: AHashSet<String> = AHashSet::new();
989 for m in &instance {
990 let adapter_key = format!("{}.{}", typ.name, m.name);
994 if m.sanitized && !adapter_bodies.contains_key(&adapter_key) {
995 continue;
996 }
997 out.push_str(&gen_method(
998 m,
999 mapper,
1000 cfg,
1001 typ,
1002 false,
1003 opaque_types,
1004 &empty_mutex_types,
1005 adapter_bodies,
1006 ));
1007 out.push_str("\n\n");
1008 }
1009
1010 for m in &statics {
1012 let adapter_key = format!("{}.{}", typ.name, m.name);
1014 if m.sanitized && !adapter_bodies.contains_key(&adapter_key) {
1015 continue;
1016 }
1017 out.push_str(&gen_static_method(
1018 m,
1019 mapper,
1020 cfg,
1021 typ,
1022 adapter_bodies,
1023 opaque_types,
1024 &empty_mutex_types,
1025 ));
1026 out.push_str("\n\n");
1027 }
1028
1029 let trimmed = out.trim_end();
1031 let content = trimmed.to_string();
1032
1033 crate::template_env::render(
1034 "generators/methods/impl_block.jinja",
1035 minijinja::context! {
1036 block_attr => cfg.method_block_attr,
1037 prefixed_name => prefixed_name,
1038 content => content,
1039 },
1040 )
1041}
1042
1043pub fn gen_opaque_impl_block(
1050 typ: &TypeDef,
1051 mapper: &dyn TypeMapper,
1052 cfg: &RustBindingConfig,
1053 opaque_types: &AHashSet<String>,
1054 mutex_types: &AHashSet<String>,
1055 adapter_bodies: &AdapterBodies,
1056) -> String {
1057 let (instance, statics) = partition_methods(&typ.methods);
1058 let has_emittable_instance = instance
1060 .iter()
1061 .any(|m| !m.sanitized || adapter_bodies.contains_key(&format!("{}.{}", typ.name, m.name)));
1062 let has_emittable_statics = statics
1063 .iter()
1064 .any(|m| !m.sanitized || adapter_bodies.contains_key(&format!("{}.{}", typ.name, m.name)));
1065 if !has_emittable_instance && !has_emittable_statics {
1066 return String::new();
1067 }
1068
1069 let mut out = String::with_capacity(2048);
1070 let prefixed_name = format!("{}{}", cfg.type_name_prefix, typ.name);
1071
1072 for m in &instance {
1074 let adapter_key = format!("{}.{}", typ.name, m.name);
1077 if m.sanitized && !adapter_bodies.contains_key(&adapter_key) {
1078 continue;
1079 }
1080 out.push_str(&gen_method(
1081 m,
1082 mapper,
1083 cfg,
1084 typ,
1085 true,
1086 opaque_types,
1087 mutex_types,
1088 adapter_bodies,
1089 ));
1090 out.push_str("\n\n");
1091 }
1092
1093 for m in &statics {
1095 let adapter_key = format!("{}.{}", typ.name, m.name);
1097 if m.sanitized && !adapter_bodies.contains_key(&adapter_key) {
1098 continue;
1099 }
1100 out.push_str(&gen_static_method(
1101 m,
1102 mapper,
1103 cfg,
1104 typ,
1105 adapter_bodies,
1106 opaque_types,
1107 mutex_types,
1108 ));
1109 out.push_str("\n\n");
1110 }
1111
1112 let trimmed = out.trim_end();
1113 let content = trimmed.to_string();
1114
1115 crate::template_env::render(
1116 "generators/methods/impl_block.jinja",
1117 minijinja::context! {
1118 block_attr => cfg.method_block_attr,
1119 prefixed_name => prefixed_name,
1120 content => content,
1121 },
1122 )
1123}