1use crate::generators::binding_helpers::{
2 apply_return_newtype_unwrap, gen_async_body, gen_call_args, gen_call_args_with_let_bindings,
3 gen_lossy_binding_to_core_fields, gen_lossy_binding_to_core_fields_mut, gen_named_let_bindings_pub,
4 gen_serde_let_bindings, gen_unimplemented_body, has_named_params, is_simple_non_opaque_param,
5 wrap_return_with_mutex,
6};
7use crate::generators::{AdapterBodies, AsyncPattern, RustBindingConfig};
8use crate::shared::{function_params, function_sig_defaults, partition_methods};
9use crate::type_mapper::TypeMapper;
10use ahash::AHashSet;
11use alef_core::ir::{MethodDef, TypeDef, TypeRef};
12use std::fmt::Write;
13
14pub fn is_trait_method_name(name: &str) -> bool {
17 crate::generators::TRAIT_METHOD_NAMES.contains(&name)
18}
19
20pub fn gen_constructor(typ: &TypeDef, mapper: &dyn TypeMapper, cfg: &RustBindingConfig) -> String {
22 gen_constructor_with_renames(typ, mapper, cfg, None)
23}
24
25pub fn gen_constructor_with_renames(
27 typ: &TypeDef,
28 mapper: &dyn TypeMapper,
29 cfg: &RustBindingConfig,
30 field_renames: Option<&std::collections::HashMap<String, String>>,
31) -> String {
32 let map_fn = |ty: &alef_core::ir::TypeRef| mapper.map_type(ty);
33
34 let (param_list, sig_defaults, assignments) = if typ.has_default {
36 crate::shared::config_constructor_parts_with_renames(
37 &typ.fields,
38 &map_fn,
39 cfg.option_duration_on_defaults,
40 field_renames,
41 )
42 } else {
43 crate::shared::constructor_parts_with_renames(&typ.fields, &map_fn, field_renames)
44 };
45
46 let mut out = String::with_capacity(512);
47 if typ.fields.len() > 7 {
49 writeln!(out, " #[allow(clippy::too_many_arguments)]").ok();
50 }
51 writeln!(out, " #[must_use]").ok();
52 if cfg.needs_signature {
53 writeln!(
54 out,
55 " {}{}{}",
56 cfg.signature_prefix, sig_defaults, cfg.signature_suffix
57 )
58 .ok();
59 }
60 write!(
61 out,
62 " {}\n pub fn new({param_list}) -> Self {{\n Self {{ {assignments} }}\n }}",
63 cfg.constructor_attr
64 )
65 .ok();
66 out
67}
68
69#[allow(clippy::too_many_arguments)]
78pub fn gen_method(
79 method: &MethodDef,
80 mapper: &dyn TypeMapper,
81 cfg: &RustBindingConfig,
82 typ: &TypeDef,
83 is_opaque: bool,
84 opaque_types: &AHashSet<String>,
85 mutex_types: &AHashSet<String>,
86 adapter_bodies: &AdapterBodies,
87) -> String {
88 let type_name = &typ.name;
89 let core_type_path = typ.rust_path.replace('-', "_");
91
92 let map_fn = |ty: &alef_core::ir::TypeRef| mapper.map_type(ty);
93 let params = function_params(&method.params, &map_fn);
94 let return_type = mapper.map_type(&method.return_type);
95 let ret = mapper.wrap_return(&return_type, method.error_type.is_some());
96
97 let core_import = cfg.core_import;
98
99 let has_ref_named_params = has_named_params(&method.params, opaque_types);
103 let (call_args, ref_let_bindings) = if has_ref_named_params {
104 (
105 gen_call_args_with_let_bindings(&method.params, opaque_types),
106 gen_named_let_bindings_pub(&method.params, opaque_types, core_import),
107 )
108 } else {
109 (gen_call_args(&method.params, opaque_types), String::new())
110 };
111
112 let is_owned_receiver = matches!(method.receiver.as_ref(), Some(alef_core::ir::ReceiverKind::Owned));
113 let is_ref_mut_receiver = matches!(method.receiver.as_ref(), Some(alef_core::ir::ReceiverKind::RefMut));
114
115 let is_functional_ref_mut = !is_opaque
122 && is_ref_mut_receiver
123 && !method.sanitized
124 && method.trait_source.is_none()
125 && method
126 .params
127 .iter()
128 .all(|p| !p.sanitized && crate::shared::is_delegatable_param(&p.ty, opaque_types));
129
130 let is_trait_method = method.trait_source.is_some();
133
134 let self_needs_mutex = is_opaque && mutex_types.contains(type_name.as_str());
136
137 let opaque_can_delegate = is_opaque
144 && !method.sanitized
145 && (!is_ref_mut_receiver || self_needs_mutex)
146 && !is_trait_method
147 && (!is_owned_receiver || typ.is_clone)
148 && method
149 .params
150 .iter()
151 .all(|p| !p.sanitized && crate::shared::is_opaque_delegatable_type(&p.ty))
152 && crate::shared::is_opaque_delegatable_type(&method.return_type);
153
154 let make_core_call = |method_name: &str| -> String {
158 if is_opaque {
159 if is_owned_receiver {
160 if self_needs_mutex {
163 format!("self.inner.lock().unwrap().clone().{method_name}({call_args})")
164 } else {
165 format!("(*self.inner).clone().{method_name}({call_args})")
166 }
167 } else if self_needs_mutex {
168 format!("self.inner.lock().unwrap().{method_name}({call_args})")
170 } else {
171 format!("self.inner.{method_name}({call_args})")
172 }
173 } else {
174 format!("{core_type_path}::from(self.clone()).{method_name}({call_args})")
175 }
176 };
177
178 let make_async_core_call = |method_name: &str| -> String {
181 if is_opaque {
182 if self_needs_mutex {
183 format!("inner.lock().unwrap().{method_name}({call_args})")
184 } else {
185 format!("inner.{method_name}({call_args})")
186 }
187 } else {
188 format!("{core_type_path}::from(self.clone()).{method_name}({call_args})")
189 }
190 };
191
192 let result_expr = apply_return_newtype_unwrap("result", &method.return_newtype_wrapper);
199 let async_result_wrap = if is_opaque {
200 wrap_return_with_mutex(
201 &result_expr,
202 &method.return_type,
203 type_name,
204 opaque_types,
205 mutex_types,
206 is_opaque,
207 method.returns_ref,
208 method.returns_cow,
209 )
210 } else {
211 match &method.return_type {
214 TypeRef::Named(_) | TypeRef::Json => format!("{result_expr}.into()"),
215 _ => result_expr.clone(),
216 }
217 };
218
219 let body = if !opaque_can_delegate {
220 let adapter_key_inner = format!("{}.{}", type_name, method.name);
222 if let Some(adapter_body) = adapter_bodies.get(&adapter_key_inner) {
223 adapter_body.clone()
224 } else if cfg.has_serde
225 && is_opaque
226 && !method.sanitized
227 && !is_trait_method
228 && has_named_params(&method.params, opaque_types)
229 && method.error_type.is_some()
230 && crate::shared::is_opaque_delegatable_type(&method.return_type)
231 {
232 let err_conv = match cfg.async_pattern {
235 AsyncPattern::Pyo3FutureIntoPy => {
236 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
237 }
238 AsyncPattern::NapiNativeAsync => {
239 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
240 }
241 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
242 _ => ".map_err(|e| e.to_string())",
243 };
244 let serde_bindings =
245 gen_serde_let_bindings(&method.params, opaque_types, cfg.core_import, err_conv, " ");
246 let serde_call_args = gen_call_args_with_let_bindings(&method.params, opaque_types);
247 let core_call = if self_needs_mutex {
248 format!("self.inner.lock().unwrap().{}({serde_call_args})", method.name)
249 } else {
250 format!("self.inner.{}({serde_call_args})", method.name)
251 };
252 if matches!(method.return_type, TypeRef::Unit) {
253 format!("{serde_bindings}{core_call}{err_conv}?;\n Ok(())")
254 } else {
255 let wrap = wrap_return_with_mutex(
256 "result",
257 &method.return_type,
258 type_name,
259 opaque_types,
260 mutex_types,
261 is_opaque,
262 method.returns_ref,
263 method.returns_cow,
264 );
265 format!("{serde_bindings}let result = {core_call}{err_conv}?;\n Ok({wrap})")
266 }
267 } else if is_functional_ref_mut {
268 let field_conversions =
277 gen_lossy_binding_to_core_fields_mut(typ, cfg.core_import, cfg.option_duration_on_defaults);
278 let core_call = format!("core_self.{}({call_args})", method.name);
279 if method.error_type.is_some() {
280 let err_conv = match cfg.async_pattern {
281 AsyncPattern::Pyo3FutureIntoPy => {
282 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
283 }
284 AsyncPattern::NapiNativeAsync => {
285 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
286 }
287 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
288 _ => ".map_err(|e| e.to_string())",
289 };
290 format!("{field_conversions}{core_call}{err_conv}?;\n Ok(core_self.into())")
291 } else {
292 format!("{field_conversions}{core_call};\n core_self.into()")
293 }
294 } else if !is_opaque
295 && !method.sanitized
296 && method
297 .params
298 .iter()
299 .all(|p| !p.sanitized && is_simple_non_opaque_param(&p.ty))
300 && crate::shared::is_delegatable_return(&method.return_type)
301 {
302 let is_ref_mut = matches!(method.receiver.as_ref(), Some(alef_core::ir::ReceiverKind::RefMut));
305 let field_conversions = if is_ref_mut {
306 gen_lossy_binding_to_core_fields_mut(typ, cfg.core_import, cfg.option_duration_on_defaults)
307 } else {
308 gen_lossy_binding_to_core_fields(typ, cfg.core_import, cfg.option_duration_on_defaults)
309 };
310 let core_call = format!("core_self.{}({call_args})", method.name);
311 let newtype_suffix = if method.return_newtype_wrapper.is_some() {
312 ".0"
313 } else {
314 ""
315 };
316 let result_wrap = match &method.return_type {
317 TypeRef::Named(n) if n == type_name && (method.returns_cow || method.returns_ref) => {
321 ".into_owned().into()".to_string()
322 }
323 TypeRef::Named(_) if method.returns_cow || method.returns_ref => ".into_owned().into()".to_string(),
324 TypeRef::Named(n) if n == type_name => ".into()".to_string(),
325 TypeRef::Named(_) => ".into()".to_string(),
326 TypeRef::String => {
327 if method.returns_ref {
328 ".to_owned()".to_string()
329 } else {
330 String::new()
331 }
332 }
333 TypeRef::Path => {
334 if method.returns_ref {
335 ".to_owned()".to_string()
336 } else {
337 ".to_string_lossy().to_string()".to_string()
338 }
339 }
340 TypeRef::Bytes => ".to_vec()".to_string(),
343 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::Named(_)) => {
345 if method.returns_ref {
346 ".map(|v| v.clone().into())".to_string()
347 } else {
348 ".map(Into::into)".to_string()
349 }
350 }
351 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::String | TypeRef::Bytes) => {
353 if method.returns_ref {
354 ".map(|v| v.to_owned())".to_string()
355 } else {
356 String::new()
357 }
358 }
359 _ => String::new(),
360 };
361 if method.error_type.is_some() {
362 let err_conv = match cfg.async_pattern {
363 AsyncPattern::Pyo3FutureIntoPy => {
364 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
365 }
366 AsyncPattern::NapiNativeAsync => {
367 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
368 }
369 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
370 _ => ".map_err(|e| e.to_string())",
371 };
372 format!(
373 "{field_conversions}let result = {core_call}{err_conv}?;\n Ok(result{newtype_suffix}{result_wrap})"
374 )
375 } else {
376 format!("{field_conversions}{core_call}{newtype_suffix}{result_wrap}")
377 }
378 } else if is_opaque
379 && !method.sanitized
380 && (!is_ref_mut_receiver || self_needs_mutex)
381 && (!is_owned_receiver || typ.is_clone)
382 && method.error_type.is_none()
383 && method
384 .params
385 .iter()
386 .all(|p| !p.sanitized && crate::shared::is_opaque_delegatable_type(&p.ty))
387 && matches!(&method.return_type, TypeRef::Named(n) if n == type_name)
388 {
389 let core_call = if is_owned_receiver {
392 if self_needs_mutex {
393 format!("self.inner.lock().unwrap().clone().{}({call_args})", method.name)
394 } else {
395 format!("(*self.inner).clone().{}({call_args})", method.name)
396 }
397 } else if self_needs_mutex {
398 format!("self.inner.lock().unwrap().{}({call_args})", method.name)
399 } else {
400 format!("self.inner.{}({call_args})", method.name)
401 };
402 let unwrapped = apply_return_newtype_unwrap(&core_call, &method.return_newtype_wrapper);
403 let arc_expr = if self_needs_mutex {
404 format!("Arc::new(std::sync::Mutex::new({unwrapped}))")
405 } else {
406 format!("Arc::new({unwrapped})")
407 };
408 format!("Self {{ inner: {arc_expr} }}")
409 } else if !is_opaque
410 && !method.sanitized
411 && !is_ref_mut_receiver
412 && (!is_owned_receiver || typ.is_clone)
413 && method.error_type.is_none()
414 && method
415 .params
416 .iter()
417 .all(|p| !p.sanitized && is_simple_non_opaque_param(&p.ty))
418 && matches!(&method.return_type, TypeRef::Named(n) if n == type_name)
419 {
420 let is_ref_mut = matches!(method.receiver.as_ref(), Some(alef_core::ir::ReceiverKind::RefMut));
423 let field_conversions = if is_ref_mut {
424 gen_lossy_binding_to_core_fields_mut(typ, cfg.core_import, cfg.option_duration_on_defaults)
425 } else {
426 gen_lossy_binding_to_core_fields(typ, cfg.core_import, cfg.option_duration_on_defaults)
427 };
428 let core_call = format!("core_self.{}({call_args})", method.name);
429 let newtype_suffix = if method.return_newtype_wrapper.is_some() {
430 ".0"
431 } else {
432 ""
433 };
434 let result_wrap = if method.returns_cow || method.returns_ref {
435 ".into_owned().into()"
436 } else {
437 ".into()"
438 };
439 format!("{field_conversions}{core_call}{newtype_suffix}{result_wrap}")
440 } else {
441 gen_unimplemented_body(
442 &method.return_type,
443 &format!("{type_name}.{}", method.name),
444 method.error_type.is_some(),
445 cfg,
446 &method.params,
447 opaque_types,
448 )
449 }
450 } else if method.is_async {
451 let inner_clone_line = if is_opaque {
452 "let inner = self.inner.clone();\n "
453 } else {
454 ""
455 };
456 let core_call_str = make_async_core_call(&method.name);
457 gen_async_body(
458 &core_call_str,
459 cfg,
460 method.error_type.is_some(),
461 &async_result_wrap,
462 is_opaque,
463 inner_clone_line,
464 matches!(method.return_type, TypeRef::Unit),
465 Some(&return_type),
466 )
467 } else {
468 let core_call = make_core_call(&method.name);
469 if method.error_type.is_some() {
470 let err_conv = match cfg.async_pattern {
472 AsyncPattern::Pyo3FutureIntoPy => {
473 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
474 }
475 AsyncPattern::NapiNativeAsync => {
476 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
477 }
478 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
479 _ => ".map_err(|e| e.to_string())",
480 };
481 if is_opaque {
482 if matches!(method.return_type, TypeRef::Unit) {
483 format!("{core_call}{err_conv}?;\n Ok(())")
485 } else {
486 let wrap = wrap_return_with_mutex(
487 &result_expr,
488 &method.return_type,
489 type_name,
490 opaque_types,
491 mutex_types,
492 is_opaque,
493 method.returns_ref,
494 method.returns_cow,
495 );
496 format!("let result = {core_call}{err_conv}?;\n Ok({wrap})")
497 }
498 } else {
499 format!("{core_call}{err_conv}")
500 }
501 } else if is_opaque {
502 let unwrapped_call = apply_return_newtype_unwrap(&core_call, &method.return_newtype_wrapper);
503 wrap_return_with_mutex(
504 &unwrapped_call,
505 &method.return_type,
506 type_name,
507 opaque_types,
508 mutex_types,
509 is_opaque,
510 method.returns_ref,
511 method.returns_cow,
512 )
513 } else {
514 core_call
515 }
516 };
517 let adapter_key = format!("{}.{}", type_name, method.name);
518 let has_adapter = adapter_bodies.contains_key(&adapter_key);
519
520 let body = if ref_let_bindings.is_empty() || has_adapter {
526 body
527 } else {
528 format!("{ref_let_bindings}{body}")
529 };
530
531 let needs_py = method.is_async && cfg.async_pattern == AsyncPattern::Pyo3FutureIntoPy;
532
533 let body = if needs_py && !opaque_can_delegate && !has_adapter {
540 let err_msg = format!("Not implemented: {type_name}.{}", method.name);
541 let suppress = if method.params.is_empty() {
543 String::new()
544 } else {
545 let names: Vec<&str> = method.params.iter().map(|p| p.name.as_str()).collect();
546 if names.len() == 1 {
547 format!("let _ = {};\n ", names[0])
548 } else {
549 format!("let _ = ({});\n ", names.join(", "))
550 }
551 };
552 format!("{suppress}Err(pyo3::exceptions::PyNotImplementedError::new_err(\"{err_msg}\"))")
553 } else {
554 body
555 };
556 let self_param = match (needs_py, params.is_empty()) {
557 (true, true) => "&self, py: Python<'py>",
558 (true, false) => "&self, py: Python<'py>, ",
559 (false, true) => "&self",
560 (false, false) => "&self, ",
561 };
562
563 let ret = if needs_py {
568 "PyResult<Bound<'py, PyAny>>".to_string()
569 } else if is_functional_ref_mut {
570 mapper.wrap_return("Self", method.error_type.is_some())
571 } else {
572 ret
573 };
574 let method_lifetime = if needs_py { "<'py>" } else { "" };
575
576 let (sig_start, sig_params, sig_end) = if self_param.len() + params.len() > 100 {
578 let wrapped_params = method
579 .params
580 .iter()
581 .map(|p| {
582 let ty = if p.optional {
583 format!("Option<{}>", mapper.map_type(&p.ty))
584 } else {
585 mapper.map_type(&p.ty)
586 };
587 format!("{}: {}", p.name, ty)
588 })
589 .collect::<Vec<_>>()
590 .join(",\n ");
591 let py_param = if needs_py { "\n py: Python<'py>," } else { "" };
592 (
593 format!(
594 "pub fn {}{method_lifetime}(\n &self,{}\n ",
595 method.name, py_param
596 ),
597 wrapped_params,
598 "\n ) -> ".to_string(),
599 )
600 } else {
601 (
602 format!("pub fn {}{method_lifetime}({}", method.name, self_param),
603 params,
604 ") -> ".to_string(),
605 )
606 };
607
608 let mut out = String::with_capacity(1024);
609 let total_params = method.params.len() + 1 + if needs_py { 1 } else { 0 };
611 if total_params > 7 {
612 writeln!(out, " #[allow(clippy::too_many_arguments)]").ok();
613 }
614 if method.error_type.is_some() {
616 writeln!(out, " #[allow(clippy::missing_errors_doc)]").ok();
617 }
618 if is_trait_method_name(&method.name) {
620 writeln!(out, " #[allow(clippy::should_implement_trait)]").ok();
621 }
622 if cfg.needs_signature {
623 let sig = function_sig_defaults(&method.params);
624 writeln!(out, " {}{}{}", cfg.signature_prefix, sig, cfg.signature_suffix).ok();
625 }
626 write!(
627 out,
628 " {}{}{}{} {{\n \
629 {body}\n }}",
630 sig_start, sig_params, sig_end, ret,
631 )
632 .ok();
633 out
634}
635
636pub fn gen_static_method(
638 method: &MethodDef,
639 mapper: &dyn TypeMapper,
640 cfg: &RustBindingConfig,
641 typ: &TypeDef,
642 adapter_bodies: &AdapterBodies,
643 opaque_types: &AHashSet<String>,
644 mutex_types: &AHashSet<String>,
645) -> String {
646 let type_name = &typ.name;
647 let core_type_path = typ.rust_path.replace('-', "_");
649 let map_fn = |ty: &alef_core::ir::TypeRef| mapper.map_type(ty);
650 let params = function_params(&method.params, &map_fn);
651 let return_type = mapper.map_type(&method.return_type);
652 let ret = mapper.wrap_return(&return_type, method.error_type.is_some());
653
654 let core_import = cfg.core_import;
655
656 let use_let_bindings = has_named_params(&method.params, opaque_types);
659 let (call_args, ref_let_bindings) = if use_let_bindings {
660 (
661 gen_call_args_with_let_bindings(&method.params, opaque_types),
662 gen_named_let_bindings_pub(&method.params, opaque_types, core_import),
663 )
664 } else {
665 (gen_call_args(&method.params, opaque_types), String::new())
666 };
667
668 let can_delegate = crate::shared::can_auto_delegate(method, opaque_types);
669
670 let body = if !can_delegate {
671 let adapter_key = format!("{}.{}", type_name, method.name);
673 if let Some(adapter_body) = adapter_bodies.get(&adapter_key) {
674 adapter_body.clone()
675 } else {
676 gen_unimplemented_body(
677 &method.return_type,
678 &format!("{type_name}::{}", method.name),
679 method.error_type.is_some(),
680 cfg,
681 &method.params,
682 opaque_types,
683 )
684 }
685 } else if method.is_async {
686 let core_call = format!("{core_type_path}::{}({call_args})", method.name);
687 let return_wrap = format!("{return_type}::from(result)");
688 gen_async_body(
689 &core_call,
690 cfg,
691 method.error_type.is_some(),
692 &return_wrap,
693 false,
694 "",
695 matches!(method.return_type, TypeRef::Unit),
696 Some(&return_type),
697 )
698 } else {
699 let core_call = format!("{core_type_path}::{}({call_args})", method.name);
700 if method.error_type.is_some() {
701 let err_conv = match cfg.async_pattern {
703 AsyncPattern::Pyo3FutureIntoPy => {
704 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
705 }
706 AsyncPattern::NapiNativeAsync => {
707 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
708 }
709 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
710 _ => ".map_err(|e| e.to_string())",
711 };
712 let val_expr = apply_return_newtype_unwrap("val", &method.return_newtype_wrapper);
714 let wrapped = wrap_return_with_mutex(
715 &val_expr,
716 &method.return_type,
717 type_name,
718 opaque_types,
719 mutex_types,
720 typ.is_opaque,
721 method.returns_ref,
722 method.returns_cow,
723 );
724 if wrapped == val_expr {
725 format!("{core_call}{err_conv}")
726 } else if wrapped == format!("{val_expr}.into()") {
727 format!("{core_call}.map(Into::into){err_conv}")
728 } else if let Some(type_path) = wrapped.strip_suffix(&format!("::from({val_expr})")) {
729 format!("{core_call}.map({type_path}::from){err_conv}")
730 } else {
731 format!("{core_call}.map(|val| {wrapped}){err_conv}")
732 }
733 } else {
734 let unwrapped_call = apply_return_newtype_unwrap(&core_call, &method.return_newtype_wrapper);
736 wrap_return_with_mutex(
737 &unwrapped_call,
738 &method.return_type,
739 type_name,
740 opaque_types,
741 mutex_types,
742 typ.is_opaque,
743 method.returns_ref,
744 method.returns_cow,
745 )
746 }
747 };
748 let body = if ref_let_bindings.is_empty() {
750 body
751 } else {
752 format!("{ref_let_bindings}{body}")
753 };
754
755 let static_needs_py = method.is_async && cfg.async_pattern == AsyncPattern::Pyo3FutureIntoPy;
756
757 let ret = if static_needs_py {
759 "PyResult<Bound<'py, PyAny>>".to_string()
760 } else {
761 ret
762 };
763 let method_lifetime = if static_needs_py { "<'py>" } else { "" };
764
765 let (sig_start, sig_params, sig_end) = if params.len() > 100 {
767 let wrapped_params = method
768 .params
769 .iter()
770 .map(|p| {
771 let ty = if p.optional {
772 format!("Option<{}>", mapper.map_type(&p.ty))
773 } else {
774 mapper.map_type(&p.ty)
775 };
776 format!("{}: {}", p.name, ty)
777 })
778 .collect::<Vec<_>>()
779 .join(",\n ");
780 if static_needs_py {
782 (
783 format!("pub fn {}{method_lifetime}(py: Python<'py>,\n ", method.name),
784 wrapped_params,
785 "\n ) -> ".to_string(),
786 )
787 } else {
788 (
789 format!("pub fn {}(\n ", method.name),
790 wrapped_params,
791 "\n ) -> ".to_string(),
792 )
793 }
794 } else if static_needs_py {
795 (
796 format!("pub fn {}{method_lifetime}(py: Python<'py>, ", method.name),
797 params,
798 ") -> ".to_string(),
799 )
800 } else {
801 (format!("pub fn {}(", method.name), params, ") -> ".to_string())
802 };
803
804 let mut out = String::with_capacity(1024);
805 let total_params = method.params.len() + if static_needs_py { 1 } else { 0 };
807 if total_params > 7 {
808 writeln!(out, " #[allow(clippy::too_many_arguments)]").ok();
809 }
810 if method.error_type.is_some() {
812 writeln!(out, " #[allow(clippy::missing_errors_doc)]").ok();
813 }
814 if is_trait_method_name(&method.name) {
816 writeln!(out, " #[allow(clippy::should_implement_trait)]").ok();
817 }
818 if let Some(attr) = cfg.static_attr {
819 writeln!(out, " #[{attr}]").ok();
820 }
821 if cfg.needs_signature {
822 let sig = function_sig_defaults(&method.params);
823 writeln!(out, " {}{}{}", cfg.signature_prefix, sig, cfg.signature_suffix).ok();
824 }
825 write!(
826 out,
827 " {}{}{}{} {{\n \
828 {body}\n }}",
829 sig_start, sig_params, sig_end, ret,
830 )
831 .ok();
832 out
833}
834
835pub fn gen_impl_block(
837 typ: &TypeDef,
838 mapper: &dyn TypeMapper,
839 cfg: &RustBindingConfig,
840 adapter_bodies: &AdapterBodies,
841 opaque_types: &AHashSet<String>,
842) -> String {
843 gen_impl_block_with_renames(typ, mapper, cfg, adapter_bodies, opaque_types, None)
844}
845
846pub fn gen_impl_block_with_renames(
848 typ: &TypeDef,
849 mapper: &dyn TypeMapper,
850 cfg: &RustBindingConfig,
851 adapter_bodies: &AdapterBodies,
852 opaque_types: &AHashSet<String>,
853 field_renames: Option<&std::collections::HashMap<String, String>>,
854) -> String {
855 let (instance, statics) = partition_methods(&typ.methods);
856 let has_emittable_instance = instance
860 .iter()
861 .any(|m| !m.sanitized || adapter_bodies.contains_key(&format!("{}.{}", typ.name, m.name)));
862 let has_emittable_statics = statics
863 .iter()
864 .any(|m| !m.sanitized || adapter_bodies.contains_key(&format!("{}.{}", typ.name, m.name)));
865 if !has_emittable_instance && !has_emittable_statics && typ.fields.is_empty() {
866 return String::new();
867 }
868
869 let prefixed_name = format!("{}{}", cfg.type_name_prefix, typ.name);
870 let mut out = String::with_capacity(2048);
871 if let Some(block_attr) = cfg.method_block_attr {
872 writeln!(out, "#[{block_attr}]").ok();
873 }
874 writeln!(out, "impl {prefixed_name} {{").ok();
875
876 if !typ.fields.is_empty() {
878 out.push_str(&gen_constructor_with_renames(typ, mapper, cfg, field_renames));
879 out.push_str("\n\n");
880 }
881
882 let empty_mutex_types: AHashSet<String> = AHashSet::new();
884 for m in &instance {
885 let adapter_key = format!("{}.{}", typ.name, m.name);
889 if m.sanitized && !adapter_bodies.contains_key(&adapter_key) {
890 continue;
891 }
892 out.push_str(&gen_method(
893 m,
894 mapper,
895 cfg,
896 typ,
897 false,
898 opaque_types,
899 &empty_mutex_types,
900 adapter_bodies,
901 ));
902 out.push_str("\n\n");
903 }
904
905 for m in &statics {
907 let adapter_key = format!("{}.{}", typ.name, m.name);
909 if m.sanitized && !adapter_bodies.contains_key(&adapter_key) {
910 continue;
911 }
912 out.push_str(&gen_static_method(
913 m,
914 mapper,
915 cfg,
916 typ,
917 adapter_bodies,
918 opaque_types,
919 &empty_mutex_types,
920 ));
921 out.push_str("\n\n");
922 }
923
924 let trimmed = out.trim_end();
926 let mut result = trimmed.to_string();
927 result.push_str("\n}");
928 result
929}
930
931pub fn gen_opaque_impl_block(
938 typ: &TypeDef,
939 mapper: &dyn TypeMapper,
940 cfg: &RustBindingConfig,
941 opaque_types: &AHashSet<String>,
942 mutex_types: &AHashSet<String>,
943 adapter_bodies: &AdapterBodies,
944) -> String {
945 let (instance, statics) = partition_methods(&typ.methods);
946 let has_emittable_instance = instance
948 .iter()
949 .any(|m| !m.sanitized || adapter_bodies.contains_key(&format!("{}.{}", typ.name, m.name)));
950 let has_emittable_statics = statics
951 .iter()
952 .any(|m| !m.sanitized || adapter_bodies.contains_key(&format!("{}.{}", typ.name, m.name)));
953 if !has_emittable_instance && !has_emittable_statics {
954 return String::new();
955 }
956
957 let mut out = String::with_capacity(2048);
958 let prefixed_name = format!("{}{}", cfg.type_name_prefix, typ.name);
959 if let Some(block_attr) = cfg.method_block_attr {
960 writeln!(out, "#[{block_attr}]").ok();
961 }
962 writeln!(out, "impl {prefixed_name} {{").ok();
963
964 for m in &instance {
966 let adapter_key = format!("{}.{}", typ.name, m.name);
969 if m.sanitized && !adapter_bodies.contains_key(&adapter_key) {
970 continue;
971 }
972 out.push_str(&gen_method(
973 m,
974 mapper,
975 cfg,
976 typ,
977 true,
978 opaque_types,
979 mutex_types,
980 adapter_bodies,
981 ));
982 out.push_str("\n\n");
983 }
984
985 for m in &statics {
987 let adapter_key = format!("{}.{}", typ.name, m.name);
989 if m.sanitized && !adapter_bodies.contains_key(&adapter_key) {
990 continue;
991 }
992 out.push_str(&gen_static_method(
993 m,
994 mapper,
995 cfg,
996 typ,
997 adapter_bodies,
998 opaque_types,
999 mutex_types,
1000 ));
1001 out.push_str("\n\n");
1002 }
1003
1004 let trimmed = out.trim_end();
1005 let mut result = trimmed.to_string();
1006 result.push_str("\n}");
1007 result
1008}