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::config::workspace::ClientConstructorConfig;
12use alef_core::ir::{MethodDef, TypeDef, TypeRef};
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_and_cfg_restore(
37 &typ.fields,
38 &map_fn,
39 cfg.option_duration_on_defaults,
40 field_renames,
41 cfg.never_skip_cfg_field_names,
42 )
43 } else {
44 crate::shared::constructor_parts_with_renames_and_cfg_restore(
45 &typ.fields,
46 &map_fn,
47 field_renames,
48 cfg.never_skip_cfg_field_names,
49 )
50 };
51
52 crate::template_env::render(
53 "generators/methods/constructor.jinja",
54 minijinja::context! {
55 has_too_many_args => typ.fields.len() > 7,
56 needs_signature => cfg.needs_signature,
57 signature_prefix => cfg.signature_prefix,
58 sig_defaults => sig_defaults,
59 signature_suffix => cfg.signature_suffix,
60 constructor_attr => cfg.constructor_attr,
61 param_list => param_list,
62 assignments => assignments,
63 },
64 )
65}
66
67#[allow(clippy::too_many_arguments)]
76pub fn gen_method(
77 method: &MethodDef,
78 mapper: &dyn TypeMapper,
79 cfg: &RustBindingConfig,
80 typ: &TypeDef,
81 is_opaque: bool,
82 opaque_types: &AHashSet<String>,
83 mutex_types: &AHashSet<String>,
84 adapter_bodies: &AdapterBodies,
85) -> String {
86 let type_name = &typ.name;
87 let core_type_path = typ.rust_path.replace('-', "_");
89
90 let map_fn = |ty: &alef_core::ir::TypeRef| mapper.map_type(ty);
91 let params = function_params(&method.params, &map_fn);
92 let return_type = mapper.map_type(&method.return_type);
93 let ret = mapper.wrap_return(&return_type, method.error_type.is_some());
94
95 let core_import = cfg.core_import;
96
97 let has_ref_named_params = has_named_params(&method.params, opaque_types);
101 let (call_args, ref_let_bindings) = if has_ref_named_params {
102 (
103 gen_call_args_with_let_bindings(&method.params, opaque_types),
104 gen_named_let_bindings_pub(&method.params, opaque_types, core_import),
105 )
106 } else if cfg.cast_uints_to_i32 || cfg.cast_large_ints_to_f64 {
107 (
109 gen_call_args_cfg(
110 &method.params,
111 opaque_types,
112 cfg.cast_uints_to_i32,
113 cfg.cast_large_ints_to_f64,
114 ),
115 String::new(),
116 )
117 } else {
118 (gen_call_args(&method.params, opaque_types), String::new())
119 };
120
121 let is_owned_receiver = matches!(method.receiver.as_ref(), Some(alef_core::ir::ReceiverKind::Owned));
122 let is_ref_mut_receiver = matches!(method.receiver.as_ref(), Some(alef_core::ir::ReceiverKind::RefMut));
123
124 let is_functional_ref_mut = !is_opaque
131 && is_ref_mut_receiver
132 && !method.sanitized
133 && method.trait_source.is_none()
134 && method
135 .params
136 .iter()
137 .all(|p| !p.sanitized && crate::shared::is_delegatable_param(&p.ty, opaque_types));
138
139 let is_trait_method = method.trait_source.is_some();
142
143 let self_needs_mutex = is_opaque && mutex_types.contains(type_name.as_str());
145
146 let opaque_can_delegate = is_opaque
153 && !method.sanitized
154 && (!is_ref_mut_receiver || self_needs_mutex)
155 && !is_trait_method
156 && (!is_owned_receiver || typ.is_clone)
157 && method
158 .params
159 .iter()
160 .all(|p| !p.sanitized && crate::shared::is_opaque_delegatable_type(&p.ty))
161 && crate::shared::is_opaque_delegatable_type(&method.return_type);
162
163 let make_core_call = |method_name: &str| -> String {
167 if is_opaque {
168 if is_owned_receiver {
169 if self_needs_mutex {
172 format!("self.inner.lock().unwrap().clone().{method_name}({call_args})")
173 } else {
174 format!("(*self.inner).clone().{method_name}({call_args})")
175 }
176 } else if self_needs_mutex {
177 format!("self.inner.lock().unwrap().{method_name}({call_args})")
179 } else {
180 format!("self.inner.{method_name}({call_args})")
181 }
182 } else {
183 format!("{core_type_path}::from(self.clone()).{method_name}({call_args})")
184 }
185 };
186
187 let make_async_core_call = |method_name: &str| -> String {
190 if is_opaque {
191 if self_needs_mutex {
192 format!("inner.lock().unwrap().{method_name}({call_args})")
193 } else {
194 format!("inner.{method_name}({call_args})")
195 }
196 } else {
197 format!("{core_type_path}::from(self.clone()).{method_name}({call_args})")
198 }
199 };
200
201 let result_expr = apply_return_newtype_unwrap("result", &method.return_newtype_wrapper);
208 let async_result_wrap = if is_opaque {
209 wrap_return_with_mutex_mapped(
210 &result_expr,
211 &method.return_type,
212 type_name,
213 opaque_types,
214 mutex_types,
215 is_opaque,
216 method.returns_ref,
217 method.returns_cow,
218 mapper,
219 )
220 } else {
221 match &method.return_type {
224 TypeRef::Named(_) | TypeRef::Json => format!("{result_expr}.into()"),
225 _ => result_expr.clone(),
226 }
227 };
228
229 let adapter_key_inner = format!("{}.{}", type_name, method.name);
232 let adapter_override = adapter_bodies.get(&adapter_key_inner).cloned();
233
234 let body = if let Some(adapter_body) = adapter_override {
235 adapter_body
236 } else if !opaque_can_delegate {
237 if cfg.has_serde
238 && is_opaque
239 && !method.sanitized
240 && !is_trait_method
241 && has_named_params(&method.params, opaque_types)
242 && method.error_type.is_some()
243 && crate::shared::is_opaque_delegatable_type(&method.return_type)
244 {
245 let err_conv = match cfg.async_pattern {
248 AsyncPattern::Pyo3FutureIntoPy => {
249 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
250 }
251 AsyncPattern::NapiNativeAsync => {
252 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
253 }
254 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
255 AsyncPattern::TokioBlockOn => {
256 ".map_err(|e| extendr_api::Error::Other(e.to_string().replace(\":\", \"_\").replace(\"/\", \"_\").replace(\"-\", \"_\").chars().take(255).collect::<String>()))"
257 }
258 _ => ".map_err(|e| e.to_string())",
259 };
260 let serde_bindings =
261 gen_serde_let_bindings(&method.params, opaque_types, cfg.core_import, err_conv, " ");
262 let serde_call_args = gen_call_args_with_let_bindings(&method.params, opaque_types);
263 let core_call = if self_needs_mutex {
264 format!("self.inner.lock().unwrap().{}({serde_call_args})", method.name)
265 } else {
266 format!("self.inner.{}({serde_call_args})", method.name)
267 };
268 if matches!(method.return_type, TypeRef::Unit) {
269 format!("{serde_bindings}{core_call}{err_conv}?;\n Ok(())")
270 } else {
271 let wrap = wrap_return_with_mutex_mapped(
272 "result",
273 &method.return_type,
274 type_name,
275 opaque_types,
276 mutex_types,
277 is_opaque,
278 method.returns_ref,
279 method.returns_cow,
280 mapper,
281 );
282 format!("{serde_bindings}let result = {core_call}{err_conv}?;\n Ok({wrap})")
283 }
284 } else if is_functional_ref_mut {
285 let field_conversions = gen_lossy_binding_to_core_fields_mut(
294 typ,
295 cfg.core_import,
296 cfg.option_duration_on_defaults,
297 opaque_types,
298 cfg.cast_uints_to_i32,
299 cfg.cast_large_ints_to_f64,
300 cfg.lossy_skip_types,
301 );
302 let core_call = format!("core_self.{}({call_args})", method.name);
303 if method.error_type.is_some() {
304 let err_conv = match cfg.async_pattern {
305 AsyncPattern::Pyo3FutureIntoPy => {
306 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
307 }
308 AsyncPattern::NapiNativeAsync => {
309 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
310 }
311 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
312 AsyncPattern::TokioBlockOn => {
313 ".map_err(|e| extendr_api::Error::Other(e.to_string().replace(\":\", \"_\").replace(\"/\", \"_\").replace(\"-\", \"_\").chars().take(255).collect::<String>()))"
314 }
315 _ => ".map_err(|e| e.to_string())",
316 };
317 format!("{field_conversions}{core_call}{err_conv}?;\n Ok(core_self.into())")
318 } else {
319 format!("{field_conversions}{core_call};\n core_self.into()")
320 }
321 } else if !is_opaque
322 && !method.sanitized
323 && method
324 .params
325 .iter()
326 .all(|p| !p.sanitized && is_simple_non_opaque_param(&p.ty))
327 && crate::shared::is_delegatable_return(&method.return_type)
328 {
329 let is_ref_mut = matches!(method.receiver.as_ref(), Some(alef_core::ir::ReceiverKind::RefMut));
332 let field_conversions = if is_ref_mut {
333 gen_lossy_binding_to_core_fields_mut(
334 typ,
335 cfg.core_import,
336 cfg.option_duration_on_defaults,
337 opaque_types,
338 cfg.cast_uints_to_i32,
339 cfg.cast_large_ints_to_f64,
340 cfg.lossy_skip_types,
341 )
342 } else {
343 gen_lossy_binding_to_core_fields(
344 typ,
345 cfg.core_import,
346 cfg.option_duration_on_defaults,
347 opaque_types,
348 cfg.cast_uints_to_i32,
349 cfg.cast_large_ints_to_f64,
350 cfg.lossy_skip_types,
351 )
352 };
353 let core_call = format!("core_self.{}({call_args})", method.name);
354 let newtype_suffix = if method.return_newtype_wrapper.is_some() {
355 ".0"
356 } else {
357 ""
358 };
359 let result_wrap = match &method.return_type {
360 TypeRef::Named(n) if n == type_name && (method.returns_cow || method.returns_ref) => {
364 ".into_owned().into()".to_string()
365 }
366 TypeRef::Named(_) if method.returns_cow || method.returns_ref => ".into_owned().into()".to_string(),
367 TypeRef::Named(n) if n == type_name => ".into()".to_string(),
368 TypeRef::Named(_) => ".into()".to_string(),
369 TypeRef::String => {
370 if method.returns_ref {
371 ".to_owned()".to_string()
372 } else {
373 String::new()
374 }
375 }
376 TypeRef::Path => {
377 if method.returns_ref {
378 ".to_owned()".to_string()
379 } else {
380 ".to_string_lossy().to_string()".to_string()
381 }
382 }
383 TypeRef::Bytes => ".to_vec()".to_string(),
386 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::Named(_)) => {
388 if method.returns_ref {
389 ".map(|v| v.clone().into())".to_string()
390 } else {
391 ".map(Into::into)".to_string()
392 }
393 }
394 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::String | TypeRef::Bytes) => {
396 if method.returns_ref {
397 ".map(|v| v.to_owned())".to_string()
398 } else {
399 String::new()
400 }
401 }
402 TypeRef::Primitive(p) => {
405 use crate::conversions::helpers::{needs_f64_cast, needs_i32_cast};
406 if cfg.cast_uints_to_i32 && needs_i32_cast(p) {
407 " as i32".to_string()
408 } else if cfg.cast_large_ints_to_f64 && needs_f64_cast(p) {
409 " as f64".to_string()
410 } else {
411 String::new()
412 }
413 }
414 TypeRef::Optional(inner) => {
416 if let TypeRef::Primitive(p) = inner.as_ref() {
417 use crate::conversions::helpers::{needs_f64_cast, needs_i32_cast};
418 if cfg.cast_uints_to_i32 && needs_i32_cast(p) {
419 ".map(|v| v as i32)".to_string()
420 } else if cfg.cast_large_ints_to_f64 && needs_f64_cast(p) {
421 ".map(|v| v as f64)".to_string()
422 } else {
423 String::new()
424 }
425 } else {
426 String::new()
427 }
428 }
429 _ => String::new(),
430 };
431 if method.error_type.is_some() {
432 let err_conv = match cfg.async_pattern {
433 AsyncPattern::Pyo3FutureIntoPy => {
434 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
435 }
436 AsyncPattern::NapiNativeAsync => {
437 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
438 }
439 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
440 _ => ".map_err(|e| e.to_string())",
441 };
442 format!(
443 "{field_conversions}let result = {core_call}{err_conv}?;\n Ok(result{newtype_suffix}{result_wrap})"
444 )
445 } else {
446 format!("{field_conversions}{core_call}{newtype_suffix}{result_wrap}")
447 }
448 } else if is_opaque
449 && !method.sanitized
450 && (!is_ref_mut_receiver || self_needs_mutex)
451 && (!is_owned_receiver || typ.is_clone)
452 && method.error_type.is_none()
453 && method
454 .params
455 .iter()
456 .all(|p| !p.sanitized && crate::shared::is_opaque_delegatable_type(&p.ty))
457 && matches!(&method.return_type, TypeRef::Named(n) if n == type_name)
458 {
459 let core_call = if is_owned_receiver {
462 if self_needs_mutex {
463 format!("self.inner.lock().unwrap().clone().{}({call_args})", method.name)
464 } else {
465 format!("(*self.inner).clone().{}({call_args})", method.name)
466 }
467 } else if self_needs_mutex {
468 format!("self.inner.lock().unwrap().{}({call_args})", method.name)
469 } else {
470 format!("self.inner.{}({call_args})", method.name)
471 };
472 let unwrapped = apply_return_newtype_unwrap(&core_call, &method.return_newtype_wrapper);
473 let arc_expr = if self_needs_mutex {
474 format!("Arc::new(std::sync::Mutex::new({unwrapped}))")
475 } else {
476 format!("Arc::new({unwrapped})")
477 };
478 format!("Self {{ inner: {arc_expr} }}")
479 } else if !is_opaque
480 && !method.sanitized
481 && !is_ref_mut_receiver
482 && (!is_owned_receiver || typ.is_clone)
483 && method.error_type.is_none()
484 && method
485 .params
486 .iter()
487 .all(|p| !p.sanitized && is_simple_non_opaque_param(&p.ty))
488 && matches!(&method.return_type, TypeRef::Named(n) if n == type_name)
489 {
490 let is_ref_mut = matches!(method.receiver.as_ref(), Some(alef_core::ir::ReceiverKind::RefMut));
493 let field_conversions = if is_ref_mut {
494 gen_lossy_binding_to_core_fields_mut(
495 typ,
496 cfg.core_import,
497 cfg.option_duration_on_defaults,
498 opaque_types,
499 cfg.cast_uints_to_i32,
500 cfg.cast_large_ints_to_f64,
501 cfg.lossy_skip_types,
502 )
503 } else {
504 gen_lossy_binding_to_core_fields(
505 typ,
506 cfg.core_import,
507 cfg.option_duration_on_defaults,
508 opaque_types,
509 cfg.cast_uints_to_i32,
510 cfg.cast_large_ints_to_f64,
511 cfg.lossy_skip_types,
512 )
513 };
514 let core_call = format!("core_self.{}({call_args})", method.name);
515 let newtype_suffix = if method.return_newtype_wrapper.is_some() {
516 ".0"
517 } else {
518 ""
519 };
520 let result_wrap = if method.returns_cow || method.returns_ref {
521 ".into_owned().into()"
522 } else {
523 ".into()"
524 };
525 format!("{field_conversions}{core_call}{newtype_suffix}{result_wrap}")
526 } else {
527 gen_unimplemented_body(
528 &method.return_type,
529 &format!("{type_name}.{}", method.name),
530 method.error_type.is_some(),
531 cfg,
532 &method.params,
533 opaque_types,
534 )
535 }
536 } else if method.is_async {
537 let inner_clone_line = if is_opaque {
538 "let inner = self.inner.clone();\n "
539 } else {
540 ""
541 };
542 let core_call_str = make_async_core_call(&method.name);
543 gen_async_body(
544 &core_call_str,
545 cfg,
546 method.error_type.is_some(),
547 &async_result_wrap,
548 is_opaque,
549 inner_clone_line,
550 matches!(method.return_type, TypeRef::Unit),
551 Some(&return_type),
552 )
553 } else {
554 let core_call = make_core_call(&method.name);
555 if method.error_type.is_some() {
556 let err_conv = match cfg.async_pattern {
558 AsyncPattern::Pyo3FutureIntoPy => {
559 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
560 }
561 AsyncPattern::NapiNativeAsync => {
562 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
563 }
564 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
565 _ => ".map_err(|e| e.to_string())",
566 };
567 if is_opaque {
568 if matches!(method.return_type, TypeRef::Unit) {
569 format!("{core_call}{err_conv}?;\n Ok(())")
571 } else {
572 let wrap = wrap_return_with_mutex_mapped(
573 &result_expr,
574 &method.return_type,
575 type_name,
576 opaque_types,
577 mutex_types,
578 is_opaque,
579 method.returns_ref,
580 method.returns_cow,
581 mapper,
582 );
583 format!("let result = {core_call}{err_conv}?;\n Ok({wrap})")
584 }
585 } else {
586 format!("{core_call}{err_conv}")
587 }
588 } else if is_opaque {
589 let unwrapped_call = apply_return_newtype_unwrap(&core_call, &method.return_newtype_wrapper);
590 wrap_return_with_mutex_mapped(
591 &unwrapped_call,
592 &method.return_type,
593 type_name,
594 opaque_types,
595 mutex_types,
596 is_opaque,
597 method.returns_ref,
598 method.returns_cow,
599 mapper,
600 )
601 } else {
602 core_call
603 }
604 };
605 let adapter_key = format!("{}.{}", type_name, method.name);
606 let has_adapter = adapter_bodies.contains_key(&adapter_key);
607
608 let body = if ref_let_bindings.is_empty() || has_adapter {
614 body
615 } else {
616 format!("{ref_let_bindings}{body}")
617 };
618
619 let needs_py = method.is_async && cfg.async_pattern == AsyncPattern::Pyo3FutureIntoPy;
620
621 let body = if needs_py && !opaque_can_delegate && !has_adapter {
628 let err_msg = format!("Not implemented: {type_name}.{}", method.name);
629 let suppress = if method.params.is_empty() {
631 String::new()
632 } else {
633 let names: Vec<&str> = method.params.iter().map(|p| p.name.as_str()).collect();
634 if names.len() == 1 {
635 format!("let _ = {};\n ", names[0])
636 } else {
637 format!("let _ = ({});\n ", names.join(", "))
638 }
639 };
640 format!("{suppress}Err(pyo3::exceptions::PyNotImplementedError::new_err(\"{err_msg}\"))")
641 } else {
642 body
643 };
644 let self_param = match (needs_py, params.is_empty()) {
645 (true, true) => "&self, py: Python<'py>",
646 (true, false) => "&self, py: Python<'py>, ",
647 (false, true) => "&self",
648 (false, false) => "&self, ",
649 };
650
651 let ret = if needs_py {
656 "PyResult<Bound<'py, PyAny>>".to_string()
657 } else if is_functional_ref_mut {
658 mapper.wrap_return("Self", method.error_type.is_some())
659 } else {
660 ret
661 };
662 let method_lifetime = if needs_py { "<'py>" } else { "" };
663
664 let (sig_start, sig_params, sig_end) = if self_param.len() + params.len() > 100 {
666 let wrapped_params = method
667 .params
668 .iter()
669 .map(|p| {
670 let ty = if p.optional {
671 format!("Option<{}>", mapper.map_type(&p.ty))
672 } else {
673 mapper.map_type(&p.ty)
674 };
675 format!("{}: {}", p.name, ty)
676 })
677 .collect::<Vec<_>>()
678 .join(",\n ");
679 let py_param = if needs_py { "\n py: Python<'py>," } else { "" };
680 (
681 format!(
682 "pub fn {}{method_lifetime}(\n &self,{}\n ",
683 method.name, py_param
684 ),
685 wrapped_params,
686 "\n ) -> ".to_string(),
687 )
688 } else {
689 (
690 format!("pub fn {}{method_lifetime}({}", method.name, self_param),
691 params,
692 ") -> ".to_string(),
693 )
694 };
695
696 let total_params = method.params.len() + 1 + if needs_py { 1 } else { 0 };
697 let sig_defaults = if cfg.needs_signature {
698 function_sig_defaults(&method.params)
699 } else {
700 String::new()
701 };
702
703 crate::template_env::render(
704 "generators/methods/method_signature.jinja",
705 minijinja::context! {
706 has_too_many_arguments => total_params > 7,
707 has_missing_errors_doc => method.error_type.is_some(),
708 has_should_implement_trait => is_trait_method_name(&method.name),
709 needs_signature => cfg.needs_signature,
710 signature_prefix => cfg.signature_prefix,
711 sig_defaults => sig_defaults,
712 signature_suffix => cfg.signature_suffix,
713 sig_start => sig_start,
714 sig_params => sig_params,
715 sig_end => sig_end,
716 ret => ret,
717 body => body,
718 },
719 )
720}
721
722pub fn gen_static_method(
724 method: &MethodDef,
725 mapper: &dyn TypeMapper,
726 cfg: &RustBindingConfig,
727 typ: &TypeDef,
728 adapter_bodies: &AdapterBodies,
729 opaque_types: &AHashSet<String>,
730 mutex_types: &AHashSet<String>,
731) -> String {
732 let type_name = &typ.name;
733 let core_type_path = typ.rust_path.replace('-', "_");
735 let map_fn = |ty: &alef_core::ir::TypeRef| mapper.map_type(ty);
736 let params = function_params(&method.params, &map_fn);
737 let return_type = mapper.map_type(&method.return_type);
738 let ret = mapper.wrap_return(&return_type, method.error_type.is_some());
739
740 let core_import = cfg.core_import;
741
742 let use_let_bindings = has_named_params(&method.params, opaque_types);
745 let (call_args, ref_let_bindings) = if use_let_bindings {
746 (
747 gen_call_args_with_let_bindings(&method.params, opaque_types),
748 gen_named_let_bindings_pub(&method.params, opaque_types, core_import),
749 )
750 } else {
751 (gen_call_args(&method.params, opaque_types), String::new())
752 };
753
754 let can_delegate = crate::shared::can_auto_delegate(method, opaque_types);
755
756 let adapter_key = format!("{}.{}", type_name, method.name);
759 let adapter_override = adapter_bodies.get(&adapter_key).cloned();
760
761 let body = if let Some(adapter_body) = adapter_override {
762 adapter_body
763 } else if !can_delegate {
764 gen_unimplemented_body(
765 &method.return_type,
766 &format!("{type_name}::{}", method.name),
767 method.error_type.is_some(),
768 cfg,
769 &method.params,
770 opaque_types,
771 )
772 } else if method.is_async {
773 let core_call = format!("{core_type_path}::{}({call_args})", method.name);
774 let return_wrap = format!("{return_type}::from(result)");
775 gen_async_body(
776 &core_call,
777 cfg,
778 method.error_type.is_some(),
779 &return_wrap,
780 false,
781 "",
782 matches!(method.return_type, TypeRef::Unit),
783 Some(&return_type),
784 )
785 } else {
786 let core_call = format!("{core_type_path}::{}({call_args})", method.name);
787 if method.error_type.is_some() {
788 let err_conv = match cfg.async_pattern {
790 AsyncPattern::Pyo3FutureIntoPy => {
791 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
792 }
793 AsyncPattern::NapiNativeAsync => {
794 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
795 }
796 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
797 _ => ".map_err(|e| e.to_string())",
798 };
799 let val_expr = apply_return_newtype_unwrap("val", &method.return_newtype_wrapper);
801 let wrapped = wrap_return_with_mutex_mapped(
802 &val_expr,
803 &method.return_type,
804 type_name,
805 opaque_types,
806 mutex_types,
807 typ.is_opaque,
808 method.returns_ref,
809 method.returns_cow,
810 mapper,
811 );
812 if wrapped == val_expr {
813 format!("{core_call}{err_conv}")
814 } else if wrapped == format!("{val_expr}.into()") {
815 format!("{core_call}.map(Into::into){err_conv}")
816 } else if let Some(type_path) = wrapped.strip_suffix(&format!("::from({val_expr})")) {
817 format!("{core_call}.map({type_path}::from){err_conv}")
818 } else {
819 format!("{core_call}.map(|val| {wrapped}){err_conv}")
820 }
821 } else {
822 let unwrapped_call = apply_return_newtype_unwrap(&core_call, &method.return_newtype_wrapper);
824 wrap_return_with_mutex_mapped(
825 &unwrapped_call,
826 &method.return_type,
827 type_name,
828 opaque_types,
829 mutex_types,
830 typ.is_opaque,
831 method.returns_ref,
832 method.returns_cow,
833 mapper,
834 )
835 }
836 };
837 let body = if ref_let_bindings.is_empty() {
839 body
840 } else {
841 format!("{ref_let_bindings}{body}")
842 };
843
844 let static_needs_py = method.is_async && cfg.async_pattern == AsyncPattern::Pyo3FutureIntoPy;
845
846 let ret = if static_needs_py {
848 "PyResult<Bound<'py, PyAny>>".to_string()
849 } else {
850 ret
851 };
852 let method_lifetime = if static_needs_py { "<'py>" } else { "" };
853
854 let (sig_start, sig_params, sig_end) = if params.len() > 100 {
856 let wrapped_params = method
857 .params
858 .iter()
859 .map(|p| {
860 let ty = if p.optional {
861 format!("Option<{}>", mapper.map_type(&p.ty))
862 } else {
863 mapper.map_type(&p.ty)
864 };
865 format!("{}: {}", p.name, ty)
866 })
867 .collect::<Vec<_>>()
868 .join(",\n ");
869 if static_needs_py {
871 (
872 format!("pub fn {}{method_lifetime}(py: Python<'py>,\n ", method.name),
873 wrapped_params,
874 "\n ) -> ".to_string(),
875 )
876 } else {
877 (
878 format!("pub fn {}(\n ", method.name),
879 wrapped_params,
880 "\n ) -> ".to_string(),
881 )
882 }
883 } else if static_needs_py {
884 (
885 format!("pub fn {}{method_lifetime}(py: Python<'py>, ", method.name),
886 params,
887 ") -> ".to_string(),
888 )
889 } else {
890 (format!("pub fn {}(", method.name), params, ") -> ".to_string())
891 };
892
893 let total_params = method.params.len() + if static_needs_py { 1 } else { 0 };
894 let sig_defaults = if cfg.needs_signature {
895 function_sig_defaults(&method.params)
896 } else {
897 String::new()
898 };
899 let static_attr_str = if let Some(attr) = cfg.static_attr {
900 format!("#[{attr}]")
901 } else {
902 String::new()
903 };
904
905 let mut out = String::with_capacity(1024);
908 if total_params > 7 {
909 out.push_str(" #[allow(clippy::too_many_arguments)]\n");
910 }
911 if method.error_type.is_some() {
912 out.push_str(" #[allow(clippy::missing_errors_doc)]\n");
913 }
914 if is_trait_method_name(&method.name) {
915 out.push_str(" #[allow(clippy::should_implement_trait)]\n");
916 }
917 if !static_attr_str.is_empty() {
918 out.push_str(&crate::template_env::render(
919 "generators/methods/static_attr.jinja",
920 minijinja::context! {
921 static_attr_str => static_attr_str,
922 },
923 ));
924 }
925 if cfg.needs_signature {
926 out.push_str(&crate::template_env::render(
927 "generators/methods/signature_attr.jinja",
928 minijinja::context! {
929 signature_prefix => &cfg.signature_prefix,
930 sig_defaults => sig_defaults,
931 signature_suffix => &cfg.signature_suffix,
932 },
933 ));
934 }
935 out.push_str(&crate::template_env::render(
936 "generators/methods/method_body.jinja",
937 minijinja::context! {
938 sig_start => sig_start,
939 sig_params => sig_params,
940 sig_end => sig_end,
941 ret => ret,
942 body => body,
943 },
944 ));
945 out
946}
947
948pub fn gen_impl_block(
950 typ: &TypeDef,
951 mapper: &dyn TypeMapper,
952 cfg: &RustBindingConfig,
953 adapter_bodies: &AdapterBodies,
954 opaque_types: &AHashSet<String>,
955) -> String {
956 gen_impl_block_with_renames(typ, mapper, cfg, adapter_bodies, opaque_types, None)
957}
958
959pub fn gen_impl_block_with_renames(
961 typ: &TypeDef,
962 mapper: &dyn TypeMapper,
963 cfg: &RustBindingConfig,
964 adapter_bodies: &AdapterBodies,
965 opaque_types: &AHashSet<String>,
966 field_renames: Option<&std::collections::HashMap<String, String>>,
967) -> String {
968 let (instance, statics) = partition_methods(&typ.methods);
969 let has_emittable_instance = instance
973 .iter()
974 .any(|m| !m.sanitized || adapter_bodies.contains_key(&format!("{}.{}", typ.name, m.name)));
975 let has_emittable_statics = statics
976 .iter()
977 .any(|m| !m.sanitized || adapter_bodies.contains_key(&format!("{}.{}", typ.name, m.name)));
978 if !has_emittable_instance && !has_emittable_statics && typ.fields.is_empty() {
979 return String::new();
980 }
981
982 let prefixed_name = format!("{}{}", cfg.type_name_prefix, typ.name);
983 let mut out = String::with_capacity(2048);
984
985 if !typ.fields.is_empty() && !cfg.skip_impl_constructor {
988 out.push_str(&gen_constructor_with_renames(typ, mapper, cfg, field_renames));
989 out.push_str("\n\n");
990 }
991
992 let empty_mutex_types: AHashSet<String> = AHashSet::new();
994 for m in &instance {
995 let adapter_key = format!("{}.{}", typ.name, m.name);
999 if m.sanitized && !adapter_bodies.contains_key(&adapter_key) {
1000 continue;
1001 }
1002 out.push_str(&gen_method(
1003 m,
1004 mapper,
1005 cfg,
1006 typ,
1007 false,
1008 opaque_types,
1009 &empty_mutex_types,
1010 adapter_bodies,
1011 ));
1012 out.push_str("\n\n");
1013 }
1014
1015 for m in &statics {
1017 let adapter_key = format!("{}.{}", typ.name, m.name);
1019 if m.sanitized && !adapter_bodies.contains_key(&adapter_key) {
1020 continue;
1021 }
1022 out.push_str(&gen_static_method(
1023 m,
1024 mapper,
1025 cfg,
1026 typ,
1027 adapter_bodies,
1028 opaque_types,
1029 &empty_mutex_types,
1030 ));
1031 out.push_str("\n\n");
1032 }
1033
1034 let trimmed = out.trim_end();
1036 let content = trimmed.to_string();
1037
1038 crate::template_env::render(
1039 "generators/methods/impl_block.jinja",
1040 minijinja::context! {
1041 block_attr => cfg.method_block_attr,
1042 prefixed_name => prefixed_name,
1043 content => content,
1044 },
1045 )
1046}
1047
1048pub fn gen_opaque_impl_block(
1055 typ: &TypeDef,
1056 mapper: &dyn TypeMapper,
1057 cfg: &RustBindingConfig,
1058 opaque_types: &AHashSet<String>,
1059 mutex_types: &AHashSet<String>,
1060 adapter_bodies: &AdapterBodies,
1061) -> String {
1062 let (instance, statics) = partition_methods(&typ.methods);
1063 let has_emittable_instance = instance
1065 .iter()
1066 .any(|m| !m.sanitized || adapter_bodies.contains_key(&format!("{}.{}", typ.name, m.name)));
1067 let has_emittable_statics = statics
1068 .iter()
1069 .any(|m| !m.sanitized || adapter_bodies.contains_key(&format!("{}.{}", typ.name, m.name)));
1070 if !has_emittable_instance && !has_emittable_statics {
1071 return String::new();
1072 }
1073
1074 let mut out = String::with_capacity(2048);
1075 let prefixed_name = format!("{}{}", cfg.type_name_prefix, typ.name);
1076
1077 for m in &instance {
1079 let adapter_key = format!("{}.{}", typ.name, m.name);
1082 if m.sanitized && !adapter_bodies.contains_key(&adapter_key) {
1083 continue;
1084 }
1085 out.push_str(&gen_method(
1086 m,
1087 mapper,
1088 cfg,
1089 typ,
1090 true,
1091 opaque_types,
1092 mutex_types,
1093 adapter_bodies,
1094 ));
1095 out.push_str("\n\n");
1096 }
1097
1098 for m in &statics {
1100 let adapter_key = format!("{}.{}", typ.name, m.name);
1102 if m.sanitized && !adapter_bodies.contains_key(&adapter_key) {
1103 continue;
1104 }
1105 out.push_str(&gen_static_method(
1106 m,
1107 mapper,
1108 cfg,
1109 typ,
1110 adapter_bodies,
1111 opaque_types,
1112 mutex_types,
1113 ));
1114 out.push_str("\n\n");
1115 }
1116
1117 let trimmed = out.trim_end();
1118 let content = trimmed.to_string();
1119
1120 crate::template_env::render(
1121 "generators/methods/impl_block.jinja",
1122 minijinja::context! {
1123 block_attr => cfg.method_block_attr,
1124 prefixed_name => prefixed_name,
1125 content => content,
1126 },
1127 )
1128}
1129
1130pub fn gen_opaque_constructor(
1139 ctor: &ClientConstructorConfig,
1140 type_name: &str,
1141 core_import: &str,
1142 constructor_attr: &str,
1143) -> String {
1144 let source_path = if core_import.is_empty() {
1145 type_name.to_string()
1146 } else {
1147 format!("{core_import}::{type_name}")
1148 };
1149
1150 let params_str = if ctor.params.is_empty() {
1151 String::new()
1152 } else {
1153 ctor.params
1154 .iter()
1155 .map(|p| format!("{}: {}", p.name, p.ty))
1156 .collect::<Vec<_>>()
1157 .join(", ")
1158 };
1159
1160 let body = ctor
1161 .body
1162 .replace("{type_name}", type_name)
1163 .replace("{source_path}", &source_path);
1164
1165 let err_ty = ctor.error_type.as_deref().unwrap_or("String");
1166
1167 let attr_prefix = if constructor_attr.is_empty() {
1168 String::new()
1169 } else {
1170 format!(" {constructor_attr}\n")
1171 };
1172
1173 format!("{attr_prefix} pub fn new({params_str}) -> Result<Self, {err_ty}> {{\n {body}\n }}\n")
1174}