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