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::{constructor_parts, 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 let map_fn = |ty: &alef_core::ir::TypeRef| mapper.map_type(ty);
23
24 let (param_list, sig_defaults, assignments) = if typ.has_default {
26 crate::shared::config_constructor_parts_with_options(&typ.fields, &map_fn, cfg.option_duration_on_defaults)
27 } else {
28 constructor_parts(&typ.fields, &map_fn)
29 };
30
31 let mut out = String::with_capacity(512);
32 if typ.fields.len() > 7 {
34 writeln!(out, " #[allow(clippy::too_many_arguments)]").ok();
35 }
36 writeln!(out, " #[must_use]").ok();
37 if cfg.needs_signature {
38 writeln!(
39 out,
40 " {}{}{}",
41 cfg.signature_prefix, sig_defaults, cfg.signature_suffix
42 )
43 .ok();
44 }
45 write!(
46 out,
47 " {}\n pub fn new({param_list}) -> Self {{\n Self {{ {assignments} }}\n }}",
48 cfg.constructor_attr
49 )
50 .ok();
51 out
52}
53
54#[allow(clippy::too_many_arguments)]
63pub fn gen_method(
64 method: &MethodDef,
65 mapper: &dyn TypeMapper,
66 cfg: &RustBindingConfig,
67 typ: &TypeDef,
68 is_opaque: bool,
69 opaque_types: &AHashSet<String>,
70 mutex_types: &AHashSet<String>,
71 adapter_bodies: &AdapterBodies,
72) -> String {
73 let type_name = &typ.name;
74 let core_type_path = typ.rust_path.replace('-', "_");
76
77 let map_fn = |ty: &alef_core::ir::TypeRef| mapper.map_type(ty);
78 let params = function_params(&method.params, &map_fn);
79 let return_type = mapper.map_type(&method.return_type);
80 let ret = mapper.wrap_return(&return_type, method.error_type.is_some());
81
82 let core_import = cfg.core_import;
83
84 let has_ref_named_params = has_named_params(&method.params, opaque_types);
88 let (call_args, ref_let_bindings) = if has_ref_named_params {
89 (
90 gen_call_args_with_let_bindings(&method.params, opaque_types),
91 gen_named_let_bindings_pub(&method.params, opaque_types, core_import),
92 )
93 } else {
94 (gen_call_args(&method.params, opaque_types), String::new())
95 };
96
97 let is_owned_receiver = matches!(method.receiver.as_ref(), Some(alef_core::ir::ReceiverKind::Owned));
98 let is_ref_mut_receiver = matches!(method.receiver.as_ref(), Some(alef_core::ir::ReceiverKind::RefMut));
99
100 let is_functional_ref_mut = !is_opaque
107 && is_ref_mut_receiver
108 && !method.sanitized
109 && method.trait_source.is_none()
110 && method
111 .params
112 .iter()
113 .all(|p| !p.sanitized && crate::shared::is_delegatable_param(&p.ty, opaque_types));
114
115 let is_trait_method = method.trait_source.is_some();
118
119 let self_needs_mutex = is_opaque && mutex_types.contains(type_name.as_str());
121
122 let opaque_can_delegate = is_opaque
129 && !method.sanitized
130 && (!is_ref_mut_receiver || self_needs_mutex)
131 && !is_trait_method
132 && (!is_owned_receiver || typ.is_clone)
133 && method
134 .params
135 .iter()
136 .all(|p| !p.sanitized && crate::shared::is_opaque_delegatable_type(&p.ty))
137 && crate::shared::is_opaque_delegatable_type(&method.return_type);
138
139 let make_core_call = |method_name: &str| -> String {
143 if is_opaque {
144 if is_owned_receiver {
145 if self_needs_mutex {
148 format!("self.inner.lock().unwrap().clone().{method_name}({call_args})")
149 } else {
150 format!("(*self.inner).clone().{method_name}({call_args})")
151 }
152 } else if self_needs_mutex {
153 format!("self.inner.lock().unwrap().{method_name}({call_args})")
155 } else {
156 format!("self.inner.{method_name}({call_args})")
157 }
158 } else {
159 format!("{core_type_path}::from(self.clone()).{method_name}({call_args})")
160 }
161 };
162
163 let make_async_core_call = |method_name: &str| -> String {
166 if is_opaque {
167 if self_needs_mutex {
168 format!("inner.lock().unwrap().{method_name}({call_args})")
169 } else {
170 format!("inner.{method_name}({call_args})")
171 }
172 } else {
173 format!("{core_type_path}::from(self.clone()).{method_name}({call_args})")
174 }
175 };
176
177 let result_expr = apply_return_newtype_unwrap("result", &method.return_newtype_wrapper);
184 let async_result_wrap = if is_opaque {
185 wrap_return_with_mutex(
186 &result_expr,
187 &method.return_type,
188 type_name,
189 opaque_types,
190 mutex_types,
191 is_opaque,
192 method.returns_ref,
193 method.returns_cow,
194 )
195 } else {
196 match &method.return_type {
199 TypeRef::Named(_) | TypeRef::Json => format!("{result_expr}.into()"),
200 _ => result_expr.clone(),
201 }
202 };
203
204 let body = if !opaque_can_delegate {
205 let adapter_key_inner = format!("{}.{}", type_name, method.name);
207 if let Some(adapter_body) = adapter_bodies.get(&adapter_key_inner) {
208 adapter_body.clone()
209 } else if cfg.has_serde
210 && is_opaque
211 && !method.sanitized
212 && !is_trait_method
213 && has_named_params(&method.params, opaque_types)
214 && method.error_type.is_some()
215 && crate::shared::is_opaque_delegatable_type(&method.return_type)
216 {
217 let err_conv = match cfg.async_pattern {
220 AsyncPattern::Pyo3FutureIntoPy => {
221 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
222 }
223 AsyncPattern::NapiNativeAsync => {
224 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
225 }
226 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
227 _ => ".map_err(|e| e.to_string())",
228 };
229 let serde_bindings =
230 gen_serde_let_bindings(&method.params, opaque_types, cfg.core_import, err_conv, " ");
231 let serde_call_args = gen_call_args_with_let_bindings(&method.params, opaque_types);
232 let core_call = if self_needs_mutex {
233 format!("self.inner.lock().unwrap().{}({serde_call_args})", method.name)
234 } else {
235 format!("self.inner.{}({serde_call_args})", method.name)
236 };
237 if matches!(method.return_type, TypeRef::Unit) {
238 format!("{serde_bindings}{core_call}{err_conv}?;\n Ok(())")
239 } else {
240 let wrap = wrap_return_with_mutex(
241 "result",
242 &method.return_type,
243 type_name,
244 opaque_types,
245 mutex_types,
246 is_opaque,
247 method.returns_ref,
248 method.returns_cow,
249 );
250 format!("{serde_bindings}let result = {core_call}{err_conv}?;\n Ok({wrap})")
251 }
252 } else if is_functional_ref_mut {
253 let field_conversions =
262 gen_lossy_binding_to_core_fields_mut(typ, cfg.core_import, cfg.option_duration_on_defaults);
263 let core_call = format!("core_self.{}({call_args})", method.name);
264 if method.error_type.is_some() {
265 let err_conv = match cfg.async_pattern {
266 AsyncPattern::Pyo3FutureIntoPy => {
267 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
268 }
269 AsyncPattern::NapiNativeAsync => {
270 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
271 }
272 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
273 _ => ".map_err(|e| e.to_string())",
274 };
275 format!("{field_conversions}{core_call}{err_conv}?;\n Ok(core_self.into())")
276 } else {
277 format!("{field_conversions}{core_call};\n core_self.into()")
278 }
279 } else if !is_opaque
280 && !method.sanitized
281 && method
282 .params
283 .iter()
284 .all(|p| !p.sanitized && is_simple_non_opaque_param(&p.ty))
285 && crate::shared::is_delegatable_return(&method.return_type)
286 {
287 let is_ref_mut = matches!(method.receiver.as_ref(), Some(alef_core::ir::ReceiverKind::RefMut));
290 let field_conversions = if is_ref_mut {
291 gen_lossy_binding_to_core_fields_mut(typ, cfg.core_import, cfg.option_duration_on_defaults)
292 } else {
293 gen_lossy_binding_to_core_fields(typ, cfg.core_import, cfg.option_duration_on_defaults)
294 };
295 let core_call = format!("core_self.{}({call_args})", method.name);
296 let newtype_suffix = if method.return_newtype_wrapper.is_some() {
297 ".0"
298 } else {
299 ""
300 };
301 let result_wrap = match &method.return_type {
302 TypeRef::Named(n) if n == type_name && (method.returns_cow || method.returns_ref) => {
306 ".into_owned().into()".to_string()
307 }
308 TypeRef::Named(_) if method.returns_cow || method.returns_ref => ".into_owned().into()".to_string(),
309 TypeRef::Named(n) if n == type_name => ".into()".to_string(),
310 TypeRef::Named(_) => ".into()".to_string(),
311 TypeRef::String | TypeRef::Path => {
312 if method.returns_ref {
313 ".to_owned()".to_string()
314 } else {
315 ".into()".to_string()
316 }
317 }
318 TypeRef::Bytes => {
321 if method.returns_ref {
322 ".to_vec()".to_string()
323 } else {
324 ".into()".to_string()
325 }
326 }
327 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::Named(_)) => {
329 if method.returns_ref {
330 ".map(|v| v.clone().into())".to_string()
331 } else {
332 ".map(Into::into)".to_string()
333 }
334 }
335 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::String | TypeRef::Bytes) => {
337 if method.returns_ref {
338 ".map(|v| v.to_owned())".to_string()
339 } else {
340 String::new()
341 }
342 }
343 _ => String::new(),
344 };
345 if method.error_type.is_some() {
346 let err_conv = match cfg.async_pattern {
347 AsyncPattern::Pyo3FutureIntoPy => {
348 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
349 }
350 AsyncPattern::NapiNativeAsync => {
351 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
352 }
353 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
354 _ => ".map_err(|e| e.to_string())",
355 };
356 format!(
357 "{field_conversions}let result = {core_call}{err_conv}?;\n Ok(result{newtype_suffix}{result_wrap})"
358 )
359 } else {
360 format!("{field_conversions}{core_call}{newtype_suffix}{result_wrap}")
361 }
362 } else if is_opaque
363 && !method.sanitized
364 && (!is_ref_mut_receiver || self_needs_mutex)
365 && (!is_owned_receiver || typ.is_clone)
366 && method.error_type.is_none()
367 && method
368 .params
369 .iter()
370 .all(|p| !p.sanitized && crate::shared::is_opaque_delegatable_type(&p.ty))
371 && matches!(&method.return_type, TypeRef::Named(n) if n == type_name)
372 {
373 let core_call = if is_owned_receiver {
376 if self_needs_mutex {
377 format!("self.inner.lock().unwrap().clone().{}({call_args})", method.name)
378 } else {
379 format!("(*self.inner).clone().{}({call_args})", method.name)
380 }
381 } else if self_needs_mutex {
382 format!("self.inner.lock().unwrap().{}({call_args})", method.name)
383 } else {
384 format!("self.inner.{}({call_args})", method.name)
385 };
386 let unwrapped = apply_return_newtype_unwrap(&core_call, &method.return_newtype_wrapper);
387 let arc_expr = if self_needs_mutex {
388 format!("Arc::new(std::sync::Mutex::new({unwrapped}))")
389 } else {
390 format!("Arc::new({unwrapped})")
391 };
392 format!("Self {{ inner: {arc_expr} }}")
393 } else if !is_opaque
394 && !method.sanitized
395 && !is_ref_mut_receiver
396 && (!is_owned_receiver || typ.is_clone)
397 && method.error_type.is_none()
398 && method
399 .params
400 .iter()
401 .all(|p| !p.sanitized && is_simple_non_opaque_param(&p.ty))
402 && matches!(&method.return_type, TypeRef::Named(n) if n == type_name)
403 {
404 let is_ref_mut = matches!(method.receiver.as_ref(), Some(alef_core::ir::ReceiverKind::RefMut));
407 let field_conversions = if is_ref_mut {
408 gen_lossy_binding_to_core_fields_mut(typ, cfg.core_import, cfg.option_duration_on_defaults)
409 } else {
410 gen_lossy_binding_to_core_fields(typ, cfg.core_import, cfg.option_duration_on_defaults)
411 };
412 let core_call = format!("core_self.{}({call_args})", method.name);
413 let newtype_suffix = if method.return_newtype_wrapper.is_some() {
414 ".0"
415 } else {
416 ""
417 };
418 let result_wrap = if method.returns_cow || method.returns_ref {
419 ".into_owned().into()"
420 } else {
421 ".into()"
422 };
423 format!("{field_conversions}{core_call}{newtype_suffix}{result_wrap}")
424 } else {
425 gen_unimplemented_body(
426 &method.return_type,
427 &format!("{type_name}.{}", method.name),
428 method.error_type.is_some(),
429 cfg,
430 &method.params,
431 opaque_types,
432 )
433 }
434 } else if method.is_async {
435 let inner_clone_line = if is_opaque {
436 "let inner = self.inner.clone();\n "
437 } else {
438 ""
439 };
440 let core_call_str = make_async_core_call(&method.name);
441 gen_async_body(
442 &core_call_str,
443 cfg,
444 method.error_type.is_some(),
445 &async_result_wrap,
446 is_opaque,
447 inner_clone_line,
448 matches!(method.return_type, TypeRef::Unit),
449 Some(&return_type),
450 )
451 } else {
452 let core_call = make_core_call(&method.name);
453 if method.error_type.is_some() {
454 let err_conv = match cfg.async_pattern {
456 AsyncPattern::Pyo3FutureIntoPy => {
457 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
458 }
459 AsyncPattern::NapiNativeAsync => {
460 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
461 }
462 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
463 _ => ".map_err(|e| e.to_string())",
464 };
465 if is_opaque {
466 if matches!(method.return_type, TypeRef::Unit) {
467 format!("{core_call}{err_conv}?;\n Ok(())")
469 } else {
470 let wrap = wrap_return_with_mutex(
471 &result_expr,
472 &method.return_type,
473 type_name,
474 opaque_types,
475 mutex_types,
476 is_opaque,
477 method.returns_ref,
478 method.returns_cow,
479 );
480 format!("let result = {core_call}{err_conv}?;\n Ok({wrap})")
481 }
482 } else {
483 format!("{core_call}{err_conv}")
484 }
485 } else if is_opaque {
486 let unwrapped_call = apply_return_newtype_unwrap(&core_call, &method.return_newtype_wrapper);
487 wrap_return_with_mutex(
488 &unwrapped_call,
489 &method.return_type,
490 type_name,
491 opaque_types,
492 mutex_types,
493 is_opaque,
494 method.returns_ref,
495 method.returns_cow,
496 )
497 } else {
498 core_call
499 }
500 };
501 let adapter_key = format!("{}.{}", type_name, method.name);
502 let has_adapter = adapter_bodies.contains_key(&adapter_key);
503
504 let body = if ref_let_bindings.is_empty() || has_adapter {
510 body
511 } else {
512 format!("{ref_let_bindings}{body}")
513 };
514
515 let needs_py = method.is_async && cfg.async_pattern == AsyncPattern::Pyo3FutureIntoPy;
516
517 let body = if needs_py && !opaque_can_delegate && !has_adapter {
524 let err_msg = format!("Not implemented: {type_name}.{}", method.name);
525 let suppress = if method.params.is_empty() {
527 String::new()
528 } else {
529 let names: Vec<&str> = method.params.iter().map(|p| p.name.as_str()).collect();
530 if names.len() == 1 {
531 format!("let _ = {};\n ", names[0])
532 } else {
533 format!("let _ = ({});\n ", names.join(", "))
534 }
535 };
536 format!("{suppress}Err(pyo3::exceptions::PyNotImplementedError::new_err(\"{err_msg}\"))")
537 } else {
538 body
539 };
540 let self_param = match (needs_py, params.is_empty()) {
541 (true, true) => "&self, py: Python<'py>",
542 (true, false) => "&self, py: Python<'py>, ",
543 (false, true) => "&self",
544 (false, false) => "&self, ",
545 };
546
547 let ret = if needs_py {
552 "PyResult<Bound<'py, PyAny>>".to_string()
553 } else if is_functional_ref_mut {
554 mapper.wrap_return("Self", method.error_type.is_some())
555 } else {
556 ret
557 };
558 let method_lifetime = if needs_py { "<'py>" } else { "" };
559
560 let (sig_start, sig_params, sig_end) = if self_param.len() + params.len() > 100 {
562 let wrapped_params = method
563 .params
564 .iter()
565 .map(|p| {
566 let ty = if p.optional {
567 format!("Option<{}>", mapper.map_type(&p.ty))
568 } else {
569 mapper.map_type(&p.ty)
570 };
571 format!("{}: {}", p.name, ty)
572 })
573 .collect::<Vec<_>>()
574 .join(",\n ");
575 let py_param = if needs_py { "\n py: Python<'py>," } else { "" };
576 (
577 format!(
578 "pub fn {}{method_lifetime}(\n &self,{}\n ",
579 method.name, py_param
580 ),
581 wrapped_params,
582 "\n ) -> ".to_string(),
583 )
584 } else {
585 (
586 format!("pub fn {}{method_lifetime}({}", method.name, self_param),
587 params,
588 ") -> ".to_string(),
589 )
590 };
591
592 let mut out = String::with_capacity(1024);
593 let total_params = method.params.len() + 1 + if needs_py { 1 } else { 0 };
595 if total_params > 7 {
596 writeln!(out, " #[allow(clippy::too_many_arguments)]").ok();
597 }
598 if method.error_type.is_some() {
600 writeln!(out, " #[allow(clippy::missing_errors_doc)]").ok();
601 }
602 if is_trait_method_name(&method.name) {
604 writeln!(out, " #[allow(clippy::should_implement_trait)]").ok();
605 }
606 if cfg.needs_signature {
607 let sig = function_sig_defaults(&method.params);
608 writeln!(out, " {}{}{}", cfg.signature_prefix, sig, cfg.signature_suffix).ok();
609 }
610 write!(
611 out,
612 " {}{}{}{} {{\n \
613 {body}\n }}",
614 sig_start, sig_params, sig_end, ret,
615 )
616 .ok();
617 out
618}
619
620pub fn gen_static_method(
622 method: &MethodDef,
623 mapper: &dyn TypeMapper,
624 cfg: &RustBindingConfig,
625 typ: &TypeDef,
626 adapter_bodies: &AdapterBodies,
627 opaque_types: &AHashSet<String>,
628 mutex_types: &AHashSet<String>,
629) -> String {
630 let type_name = &typ.name;
631 let core_type_path = typ.rust_path.replace('-', "_");
633 let map_fn = |ty: &alef_core::ir::TypeRef| mapper.map_type(ty);
634 let params = function_params(&method.params, &map_fn);
635 let return_type = mapper.map_type(&method.return_type);
636 let ret = mapper.wrap_return(&return_type, method.error_type.is_some());
637
638 let core_import = cfg.core_import;
639
640 let use_let_bindings = has_named_params(&method.params, opaque_types);
643 let (call_args, ref_let_bindings) = if use_let_bindings {
644 (
645 gen_call_args_with_let_bindings(&method.params, opaque_types),
646 gen_named_let_bindings_pub(&method.params, opaque_types, core_import),
647 )
648 } else {
649 (gen_call_args(&method.params, opaque_types), String::new())
650 };
651
652 let can_delegate = crate::shared::can_auto_delegate(method, opaque_types);
653
654 let body = if !can_delegate {
655 let adapter_key = format!("{}.{}", type_name, method.name);
657 if let Some(adapter_body) = adapter_bodies.get(&adapter_key) {
658 adapter_body.clone()
659 } else {
660 gen_unimplemented_body(
661 &method.return_type,
662 &format!("{type_name}::{}", method.name),
663 method.error_type.is_some(),
664 cfg,
665 &method.params,
666 opaque_types,
667 )
668 }
669 } else if method.is_async {
670 let core_call = format!("{core_type_path}::{}({call_args})", method.name);
671 let return_wrap = format!("{return_type}::from(result)");
672 gen_async_body(
673 &core_call,
674 cfg,
675 method.error_type.is_some(),
676 &return_wrap,
677 false,
678 "",
679 matches!(method.return_type, TypeRef::Unit),
680 Some(&return_type),
681 )
682 } else {
683 let core_call = format!("{core_type_path}::{}({call_args})", method.name);
684 if method.error_type.is_some() {
685 let err_conv = match cfg.async_pattern {
687 AsyncPattern::Pyo3FutureIntoPy => {
688 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
689 }
690 AsyncPattern::NapiNativeAsync => {
691 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
692 }
693 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
694 _ => ".map_err(|e| e.to_string())",
695 };
696 let val_expr = apply_return_newtype_unwrap("val", &method.return_newtype_wrapper);
698 let wrapped = wrap_return_with_mutex(
699 &val_expr,
700 &method.return_type,
701 type_name,
702 opaque_types,
703 mutex_types,
704 typ.is_opaque,
705 method.returns_ref,
706 method.returns_cow,
707 );
708 if wrapped == val_expr {
709 format!("{core_call}{err_conv}")
710 } else {
711 format!("{core_call}.map(|val| {wrapped}){err_conv}")
712 }
713 } else {
714 let unwrapped_call = apply_return_newtype_unwrap(&core_call, &method.return_newtype_wrapper);
716 wrap_return_with_mutex(
717 &unwrapped_call,
718 &method.return_type,
719 type_name,
720 opaque_types,
721 mutex_types,
722 typ.is_opaque,
723 method.returns_ref,
724 method.returns_cow,
725 )
726 }
727 };
728 let body = if ref_let_bindings.is_empty() {
730 body
731 } else {
732 format!("{ref_let_bindings}{body}")
733 };
734
735 let static_needs_py = method.is_async && cfg.async_pattern == AsyncPattern::Pyo3FutureIntoPy;
736
737 let ret = if static_needs_py {
739 "PyResult<Bound<'py, PyAny>>".to_string()
740 } else {
741 ret
742 };
743 let method_lifetime = if static_needs_py { "<'py>" } else { "" };
744
745 let (sig_start, sig_params, sig_end) = if params.len() > 100 {
747 let wrapped_params = method
748 .params
749 .iter()
750 .map(|p| {
751 let ty = if p.optional {
752 format!("Option<{}>", mapper.map_type(&p.ty))
753 } else {
754 mapper.map_type(&p.ty)
755 };
756 format!("{}: {}", p.name, ty)
757 })
758 .collect::<Vec<_>>()
759 .join(",\n ");
760 if static_needs_py {
762 (
763 format!("pub fn {}{method_lifetime}(py: Python<'py>,\n ", method.name),
764 wrapped_params,
765 "\n ) -> ".to_string(),
766 )
767 } else {
768 (
769 format!("pub fn {}(\n ", method.name),
770 wrapped_params,
771 "\n ) -> ".to_string(),
772 )
773 }
774 } else if static_needs_py {
775 (
776 format!("pub fn {}{method_lifetime}(py: Python<'py>, ", method.name),
777 params,
778 ") -> ".to_string(),
779 )
780 } else {
781 (format!("pub fn {}(", method.name), params, ") -> ".to_string())
782 };
783
784 let mut out = String::with_capacity(1024);
785 let total_params = method.params.len() + if static_needs_py { 1 } else { 0 };
787 if total_params > 7 {
788 writeln!(out, " #[allow(clippy::too_many_arguments)]").ok();
789 }
790 if method.error_type.is_some() {
792 writeln!(out, " #[allow(clippy::missing_errors_doc)]").ok();
793 }
794 if is_trait_method_name(&method.name) {
796 writeln!(out, " #[allow(clippy::should_implement_trait)]").ok();
797 }
798 if let Some(attr) = cfg.static_attr {
799 writeln!(out, " #[{attr}]").ok();
800 }
801 if cfg.needs_signature {
802 let sig = function_sig_defaults(&method.params);
803 writeln!(out, " {}{}{}", cfg.signature_prefix, sig, cfg.signature_suffix).ok();
804 }
805 write!(
806 out,
807 " {}{}{}{} {{\n \
808 {body}\n }}",
809 sig_start, sig_params, sig_end, ret,
810 )
811 .ok();
812 out
813}
814
815pub fn gen_impl_block(
817 typ: &TypeDef,
818 mapper: &dyn TypeMapper,
819 cfg: &RustBindingConfig,
820 adapter_bodies: &AdapterBodies,
821 opaque_types: &AHashSet<String>,
822) -> String {
823 let (instance, statics) = partition_methods(&typ.methods);
824 let has_emittable_instance = instance
828 .iter()
829 .any(|m| !m.sanitized || adapter_bodies.contains_key(&format!("{}.{}", typ.name, m.name)));
830 let has_emittable_statics = statics
831 .iter()
832 .any(|m| !m.sanitized || adapter_bodies.contains_key(&format!("{}.{}", typ.name, m.name)));
833 if !has_emittable_instance && !has_emittable_statics && typ.fields.is_empty() {
834 return String::new();
835 }
836
837 let prefixed_name = format!("{}{}", cfg.type_name_prefix, typ.name);
838 let mut out = String::with_capacity(2048);
839 if let Some(block_attr) = cfg.method_block_attr {
840 writeln!(out, "#[{block_attr}]").ok();
841 }
842 writeln!(out, "impl {prefixed_name} {{").ok();
843
844 if !typ.fields.is_empty() {
846 out.push_str(&gen_constructor(typ, mapper, cfg));
847 out.push_str("\n\n");
848 }
849
850 let empty_mutex_types: AHashSet<String> = AHashSet::new();
852 for m in &instance {
853 let adapter_key = format!("{}.{}", typ.name, m.name);
857 if m.sanitized && !adapter_bodies.contains_key(&adapter_key) {
858 continue;
859 }
860 out.push_str(&gen_method(
861 m,
862 mapper,
863 cfg,
864 typ,
865 false,
866 opaque_types,
867 &empty_mutex_types,
868 adapter_bodies,
869 ));
870 out.push_str("\n\n");
871 }
872
873 for m in &statics {
875 let adapter_key = format!("{}.{}", typ.name, m.name);
877 if m.sanitized && !adapter_bodies.contains_key(&adapter_key) {
878 continue;
879 }
880 out.push_str(&gen_static_method(
881 m,
882 mapper,
883 cfg,
884 typ,
885 adapter_bodies,
886 opaque_types,
887 &empty_mutex_types,
888 ));
889 out.push_str("\n\n");
890 }
891
892 let trimmed = out.trim_end();
894 let mut result = trimmed.to_string();
895 result.push_str("\n}");
896 result
897}
898
899pub fn gen_opaque_impl_block(
906 typ: &TypeDef,
907 mapper: &dyn TypeMapper,
908 cfg: &RustBindingConfig,
909 opaque_types: &AHashSet<String>,
910 mutex_types: &AHashSet<String>,
911 adapter_bodies: &AdapterBodies,
912) -> String {
913 let (instance, statics) = partition_methods(&typ.methods);
914 let has_emittable_instance = instance
916 .iter()
917 .any(|m| !m.sanitized || adapter_bodies.contains_key(&format!("{}.{}", typ.name, m.name)));
918 let has_emittable_statics = statics
919 .iter()
920 .any(|m| !m.sanitized || adapter_bodies.contains_key(&format!("{}.{}", typ.name, m.name)));
921 if !has_emittable_instance && !has_emittable_statics {
922 return String::new();
923 }
924
925 let mut out = String::with_capacity(2048);
926 let prefixed_name = format!("{}{}", cfg.type_name_prefix, typ.name);
927 if let Some(block_attr) = cfg.method_block_attr {
928 writeln!(out, "#[{block_attr}]").ok();
929 }
930 writeln!(out, "impl {prefixed_name} {{").ok();
931
932 for m in &instance {
934 let adapter_key = format!("{}.{}", typ.name, m.name);
937 if m.sanitized && !adapter_bodies.contains_key(&adapter_key) {
938 continue;
939 }
940 out.push_str(&gen_method(
941 m,
942 mapper,
943 cfg,
944 typ,
945 true,
946 opaque_types,
947 mutex_types,
948 adapter_bodies,
949 ));
950 out.push_str("\n\n");
951 }
952
953 for m in &statics {
955 let adapter_key = format!("{}.{}", typ.name, m.name);
957 if m.sanitized && !adapter_bodies.contains_key(&adapter_key) {
958 continue;
959 }
960 out.push_str(&gen_static_method(
961 m,
962 mapper,
963 cfg,
964 typ,
965 adapter_bodies,
966 opaque_types,
967 mutex_types,
968 ));
969 out.push_str("\n\n");
970 }
971
972 let trimmed = out.trim_end();
973 let mut result = trimmed.to_string();
974 result.push_str("\n}");
975 result
976}