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(
36 &typ.fields,
37 &map_fn,
38 cfg.option_duration_on_defaults,
39 field_renames,
40 )
41 } else {
42 crate::shared::constructor_parts_with_renames(&typ.fields, &map_fn, field_renames)
43 };
44
45 crate::template_env::render(
46 "generators/methods/constructor.jinja",
47 minijinja::context! {
48 has_too_many_args => typ.fields.len() > 7,
49 needs_signature => cfg.needs_signature,
50 signature_prefix => cfg.signature_prefix,
51 sig_defaults => sig_defaults,
52 signature_suffix => cfg.signature_suffix,
53 constructor_attr => cfg.constructor_attr,
54 param_list => param_list,
55 assignments => assignments,
56 },
57 )
58}
59
60#[allow(clippy::too_many_arguments)]
69pub fn gen_method(
70 method: &MethodDef,
71 mapper: &dyn TypeMapper,
72 cfg: &RustBindingConfig,
73 typ: &TypeDef,
74 is_opaque: bool,
75 opaque_types: &AHashSet<String>,
76 mutex_types: &AHashSet<String>,
77 adapter_bodies: &AdapterBodies,
78) -> String {
79 let type_name = &typ.name;
80 let core_type_path = typ.rust_path.replace('-', "_");
82
83 let map_fn = |ty: &alef_core::ir::TypeRef| mapper.map_type(ty);
84 let params = function_params(&method.params, &map_fn);
85 let return_type = mapper.map_type(&method.return_type);
86 let ret = mapper.wrap_return(&return_type, method.error_type.is_some());
87
88 let core_import = cfg.core_import;
89
90 let has_ref_named_params = has_named_params(&method.params, opaque_types);
94 let (call_args, ref_let_bindings) = if has_ref_named_params {
95 (
96 gen_call_args_with_let_bindings(&method.params, opaque_types),
97 gen_named_let_bindings_pub(&method.params, opaque_types, core_import),
98 )
99 } else if cfg.cast_uints_to_i32 || cfg.cast_large_ints_to_f64 {
100 (
102 gen_call_args_cfg(
103 &method.params,
104 opaque_types,
105 cfg.cast_uints_to_i32,
106 cfg.cast_large_ints_to_f64,
107 ),
108 String::new(),
109 )
110 } else {
111 (gen_call_args(&method.params, opaque_types), String::new())
112 };
113
114 let is_owned_receiver = matches!(method.receiver.as_ref(), Some(alef_core::ir::ReceiverKind::Owned));
115 let is_ref_mut_receiver = matches!(method.receiver.as_ref(), Some(alef_core::ir::ReceiverKind::RefMut));
116
117 let is_functional_ref_mut = !is_opaque
124 && is_ref_mut_receiver
125 && !method.sanitized
126 && method.trait_source.is_none()
127 && method
128 .params
129 .iter()
130 .all(|p| !p.sanitized && crate::shared::is_delegatable_param(&p.ty, opaque_types));
131
132 let is_trait_method = method.trait_source.is_some();
135
136 let self_needs_mutex = is_opaque && mutex_types.contains(type_name.as_str());
138
139 let opaque_can_delegate = is_opaque
146 && !method.sanitized
147 && (!is_ref_mut_receiver || self_needs_mutex)
148 && !is_trait_method
149 && (!is_owned_receiver || typ.is_clone)
150 && method
151 .params
152 .iter()
153 .all(|p| !p.sanitized && crate::shared::is_opaque_delegatable_type(&p.ty))
154 && crate::shared::is_opaque_delegatable_type(&method.return_type);
155
156 let make_core_call = |method_name: &str| -> String {
160 if is_opaque {
161 if is_owned_receiver {
162 if self_needs_mutex {
165 format!("self.inner.lock().unwrap().clone().{method_name}({call_args})")
166 } else {
167 format!("(*self.inner).clone().{method_name}({call_args})")
168 }
169 } else if self_needs_mutex {
170 format!("self.inner.lock().unwrap().{method_name}({call_args})")
172 } else {
173 format!("self.inner.{method_name}({call_args})")
174 }
175 } else {
176 format!("{core_type_path}::from(self.clone()).{method_name}({call_args})")
177 }
178 };
179
180 let make_async_core_call = |method_name: &str| -> String {
183 if is_opaque {
184 if self_needs_mutex {
185 format!("inner.lock().unwrap().{method_name}({call_args})")
186 } else {
187 format!("inner.{method_name}({call_args})")
188 }
189 } else {
190 format!("{core_type_path}::from(self.clone()).{method_name}({call_args})")
191 }
192 };
193
194 let result_expr = apply_return_newtype_unwrap("result", &method.return_newtype_wrapper);
201 let async_result_wrap = if is_opaque {
202 wrap_return_with_mutex(
203 &result_expr,
204 &method.return_type,
205 type_name,
206 opaque_types,
207 mutex_types,
208 is_opaque,
209 method.returns_ref,
210 method.returns_cow,
211 )
212 } else {
213 match &method.return_type {
216 TypeRef::Named(_) | TypeRef::Json => format!("{result_expr}.into()"),
217 _ => result_expr.clone(),
218 }
219 };
220
221 let body = if !opaque_can_delegate {
222 let adapter_key_inner = format!("{}.{}", type_name, method.name);
224 if let Some(adapter_body) = adapter_bodies.get(&adapter_key_inner) {
225 adapter_body.clone()
226 } else if cfg.has_serde
227 && is_opaque
228 && !method.sanitized
229 && !is_trait_method
230 && has_named_params(&method.params, opaque_types)
231 && method.error_type.is_some()
232 && crate::shared::is_opaque_delegatable_type(&method.return_type)
233 {
234 let err_conv = match cfg.async_pattern {
237 AsyncPattern::Pyo3FutureIntoPy => {
238 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
239 }
240 AsyncPattern::NapiNativeAsync => {
241 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
242 }
243 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
244 AsyncPattern::TokioBlockOn => {
245 ".map_err(|e| extendr_api::Error::Other(e.to_string().replace(\":\", \"_\").replace(\"/\", \"_\").replace(\"-\", \"_\").chars().take(255).collect::<String>()))"
246 }
247 _ => ".map_err(|e| e.to_string())",
248 };
249 let serde_bindings =
250 gen_serde_let_bindings(&method.params, opaque_types, cfg.core_import, err_conv, " ");
251 let serde_call_args = gen_call_args_with_let_bindings(&method.params, opaque_types);
252 let core_call = if self_needs_mutex {
253 format!("self.inner.lock().unwrap().{}({serde_call_args})", method.name)
254 } else {
255 format!("self.inner.{}({serde_call_args})", method.name)
256 };
257 if matches!(method.return_type, TypeRef::Unit) {
258 format!("{serde_bindings}{core_call}{err_conv}?;\n Ok(())")
259 } else {
260 let wrap = wrap_return_with_mutex(
261 "result",
262 &method.return_type,
263 type_name,
264 opaque_types,
265 mutex_types,
266 is_opaque,
267 method.returns_ref,
268 method.returns_cow,
269 );
270 format!("{serde_bindings}let result = {core_call}{err_conv}?;\n Ok({wrap})")
271 }
272 } else if is_functional_ref_mut {
273 let field_conversions = gen_lossy_binding_to_core_fields_mut(
282 typ,
283 cfg.core_import,
284 cfg.option_duration_on_defaults,
285 opaque_types,
286 cfg.cast_uints_to_i32,
287 cfg.cast_large_ints_to_f64,
288 cfg.lossy_skip_types,
289 );
290 let core_call = format!("core_self.{}({call_args})", method.name);
291 if method.error_type.is_some() {
292 let err_conv = match cfg.async_pattern {
293 AsyncPattern::Pyo3FutureIntoPy => {
294 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
295 }
296 AsyncPattern::NapiNativeAsync => {
297 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
298 }
299 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
300 AsyncPattern::TokioBlockOn => {
301 ".map_err(|e| extendr_api::Error::Other(e.to_string().replace(\":\", \"_\").replace(\"/\", \"_\").replace(\"-\", \"_\").chars().take(255).collect::<String>()))"
302 }
303 _ => ".map_err(|e| e.to_string())",
304 };
305 format!("{field_conversions}{core_call}{err_conv}?;\n Ok(core_self.into())")
306 } else {
307 format!("{field_conversions}{core_call};\n core_self.into()")
308 }
309 } else if !is_opaque
310 && !method.sanitized
311 && method
312 .params
313 .iter()
314 .all(|p| !p.sanitized && is_simple_non_opaque_param(&p.ty))
315 && crate::shared::is_delegatable_return(&method.return_type)
316 {
317 let is_ref_mut = matches!(method.receiver.as_ref(), Some(alef_core::ir::ReceiverKind::RefMut));
320 let field_conversions = if is_ref_mut {
321 gen_lossy_binding_to_core_fields_mut(
322 typ,
323 cfg.core_import,
324 cfg.option_duration_on_defaults,
325 opaque_types,
326 cfg.cast_uints_to_i32,
327 cfg.cast_large_ints_to_f64,
328 cfg.lossy_skip_types,
329 )
330 } else {
331 gen_lossy_binding_to_core_fields(
332 typ,
333 cfg.core_import,
334 cfg.option_duration_on_defaults,
335 opaque_types,
336 cfg.cast_uints_to_i32,
337 cfg.cast_large_ints_to_f64,
338 cfg.lossy_skip_types,
339 )
340 };
341 let core_call = format!("core_self.{}({call_args})", method.name);
342 let newtype_suffix = if method.return_newtype_wrapper.is_some() {
343 ".0"
344 } else {
345 ""
346 };
347 let result_wrap = match &method.return_type {
348 TypeRef::Named(n) if n == type_name && (method.returns_cow || method.returns_ref) => {
352 ".into_owned().into()".to_string()
353 }
354 TypeRef::Named(_) if method.returns_cow || method.returns_ref => ".into_owned().into()".to_string(),
355 TypeRef::Named(n) if n == type_name => ".into()".to_string(),
356 TypeRef::Named(_) => ".into()".to_string(),
357 TypeRef::String => {
358 if method.returns_ref {
359 ".to_owned()".to_string()
360 } else {
361 String::new()
362 }
363 }
364 TypeRef::Path => {
365 if method.returns_ref {
366 ".to_owned()".to_string()
367 } else {
368 ".to_string_lossy().to_string()".to_string()
369 }
370 }
371 TypeRef::Bytes => ".to_vec()".to_string(),
374 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::Named(_)) => {
376 if method.returns_ref {
377 ".map(|v| v.clone().into())".to_string()
378 } else {
379 ".map(Into::into)".to_string()
380 }
381 }
382 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::String | TypeRef::Bytes) => {
384 if method.returns_ref {
385 ".map(|v| v.to_owned())".to_string()
386 } else {
387 String::new()
388 }
389 }
390 TypeRef::Primitive(p) => {
393 use crate::conversions::helpers::{needs_f64_cast, needs_i32_cast};
394 if cfg.cast_uints_to_i32 && needs_i32_cast(p) {
395 " as i32".to_string()
396 } else if cfg.cast_large_ints_to_f64 && needs_f64_cast(p) {
397 " as f64".to_string()
398 } else {
399 String::new()
400 }
401 }
402 TypeRef::Optional(inner) => {
404 if let TypeRef::Primitive(p) = inner.as_ref() {
405 use crate::conversions::helpers::{needs_f64_cast, needs_i32_cast};
406 if cfg.cast_uints_to_i32 && needs_i32_cast(p) {
407 ".map(|v| v as i32)".to_string()
408 } else if cfg.cast_large_ints_to_f64 && needs_f64_cast(p) {
409 ".map(|v| v as f64)".to_string()
410 } else {
411 String::new()
412 }
413 } else {
414 String::new()
415 }
416 }
417 _ => String::new(),
418 };
419 if method.error_type.is_some() {
420 let err_conv = match cfg.async_pattern {
421 AsyncPattern::Pyo3FutureIntoPy => {
422 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
423 }
424 AsyncPattern::NapiNativeAsync => {
425 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
426 }
427 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
428 _ => ".map_err(|e| e.to_string())",
429 };
430 format!(
431 "{field_conversions}let result = {core_call}{err_conv}?;\n Ok(result{newtype_suffix}{result_wrap})"
432 )
433 } else {
434 format!("{field_conversions}{core_call}{newtype_suffix}{result_wrap}")
435 }
436 } else if is_opaque
437 && !method.sanitized
438 && (!is_ref_mut_receiver || self_needs_mutex)
439 && (!is_owned_receiver || typ.is_clone)
440 && method.error_type.is_none()
441 && method
442 .params
443 .iter()
444 .all(|p| !p.sanitized && crate::shared::is_opaque_delegatable_type(&p.ty))
445 && matches!(&method.return_type, TypeRef::Named(n) if n == type_name)
446 {
447 let core_call = if is_owned_receiver {
450 if self_needs_mutex {
451 format!("self.inner.lock().unwrap().clone().{}({call_args})", method.name)
452 } else {
453 format!("(*self.inner).clone().{}({call_args})", method.name)
454 }
455 } else if self_needs_mutex {
456 format!("self.inner.lock().unwrap().{}({call_args})", method.name)
457 } else {
458 format!("self.inner.{}({call_args})", method.name)
459 };
460 let unwrapped = apply_return_newtype_unwrap(&core_call, &method.return_newtype_wrapper);
461 let arc_expr = if self_needs_mutex {
462 format!("Arc::new(std::sync::Mutex::new({unwrapped}))")
463 } else {
464 format!("Arc::new({unwrapped})")
465 };
466 format!("Self {{ inner: {arc_expr} }}")
467 } else if !is_opaque
468 && !method.sanitized
469 && !is_ref_mut_receiver
470 && (!is_owned_receiver || typ.is_clone)
471 && method.error_type.is_none()
472 && method
473 .params
474 .iter()
475 .all(|p| !p.sanitized && is_simple_non_opaque_param(&p.ty))
476 && matches!(&method.return_type, TypeRef::Named(n) if n == type_name)
477 {
478 let is_ref_mut = matches!(method.receiver.as_ref(), Some(alef_core::ir::ReceiverKind::RefMut));
481 let field_conversions = if is_ref_mut {
482 gen_lossy_binding_to_core_fields_mut(
483 typ,
484 cfg.core_import,
485 cfg.option_duration_on_defaults,
486 opaque_types,
487 cfg.cast_uints_to_i32,
488 cfg.cast_large_ints_to_f64,
489 cfg.lossy_skip_types,
490 )
491 } else {
492 gen_lossy_binding_to_core_fields(
493 typ,
494 cfg.core_import,
495 cfg.option_duration_on_defaults,
496 opaque_types,
497 cfg.cast_uints_to_i32,
498 cfg.cast_large_ints_to_f64,
499 cfg.lossy_skip_types,
500 )
501 };
502 let core_call = format!("core_self.{}({call_args})", method.name);
503 let newtype_suffix = if method.return_newtype_wrapper.is_some() {
504 ".0"
505 } else {
506 ""
507 };
508 let result_wrap = if method.returns_cow || method.returns_ref {
509 ".into_owned().into()"
510 } else {
511 ".into()"
512 };
513 format!("{field_conversions}{core_call}{newtype_suffix}{result_wrap}")
514 } else {
515 gen_unimplemented_body(
516 &method.return_type,
517 &format!("{type_name}.{}", method.name),
518 method.error_type.is_some(),
519 cfg,
520 &method.params,
521 opaque_types,
522 )
523 }
524 } else if method.is_async {
525 let inner_clone_line = if is_opaque {
526 "let inner = self.inner.clone();\n "
527 } else {
528 ""
529 };
530 let core_call_str = make_async_core_call(&method.name);
531 gen_async_body(
532 &core_call_str,
533 cfg,
534 method.error_type.is_some(),
535 &async_result_wrap,
536 is_opaque,
537 inner_clone_line,
538 matches!(method.return_type, TypeRef::Unit),
539 Some(&return_type),
540 )
541 } else {
542 let core_call = make_core_call(&method.name);
543 if method.error_type.is_some() {
544 let err_conv = match cfg.async_pattern {
546 AsyncPattern::Pyo3FutureIntoPy => {
547 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
548 }
549 AsyncPattern::NapiNativeAsync => {
550 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
551 }
552 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
553 _ => ".map_err(|e| e.to_string())",
554 };
555 if is_opaque {
556 if matches!(method.return_type, TypeRef::Unit) {
557 format!("{core_call}{err_conv}?;\n Ok(())")
559 } else {
560 let wrap = wrap_return_with_mutex(
561 &result_expr,
562 &method.return_type,
563 type_name,
564 opaque_types,
565 mutex_types,
566 is_opaque,
567 method.returns_ref,
568 method.returns_cow,
569 );
570 format!("let result = {core_call}{err_conv}?;\n Ok({wrap})")
571 }
572 } else {
573 format!("{core_call}{err_conv}")
574 }
575 } else if is_opaque {
576 let unwrapped_call = apply_return_newtype_unwrap(&core_call, &method.return_newtype_wrapper);
577 wrap_return_with_mutex(
578 &unwrapped_call,
579 &method.return_type,
580 type_name,
581 opaque_types,
582 mutex_types,
583 is_opaque,
584 method.returns_ref,
585 method.returns_cow,
586 )
587 } else {
588 core_call
589 }
590 };
591 let adapter_key = format!("{}.{}", type_name, method.name);
592 let has_adapter = adapter_bodies.contains_key(&adapter_key);
593
594 let body = if ref_let_bindings.is_empty() || has_adapter {
600 body
601 } else {
602 format!("{ref_let_bindings}{body}")
603 };
604
605 let needs_py = method.is_async && cfg.async_pattern == AsyncPattern::Pyo3FutureIntoPy;
606
607 let body = if needs_py && !opaque_can_delegate && !has_adapter {
614 let err_msg = format!("Not implemented: {type_name}.{}", method.name);
615 let suppress = if method.params.is_empty() {
617 String::new()
618 } else {
619 let names: Vec<&str> = method.params.iter().map(|p| p.name.as_str()).collect();
620 if names.len() == 1 {
621 format!("let _ = {};\n ", names[0])
622 } else {
623 format!("let _ = ({});\n ", names.join(", "))
624 }
625 };
626 format!("{suppress}Err(pyo3::exceptions::PyNotImplementedError::new_err(\"{err_msg}\"))")
627 } else {
628 body
629 };
630 let self_param = match (needs_py, params.is_empty()) {
631 (true, true) => "&self, py: Python<'py>",
632 (true, false) => "&self, py: Python<'py>, ",
633 (false, true) => "&self",
634 (false, false) => "&self, ",
635 };
636
637 let ret = if needs_py {
642 "PyResult<Bound<'py, PyAny>>".to_string()
643 } else if is_functional_ref_mut {
644 mapper.wrap_return("Self", method.error_type.is_some())
645 } else {
646 ret
647 };
648 let method_lifetime = if needs_py { "<'py>" } else { "" };
649
650 let (sig_start, sig_params, sig_end) = if self_param.len() + params.len() > 100 {
652 let wrapped_params = method
653 .params
654 .iter()
655 .map(|p| {
656 let ty = if p.optional {
657 format!("Option<{}>", mapper.map_type(&p.ty))
658 } else {
659 mapper.map_type(&p.ty)
660 };
661 format!("{}: {}", p.name, ty)
662 })
663 .collect::<Vec<_>>()
664 .join(",\n ");
665 let py_param = if needs_py { "\n py: Python<'py>," } else { "" };
666 (
667 format!(
668 "pub fn {}{method_lifetime}(\n &self,{}\n ",
669 method.name, py_param
670 ),
671 wrapped_params,
672 "\n ) -> ".to_string(),
673 )
674 } else {
675 (
676 format!("pub fn {}{method_lifetime}({}", method.name, self_param),
677 params,
678 ") -> ".to_string(),
679 )
680 };
681
682 let total_params = method.params.len() + 1 + if needs_py { 1 } else { 0 };
683 let sig_defaults = if cfg.needs_signature {
684 function_sig_defaults(&method.params)
685 } else {
686 String::new()
687 };
688
689 crate::template_env::render(
690 "generators/methods/method_signature.jinja",
691 minijinja::context! {
692 has_too_many_arguments => total_params > 7,
693 has_missing_errors_doc => method.error_type.is_some(),
694 has_should_implement_trait => is_trait_method_name(&method.name),
695 needs_signature => cfg.needs_signature,
696 signature_prefix => cfg.signature_prefix,
697 sig_defaults => sig_defaults,
698 signature_suffix => cfg.signature_suffix,
699 sig_start => sig_start,
700 sig_params => sig_params,
701 sig_end => sig_end,
702 ret => ret,
703 body => body,
704 },
705 )
706}
707
708pub fn gen_static_method(
710 method: &MethodDef,
711 mapper: &dyn TypeMapper,
712 cfg: &RustBindingConfig,
713 typ: &TypeDef,
714 adapter_bodies: &AdapterBodies,
715 opaque_types: &AHashSet<String>,
716 mutex_types: &AHashSet<String>,
717) -> String {
718 let type_name = &typ.name;
719 let core_type_path = typ.rust_path.replace('-', "_");
721 let map_fn = |ty: &alef_core::ir::TypeRef| mapper.map_type(ty);
722 let params = function_params(&method.params, &map_fn);
723 let return_type = mapper.map_type(&method.return_type);
724 let ret = mapper.wrap_return(&return_type, method.error_type.is_some());
725
726 let core_import = cfg.core_import;
727
728 let use_let_bindings = has_named_params(&method.params, opaque_types);
731 let (call_args, ref_let_bindings) = if use_let_bindings {
732 (
733 gen_call_args_with_let_bindings(&method.params, opaque_types),
734 gen_named_let_bindings_pub(&method.params, opaque_types, core_import),
735 )
736 } else {
737 (gen_call_args(&method.params, opaque_types), String::new())
738 };
739
740 let can_delegate = crate::shared::can_auto_delegate(method, opaque_types);
741
742 let body = if !can_delegate {
743 let adapter_key = format!("{}.{}", type_name, method.name);
745 if let Some(adapter_body) = adapter_bodies.get(&adapter_key) {
746 adapter_body.clone()
747 } else {
748 gen_unimplemented_body(
749 &method.return_type,
750 &format!("{type_name}::{}", method.name),
751 method.error_type.is_some(),
752 cfg,
753 &method.params,
754 opaque_types,
755 )
756 }
757 } else if method.is_async {
758 let core_call = format!("{core_type_path}::{}({call_args})", method.name);
759 let return_wrap = format!("{return_type}::from(result)");
760 gen_async_body(
761 &core_call,
762 cfg,
763 method.error_type.is_some(),
764 &return_wrap,
765 false,
766 "",
767 matches!(method.return_type, TypeRef::Unit),
768 Some(&return_type),
769 )
770 } else {
771 let core_call = format!("{core_type_path}::{}({call_args})", method.name);
772 if method.error_type.is_some() {
773 let err_conv = match cfg.async_pattern {
775 AsyncPattern::Pyo3FutureIntoPy => {
776 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
777 }
778 AsyncPattern::NapiNativeAsync => {
779 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
780 }
781 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
782 _ => ".map_err(|e| e.to_string())",
783 };
784 let val_expr = apply_return_newtype_unwrap("val", &method.return_newtype_wrapper);
786 let wrapped = wrap_return_with_mutex(
787 &val_expr,
788 &method.return_type,
789 type_name,
790 opaque_types,
791 mutex_types,
792 typ.is_opaque,
793 method.returns_ref,
794 method.returns_cow,
795 );
796 if wrapped == val_expr {
797 format!("{core_call}{err_conv}")
798 } else if wrapped == format!("{val_expr}.into()") {
799 format!("{core_call}.map(Into::into){err_conv}")
800 } else if let Some(type_path) = wrapped.strip_suffix(&format!("::from({val_expr})")) {
801 format!("{core_call}.map({type_path}::from){err_conv}")
802 } else {
803 format!("{core_call}.map(|val| {wrapped}){err_conv}")
804 }
805 } else {
806 let unwrapped_call = apply_return_newtype_unwrap(&core_call, &method.return_newtype_wrapper);
808 wrap_return_with_mutex(
809 &unwrapped_call,
810 &method.return_type,
811 type_name,
812 opaque_types,
813 mutex_types,
814 typ.is_opaque,
815 method.returns_ref,
816 method.returns_cow,
817 )
818 }
819 };
820 let body = if ref_let_bindings.is_empty() {
822 body
823 } else {
824 format!("{ref_let_bindings}{body}")
825 };
826
827 let static_needs_py = method.is_async && cfg.async_pattern == AsyncPattern::Pyo3FutureIntoPy;
828
829 let ret = if static_needs_py {
831 "PyResult<Bound<'py, PyAny>>".to_string()
832 } else {
833 ret
834 };
835 let method_lifetime = if static_needs_py { "<'py>" } else { "" };
836
837 let (sig_start, sig_params, sig_end) = if params.len() > 100 {
839 let wrapped_params = method
840 .params
841 .iter()
842 .map(|p| {
843 let ty = if p.optional {
844 format!("Option<{}>", mapper.map_type(&p.ty))
845 } else {
846 mapper.map_type(&p.ty)
847 };
848 format!("{}: {}", p.name, ty)
849 })
850 .collect::<Vec<_>>()
851 .join(",\n ");
852 if static_needs_py {
854 (
855 format!("pub fn {}{method_lifetime}(py: Python<'py>,\n ", method.name),
856 wrapped_params,
857 "\n ) -> ".to_string(),
858 )
859 } else {
860 (
861 format!("pub fn {}(\n ", method.name),
862 wrapped_params,
863 "\n ) -> ".to_string(),
864 )
865 }
866 } else if static_needs_py {
867 (
868 format!("pub fn {}{method_lifetime}(py: Python<'py>, ", method.name),
869 params,
870 ") -> ".to_string(),
871 )
872 } else {
873 (format!("pub fn {}(", method.name), params, ") -> ".to_string())
874 };
875
876 let total_params = method.params.len() + if static_needs_py { 1 } else { 0 };
877 let sig_defaults = if cfg.needs_signature {
878 function_sig_defaults(&method.params)
879 } else {
880 String::new()
881 };
882 let static_attr_str = if let Some(attr) = cfg.static_attr {
883 format!("#[{attr}]")
884 } else {
885 String::new()
886 };
887
888 let mut out = String::with_capacity(1024);
891 if total_params > 7 {
892 out.push_str(" #[allow(clippy::too_many_arguments)]\n");
893 }
894 if method.error_type.is_some() {
895 out.push_str(" #[allow(clippy::missing_errors_doc)]\n");
896 }
897 if is_trait_method_name(&method.name) {
898 out.push_str(" #[allow(clippy::should_implement_trait)]\n");
899 }
900 if !static_attr_str.is_empty() {
901 out.push_str(&crate::template_env::render(
902 "generators/methods/static_attr.jinja",
903 minijinja::context! {
904 static_attr_str => static_attr_str,
905 },
906 ));
907 }
908 if cfg.needs_signature {
909 out.push_str(&crate::template_env::render(
910 "generators/methods/signature_attr.jinja",
911 minijinja::context! {
912 signature_prefix => &cfg.signature_prefix,
913 sig_defaults => sig_defaults,
914 signature_suffix => &cfg.signature_suffix,
915 },
916 ));
917 }
918 out.push_str(&crate::template_env::render(
919 "generators/methods/method_body.jinja",
920 minijinja::context! {
921 sig_start => sig_start,
922 sig_params => sig_params,
923 sig_end => sig_end,
924 ret => ret,
925 body => body,
926 },
927 ));
928 out
929}
930
931pub fn gen_impl_block(
933 typ: &TypeDef,
934 mapper: &dyn TypeMapper,
935 cfg: &RustBindingConfig,
936 adapter_bodies: &AdapterBodies,
937 opaque_types: &AHashSet<String>,
938) -> String {
939 gen_impl_block_with_renames(typ, mapper, cfg, adapter_bodies, opaque_types, None)
940}
941
942pub fn gen_impl_block_with_renames(
944 typ: &TypeDef,
945 mapper: &dyn TypeMapper,
946 cfg: &RustBindingConfig,
947 adapter_bodies: &AdapterBodies,
948 opaque_types: &AHashSet<String>,
949 field_renames: Option<&std::collections::HashMap<String, String>>,
950) -> String {
951 let (instance, statics) = partition_methods(&typ.methods);
952 let has_emittable_instance = instance
956 .iter()
957 .any(|m| !m.sanitized || adapter_bodies.contains_key(&format!("{}.{}", typ.name, m.name)));
958 let has_emittable_statics = statics
959 .iter()
960 .any(|m| !m.sanitized || adapter_bodies.contains_key(&format!("{}.{}", typ.name, m.name)));
961 if !has_emittable_instance && !has_emittable_statics && typ.fields.is_empty() {
962 return String::new();
963 }
964
965 let prefixed_name = format!("{}{}", cfg.type_name_prefix, typ.name);
966 let mut out = String::with_capacity(2048);
967
968 if !typ.fields.is_empty() && !cfg.skip_impl_constructor {
971 out.push_str(&gen_constructor_with_renames(typ, mapper, cfg, field_renames));
972 out.push_str("\n\n");
973 }
974
975 let empty_mutex_types: AHashSet<String> = AHashSet::new();
977 for m in &instance {
978 let adapter_key = format!("{}.{}", typ.name, m.name);
982 if m.sanitized && !adapter_bodies.contains_key(&adapter_key) {
983 continue;
984 }
985 out.push_str(&gen_method(
986 m,
987 mapper,
988 cfg,
989 typ,
990 false,
991 opaque_types,
992 &empty_mutex_types,
993 adapter_bodies,
994 ));
995 out.push_str("\n\n");
996 }
997
998 for m in &statics {
1000 let adapter_key = format!("{}.{}", typ.name, m.name);
1002 if m.sanitized && !adapter_bodies.contains_key(&adapter_key) {
1003 continue;
1004 }
1005 out.push_str(&gen_static_method(
1006 m,
1007 mapper,
1008 cfg,
1009 typ,
1010 adapter_bodies,
1011 opaque_types,
1012 &empty_mutex_types,
1013 ));
1014 out.push_str("\n\n");
1015 }
1016
1017 let trimmed = out.trim_end();
1019 let content = trimmed.to_string();
1020
1021 crate::template_env::render(
1022 "generators/methods/impl_block.jinja",
1023 minijinja::context! {
1024 block_attr => cfg.method_block_attr,
1025 prefixed_name => prefixed_name,
1026 content => content,
1027 },
1028 )
1029}
1030
1031pub fn gen_opaque_impl_block(
1038 typ: &TypeDef,
1039 mapper: &dyn TypeMapper,
1040 cfg: &RustBindingConfig,
1041 opaque_types: &AHashSet<String>,
1042 mutex_types: &AHashSet<String>,
1043 adapter_bodies: &AdapterBodies,
1044) -> String {
1045 let (instance, statics) = partition_methods(&typ.methods);
1046 let has_emittable_instance = instance
1048 .iter()
1049 .any(|m| !m.sanitized || adapter_bodies.contains_key(&format!("{}.{}", typ.name, m.name)));
1050 let has_emittable_statics = statics
1051 .iter()
1052 .any(|m| !m.sanitized || adapter_bodies.contains_key(&format!("{}.{}", typ.name, m.name)));
1053 if !has_emittable_instance && !has_emittable_statics {
1054 return String::new();
1055 }
1056
1057 let mut out = String::with_capacity(2048);
1058 let prefixed_name = format!("{}{}", cfg.type_name_prefix, typ.name);
1059
1060 for m in &instance {
1062 let adapter_key = format!("{}.{}", typ.name, m.name);
1065 if m.sanitized && !adapter_bodies.contains_key(&adapter_key) {
1066 continue;
1067 }
1068 out.push_str(&gen_method(
1069 m,
1070 mapper,
1071 cfg,
1072 typ,
1073 true,
1074 opaque_types,
1075 mutex_types,
1076 adapter_bodies,
1077 ));
1078 out.push_str("\n\n");
1079 }
1080
1081 for m in &statics {
1083 let adapter_key = format!("{}.{}", typ.name, m.name);
1085 if m.sanitized && !adapter_bodies.contains_key(&adapter_key) {
1086 continue;
1087 }
1088 out.push_str(&gen_static_method(
1089 m,
1090 mapper,
1091 cfg,
1092 typ,
1093 adapter_bodies,
1094 opaque_types,
1095 mutex_types,
1096 ));
1097 out.push_str("\n\n");
1098 }
1099
1100 let trimmed = out.trim_end();
1101 let content = trimmed.to_string();
1102
1103 crate::template_env::render(
1104 "generators/methods/impl_block.jinja",
1105 minijinja::context! {
1106 block_attr => cfg.method_block_attr,
1107 prefixed_name => prefixed_name,
1108 content => content,
1109 },
1110 )
1111}