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