1use crate::generators::binding_helpers::{
2 gen_async_body, gen_call_args, gen_call_args_with_let_bindings, gen_named_let_bindings, gen_serde_let_bindings,
3 gen_unimplemented_body, has_named_params,
4};
5use crate::generators::{AdapterBodies, AsyncPattern, RustBindingConfig};
6use crate::shared::{function_params, function_sig_defaults};
7use crate::type_mapper::TypeMapper;
8use ahash::{AHashMap, AHashSet};
9use alef_core::ir::{ApiSurface, FunctionDef, TypeRef};
10use std::fmt::Write;
11
12pub fn gen_function(
14 func: &FunctionDef,
15 mapper: &dyn TypeMapper,
16 cfg: &RustBindingConfig,
17 adapter_bodies: &AdapterBodies,
18 opaque_types: &AHashSet<String>,
19) -> String {
20 let map_fn = |ty: &alef_core::ir::TypeRef| mapper.map_type(ty);
21 let params = function_params(&func.params, &map_fn);
22 let return_type = mapper.map_type(&func.return_type);
23 let ret = mapper.wrap_return(&return_type, func.error_type.is_some());
24
25 let use_let_bindings = has_named_params(&func.params, opaque_types);
27 let call_args = if use_let_bindings {
28 gen_call_args_with_let_bindings(&func.params, opaque_types)
29 } else {
30 gen_call_args(&func.params, opaque_types)
31 };
32 let core_import = cfg.core_import;
33 let let_bindings = if use_let_bindings {
34 gen_named_let_bindings(&func.params, opaque_types, core_import)
35 } else {
36 String::new()
37 };
38
39 let core_fn_path = {
41 let path = func.rust_path.replace('-', "_");
42 if path.starts_with(core_import) {
43 path
44 } else {
45 format!("{core_import}::{}", func.name)
46 }
47 };
48
49 let can_delegate = crate::shared::can_auto_delegate_function(func, opaque_types);
50
51 let serde_err_conv = match cfg.async_pattern {
53 AsyncPattern::Pyo3FutureIntoPy => ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))",
54 AsyncPattern::NapiNativeAsync => ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))",
55 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
56 _ => ".map_err(|e| e.to_string())",
57 };
58
59 let body = if !can_delegate {
61 if let Some(adapter_body) = adapter_bodies.get(&func.name) {
63 adapter_body.clone()
64 } else if cfg.has_serde && use_let_bindings && func.error_type.is_some() {
65 let is_async_pyo3 = func.is_async && cfg.async_pattern == AsyncPattern::Pyo3FutureIntoPy;
70 let (serde_indent, serde_err_async) = if is_async_pyo3 {
71 (
72 " ",
73 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))",
74 )
75 } else {
76 (" ", serde_err_conv)
77 };
78 let serde_bindings =
79 gen_serde_let_bindings(&func.params, opaque_types, core_import, serde_err_async, serde_indent);
80 let core_call = format!("{core_fn_path}({call_args})");
81
82 let returns_ref = func.returns_ref;
84 let wrap_return = |expr: &str| -> String {
85 match &func.return_type {
86 TypeRef::Vec(inner) => {
87 match inner.as_ref() {
89 TypeRef::Named(_) => {
90 format!("{expr}.into_iter().map(Into::into).collect()")
92 }
93 _ => expr.to_string(),
94 }
95 }
96 TypeRef::Named(name) if opaque_types.contains(name.as_str()) => {
97 if returns_ref {
98 format!("{name} {{ inner: Arc::new({expr}.clone()) }}")
99 } else {
100 format!("{name} {{ inner: Arc::new({expr}) }}")
101 }
102 }
103 TypeRef::Named(_) => {
104 if returns_ref {
106 format!("{return_type}::from({expr}.clone())")
107 } else {
108 format!("{return_type}::from({expr})")
109 }
110 }
111 TypeRef::String | TypeRef::Bytes => format!("{expr}.into()"),
112 TypeRef::Path => format!("{expr}.to_string_lossy().to_string()"),
113 TypeRef::Json => format!("{expr}.to_string()"),
114 _ => expr.to_string(),
115 }
116 };
117
118 if is_async_pyo3 {
119 let is_unit = matches!(func.return_type, TypeRef::Unit);
121 let wrapped = wrap_return("result");
122 let core_await = format!(
123 "{core_call}.await\n .map_err(|e| PyErr::new::<PyRuntimeError, _>(e.to_string()))?"
124 );
125 let inner_body = if is_unit {
126 format!("{serde_bindings}{core_await};\n Ok(())")
127 } else {
128 if wrapped.contains(".into()") || wrapped.contains("::from(") || wrapped.contains("Into::into") {
132 format!(
134 "{serde_bindings}let result = {core_await};\n let wrapped_result: {return_type} = {wrapped};\n Ok(wrapped_result)"
135 )
136 } else {
137 format!("{serde_bindings}let result = {core_await};\n Ok({wrapped})")
138 }
139 };
140 format!("pyo3_async_runtimes::tokio::future_into_py(py, async move {{\n{inner_body}\n }})")
141 } else if func.is_async {
142 let is_unit = matches!(func.return_type, TypeRef::Unit);
144 let wrapped = wrap_return("result");
145 let async_body = gen_async_body(
146 &core_call,
147 cfg,
148 func.error_type.is_some(),
149 &wrapped,
150 false,
151 "",
152 is_unit,
153 Some(&return_type),
154 );
155 format!("{serde_bindings}{async_body}")
156 } else if matches!(func.return_type, TypeRef::Unit) {
157 let await_kw = if func.is_async { ".await" } else { "" };
159 let debug_marker = if func.is_async { "/*ASYNC_UNIT*/ " } else { "" };
160 format!("{serde_bindings}{debug_marker}{core_call}{await_kw}{serde_err_conv}?;\n Ok(())")
161 } else {
162 let wrapped = wrap_return("val");
163 let await_kw = if func.is_async { ".await" } else { "" };
164 if wrapped == "val" {
165 format!("{serde_bindings}{core_call}{await_kw}{serde_err_conv}")
166 } else if wrapped == "val.into()" {
167 format!("{serde_bindings}{core_call}{await_kw}.map(Into::into){serde_err_conv}")
168 } else if let Some(type_path) = wrapped.strip_suffix("::from(val)") {
169 format!("{serde_bindings}{core_call}{await_kw}.map({type_path}::from){serde_err_conv}")
170 } else {
171 format!("{serde_bindings}{core_call}{await_kw}.map(|val| {wrapped}){serde_err_conv}")
172 }
173 }
174 } else if func.is_async && cfg.async_pattern == AsyncPattern::Pyo3FutureIntoPy {
175 let suppress = if func.params.is_empty() {
177 String::new()
178 } else {
179 let names: Vec<&str> = func.params.iter().map(|p| p.name.as_str()).collect();
180 format!("let _ = ({});\n ", names.join(", "))
181 };
182 format!(
183 "{suppress}Err(pyo3::exceptions::PyNotImplementedError::new_err(\"not implemented: {}\"))",
184 func.name
185 )
186 } else {
187 gen_unimplemented_body(
189 &func.return_type,
190 &func.name,
191 func.error_type.is_some(),
192 cfg,
193 &func.params,
194 opaque_types,
195 )
196 }
197 } else if func.is_async {
198 let core_call = format!("{core_fn_path}({call_args})");
200 let return_wrap = match &func.return_type {
203 TypeRef::Named(n) if opaque_types.contains(n.as_str()) => {
204 format!("{n} {{ inner: Arc::new(result) }}")
205 }
206 TypeRef::Named(_) => {
207 format!("{return_type}::from(result)")
208 }
209 TypeRef::Vec(inner) => match inner.as_ref() {
210 TypeRef::Named(n) if opaque_types.contains(n.as_str()) => {
211 format!("result.into_iter().map(|v| {n} {{ inner: Arc::new(v) }}).collect::<Vec<_>>()")
212 }
213 TypeRef::Named(_) => {
214 let inner_mapped = mapper.map_type(inner);
215 format!("result.into_iter().map({inner_mapped}::from).collect::<Vec<_>>()")
216 }
217 _ => "result".to_string(),
218 },
219 TypeRef::Unit => "result".to_string(),
220 _ => super::binding_helpers::wrap_return(
221 "result",
222 &func.return_type,
223 "",
224 opaque_types,
225 false,
226 func.returns_ref,
227 false,
228 ),
229 };
230 let async_body = gen_async_body(
231 &core_call,
232 cfg,
233 func.error_type.is_some(),
234 &return_wrap,
235 false,
236 "",
237 matches!(func.return_type, TypeRef::Unit),
238 Some(&return_type),
239 );
240 format!("{let_bindings}{async_body}")
241 } else {
242 let core_call = format!("{core_fn_path}({call_args})");
243
244 let returns_ref = func.returns_ref;
246 let wrap_return = |expr: &str| -> String {
247 match &func.return_type {
248 TypeRef::Named(name) if opaque_types.contains(name.as_str()) => {
250 if returns_ref {
251 format!("{name} {{ inner: Arc::new({expr}.clone()) }}")
252 } else {
253 format!("{name} {{ inner: Arc::new({expr}) }}")
254 }
255 }
256 TypeRef::Named(_name) => {
258 if returns_ref {
259 format!("{expr}.clone().into()")
260 } else {
261 format!("{expr}.into()")
262 }
263 }
264 TypeRef::String | TypeRef::Bytes => {
266 if returns_ref {
267 format!("{expr}.into()")
268 } else {
269 expr.to_string()
270 }
271 }
272 TypeRef::Path => format!("{expr}.to_string_lossy().to_string()"),
274 TypeRef::Json => format!("{expr}.to_string()"),
276 TypeRef::Optional(inner) => match inner.as_ref() {
278 TypeRef::Named(name) if opaque_types.contains(name.as_str()) => {
279 if returns_ref {
280 format!("{expr}.map(|v| {name} {{ inner: Arc::new(v.clone()) }})")
281 } else {
282 format!("{expr}.map(|v| {name} {{ inner: Arc::new(v) }})")
283 }
284 }
285 TypeRef::Named(_) => {
286 if returns_ref {
287 format!("{expr}.map(|v| v.clone().into())")
288 } else {
289 format!("{expr}.map(Into::into)")
290 }
291 }
292 TypeRef::Path => {
293 format!("{expr}.map(|v| v.to_string_lossy().to_string())")
294 }
295 TypeRef::String | TypeRef::Bytes => {
296 if returns_ref {
297 format!("{expr}.map(Into::into)")
298 } else {
299 expr.to_string()
300 }
301 }
302 TypeRef::Vec(vi) => match vi.as_ref() {
303 TypeRef::Named(name) if opaque_types.contains(name.as_str()) => {
304 format!("{expr}.map(|v| v.into_iter().map(|x| {name} {{ inner: Arc::new(x) }}).collect())")
305 }
306 TypeRef::Named(_) => {
307 format!("{expr}.map(|v| v.into_iter().map(Into::into).collect())")
308 }
309 _ => expr.to_string(),
310 },
311 _ => expr.to_string(),
312 },
313 TypeRef::Vec(inner) => match inner.as_ref() {
315 TypeRef::Named(name) if opaque_types.contains(name.as_str()) => {
316 if returns_ref {
317 format!("{expr}.into_iter().map(|v| {name} {{ inner: Arc::new(v.clone()) }}).collect()")
318 } else {
319 format!("{expr}.into_iter().map(|v| {name} {{ inner: Arc::new(v) }}).collect()")
320 }
321 }
322 TypeRef::Named(_) => {
323 if returns_ref {
324 format!("{expr}.into_iter().map(|v| v.clone().into()).collect()")
325 } else {
326 format!("{expr}.into_iter().map(Into::into).collect()")
327 }
328 }
329 TypeRef::Path => {
330 format!("{expr}.into_iter().map(|v| v.to_string_lossy().to_string()).collect()")
331 }
332 TypeRef::String | TypeRef::Bytes => {
333 if returns_ref {
334 format!("{expr}.into_iter().map(Into::into).collect()")
335 } else {
336 expr.to_string()
337 }
338 }
339 _ => expr.to_string(),
340 },
341 _ => expr.to_string(),
342 }
343 };
344
345 if func.error_type.is_some() {
346 let err_conv = match cfg.async_pattern {
348 AsyncPattern::Pyo3FutureIntoPy => {
349 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
350 }
351 AsyncPattern::NapiNativeAsync => {
352 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
353 }
354 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
355 _ => ".map_err(|e| e.to_string())",
356 };
357 let wrapped = wrap_return("val");
358 if wrapped == "val" {
359 format!("{core_call}{err_conv}")
360 } else if wrapped == "val.into()" {
361 format!("{core_call}.map(Into::into){err_conv}")
362 } else if let Some(type_path) = wrapped.strip_suffix("::from(val)") {
363 format!("{core_call}.map({type_path}::from){err_conv}")
364 } else {
365 format!("{core_call}.map(|val| {wrapped}){err_conv}")
366 }
367 } else {
368 wrap_return(&core_call)
369 }
370 };
371
372 let body = if !let_bindings.is_empty() && !func.is_async {
376 if can_delegate {
377 format!("{let_bindings}{body}")
378 } else {
379 let vec_str_bindings: String = func.params.iter().filter(|p| {
382 p.is_ref && matches!(&p.ty, TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::String | TypeRef::Char))
383 }).map(|p| {
384 if p.optional {
387 format!("let {}_refs: Vec<&str> = {}.as_ref().map(|v| v.iter().map(|s| s.as_str()).collect()).unwrap_or_default();\n ", p.name, p.name)
388 } else {
389 format!("let {}_refs: Vec<&str> = {}.iter().map(|s| s.as_str()).collect();\n ", p.name, p.name)
390 }
391 }).collect();
392 if !vec_str_bindings.is_empty() {
393 format!("{vec_str_bindings}{body}")
394 } else {
395 body
396 }
397 }
398 } else {
399 body
400 };
401
402 let async_kw = if func.is_async { "async " } else { "" };
404 let func_needs_py = func.is_async && cfg.async_pattern == AsyncPattern::Pyo3FutureIntoPy;
405
406 let ret = if func_needs_py {
408 "PyResult<Bound<'py, PyAny>>".to_string()
409 } else {
410 ret
411 };
412 let func_lifetime = if func_needs_py { "<'py>" } else { "" };
413
414 let (func_sig, _params_formatted) = if params.len() > 100 {
415 let mut seen_optional = false;
417 let wrapped_params = func
418 .params
419 .iter()
420 .map(|p| {
421 if p.optional {
422 seen_optional = true;
423 }
424 let ty = if p.optional || seen_optional {
425 format!("Option<{}>", mapper.map_type(&p.ty))
426 } else {
427 mapper.map_type(&p.ty)
428 };
429 format!("{}: {}", p.name, ty)
430 })
431 .collect::<Vec<_>>()
432 .join(",\n ");
433
434 if func_needs_py {
436 (
437 format!(
438 "pub fn {}{func_lifetime}(py: Python<'py>,\n {}\n) -> {ret}",
439 func.name,
440 wrapped_params,
441 ret = ret
442 ),
443 "",
444 )
445 } else {
446 (
447 format!(
448 "pub {async_kw}fn {}(\n {}\n) -> {ret}",
449 func.name,
450 wrapped_params,
451 ret = ret
452 ),
453 "",
454 )
455 }
456 } else if func_needs_py {
457 (
458 format!(
459 "pub fn {}{func_lifetime}(py: Python<'py>, {params}) -> {ret}",
460 func.name
461 ),
462 "",
463 )
464 } else {
465 (format!("pub {async_kw}fn {}({params}) -> {ret}", func.name), "")
466 };
467
468 let mut out = String::with_capacity(1024);
469 let total_params = func.params.len() + if func_needs_py { 1 } else { 0 };
471 if total_params > 7 {
472 writeln!(out, "#[allow(clippy::too_many_arguments)]").ok();
473 }
474 if func.error_type.is_some() {
476 writeln!(out, "#[allow(clippy::missing_errors_doc)]").ok();
477 }
478 let attr_inner = cfg
479 .function_attr
480 .trim_start_matches('#')
481 .trim_start_matches('[')
482 .trim_end_matches(']');
483 writeln!(out, "#[{attr_inner}]").ok();
484 if cfg.needs_signature {
485 let sig = function_sig_defaults(&func.params);
486 writeln!(out, "{}{}{}", cfg.signature_prefix, sig, cfg.signature_suffix).ok();
487 }
488 write!(out, "{} {{\n {body}\n}}", func_sig,).ok();
489 out
490}
491
492pub fn collect_trait_imports(api: &ApiSurface) -> Vec<String> {
499 let mut traits: AHashSet<String> = AHashSet::new();
504 for typ in api.types.iter().filter(|typ| !typ.is_trait) {
505 for method in &typ.methods {
506 if let Some(ref trait_path) = method.trait_source {
507 traits.insert(trait_path.clone());
508 }
509 }
510 }
511
512 let mut by_name: AHashMap<String, String> = AHashMap::new();
514 for path in traits {
515 let name = path.split("::").last().unwrap_or(&path).to_string();
516 let entry = by_name.entry(name).or_insert_with(|| path.clone());
517 if path.len() < entry.len() {
519 *entry = path;
520 }
521 }
522
523 let mut sorted: Vec<String> = by_name.into_values().collect();
524 sorted.sort();
525 sorted
526}
527
528pub fn has_unresolved_trait_methods(api: &ApiSurface) -> bool {
534 let mut method_counts: AHashMap<&str, (usize, usize)> = AHashMap::new(); for typ in api.types.iter().filter(|typ| !typ.is_trait) {
539 if typ.is_trait {
540 continue;
541 }
542 for method in &typ.methods {
543 let entry = method_counts.entry(&method.name).or_insert((0, 0));
544 entry.0 += 1;
545 if method.trait_source.is_some() {
546 entry.1 += 1;
547 }
548 }
549 }
550 method_counts
552 .values()
553 .any(|&(total, with_source)| total >= 3 && with_source == 0)
554}
555
556pub fn collect_explicit_core_imports(api: &ApiSurface) -> Vec<String> {
567 let mut names = std::collections::BTreeSet::new();
568 for typ in api.types.iter().filter(|typ| !typ.is_trait) {
569 names.insert(typ.name.clone());
570 }
571 for e in &api.enums {
572 names.insert(e.name.clone());
573 }
574 names.into_iter().collect()
575}