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::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 let_bindings = if use_let_bindings {
33 gen_named_let_bindings(&func.params, opaque_types)
34 } else {
35 String::new()
36 };
37 let core_import = cfg.core_import;
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 serde_bindings =
68 gen_serde_let_bindings(&func.params, opaque_types, core_import, serde_err_conv, " ");
69 let core_call = format!("{core_fn_path}({call_args})");
70
71 let returns_ref = func.returns_ref;
73 let wrap_return = |expr: &str| -> String {
74 match &func.return_type {
75 TypeRef::Named(name) if opaque_types.contains(name.as_str()) => {
76 if returns_ref {
77 format!("{name} {{ inner: Arc::new({expr}.clone()) }}")
78 } else {
79 format!("{name} {{ inner: Arc::new({expr}) }}")
80 }
81 }
82 TypeRef::Named(_name) => {
83 if returns_ref {
84 format!("{expr}.clone().into()")
85 } else {
86 format!("{expr}.into()")
87 }
88 }
89 TypeRef::String | TypeRef::Bytes => format!("{expr}.into()"),
90 TypeRef::Path => format!("{expr}.to_string_lossy().to_string()"),
91 TypeRef::Json => format!("{expr}.to_string()"),
92 _ => expr.to_string(),
93 }
94 };
95
96 if matches!(func.return_type, TypeRef::Unit) {
97 format!("{serde_bindings}{core_call}{serde_err_conv}?;\n Ok(())")
99 } else {
100 let wrapped = wrap_return("val");
101 if wrapped == "val" {
102 format!("{serde_bindings}{core_call}{serde_err_conv}")
103 } else {
104 format!("{serde_bindings}{core_call}.map(|val| {wrapped}){serde_err_conv}")
105 }
106 }
107 } else {
108 gen_unimplemented_body(
110 &func.return_type,
111 &func.name,
112 func.error_type.is_some(),
113 cfg,
114 &func.params,
115 )
116 }
117 } else if func.is_async {
118 let core_call = format!("{core_fn_path}({call_args})");
119 let return_wrap = format!("{return_type}::from(result)");
120 let async_body = gen_async_body(
121 &core_call,
122 cfg,
123 func.error_type.is_some(),
124 &return_wrap,
125 false,
126 "",
127 matches!(func.return_type, TypeRef::Unit),
128 );
129 format!("{let_bindings}{async_body}")
130 } else {
131 let core_call = format!("{core_fn_path}({call_args})");
132
133 let returns_ref = func.returns_ref;
135 let wrap_return = |expr: &str| -> String {
136 match &func.return_type {
137 TypeRef::Named(name) if opaque_types.contains(name.as_str()) => {
139 if returns_ref {
140 format!("{name} {{ inner: Arc::new({expr}.clone()) }}")
141 } else {
142 format!("{name} {{ inner: Arc::new({expr}) }}")
143 }
144 }
145 TypeRef::Named(_name) => {
147 if returns_ref {
148 format!("{expr}.clone().into()")
149 } else {
150 format!("{expr}.into()")
151 }
152 }
153 TypeRef::String | TypeRef::Bytes => format!("{expr}.into()"),
155 TypeRef::Path => format!("{expr}.to_string_lossy().to_string()"),
157 TypeRef::Json => format!("{expr}.to_string()"),
159 TypeRef::Optional(inner) => match inner.as_ref() {
161 TypeRef::Named(name) if opaque_types.contains(name.as_str()) => {
162 if returns_ref {
163 format!("{expr}.map(|v| {name} {{ inner: Arc::new(v.clone()) }})")
164 } else {
165 format!("{expr}.map(|v| {name} {{ inner: Arc::new(v) }})")
166 }
167 }
168 TypeRef::Named(_) => {
169 if returns_ref {
170 format!("{expr}.map(|v| v.clone().into())")
171 } else {
172 format!("{expr}.map(Into::into)")
173 }
174 }
175 TypeRef::String | TypeRef::Bytes | TypeRef::Path => {
176 format!("{expr}.map(Into::into)")
177 }
178 _ => expr.to_string(),
179 },
180 TypeRef::Vec(inner) => match inner.as_ref() {
182 TypeRef::Named(name) if opaque_types.contains(name.as_str()) => {
183 if returns_ref {
184 format!("{expr}.into_iter().map(|v| {name} {{ inner: Arc::new(v.clone()) }}).collect()")
185 } else {
186 format!("{expr}.into_iter().map(|v| {name} {{ inner: Arc::new(v) }}).collect()")
187 }
188 }
189 TypeRef::Named(_) => {
190 if returns_ref {
191 format!("{expr}.into_iter().map(|v| v.clone().into()).collect()")
192 } else {
193 format!("{expr}.into_iter().map(Into::into).collect()")
194 }
195 }
196 TypeRef::String | TypeRef::Bytes | TypeRef::Path => {
197 format!("{expr}.into_iter().map(Into::into).collect()")
198 }
199 _ => expr.to_string(),
200 },
201 _ => expr.to_string(),
202 }
203 };
204
205 if func.error_type.is_some() {
206 let err_conv = match cfg.async_pattern {
208 AsyncPattern::Pyo3FutureIntoPy => {
209 ".map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))"
210 }
211 AsyncPattern::NapiNativeAsync => {
212 ".map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))"
213 }
214 AsyncPattern::WasmNativeAsync => ".map_err(|e| JsValue::from_str(&e.to_string()))",
215 _ => ".map_err(|e| e.to_string())",
216 };
217 let wrapped = wrap_return("val");
218 if wrapped == "val" {
219 format!("{core_call}{err_conv}")
220 } else {
221 format!("{core_call}.map(|val| {wrapped}){err_conv}")
222 }
223 } else {
224 wrap_return(&core_call)
225 }
226 };
227
228 let body = if !let_bindings.is_empty() && can_delegate && !func.is_async {
230 format!("{let_bindings}{body}")
231 } else {
232 body
233 };
234
235 let async_kw = if func.is_async { "async " } else { "" };
237 let func_needs_py = func.is_async && cfg.async_pattern == AsyncPattern::Pyo3FutureIntoPy;
238
239 let ret = if func_needs_py {
241 "PyResult<Bound<'py, PyAny>>".to_string()
242 } else {
243 ret
244 };
245 let func_lifetime = if func_needs_py { "<'py>" } else { "" };
246
247 let (func_sig, _params_formatted) = if params.len() > 100 {
248 let wrapped_params = func
249 .params
250 .iter()
251 .map(|p| {
252 let ty = if p.optional {
253 format!("Option<{}>", mapper.map_type(&p.ty))
254 } else {
255 mapper.map_type(&p.ty)
256 };
257 format!("{}: {}", p.name, ty)
258 })
259 .collect::<Vec<_>>()
260 .join(",\n ");
261
262 if func_needs_py {
264 (
265 format!(
266 "pub fn {}{func_lifetime}(py: Python<'py>,\n {}\n) -> {ret}",
267 func.name,
268 wrapped_params,
269 ret = ret
270 ),
271 "",
272 )
273 } else {
274 (
275 format!(
276 "pub {async_kw}fn {}(\n {}\n) -> {ret}",
277 func.name,
278 wrapped_params,
279 ret = ret
280 ),
281 "",
282 )
283 }
284 } else if func_needs_py {
285 (
286 format!(
287 "pub fn {}{func_lifetime}(py: Python<'py>, {params}) -> {ret}",
288 func.name
289 ),
290 "",
291 )
292 } else {
293 (format!("pub {async_kw}fn {}({params}) -> {ret}", func.name), "")
294 };
295
296 let mut out = String::with_capacity(1024);
297 let total_params = func.params.len() + if func_needs_py { 1 } else { 0 };
299 if total_params > 7 {
300 writeln!(out, "#[allow(clippy::too_many_arguments)]").ok();
301 }
302 if func.error_type.is_some() {
304 writeln!(out, "#[allow(clippy::missing_errors_doc)]").ok();
305 }
306 let attr_inner = cfg
307 .function_attr
308 .trim_start_matches('#')
309 .trim_start_matches('[')
310 .trim_end_matches(']');
311 writeln!(out, "#[{attr_inner}]").ok();
312 if cfg.needs_signature {
313 let sig = function_sig_defaults(&func.params);
314 writeln!(out, "{}{}{}", cfg.signature_prefix, sig, cfg.signature_suffix).ok();
315 }
316 write!(out, "{} {{\n {body}\n}}", func_sig,).ok();
317 out
318}
319
320pub fn collect_trait_imports(api: &ApiSurface) -> Vec<String> {
325 let mut traits: AHashSet<String> = AHashSet::new();
326 for typ in &api.types {
327 if !typ.is_opaque {
328 continue;
329 }
330 for method in &typ.methods {
331 if let Some(ref trait_path) = method.trait_source {
332 traits.insert(trait_path.clone());
333 }
334 }
335 }
336 let mut sorted: Vec<String> = traits.into_iter().collect();
337 sorted.sort();
338 sorted
339}