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 | TypeRef::Path => {
327 if method.returns_ref {
328 ".to_owned()".to_string()
329 } else {
330 ".into()".to_string()
331 }
332 }
333 TypeRef::Bytes => ".to_vec()".to_string(),
336 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::Named(_)) => {
338 if method.returns_ref {
339 ".map(|v| v.clone().into())".to_string()
340 } else {
341 ".map(Into::into)".to_string()
342 }
343 }
344 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::String | TypeRef::Bytes) => {
346 if method.returns_ref {
347 ".map(|v| v.to_owned())".to_string()
348 } else {
349 String::new()
350 }
351 }
352 _ => String::new(),
353 };
354 if method.error_type.is_some() {
355 let err_conv = match cfg.async_pattern {
356 AsyncPattern::Pyo3FutureIntoPy => {
357 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
358 }
359 AsyncPattern::NapiNativeAsync => {
360 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
361 }
362 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
363 _ => ".map_err(|e| e.to_string())",
364 };
365 format!(
366 "{field_conversions}let result = {core_call}{err_conv}?;\n Ok(result{newtype_suffix}{result_wrap})"
367 )
368 } else {
369 format!("{field_conversions}{core_call}{newtype_suffix}{result_wrap}")
370 }
371 } else if is_opaque
372 && !method.sanitized
373 && (!is_ref_mut_receiver || self_needs_mutex)
374 && (!is_owned_receiver || typ.is_clone)
375 && method.error_type.is_none()
376 && method
377 .params
378 .iter()
379 .all(|p| !p.sanitized && crate::shared::is_opaque_delegatable_type(&p.ty))
380 && matches!(&method.return_type, TypeRef::Named(n) if n == type_name)
381 {
382 let core_call = if is_owned_receiver {
385 if self_needs_mutex {
386 format!("self.inner.lock().unwrap().clone().{}({call_args})", method.name)
387 } else {
388 format!("(*self.inner).clone().{}({call_args})", method.name)
389 }
390 } else if self_needs_mutex {
391 format!("self.inner.lock().unwrap().{}({call_args})", method.name)
392 } else {
393 format!("self.inner.{}({call_args})", method.name)
394 };
395 let unwrapped = apply_return_newtype_unwrap(&core_call, &method.return_newtype_wrapper);
396 let arc_expr = if self_needs_mutex {
397 format!("Arc::new(std::sync::Mutex::new({unwrapped}))")
398 } else {
399 format!("Arc::new({unwrapped})")
400 };
401 format!("Self {{ inner: {arc_expr} }}")
402 } else if !is_opaque
403 && !method.sanitized
404 && !is_ref_mut_receiver
405 && (!is_owned_receiver || typ.is_clone)
406 && method.error_type.is_none()
407 && method
408 .params
409 .iter()
410 .all(|p| !p.sanitized && is_simple_non_opaque_param(&p.ty))
411 && matches!(&method.return_type, TypeRef::Named(n) if n == type_name)
412 {
413 let is_ref_mut = matches!(method.receiver.as_ref(), Some(alef_core::ir::ReceiverKind::RefMut));
416 let field_conversions = if is_ref_mut {
417 gen_lossy_binding_to_core_fields_mut(typ, cfg.core_import, cfg.option_duration_on_defaults)
418 } else {
419 gen_lossy_binding_to_core_fields(typ, cfg.core_import, cfg.option_duration_on_defaults)
420 };
421 let core_call = format!("core_self.{}({call_args})", method.name);
422 let newtype_suffix = if method.return_newtype_wrapper.is_some() {
423 ".0"
424 } else {
425 ""
426 };
427 let result_wrap = if method.returns_cow || method.returns_ref {
428 ".into_owned().into()"
429 } else {
430 ".into()"
431 };
432 format!("{field_conversions}{core_call}{newtype_suffix}{result_wrap}")
433 } else {
434 gen_unimplemented_body(
435 &method.return_type,
436 &format!("{type_name}.{}", method.name),
437 method.error_type.is_some(),
438 cfg,
439 &method.params,
440 opaque_types,
441 )
442 }
443 } else if method.is_async {
444 let inner_clone_line = if is_opaque {
445 "let inner = self.inner.clone();\n "
446 } else {
447 ""
448 };
449 let core_call_str = make_async_core_call(&method.name);
450 gen_async_body(
451 &core_call_str,
452 cfg,
453 method.error_type.is_some(),
454 &async_result_wrap,
455 is_opaque,
456 inner_clone_line,
457 matches!(method.return_type, TypeRef::Unit),
458 Some(&return_type),
459 )
460 } else {
461 let core_call = make_core_call(&method.name);
462 if method.error_type.is_some() {
463 let err_conv = match cfg.async_pattern {
465 AsyncPattern::Pyo3FutureIntoPy => {
466 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
467 }
468 AsyncPattern::NapiNativeAsync => {
469 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
470 }
471 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
472 _ => ".map_err(|e| e.to_string())",
473 };
474 if is_opaque {
475 if matches!(method.return_type, TypeRef::Unit) {
476 format!("{core_call}{err_conv}?;\n Ok(())")
478 } else {
479 let wrap = wrap_return_with_mutex(
480 &result_expr,
481 &method.return_type,
482 type_name,
483 opaque_types,
484 mutex_types,
485 is_opaque,
486 method.returns_ref,
487 method.returns_cow,
488 );
489 format!("let result = {core_call}{err_conv}?;\n Ok({wrap})")
490 }
491 } else {
492 format!("{core_call}{err_conv}")
493 }
494 } else if is_opaque {
495 let unwrapped_call = apply_return_newtype_unwrap(&core_call, &method.return_newtype_wrapper);
496 wrap_return_with_mutex(
497 &unwrapped_call,
498 &method.return_type,
499 type_name,
500 opaque_types,
501 mutex_types,
502 is_opaque,
503 method.returns_ref,
504 method.returns_cow,
505 )
506 } else {
507 core_call
508 }
509 };
510 let adapter_key = format!("{}.{}", type_name, method.name);
511 let has_adapter = adapter_bodies.contains_key(&adapter_key);
512
513 let body = if ref_let_bindings.is_empty() || has_adapter {
519 body
520 } else {
521 format!("{ref_let_bindings}{body}")
522 };
523
524 let needs_py = method.is_async && cfg.async_pattern == AsyncPattern::Pyo3FutureIntoPy;
525
526 let body = if needs_py && !opaque_can_delegate && !has_adapter {
533 let err_msg = format!("Not implemented: {type_name}.{}", method.name);
534 let suppress = if method.params.is_empty() {
536 String::new()
537 } else {
538 let names: Vec<&str> = method.params.iter().map(|p| p.name.as_str()).collect();
539 if names.len() == 1 {
540 format!("let _ = {};\n ", names[0])
541 } else {
542 format!("let _ = ({});\n ", names.join(", "))
543 }
544 };
545 format!("{suppress}Err(pyo3::exceptions::PyNotImplementedError::new_err(\"{err_msg}\"))")
546 } else {
547 body
548 };
549 let self_param = match (needs_py, params.is_empty()) {
550 (true, true) => "&self, py: Python<'py>",
551 (true, false) => "&self, py: Python<'py>, ",
552 (false, true) => "&self",
553 (false, false) => "&self, ",
554 };
555
556 let ret = if needs_py {
561 "PyResult<Bound<'py, PyAny>>".to_string()
562 } else if is_functional_ref_mut {
563 mapper.wrap_return("Self", method.error_type.is_some())
564 } else {
565 ret
566 };
567 let method_lifetime = if needs_py { "<'py>" } else { "" };
568
569 let (sig_start, sig_params, sig_end) = if self_param.len() + params.len() > 100 {
571 let wrapped_params = method
572 .params
573 .iter()
574 .map(|p| {
575 let ty = if p.optional {
576 format!("Option<{}>", mapper.map_type(&p.ty))
577 } else {
578 mapper.map_type(&p.ty)
579 };
580 format!("{}: {}", p.name, ty)
581 })
582 .collect::<Vec<_>>()
583 .join(",\n ");
584 let py_param = if needs_py { "\n py: Python<'py>," } else { "" };
585 (
586 format!(
587 "pub fn {}{method_lifetime}(\n &self,{}\n ",
588 method.name, py_param
589 ),
590 wrapped_params,
591 "\n ) -> ".to_string(),
592 )
593 } else {
594 (
595 format!("pub fn {}{method_lifetime}({}", method.name, self_param),
596 params,
597 ") -> ".to_string(),
598 )
599 };
600
601 let mut out = String::with_capacity(1024);
602 let total_params = method.params.len() + 1 + if needs_py { 1 } else { 0 };
604 if total_params > 7 {
605 writeln!(out, " #[allow(clippy::too_many_arguments)]").ok();
606 }
607 if method.error_type.is_some() {
609 writeln!(out, " #[allow(clippy::missing_errors_doc)]").ok();
610 }
611 if is_trait_method_name(&method.name) {
613 writeln!(out, " #[allow(clippy::should_implement_trait)]").ok();
614 }
615 if cfg.needs_signature {
616 let sig = function_sig_defaults(&method.params);
617 writeln!(out, " {}{}{}", cfg.signature_prefix, sig, cfg.signature_suffix).ok();
618 }
619 write!(
620 out,
621 " {}{}{}{} {{\n \
622 {body}\n }}",
623 sig_start, sig_params, sig_end, ret,
624 )
625 .ok();
626 out
627}
628
629pub fn gen_static_method(
631 method: &MethodDef,
632 mapper: &dyn TypeMapper,
633 cfg: &RustBindingConfig,
634 typ: &TypeDef,
635 adapter_bodies: &AdapterBodies,
636 opaque_types: &AHashSet<String>,
637 mutex_types: &AHashSet<String>,
638) -> String {
639 let type_name = &typ.name;
640 let core_type_path = typ.rust_path.replace('-', "_");
642 let map_fn = |ty: &alef_core::ir::TypeRef| mapper.map_type(ty);
643 let params = function_params(&method.params, &map_fn);
644 let return_type = mapper.map_type(&method.return_type);
645 let ret = mapper.wrap_return(&return_type, method.error_type.is_some());
646
647 let core_import = cfg.core_import;
648
649 let use_let_bindings = has_named_params(&method.params, opaque_types);
652 let (call_args, ref_let_bindings) = if use_let_bindings {
653 (
654 gen_call_args_with_let_bindings(&method.params, opaque_types),
655 gen_named_let_bindings_pub(&method.params, opaque_types, core_import),
656 )
657 } else {
658 (gen_call_args(&method.params, opaque_types), String::new())
659 };
660
661 let can_delegate = crate::shared::can_auto_delegate(method, opaque_types);
662
663 let body = if !can_delegate {
664 let adapter_key = format!("{}.{}", type_name, method.name);
666 if let Some(adapter_body) = adapter_bodies.get(&adapter_key) {
667 adapter_body.clone()
668 } else {
669 gen_unimplemented_body(
670 &method.return_type,
671 &format!("{type_name}::{}", method.name),
672 method.error_type.is_some(),
673 cfg,
674 &method.params,
675 opaque_types,
676 )
677 }
678 } else if method.is_async {
679 let core_call = format!("{core_type_path}::{}({call_args})", method.name);
680 let return_wrap = format!("{return_type}::from(result)");
681 gen_async_body(
682 &core_call,
683 cfg,
684 method.error_type.is_some(),
685 &return_wrap,
686 false,
687 "",
688 matches!(method.return_type, TypeRef::Unit),
689 Some(&return_type),
690 )
691 } else {
692 let core_call = format!("{core_type_path}::{}({call_args})", method.name);
693 if method.error_type.is_some() {
694 let err_conv = match cfg.async_pattern {
696 AsyncPattern::Pyo3FutureIntoPy => {
697 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
698 }
699 AsyncPattern::NapiNativeAsync => {
700 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
701 }
702 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
703 _ => ".map_err(|e| e.to_string())",
704 };
705 let val_expr = apply_return_newtype_unwrap("val", &method.return_newtype_wrapper);
707 let wrapped = wrap_return_with_mutex(
708 &val_expr,
709 &method.return_type,
710 type_name,
711 opaque_types,
712 mutex_types,
713 typ.is_opaque,
714 method.returns_ref,
715 method.returns_cow,
716 );
717 if wrapped == val_expr {
718 format!("{core_call}{err_conv}")
719 } else {
720 format!("{core_call}.map(|val| {wrapped}){err_conv}")
721 }
722 } else {
723 let unwrapped_call = apply_return_newtype_unwrap(&core_call, &method.return_newtype_wrapper);
725 wrap_return_with_mutex(
726 &unwrapped_call,
727 &method.return_type,
728 type_name,
729 opaque_types,
730 mutex_types,
731 typ.is_opaque,
732 method.returns_ref,
733 method.returns_cow,
734 )
735 }
736 };
737 let body = if ref_let_bindings.is_empty() {
739 body
740 } else {
741 format!("{ref_let_bindings}{body}")
742 };
743
744 let static_needs_py = method.is_async && cfg.async_pattern == AsyncPattern::Pyo3FutureIntoPy;
745
746 let ret = if static_needs_py {
748 "PyResult<Bound<'py, PyAny>>".to_string()
749 } else {
750 ret
751 };
752 let method_lifetime = if static_needs_py { "<'py>" } else { "" };
753
754 let (sig_start, sig_params, sig_end) = if params.len() > 100 {
756 let wrapped_params = method
757 .params
758 .iter()
759 .map(|p| {
760 let ty = if p.optional {
761 format!("Option<{}>", mapper.map_type(&p.ty))
762 } else {
763 mapper.map_type(&p.ty)
764 };
765 format!("{}: {}", p.name, ty)
766 })
767 .collect::<Vec<_>>()
768 .join(",\n ");
769 if static_needs_py {
771 (
772 format!("pub fn {}{method_lifetime}(py: Python<'py>,\n ", method.name),
773 wrapped_params,
774 "\n ) -> ".to_string(),
775 )
776 } else {
777 (
778 format!("pub fn {}(\n ", method.name),
779 wrapped_params,
780 "\n ) -> ".to_string(),
781 )
782 }
783 } else if static_needs_py {
784 (
785 format!("pub fn {}{method_lifetime}(py: Python<'py>, ", method.name),
786 params,
787 ") -> ".to_string(),
788 )
789 } else {
790 (format!("pub fn {}(", method.name), params, ") -> ".to_string())
791 };
792
793 let mut out = String::with_capacity(1024);
794 let total_params = method.params.len() + if static_needs_py { 1 } else { 0 };
796 if total_params > 7 {
797 writeln!(out, " #[allow(clippy::too_many_arguments)]").ok();
798 }
799 if method.error_type.is_some() {
801 writeln!(out, " #[allow(clippy::missing_errors_doc)]").ok();
802 }
803 if is_trait_method_name(&method.name) {
805 writeln!(out, " #[allow(clippy::should_implement_trait)]").ok();
806 }
807 if let Some(attr) = cfg.static_attr {
808 writeln!(out, " #[{attr}]").ok();
809 }
810 if cfg.needs_signature {
811 let sig = function_sig_defaults(&method.params);
812 writeln!(out, " {}{}{}", cfg.signature_prefix, sig, cfg.signature_suffix).ok();
813 }
814 write!(
815 out,
816 " {}{}{}{} {{\n \
817 {body}\n }}",
818 sig_start, sig_params, sig_end, ret,
819 )
820 .ok();
821 out
822}
823
824pub fn gen_impl_block(
826 typ: &TypeDef,
827 mapper: &dyn TypeMapper,
828 cfg: &RustBindingConfig,
829 adapter_bodies: &AdapterBodies,
830 opaque_types: &AHashSet<String>,
831) -> String {
832 gen_impl_block_with_renames(typ, mapper, cfg, adapter_bodies, opaque_types, None)
833}
834
835pub fn gen_impl_block_with_renames(
837 typ: &TypeDef,
838 mapper: &dyn TypeMapper,
839 cfg: &RustBindingConfig,
840 adapter_bodies: &AdapterBodies,
841 opaque_types: &AHashSet<String>,
842 field_renames: Option<&std::collections::HashMap<String, String>>,
843) -> String {
844 let (instance, statics) = partition_methods(&typ.methods);
845 let has_emittable_instance = instance
849 .iter()
850 .any(|m| !m.sanitized || adapter_bodies.contains_key(&format!("{}.{}", typ.name, m.name)));
851 let has_emittable_statics = statics
852 .iter()
853 .any(|m| !m.sanitized || adapter_bodies.contains_key(&format!("{}.{}", typ.name, m.name)));
854 if !has_emittable_instance && !has_emittable_statics && typ.fields.is_empty() {
855 return String::new();
856 }
857
858 let prefixed_name = format!("{}{}", cfg.type_name_prefix, typ.name);
859 let mut out = String::with_capacity(2048);
860 if let Some(block_attr) = cfg.method_block_attr {
861 writeln!(out, "#[{block_attr}]").ok();
862 }
863 writeln!(out, "impl {prefixed_name} {{").ok();
864
865 if !typ.fields.is_empty() {
867 out.push_str(&gen_constructor_with_renames(typ, mapper, cfg, field_renames));
868 out.push_str("\n\n");
869 }
870
871 let empty_mutex_types: AHashSet<String> = AHashSet::new();
873 for m in &instance {
874 let adapter_key = format!("{}.{}", typ.name, m.name);
878 if m.sanitized && !adapter_bodies.contains_key(&adapter_key) {
879 continue;
880 }
881 out.push_str(&gen_method(
882 m,
883 mapper,
884 cfg,
885 typ,
886 false,
887 opaque_types,
888 &empty_mutex_types,
889 adapter_bodies,
890 ));
891 out.push_str("\n\n");
892 }
893
894 for m in &statics {
896 let adapter_key = format!("{}.{}", typ.name, m.name);
898 if m.sanitized && !adapter_bodies.contains_key(&adapter_key) {
899 continue;
900 }
901 out.push_str(&gen_static_method(
902 m,
903 mapper,
904 cfg,
905 typ,
906 adapter_bodies,
907 opaque_types,
908 &empty_mutex_types,
909 ));
910 out.push_str("\n\n");
911 }
912
913 let trimmed = out.trim_end();
915 let mut result = trimmed.to_string();
916 result.push_str("\n}");
917 result
918}
919
920pub fn gen_opaque_impl_block(
927 typ: &TypeDef,
928 mapper: &dyn TypeMapper,
929 cfg: &RustBindingConfig,
930 opaque_types: &AHashSet<String>,
931 mutex_types: &AHashSet<String>,
932 adapter_bodies: &AdapterBodies,
933) -> String {
934 let (instance, statics) = partition_methods(&typ.methods);
935 let has_emittable_instance = instance
937 .iter()
938 .any(|m| !m.sanitized || adapter_bodies.contains_key(&format!("{}.{}", typ.name, m.name)));
939 let has_emittable_statics = statics
940 .iter()
941 .any(|m| !m.sanitized || adapter_bodies.contains_key(&format!("{}.{}", typ.name, m.name)));
942 if !has_emittable_instance && !has_emittable_statics {
943 return String::new();
944 }
945
946 let mut out = String::with_capacity(2048);
947 let prefixed_name = format!("{}{}", cfg.type_name_prefix, typ.name);
948 if let Some(block_attr) = cfg.method_block_attr {
949 writeln!(out, "#[{block_attr}]").ok();
950 }
951 writeln!(out, "impl {prefixed_name} {{").ok();
952
953 for m in &instance {
955 let adapter_key = format!("{}.{}", typ.name, m.name);
958 if m.sanitized && !adapter_bodies.contains_key(&adapter_key) {
959 continue;
960 }
961 out.push_str(&gen_method(
962 m,
963 mapper,
964 cfg,
965 typ,
966 true,
967 opaque_types,
968 mutex_types,
969 adapter_bodies,
970 ));
971 out.push_str("\n\n");
972 }
973
974 for m in &statics {
976 let adapter_key = format!("{}.{}", typ.name, m.name);
978 if m.sanitized && !adapter_bodies.contains_key(&adapter_key) {
979 continue;
980 }
981 out.push_str(&gen_static_method(
982 m,
983 mapper,
984 cfg,
985 typ,
986 adapter_bodies,
987 opaque_types,
988 mutex_types,
989 ));
990 out.push_str("\n\n");
991 }
992
993 let trimmed = out.trim_end();
994 let mut result = trimmed.to_string();
995 result.push_str("\n}");
996 result
997}