1use crate::conversions::helpers::{core_prim_str, needs_f64_cast, needs_i32_cast};
2use crate::generators::{AsyncPattern, RustBindingConfig};
3use crate::type_mapper::TypeMapper;
4use ahash::AHashSet;
5use alef_core::ir::{CoreWrapper, ParamDef, TypeDef, TypeRef};
6
7fn arc_wrap(val: &str, name: &str, mutex_types: &AHashSet<String>) -> String {
12 let needs_mutex = mutex_types.contains(name);
13 crate::template_env::render(
14 "binding_helpers/arc_wrap.jinja",
15 minijinja::context! {
16 val => val,
17 needs_mutex => needs_mutex,
18 },
19 )
20 .trim_end_matches('\n')
21 .to_string()
22}
23
24fn expr_is_already_arc(expr: &str) -> bool {
31 let trimmed = expr.trim();
32 trimmed == "self.inner"
33 || trimmed == "self.inner.clone()"
34 || trimmed.starts_with("self.inner.as_ref()")
35 || trimmed.starts_with("self.inner.clone()")
36}
37
38#[allow(clippy::too_many_arguments)]
52pub fn wrap_return_with_mutex(
53 expr: &str,
54 return_type: &TypeRef,
55 type_name: &str,
56 opaque_types: &AHashSet<String>,
57 mutex_types: &AHashSet<String>,
58 self_is_opaque: bool,
59 returns_ref: bool,
60 returns_cow: bool,
61) -> String {
62 wrap_return_with_mutex_mapped(
63 expr,
64 return_type,
65 type_name,
66 opaque_types,
67 mutex_types,
68 self_is_opaque,
69 returns_ref,
70 returns_cow,
71 &crate::type_mapper::IdentityMapper,
72 )
73}
74
75#[allow(clippy::too_many_arguments)]
76pub fn wrap_return_with_mutex_mapped(
77 expr: &str,
78 return_type: &TypeRef,
79 type_name: &str,
80 opaque_types: &AHashSet<String>,
81 mutex_types: &AHashSet<String>,
82 self_is_opaque: bool,
83 returns_ref: bool,
84 returns_cow: bool,
85 mapper: &dyn TypeMapper,
86) -> String {
87 let self_arc = arc_wrap("", type_name, mutex_types); let _ = self_arc; match return_type {
90 TypeRef::Named(n) if n == type_name && self_is_opaque => {
91 if expr_is_already_arc(expr) {
94 return format!("Self {{ inner: {expr} }}");
95 }
96 let inner = if returns_cow {
97 format!("{expr}.into_owned()")
98 } else if returns_ref {
99 format!("{expr}.clone()")
100 } else {
101 expr.to_string()
102 };
103 format!("Self {{ inner: {} }}", arc_wrap(&inner, type_name, mutex_types))
104 }
105 TypeRef::Named(n) if opaque_types.contains(n.as_str()) => {
106 let mapped_n = mapper.named(n);
107 if expr_is_already_arc(expr) {
109 return format!("{mapped_n} {{ inner: {expr} }}");
110 }
111 let inner = if returns_cow {
112 format!("{expr}.into_owned()")
113 } else if returns_ref {
114 format!("{expr}.clone()")
115 } else {
116 expr.to_string()
117 };
118 format!("{mapped_n} {{ inner: {} }}", arc_wrap(&inner, n, mutex_types))
119 }
120 TypeRef::Named(_) => {
121 if returns_cow {
128 format!("{expr}.into_owned().into()")
129 } else if returns_ref {
130 format!("{expr}.clone().into()")
131 } else {
132 format!("{expr}.into()")
133 }
134 }
135 TypeRef::String => {
137 if returns_ref {
138 format!("{expr}.into()")
139 } else {
140 expr.to_string()
141 }
142 }
143 TypeRef::Bytes => format!("{expr}.to_vec()"),
146 TypeRef::Path => format!("{expr}.to_string_lossy().to_string()"),
148 TypeRef::Duration => format!("{expr}.as_millis() as u64"),
150 TypeRef::Json => format!("{expr}.to_string()"),
152 TypeRef::Optional(inner) => match inner.as_ref() {
154 TypeRef::Named(n) if opaque_types.contains(n.as_str()) => {
155 let mapped_n = mapper.named(n);
156 let wrap = arc_wrap("v", n, mutex_types);
157 if returns_ref {
158 format!(
159 "{expr}.map(|v| {mapped_n} {{ inner: {} }})",
160 arc_wrap("v.clone()", n, mutex_types)
161 )
162 } else {
163 format!("{expr}.map(|v| {mapped_n} {{ inner: {wrap} }})")
164 }
165 }
166 TypeRef::Named(_) => {
167 if returns_ref {
168 format!("{expr}.map(|v| v.clone().into())")
169 } else {
170 format!("{expr}.map(Into::into)")
171 }
172 }
173 TypeRef::Path => {
174 format!("{expr}.map(Into::into)")
175 }
176 TypeRef::String | TypeRef::Bytes => {
177 if returns_ref {
178 format!("{expr}.map(Into::into)")
179 } else {
180 expr.to_string()
181 }
182 }
183 TypeRef::Duration => format!("{expr}.map(|d| d.as_millis() as u64)"),
184 TypeRef::Json => format!("{expr}.map(ToString::to_string)"),
185 TypeRef::Vec(vec_inner) => match vec_inner.as_ref() {
187 TypeRef::Named(n) if opaque_types.contains(n.as_str()) => {
188 let mapped_n = mapper.named(n);
189 if returns_ref {
190 let wrap = arc_wrap("x.clone()", n, mutex_types);
191 format!("{expr}.map(|v| v.into_iter().map(|x| {mapped_n} {{ inner: {wrap} }}).collect())")
192 } else {
193 let wrap = arc_wrap("x", n, mutex_types);
194 format!("{expr}.map(|v| v.into_iter().map(|x| {mapped_n} {{ inner: {wrap} }}).collect())")
195 }
196 }
197 TypeRef::Named(_) => {
198 if returns_ref {
199 format!("{expr}.map(|v| v.into_iter().map(|x| x.clone().into()).collect())")
200 } else {
201 format!("{expr}.map(|v| v.into_iter().map(Into::into).collect())")
202 }
203 }
204 _ => expr.to_string(),
205 },
206 _ => expr.to_string(),
207 },
208 TypeRef::Vec(inner) => match inner.as_ref() {
210 TypeRef::Named(n) if opaque_types.contains(n.as_str()) => {
211 let mapped_n = mapper.named(n);
212 if returns_ref {
213 let wrap = arc_wrap("v.clone()", n, mutex_types);
214 format!("{expr}.into_iter().map(|v| {mapped_n} {{ inner: {wrap} }}).collect()")
215 } else {
216 let wrap = arc_wrap("v", n, mutex_types);
217 format!("{expr}.into_iter().map(|v| {mapped_n} {{ inner: {wrap} }}).collect()")
218 }
219 }
220 TypeRef::Named(_) => {
221 if returns_ref {
222 format!("{expr}.into_iter().map(|v| v.clone().into()).collect()")
223 } else {
224 format!("{expr}.into_iter().map(Into::into).collect()")
225 }
226 }
227 TypeRef::Path => {
228 format!("{expr}.into_iter().map(Into::into).collect()")
229 }
230 TypeRef::String => {
231 if returns_ref {
232 format!("{expr}.iter().map(|s| s.to_string()).collect()")
236 } else {
237 expr.to_string()
238 }
239 }
240 TypeRef::Bytes => {
241 if returns_ref {
242 format!("{expr}.iter().map(|b| b.to_vec()).collect()")
243 } else {
244 expr.to_string()
245 }
246 }
247 _ => expr.to_string(),
248 },
249 _ => expr.to_string(),
250 }
251}
252
253pub fn wrap_return(
258 expr: &str,
259 return_type: &TypeRef,
260 type_name: &str,
261 opaque_types: &AHashSet<String>,
262 self_is_opaque: bool,
263 returns_ref: bool,
264 returns_cow: bool,
265) -> String {
266 wrap_return_with_mutex(
267 expr,
268 return_type,
269 type_name,
270 opaque_types,
271 &AHashSet::new(),
272 self_is_opaque,
273 returns_ref,
274 returns_cow,
275 )
276}
277
278pub fn apply_return_newtype_unwrap(expr: &str, return_newtype_wrapper: &Option<String>) -> String {
283 match return_newtype_wrapper {
284 Some(_) => crate::template_env::render(
285 "binding_helpers/return_newtype_unwrap.jinja",
286 minijinja::context! {
287 expr => expr,
288 },
289 )
290 .trim_end_matches('\n')
291 .to_string(),
292 None => expr.to_string(),
293 }
294}
295
296pub fn gen_call_args(params: &[ParamDef], opaque_types: &AHashSet<String>) -> String {
307 params
308 .iter()
309 .enumerate()
310 .map(|(idx, p)| {
311 let promoted = crate::shared::is_promoted_optional(params, idx);
312 let unwrap_suffix = if promoted && p.optional {
317 format!(".expect(\"'{}' is required\")", p.name)
318 } else {
319 String::new()
320 };
321 if let Some(newtype_path) = &p.newtype_wrapper {
324 return if p.optional {
325 format!("{}.map({newtype_path})", p.name)
326 } else if promoted {
327 format!("{newtype_path}({}{})", p.name, unwrap_suffix)
328 } else {
329 format!("{newtype_path}({})", p.name)
330 };
331 }
332 match &p.ty {
333 TypeRef::Named(name) if opaque_types.contains(name.as_str()) => {
334 if p.optional {
336 format!("{}.as_ref().map(|v| &v.inner)", p.name)
337 } else if promoted {
338 format!("{}{}.inner.as_ref()", p.name, unwrap_suffix)
339 } else {
340 format!("&{}.inner", p.name)
341 }
342 }
343 TypeRef::Named(_) => {
344 if p.optional {
345 if p.is_ref {
346 format!("{}.as_ref()", p.name)
349 } else {
350 format!("{}.map(Into::into)", p.name)
351 }
352 } else if promoted {
353 format!("{}{}.into()", p.name, unwrap_suffix)
354 } else {
355 format!("{}.into()", p.name)
356 }
357 }
358 TypeRef::String | TypeRef::Char => {
362 if p.optional {
363 if p.is_ref {
364 format!("{}.as_deref()", p.name)
365 } else {
366 p.name.clone()
367 }
368 } else if promoted {
369 if p.is_ref {
370 format!("&{}{}", p.name, unwrap_suffix)
371 } else {
372 format!("{}{}", p.name, unwrap_suffix)
373 }
374 } else if p.is_ref {
375 format!("&{}", p.name)
376 } else {
377 p.name.clone()
378 }
379 }
380 TypeRef::Path => {
382 if p.optional && p.is_ref {
383 format!("{}.as_deref().map(std::path::Path::new)", p.name)
384 } else if p.optional {
385 format!("{}.map(std::path::PathBuf::from)", p.name)
386 } else if promoted {
387 format!("std::path::PathBuf::from({}{})", p.name, unwrap_suffix)
388 } else if p.is_ref {
389 format!("std::path::Path::new(&{})", p.name)
390 } else {
391 format!("std::path::PathBuf::from({})", p.name)
392 }
393 }
394 TypeRef::Bytes => {
395 if p.optional {
396 if p.is_ref {
397 format!("{}.as_deref()", p.name)
398 } else {
399 p.name.clone()
400 }
401 } else if promoted {
402 if p.is_ref {
405 format!("&{}{}", p.name, unwrap_suffix)
406 } else {
407 format!("{}{}", p.name, unwrap_suffix)
408 }
409 } else {
410 if p.is_ref {
413 format!("&{}", p.name)
414 } else {
415 p.name.clone()
416 }
417 }
418 }
419 TypeRef::Duration => {
421 if p.optional {
422 format!("{}.map(std::time::Duration::from_millis)", p.name)
423 } else if promoted {
424 format!("std::time::Duration::from_millis({}{})", p.name, unwrap_suffix)
425 } else {
426 format!("std::time::Duration::from_millis({})", p.name)
427 }
428 }
429 TypeRef::Json => {
430 if p.optional {
432 format!("{}.as_ref().and_then(|s| serde_json::from_str(s).ok())", p.name)
433 } else if promoted {
434 format!("serde_json::from_str(&{}{}).unwrap_or_default()", p.name, unwrap_suffix)
435 } else {
436 format!("serde_json::from_str(&{}).unwrap_or_default()", p.name)
437 }
438 }
439 TypeRef::Vec(inner) => {
440 if matches!(inner.as_ref(), TypeRef::Named(_)) {
442 if p.optional {
443 if p.is_ref {
444 format!("{}.as_deref()", p.name)
445 } else {
446 p.name.clone()
447 }
448 } else if promoted {
449 if p.is_ref {
450 format!("&{}{}", p.name, unwrap_suffix)
451 } else {
452 format!("{}{}", p.name, unwrap_suffix)
453 }
454 } else if p.is_ref {
455 format!("&{}", p.name)
456 } else {
457 p.name.clone()
458 }
459 } else if promoted {
460 format!("{}{}", p.name, unwrap_suffix)
461 } else if p.is_mut && p.optional {
462 format!("{}.as_deref_mut()", p.name)
463 } else if p.is_mut {
464 format!("&mut {}", p.name)
465 } else if p.is_ref && p.optional {
466 format!("{}.as_deref()", p.name)
467 } else if p.is_ref {
468 format!("&{}", p.name)
469 } else {
470 p.name.clone()
471 }
472 }
473 _ => {
474 if promoted {
475 format!("{}{}", p.name, unwrap_suffix)
476 } else if p.is_mut && p.optional {
477 format!("{}.as_deref_mut()", p.name)
478 } else if p.is_mut {
479 format!("&mut {}", p.name)
480 } else if p.is_ref && p.optional {
481 format!("{}.as_deref()", p.name)
484 } else if p.is_ref {
485 format!("&{}", p.name)
486 } else {
487 p.name.clone()
488 }
489 }
490 }
491 })
492 .collect::<Vec<_>>()
493 .join(", ")
494}
495
496pub fn gen_call_args_cfg(
503 params: &[ParamDef],
504 opaque_types: &AHashSet<String>,
505 cast_uints_to_i32: bool,
506 cast_large_ints_to_f64: bool,
507) -> String {
508 params
509 .iter()
510 .enumerate()
511 .map(|(idx, p)| {
512 let promoted = crate::shared::is_promoted_optional(params, idx);
513 let unwrap_suffix = if promoted && p.optional {
514 format!(".expect(\"'{}' is required\")", p.name)
515 } else {
516 String::new()
517 };
518 if p.newtype_wrapper.is_some() {
520 return gen_call_args(std::slice::from_ref(p), opaque_types);
523 }
524 if let TypeRef::Primitive(prim) = &p.ty {
526 let core_ty = core_prim_str(prim);
527 let needs_cast =
528 (cast_uints_to_i32 && needs_i32_cast(prim)) || (cast_large_ints_to_f64 && needs_f64_cast(prim));
529 if needs_cast {
530 return if p.optional {
531 format!("{}.map(|v| v as {core_ty})", p.name)
532 } else if promoted {
533 format!("({}{}) as {core_ty}", p.name, unwrap_suffix)
534 } else {
535 format!("{} as {core_ty}", p.name)
536 };
537 }
538 }
539 gen_call_args(std::slice::from_ref(p), opaque_types)
541 })
542 .collect::<Vec<_>>()
543 .join(", ")
544}
545
546pub fn gen_call_args_with_let_bindings(params: &[ParamDef], opaque_types: &AHashSet<String>) -> String {
549 params
550 .iter()
551 .enumerate()
552 .map(|(idx, p)| {
553 let promoted = crate::shared::is_promoted_optional(params, idx);
554 let unwrap_suffix = if promoted {
555 format!(".expect(\"'{}' is required\")", p.name)
556 } else {
557 String::new()
558 };
559 if let Some(newtype_path) = &p.newtype_wrapper {
561 return if p.optional {
562 format!("{}.map({newtype_path})", p.name)
563 } else if promoted {
564 format!("{newtype_path}({}{})", p.name, unwrap_suffix)
565 } else {
566 format!("{newtype_path}({})", p.name)
567 };
568 }
569 match &p.ty {
570 TypeRef::Named(name) if opaque_types.contains(name.as_str()) => {
571 if p.optional {
572 format!("{}.as_ref().map(|v| &v.inner)", p.name)
573 } else if promoted {
574 format!("{}{}.inner.as_ref()", p.name, unwrap_suffix)
575 } else {
576 format!("&{}.inner", p.name)
577 }
578 }
579 TypeRef::Named(_) => {
580 if p.optional && p.is_ref {
581 format!("{}_core", p.name)
583 } else if p.is_ref {
584 format!("&{}_core", p.name)
586 } else {
587 format!("{}_core", p.name)
588 }
589 }
590 TypeRef::String | TypeRef::Char => {
591 if p.optional {
592 if p.is_ref {
593 format!("{}.as_deref()", p.name)
594 } else {
595 p.name.clone()
596 }
597 } else if promoted {
598 if p.is_ref {
599 format!("&{}{}", p.name, unwrap_suffix)
600 } else {
601 format!("{}{}", p.name, unwrap_suffix)
602 }
603 } else if p.is_ref {
604 format!("&{}", p.name)
605 } else {
606 p.name.clone()
607 }
608 }
609 TypeRef::Path => {
610 if promoted {
611 format!("std::path::PathBuf::from({}{})", p.name, unwrap_suffix)
612 } else if p.optional && p.is_ref {
613 format!("{}.as_deref().map(std::path::Path::new)", p.name)
614 } else if p.optional {
615 format!("{}.map(std::path::PathBuf::from)", p.name)
616 } else if p.is_ref {
617 format!("std::path::Path::new(&{})", p.name)
618 } else {
619 format!("std::path::PathBuf::from({})", p.name)
620 }
621 }
622 TypeRef::Bytes => {
623 if p.optional {
624 if p.is_ref {
625 format!("{}.as_deref()", p.name)
626 } else {
627 p.name.clone()
628 }
629 } else if promoted {
630 if p.is_ref {
633 format!("&{}{}", p.name, unwrap_suffix)
634 } else {
635 format!("{}{}", p.name, unwrap_suffix)
636 }
637 } else {
638 if p.is_ref {
641 format!("&{}", p.name)
642 } else {
643 p.name.clone()
644 }
645 }
646 }
647 TypeRef::Duration => {
648 if p.optional {
649 format!("{}.map(std::time::Duration::from_millis)", p.name)
650 } else if promoted {
651 format!("std::time::Duration::from_millis({}{})", p.name, unwrap_suffix)
652 } else {
653 format!("std::time::Duration::from_millis({})", p.name)
654 }
655 }
656 TypeRef::Vec(inner) => {
657 if matches!(inner.as_ref(), TypeRef::String) && p.sanitized && p.original_type.is_some() {
660 if p.optional && p.is_ref {
661 format!("{}_core.as_deref()", p.name)
662 } else if p.optional {
663 format!("{}_core", p.name)
664 } else if p.is_ref {
665 format!("&{}_core", p.name)
666 } else {
667 format!("{}_core", p.name)
668 }
669 } else if matches!(inner.as_ref(), TypeRef::Named(_)) {
670 if p.optional && p.is_ref {
672 format!("{}_core.as_deref()", p.name)
674 } else if p.optional {
675 format!("{}_core", p.name)
677 } else if p.is_ref {
678 format!("&{}_core", p.name)
679 } else {
680 format!("{}_core", p.name)
681 }
682 } else if matches!(inner.as_ref(), TypeRef::String | TypeRef::Char) && p.is_ref {
683 if p.optional {
686 format!("{}.as_deref()", p.name)
687 } else {
688 format!("&{}_refs", p.name)
689 }
690 } else if promoted {
691 format!("{}{}", p.name, unwrap_suffix)
692 } else if p.is_ref && p.optional {
693 format!("{}.as_deref()", p.name)
694 } else if p.is_ref {
695 format!("&{}", p.name)
696 } else {
697 p.name.clone()
698 }
699 }
700 _ => {
701 if promoted {
702 format!("{}{}", p.name, unwrap_suffix)
703 } else if p.is_ref && p.optional {
704 format!("{}.as_deref()", p.name)
705 } else if p.is_ref {
706 format!("&{}", p.name)
707 } else {
708 p.name.clone()
709 }
710 }
711 }
712 })
713 .collect::<Vec<_>>()
714 .join(", ")
715}
716
717pub fn gen_named_let_bindings_pub(params: &[ParamDef], opaque_types: &AHashSet<String>, core_import: &str) -> String {
719 gen_named_let_bindings(params, opaque_types, core_import)
720}
721
722pub fn gen_named_let_bindings_no_promote(
725 params: &[ParamDef],
726 opaque_types: &AHashSet<String>,
727 core_import: &str,
728) -> String {
729 gen_named_let_bindings_inner(params, opaque_types, core_import, false)
730}
731
732pub(super) fn gen_named_let_bindings(
733 params: &[ParamDef],
734 opaque_types: &AHashSet<String>,
735 core_import: &str,
736) -> String {
737 gen_named_let_bindings_inner(params, opaque_types, core_import, true)
738}
739
740pub(super) fn gen_named_let_bindings_by_ref(
744 params: &[ParamDef],
745 opaque_types: &AHashSet<String>,
746 core_import: &str,
747) -> String {
748 let mut bindings = String::new();
749 for (idx, p) in params.iter().enumerate() {
750 match &p.ty {
751 TypeRef::Named(name) if !opaque_types.contains(name.as_str()) => {
752 let promoted = crate::shared::is_promoted_optional(params, idx);
753 let core_type_path = format!("{core_import}::{name}");
754 let binding = if p.optional {
755 crate::template_env::render(
756 "binding_helpers/named_let_binding_by_ref_optional.jinja",
757 minijinja::context! {
758 name => &p.name,
759 core_type_path => &core_type_path,
760 },
761 )
762 } else if promoted {
763 crate::template_env::render(
764 "binding_helpers/named_let_binding_by_ref_promoted.jinja",
765 minijinja::context! {
766 name => &p.name,
767 core_type_path => &core_type_path,
768 },
769 )
770 } else {
771 crate::template_env::render(
772 "binding_helpers/named_let_binding_by_ref_simple.jinja",
773 minijinja::context! {
774 name => &p.name,
775 core_type_path => &core_type_path,
776 },
777 )
778 };
779 bindings.push_str(&binding);
780 bindings.push_str("\n ");
781 }
782 TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Named(n) if !opaque_types.contains(n.as_str())) => {
783 let binding = if p.optional {
784 crate::template_env::render(
785 "binding_helpers/vec_named_let_binding_by_ref_optional.jinja",
786 minijinja::context! {
787 name => &p.name,
788 },
789 )
790 } else {
791 let promoted = crate::shared::is_promoted_optional(params, idx);
792 if promoted {
793 crate::template_env::render(
794 "binding_helpers/vec_named_let_binding_by_ref_promoted.jinja",
795 minijinja::context! {
796 name => &p.name,
797 },
798 )
799 } else {
800 crate::template_env::render(
801 "binding_helpers/vec_named_let_binding_by_ref_simple.jinja",
802 minijinja::context! {
803 name => &p.name,
804 },
805 )
806 }
807 };
808 bindings.push_str(&binding);
809 bindings.push_str("\n ");
810 }
811 TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::String | TypeRef::Char) && p.is_ref => {
812 let binding = if p.optional {
813 crate::template_env::render(
814 "binding_helpers/vec_string_refs_binding_optional.jinja",
815 minijinja::context! {
816 name => &p.name,
817 },
818 )
819 } else {
820 crate::template_env::render(
821 "binding_helpers/vec_string_refs_binding_simple.jinja",
822 minijinja::context! {
823 name => &p.name,
824 },
825 )
826 };
827 bindings.push_str(&binding);
828 bindings.push_str("\n ");
829 }
830 _ => {}
831 }
832 }
833 bindings
834}
835
836fn gen_named_let_bindings_inner(
837 params: &[ParamDef],
838 opaque_types: &AHashSet<String>,
839 core_import: &str,
840 promote: bool,
841) -> String {
842 let mut bindings = String::new();
843 for (idx, p) in params.iter().enumerate() {
844 match &p.ty {
845 TypeRef::Named(name) if !opaque_types.contains(name.as_str()) => {
846 let promoted = promote && crate::shared::is_promoted_optional(params, idx);
847 let core_type_path = format!("{}::{}", core_import, name);
848 let binding = if p.optional {
849 if p.is_ref {
850 crate::template_env::render(
851 "binding_helpers/named_let_binding_optional_ref.jinja",
852 minijinja::context! {
853 name => &p.name,
854 core_type_path => &core_type_path,
855 },
856 )
857 } else {
858 crate::template_env::render(
859 "binding_helpers/named_let_binding_optional.jinja",
860 minijinja::context! {
861 name => &p.name,
862 core_type_path => &core_type_path,
863 },
864 )
865 }
866 } else if promoted {
867 crate::template_env::render(
868 "binding_helpers/named_let_binding_promoted.jinja",
869 minijinja::context! {
870 name => &p.name,
871 core_type_path => &core_type_path,
872 },
873 )
874 } else {
875 crate::template_env::render(
876 "binding_helpers/named_let_binding_simple.jinja",
877 minijinja::context! {
878 name => &p.name,
879 core_type_path => &core_type_path,
880 },
881 )
882 };
883 bindings.push_str(&binding);
884 bindings.push_str("\n ");
885 }
886 TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Named(n) if !opaque_types.contains(n.as_str())) => {
887 let promoted = promote && crate::shared::is_promoted_optional(params, idx);
888 let binding = if p.optional && p.is_ref {
889 crate::template_env::render(
890 "binding_helpers/vec_named_let_binding_optional.jinja",
891 minijinja::context! {
892 name => &p.name,
893 },
894 )
895 } else if p.optional {
896 crate::template_env::render(
897 "binding_helpers/vec_named_let_binding_optional_no_ref.jinja",
898 minijinja::context! {
899 name => &p.name,
900 },
901 )
902 } else if promoted {
903 crate::template_env::render(
904 "binding_helpers/vec_named_let_binding_promoted.jinja",
905 minijinja::context! {
906 name => &p.name,
907 },
908 )
909 } else {
910 crate::template_env::render(
911 "binding_helpers/vec_named_let_binding_simple.jinja",
912 minijinja::context! {
913 name => &p.name,
914 },
915 )
916 };
917 bindings.push_str(&binding);
918 bindings.push_str("\n ");
919 }
920 TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::String | TypeRef::Char) && p.is_ref => {
923 let binding = if p.optional {
924 crate::template_env::render(
925 "binding_helpers/vec_string_refs_binding_optional.jinja",
926 minijinja::context! {
927 name => &p.name,
928 },
929 )
930 } else {
931 crate::template_env::render(
932 "binding_helpers/vec_string_refs_binding_simple.jinja",
933 minijinja::context! {
934 name => &p.name,
935 },
936 )
937 };
938 bindings.push_str(&binding);
939 bindings.push_str("\n ");
940 }
941 TypeRef::Vec(inner)
943 if matches!(inner.as_ref(), TypeRef::String) && p.sanitized && p.original_type.is_some() =>
944 {
945 let template = if p.optional {
946 "binding_helpers/sanitized_vec_string_filter_optional.jinja"
947 } else {
948 "binding_helpers/sanitized_vec_string_filter_simple.jinja"
949 };
950 bindings.push_str(&crate::template_env::render(
951 template,
952 minijinja::context! {
953 name => &p.name,
954 },
955 ));
956 }
957 _ => {}
958 }
959 }
960 bindings
961}
962
963pub fn gen_serde_let_bindings(
970 params: &[ParamDef],
971 opaque_types: &AHashSet<String>,
972 core_import: &str,
973 err_conv: &str,
974 indent: &str,
975) -> String {
976 let mut bindings = String::new();
977 for (idx, p) in params.iter().enumerate() {
978 let promoted = crate::shared::is_promoted_optional(params, idx);
979 match &p.ty {
980 TypeRef::Named(name) if !opaque_types.contains(name.as_str()) => {
981 let core_path = format!("{}::{}", core_import, name);
982 if p.optional {
983 bindings.push_str(&crate::template_env::render(
984 "binding_helpers/serde_named_let_binding_optional.jinja",
985 minijinja::context! {
986 name => &p.name,
987 core_path => core_path,
988 err_conv => err_conv,
989 indent => indent,
990 },
991 ));
992 } else if promoted {
993 bindings.push_str(&crate::template_env::render(
997 "binding_helpers/serde_named_let_binding_promoted.jinja",
998 minijinja::context! {
999 name => &p.name,
1000 core_path => core_path,
1001 err_conv => err_conv,
1002 indent => indent,
1003 },
1004 ));
1005 } else {
1006 bindings.push_str(&crate::template_env::render(
1007 "binding_helpers/serde_named_let_binding_simple.jinja",
1008 minijinja::context! {
1009 name => &p.name,
1010 core_path => core_path,
1011 err_conv => err_conv,
1012 indent => indent,
1013 },
1014 ));
1015 }
1016 }
1017 TypeRef::Vec(inner) => {
1018 if let TypeRef::Named(name) = inner.as_ref() {
1019 if !opaque_types.contains(name.as_str()) {
1020 let core_path = format!("{}::{}", core_import, name);
1021 if p.optional {
1022 bindings.push_str(&crate::template_env::render(
1023 "binding_helpers/serde_vec_named_optional.jinja",
1024 minijinja::context! {
1025 name => &p.name,
1026 core_path => core_path,
1027 err_conv => err_conv,
1028 indent => indent,
1029 },
1030 ));
1031 } else {
1032 bindings.push_str(&crate::template_env::render(
1033 "binding_helpers/serde_vec_named_simple.jinja",
1034 minijinja::context! {
1035 name => &p.name,
1036 core_path => core_path,
1037 err_conv => err_conv,
1038 indent => indent,
1039 },
1040 ));
1041 }
1042 }
1043 } else if matches!(inner.as_ref(), TypeRef::String) && p.sanitized && p.original_type.is_some() {
1044 let template = if p.optional {
1047 "binding_helpers/serde_sanitized_vec_string_optional.jinja"
1048 } else {
1049 "binding_helpers/serde_sanitized_vec_string_simple.jinja"
1050 };
1051 bindings.push_str(&crate::template_env::render(
1052 template,
1053 minijinja::context! {
1054 name => &p.name,
1055 err_conv => err_conv,
1056 indent => indent,
1057 },
1058 ));
1059 }
1060 }
1061 _ => {}
1062 }
1063 }
1064 bindings
1065}
1066
1067pub fn has_named_params(params: &[ParamDef], opaque_types: &AHashSet<String>) -> bool {
1072 params.iter().any(|p| match &p.ty {
1073 TypeRef::Named(name) if !opaque_types.contains(name.as_str()) => true,
1074 TypeRef::Vec(inner) => {
1075 matches!(inner.as_ref(), TypeRef::Named(name) if !opaque_types.contains(name.as_str()))
1079 || (matches!(inner.as_ref(), TypeRef::String | TypeRef::Char) && p.is_ref)
1080 || (matches!(inner.as_ref(), TypeRef::String) && p.sanitized && p.original_type.is_some())
1081 }
1082 _ => false,
1083 })
1084}
1085
1086pub fn is_simple_non_opaque_param(ty: &TypeRef) -> bool {
1089 match ty {
1090 TypeRef::Primitive(_)
1091 | TypeRef::String
1092 | TypeRef::Char
1093 | TypeRef::Bytes
1094 | TypeRef::Path
1095 | TypeRef::Unit
1096 | TypeRef::Duration => true,
1097 TypeRef::Optional(inner) => is_simple_non_opaque_param(inner),
1098 _ => false,
1099 }
1100}
1101
1102pub fn gen_lossy_binding_to_core_fields(
1115 typ: &TypeDef,
1116 core_import: &str,
1117 option_duration_on_defaults: bool,
1118 opaque_types: &AHashSet<String>,
1119 cast_uints_to_i32: bool,
1120 cast_large_ints_to_f64: bool,
1121 skip_types: &[String],
1122) -> String {
1123 gen_lossy_binding_to_core_fields_inner(
1124 typ,
1125 core_import,
1126 false,
1127 option_duration_on_defaults,
1128 opaque_types,
1129 cast_uints_to_i32,
1130 cast_large_ints_to_f64,
1131 skip_types,
1132 )
1133}
1134
1135pub fn gen_lossy_binding_to_core_fields_mut(
1137 typ: &TypeDef,
1138 core_import: &str,
1139 option_duration_on_defaults: bool,
1140 opaque_types: &AHashSet<String>,
1141 cast_uints_to_i32: bool,
1142 cast_large_ints_to_f64: bool,
1143 skip_types: &[String],
1144) -> String {
1145 gen_lossy_binding_to_core_fields_inner(
1146 typ,
1147 core_import,
1148 true,
1149 option_duration_on_defaults,
1150 opaque_types,
1151 cast_uints_to_i32,
1152 cast_large_ints_to_f64,
1153 skip_types,
1154 )
1155}
1156
1157#[allow(clippy::too_many_arguments)]
1158fn gen_lossy_binding_to_core_fields_inner(
1159 typ: &TypeDef,
1160 core_import: &str,
1161 needs_mut: bool,
1162 option_duration_on_defaults: bool,
1163 opaque_types: &AHashSet<String>,
1164 cast_uints_to_i32: bool,
1165 cast_large_ints_to_f64: bool,
1166 skip_types: &[String],
1167) -> String {
1168 let core_path = crate::conversions::core_type_path(typ, core_import);
1169 let mut_kw = if needs_mut { "mut " } else { "" };
1170 let allow = if typ.has_stripped_cfg_fields {
1175 "#[allow(clippy::needless_update)]\n "
1176 } else {
1177 ""
1178 };
1179 let mut out = format!("{allow}let {mut_kw}core_self = {core_path} {{\n");
1180 for field in &typ.fields {
1181 if field.cfg.is_some() {
1184 continue;
1185 }
1186 let name = &field.name;
1187 if field.sanitized && field.core_wrapper != CoreWrapper::Cow {
1188 out.push_str(&crate::template_env::render(
1189 "binding_helpers/struct_field_default.jinja",
1190 minijinja::context! {
1191 name => &field.name,
1192 },
1193 ));
1194 out.push('\n');
1195 continue;
1196 }
1197 let is_opaque_named = match &field.ty {
1202 TypeRef::Named(n) => opaque_types.contains(n.as_str()),
1203 TypeRef::Optional(inner) => {
1204 matches!(inner.as_ref(), TypeRef::Named(n) if opaque_types.contains(n.as_str()))
1205 }
1206 _ => false,
1207 };
1208 if is_opaque_named {
1209 out.push_str(&crate::template_env::render(
1210 "binding_helpers/struct_field_default.jinja",
1211 minijinja::context! {
1212 name => &field.name,
1213 },
1214 ));
1215 out.push('\n');
1216 continue;
1217 }
1218 let is_skip_named = match &field.ty {
1221 TypeRef::Named(n) => skip_types.contains(n),
1222 TypeRef::Optional(inner) => {
1223 matches!(inner.as_ref(), TypeRef::Named(n) if skip_types.contains(n))
1224 }
1225 _ => false,
1226 };
1227 if is_skip_named {
1228 out.push_str(&crate::template_env::render(
1229 "binding_helpers/default_field.jinja",
1230 minijinja::context! {
1231 name => &name,
1232 },
1233 ));
1234 continue;
1235 }
1236 let expr = match &field.ty {
1237 TypeRef::Primitive(p) if cast_uints_to_i32 && needs_i32_cast(p) => {
1238 let core_ty = core_prim_str(p);
1239 if field.optional {
1240 format!("self.{name}.map(|v| v as {core_ty})")
1241 } else {
1242 format!("self.{name} as {core_ty}")
1243 }
1244 }
1245 TypeRef::Primitive(p) if cast_large_ints_to_f64 && needs_f64_cast(p) => {
1246 let core_ty = core_prim_str(p);
1247 if field.optional {
1248 format!("self.{name}.map(|v| v as {core_ty})")
1249 } else {
1250 format!("self.{name} as {core_ty}")
1251 }
1252 }
1253 TypeRef::Primitive(_) => format!("self.{name}"),
1254 TypeRef::Duration => {
1255 if field.optional {
1256 format!("self.{name}.map(std::time::Duration::from_millis)")
1257 } else if option_duration_on_defaults && typ.has_default {
1258 format!("self.{name}.map(std::time::Duration::from_millis).unwrap_or_default()")
1263 } else {
1264 format!("std::time::Duration::from_millis(self.{name})")
1265 }
1266 }
1267 TypeRef::String => {
1268 if field.core_wrapper == CoreWrapper::Cow {
1269 format!("self.{name}.clone().into()")
1270 } else {
1271 format!("self.{name}.clone()")
1272 }
1273 }
1274 TypeRef::Bytes => {
1278 if field.core_wrapper == CoreWrapper::Bytes {
1279 format!("self.{name}.clone().into()")
1280 } else {
1281 format!("self.{name}.clone()")
1282 }
1283 }
1284 TypeRef::Char => {
1285 if field.optional {
1286 format!("self.{name}.as_ref().and_then(|s| s.chars().next())")
1287 } else {
1288 format!("self.{name}.chars().next().unwrap_or('*')")
1289 }
1290 }
1291 TypeRef::Path => {
1292 if field.optional {
1293 format!("self.{name}.clone().map(Into::into)")
1294 } else {
1295 format!("self.{name}.clone().into()")
1296 }
1297 }
1298 TypeRef::Named(_) => {
1299 if field.optional {
1300 format!("self.{name}.clone().map(Into::into)")
1301 } else {
1302 format!("self.{name}.clone().into()")
1303 }
1304 }
1305 TypeRef::Vec(inner) => match inner.as_ref() {
1306 TypeRef::Named(_) => {
1307 if field.optional {
1308 format!("self.{name}.clone().map(|v| v.into_iter().map(Into::into).collect())")
1310 } else {
1311 format!("self.{name}.clone().into_iter().map(Into::into).collect()")
1312 }
1313 }
1314 TypeRef::Primitive(p) if cast_uints_to_i32 && needs_i32_cast(p) => {
1316 let core_ty = core_prim_str(p);
1317 if field.optional {
1318 format!("self.{name}.clone().map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
1319 } else {
1320 format!("self.{name}.clone().into_iter().map(|v| v as {core_ty}).collect()")
1321 }
1322 }
1323 TypeRef::Primitive(p) if cast_large_ints_to_f64 && needs_f64_cast(p) => {
1325 let core_ty = core_prim_str(p);
1326 if field.optional {
1327 format!("self.{name}.clone().map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
1328 } else {
1329 format!("self.{name}.clone().into_iter().map(|v| v as {core_ty}).collect()")
1330 }
1331 }
1332 _ => format!("self.{name}.clone()"),
1333 },
1334 TypeRef::Optional(inner) => {
1335 let base = match inner.as_ref() {
1339 TypeRef::Named(_) => {
1340 format!("self.{name}.clone().map(Into::into)")
1341 }
1342 TypeRef::Duration => {
1343 format!("self.{name}.map(|v| std::time::Duration::from_millis(v as u64))")
1344 }
1345 TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Named(_)) => {
1346 format!("self.{name}.clone().map(|v| v.into_iter().map(Into::into).collect())")
1347 }
1348 TypeRef::Vec(vi) => match vi.as_ref() {
1350 TypeRef::Primitive(p) if cast_uints_to_i32 && needs_i32_cast(p) => {
1351 let core_ty = core_prim_str(p);
1352 format!("self.{name}.clone().map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
1353 }
1354 TypeRef::Primitive(p) if cast_large_ints_to_f64 && needs_f64_cast(p) => {
1356 let core_ty = core_prim_str(p);
1357 format!("self.{name}.clone().map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
1358 }
1359 _ => format!("self.{name}.clone()"),
1360 },
1361 _ => format!("self.{name}.clone()"),
1362 };
1363 if field.optional {
1364 format!("({base}).map(Some)")
1365 } else {
1366 base
1367 }
1368 }
1369 TypeRef::Map(_, v) => match v.as_ref() {
1370 TypeRef::Json => {
1371 if field.optional {
1376 format!(
1377 "self.{name}.clone().map(|m| m.into_iter().map(|(k, v)| \
1378 (k.into(), serde_json::from_str(&v).unwrap_or(serde_json::Value::String(v)))).collect())"
1379 )
1380 } else {
1381 format!(
1382 "self.{name}.clone().into_iter().map(|(k, v)| \
1383 (k.into(), serde_json::from_str(&v).unwrap_or(serde_json::Value::String(v)))).collect()"
1384 )
1385 }
1386 }
1387 TypeRef::Named(_) => {
1390 if field.optional {
1391 format!(
1392 "self.{name}.clone().map(|m| m.into_iter().map(|(k, v)| (k.into(), v.into())).collect())"
1393 )
1394 } else {
1395 format!("self.{name}.clone().into_iter().map(|(k, v)| (k.into(), v.into())).collect()")
1396 }
1397 }
1398 TypeRef::Primitive(p) if cast_uints_to_i32 && needs_i32_cast(p) => {
1400 let core_ty = core_prim_str(p);
1401 if field.optional {
1402 format!(
1403 "self.{name}.clone().map(|m| m.into_iter().map(|(k, v)| (k.into(), v as {core_ty})).collect())"
1404 )
1405 } else {
1406 format!("self.{name}.clone().into_iter().map(|(k, v)| (k.into(), v as {core_ty})).collect()")
1407 }
1408 }
1409 TypeRef::Primitive(p) if cast_large_ints_to_f64 && needs_f64_cast(p) => {
1411 let core_ty = core_prim_str(p);
1412 if field.optional {
1413 format!(
1414 "self.{name}.clone().map(|m| m.into_iter().map(|(k, v)| (k.into(), v as {core_ty})).collect())"
1415 )
1416 } else {
1417 format!("self.{name}.clone().into_iter().map(|(k, v)| (k.into(), v as {core_ty})).collect()")
1418 }
1419 }
1420 _ => {
1422 if field.optional {
1423 format!("self.{name}.clone().map(|m| m.into_iter().map(|(k, v)| (k.into(), v)).collect())")
1424 } else {
1425 format!("self.{name}.clone().into_iter().map(|(k, v)| (k.into(), v)).collect()")
1426 }
1427 }
1428 },
1429 TypeRef::Unit => format!("self.{name}.clone()"),
1430 TypeRef::Json => {
1431 if field.optional {
1433 format!("self.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok())")
1434 } else {
1435 format!("serde_json::from_str(&self.{name}).unwrap_or_default()")
1436 }
1437 }
1438 };
1439 let expr = if let Some(newtype_path) = &field.newtype_wrapper {
1444 match &field.ty {
1445 TypeRef::Optional(_) => format!("({expr}).map({newtype_path})"),
1446 TypeRef::Vec(_) => format!("({expr}).into_iter().map({newtype_path}).collect::<Vec<_>>()"),
1447 _ if field.optional => format!("({expr}).map({newtype_path})"),
1448 _ => format!("{newtype_path}({expr})"),
1449 }
1450 } else {
1451 expr
1452 };
1453 out.push_str(&crate::template_env::render(
1454 "binding_helpers/struct_field_line.jinja",
1455 minijinja::context! {
1456 name => &field.name,
1457 expr => &expr,
1458 },
1459 ));
1460 out.push('\n');
1461 }
1462 if typ.has_stripped_cfg_fields {
1464 out.push_str(" ..Default::default()\n");
1465 }
1466 out.push_str(" };\n ");
1467 out
1468}
1469
1470#[allow(clippy::too_many_arguments)]
1485pub fn gen_async_body(
1486 core_call: &str,
1487 cfg: &RustBindingConfig,
1488 has_error: bool,
1489 return_wrap: &str,
1490 is_opaque: bool,
1491 inner_clone_line: &str,
1492 is_unit_return: bool,
1493 return_type: Option<&str>,
1494) -> String {
1495 let pattern_body = match cfg.async_pattern {
1496 AsyncPattern::Pyo3FutureIntoPy => {
1497 let result_handling = if has_error {
1498 format!(
1499 "let result = {core_call}.await\n \
1500 .map_err(|e| PyErr::new::<PyRuntimeError, _>(e.to_string()))?;"
1501 )
1502 } else if is_unit_return {
1503 format!("{core_call}.await;")
1504 } else {
1505 format!("let result = {core_call}.await;")
1506 };
1507 let (ok_expr, extra_binding) = if is_unit_return && !has_error {
1508 ("()".to_string(), String::new())
1509 } else if return_wrap.contains(".into()") || return_wrap.contains("::from(") {
1510 let wrapped_var = "wrapped_result";
1514 let binding = if let Some(ret_type) = return_type {
1515 format!("let {wrapped_var}: {ret_type} = {return_wrap};\n ")
1517 } else {
1518 format!("let {wrapped_var} = {return_wrap};\n ")
1519 };
1520 (wrapped_var.to_string(), binding)
1521 } else {
1522 (return_wrap.to_string(), String::new())
1523 };
1524 crate::template_env::render(
1525 "binding_helpers/async_body_pyo3.jinja",
1526 minijinja::context! {
1527 result_handling => result_handling,
1528 extra_binding => extra_binding,
1529 ok_expr => ok_expr,
1530 },
1531 )
1532 }
1533 AsyncPattern::WasmNativeAsync => {
1534 let result_handling = if has_error {
1535 format!(
1536 "let result = {core_call}.await\n \
1537 .map_err(|e| JsValue::from_str(&e.to_string()))?;"
1538 )
1539 } else if is_unit_return {
1540 format!("{core_call}.await;")
1541 } else {
1542 format!("let result = {core_call}.await;")
1543 };
1544 let ok_expr = if is_unit_return && !has_error {
1545 "()"
1546 } else {
1547 return_wrap
1548 };
1549 crate::template_env::render(
1550 "binding_helpers/async_body_wasm.jinja",
1551 minijinja::context! {
1552 result_handling => result_handling,
1553 ok_expr => ok_expr,
1554 },
1555 )
1556 }
1557 AsyncPattern::NapiNativeAsync => {
1558 let result_handling = if has_error {
1559 format!(
1560 "let result = {core_call}.await\n \
1561 .map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))?;"
1562 )
1563 } else if is_unit_return {
1564 format!("{core_call}.await;")
1565 } else {
1566 format!("let result = {core_call}.await;")
1567 };
1568 let (needs_ok_wrapper, ok_expr) = if !has_error && !is_unit_return {
1569 (false, return_wrap.to_string())
1571 } else {
1572 let expr = if is_unit_return && !has_error {
1573 "()".to_string()
1574 } else {
1575 return_wrap.to_string()
1576 };
1577 (true, expr)
1578 };
1579 crate::template_env::render(
1580 "binding_helpers/async_body_napi.jinja",
1581 minijinja::context! {
1582 result_handling => result_handling,
1583 needs_ok_wrapper => needs_ok_wrapper,
1584 ok_expr => ok_expr,
1585 return_wrap => return_wrap,
1586 },
1587 )
1588 }
1589 AsyncPattern::TokioBlockOn => {
1590 let rt_new = "tokio::runtime::Runtime::new()\
1591 .map_err(|e| extendr_api::Error::Other(e.to_string()))?";
1592 crate::template_env::render(
1593 "binding_helpers/async_body_tokio.jinja",
1594 minijinja::context! {
1595 has_error => has_error,
1596 is_opaque => is_opaque,
1597 is_unit_return => is_unit_return,
1598 core_call => core_call,
1599 return_wrap => return_wrap,
1600 rt_new => rt_new,
1601 },
1602 )
1603 }
1604 AsyncPattern::None => "todo!(\"async not supported by backend\")".to_string(),
1605 };
1606 if inner_clone_line.is_empty() {
1607 pattern_body
1608 } else {
1609 format!("{inner_clone_line}{pattern_body}")
1610 }
1611}
1612
1613pub fn gen_unimplemented_body(
1620 return_type: &TypeRef,
1621 fn_name: &str,
1622 has_error: bool,
1623 cfg: &RustBindingConfig,
1624 params: &[ParamDef],
1625 opaque_types: &AHashSet<String>,
1626) -> String {
1627 let suppress = if params.is_empty() {
1629 String::new()
1630 } else {
1631 let names: Vec<&str> = params.iter().map(|p| p.name.as_str()).collect();
1632 if names.len() == 1 {
1633 format!("let _ = {};\n ", names[0])
1634 } else {
1635 format!("let _ = ({});\n ", names.join(", "))
1636 }
1637 };
1638 let err_msg = format!("Not implemented: {fn_name}");
1639 let body = if has_error {
1640 match cfg.async_pattern {
1642 AsyncPattern::Pyo3FutureIntoPy => {
1643 format!("Err(pyo3::exceptions::PyNotImplementedError::new_err(\"{err_msg}\"))")
1644 }
1645 AsyncPattern::NapiNativeAsync => {
1646 format!("Err(napi::Error::new(napi::Status::GenericFailure, \"{err_msg}\"))")
1647 }
1648 AsyncPattern::WasmNativeAsync => {
1649 format!("Err(JsValue::from_str(\"{err_msg}\"))")
1650 }
1651 _ if cfg.cast_uints_to_i32 => {
1654 format!("Err(extendr_api::Error::Other(\"{err_msg}\".to_string()))")
1655 }
1656 _ => format!("Err(\"{err_msg}\".to_string())"),
1657 }
1658 } else {
1659 match return_type {
1661 TypeRef::Unit => "()".to_string(),
1662 TypeRef::String | TypeRef::Char | TypeRef::Path => format!("String::from(\"[unimplemented: {fn_name}]\")"),
1663 TypeRef::Bytes => "Vec::new()".to_string(),
1664 TypeRef::Primitive(p) => match p {
1665 alef_core::ir::PrimitiveType::Bool => "false".to_string(),
1666 alef_core::ir::PrimitiveType::F32 => "0.0f32".to_string(),
1667 alef_core::ir::PrimitiveType::F64 => "0.0f64".to_string(),
1668 _ => "0".to_string(),
1669 },
1670 TypeRef::Optional(_) => "None".to_string(),
1671 TypeRef::Vec(_) => "Vec::new()".to_string(),
1672 TypeRef::Map(_, _) => "Default::default()".to_string(),
1673 TypeRef::Duration => "0".to_string(),
1674 TypeRef::Named(name) => {
1675 if opaque_types.contains(name.as_str()) {
1679 format!("todo!(\"{err_msg}\")")
1680 } else {
1681 "Default::default()".to_string()
1682 }
1683 }
1684 TypeRef::Json => {
1685 "Default::default()".to_string()
1687 }
1688 }
1689 };
1690 format!("{suppress}{body}")
1691}