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 {
1086 match ty {
1087 TypeRef::Primitive(_)
1088 | TypeRef::String
1089 | TypeRef::Char
1090 | TypeRef::Bytes
1091 | TypeRef::Path
1092 | TypeRef::Unit
1093 | TypeRef::Duration => true,
1094 TypeRef::Optional(inner) => is_simple_non_opaque_param(inner),
1095 _ => false,
1096 }
1097}
1098
1099pub fn gen_lossy_binding_to_core_fields(
1112 typ: &TypeDef,
1113 core_import: &str,
1114 option_duration_on_defaults: bool,
1115 opaque_types: &AHashSet<String>,
1116 cast_uints_to_i32: bool,
1117 cast_large_ints_to_f64: bool,
1118 skip_types: &[String],
1119) -> String {
1120 gen_lossy_binding_to_core_fields_inner(
1121 typ,
1122 core_import,
1123 false,
1124 option_duration_on_defaults,
1125 opaque_types,
1126 cast_uints_to_i32,
1127 cast_large_ints_to_f64,
1128 skip_types,
1129 )
1130}
1131
1132pub fn gen_lossy_binding_to_core_fields_mut(
1134 typ: &TypeDef,
1135 core_import: &str,
1136 option_duration_on_defaults: bool,
1137 opaque_types: &AHashSet<String>,
1138 cast_uints_to_i32: bool,
1139 cast_large_ints_to_f64: bool,
1140 skip_types: &[String],
1141) -> String {
1142 gen_lossy_binding_to_core_fields_inner(
1143 typ,
1144 core_import,
1145 true,
1146 option_duration_on_defaults,
1147 opaque_types,
1148 cast_uints_to_i32,
1149 cast_large_ints_to_f64,
1150 skip_types,
1151 )
1152}
1153
1154#[allow(clippy::too_many_arguments)]
1155fn gen_lossy_binding_to_core_fields_inner(
1156 typ: &TypeDef,
1157 core_import: &str,
1158 needs_mut: bool,
1159 option_duration_on_defaults: bool,
1160 opaque_types: &AHashSet<String>,
1161 cast_uints_to_i32: bool,
1162 cast_large_ints_to_f64: bool,
1163 skip_types: &[String],
1164) -> String {
1165 let core_path = crate::conversions::core_type_path(typ, core_import);
1166 let mut_kw = if needs_mut { "mut " } else { "" };
1167 let allow = if typ.has_stripped_cfg_fields {
1172 "#[allow(clippy::needless_update)]\n "
1173 } else {
1174 ""
1175 };
1176 let mut out = format!("{allow}let {mut_kw}core_self = {core_path} {{\n");
1177 for field in &typ.fields {
1178 if field.cfg.is_some() {
1181 continue;
1182 }
1183 let name = &field.name;
1184 if field.sanitized && field.core_wrapper != CoreWrapper::Cow {
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 out.push('\n');
1192 continue;
1193 }
1194 let is_opaque_named = match &field.ty {
1199 TypeRef::Named(n) => opaque_types.contains(n.as_str()),
1200 TypeRef::Optional(inner) => {
1201 matches!(inner.as_ref(), TypeRef::Named(n) if opaque_types.contains(n.as_str()))
1202 }
1203 _ => false,
1204 };
1205 if is_opaque_named {
1206 out.push_str(&crate::template_env::render(
1207 "binding_helpers/struct_field_default.jinja",
1208 minijinja::context! {
1209 name => &field.name,
1210 },
1211 ));
1212 out.push('\n');
1213 continue;
1214 }
1215 let is_skip_named = match &field.ty {
1218 TypeRef::Named(n) => skip_types.contains(n),
1219 TypeRef::Optional(inner) => {
1220 matches!(inner.as_ref(), TypeRef::Named(n) if skip_types.contains(n))
1221 }
1222 _ => false,
1223 };
1224 if is_skip_named {
1225 out.push_str(&crate::template_env::render(
1226 "binding_helpers/default_field.jinja",
1227 minijinja::context! {
1228 name => &name,
1229 },
1230 ));
1231 continue;
1232 }
1233 let expr = match &field.ty {
1234 TypeRef::Primitive(p) if cast_uints_to_i32 && needs_i32_cast(p) => {
1235 let core_ty = core_prim_str(p);
1236 if field.optional {
1237 format!("self.{name}.map(|v| v as {core_ty})")
1238 } else {
1239 format!("self.{name} as {core_ty}")
1240 }
1241 }
1242 TypeRef::Primitive(p) if cast_large_ints_to_f64 && needs_f64_cast(p) => {
1243 let core_ty = core_prim_str(p);
1244 if field.optional {
1245 format!("self.{name}.map(|v| v as {core_ty})")
1246 } else {
1247 format!("self.{name} as {core_ty}")
1248 }
1249 }
1250 TypeRef::Primitive(_) => format!("self.{name}"),
1251 TypeRef::Duration => {
1252 if field.optional {
1253 format!("self.{name}.map(std::time::Duration::from_millis)")
1254 } else if option_duration_on_defaults && typ.has_default {
1255 format!("self.{name}.map(std::time::Duration::from_millis).unwrap_or_default()")
1260 } else {
1261 format!("std::time::Duration::from_millis(self.{name})")
1262 }
1263 }
1264 TypeRef::String => {
1265 if field.core_wrapper == CoreWrapper::Cow {
1266 format!("self.{name}.clone().into()")
1267 } else {
1268 format!("self.{name}.clone()")
1269 }
1270 }
1271 TypeRef::Bytes => {
1275 if field.core_wrapper == CoreWrapper::Bytes {
1276 format!("self.{name}.clone().into()")
1277 } else {
1278 format!("self.{name}.clone()")
1279 }
1280 }
1281 TypeRef::Char => {
1282 if field.optional {
1283 format!("self.{name}.as_ref().and_then(|s| s.chars().next())")
1284 } else {
1285 format!("self.{name}.chars().next().unwrap_or('*')")
1286 }
1287 }
1288 TypeRef::Path => {
1289 if field.optional {
1290 format!("self.{name}.clone().map(Into::into)")
1291 } else {
1292 format!("self.{name}.clone().into()")
1293 }
1294 }
1295 TypeRef::Named(_) => {
1296 if field.optional {
1297 format!("self.{name}.clone().map(Into::into)")
1298 } else {
1299 format!("self.{name}.clone().into()")
1300 }
1301 }
1302 TypeRef::Vec(inner) => match inner.as_ref() {
1303 TypeRef::Named(_) => {
1304 if field.optional {
1305 format!("self.{name}.clone().map(|v| v.into_iter().map(Into::into).collect())")
1307 } else {
1308 format!("self.{name}.clone().into_iter().map(Into::into).collect()")
1309 }
1310 }
1311 TypeRef::Primitive(p) if cast_uints_to_i32 && needs_i32_cast(p) => {
1313 let core_ty = core_prim_str(p);
1314 if field.optional {
1315 format!("self.{name}.clone().map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
1316 } else {
1317 format!("self.{name}.clone().into_iter().map(|v| v as {core_ty}).collect()")
1318 }
1319 }
1320 TypeRef::Primitive(p) if cast_large_ints_to_f64 && needs_f64_cast(p) => {
1322 let core_ty = core_prim_str(p);
1323 if field.optional {
1324 format!("self.{name}.clone().map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
1325 } else {
1326 format!("self.{name}.clone().into_iter().map(|v| v as {core_ty}).collect()")
1327 }
1328 }
1329 _ => format!("self.{name}.clone()"),
1330 },
1331 TypeRef::Optional(inner) => {
1332 let base = match inner.as_ref() {
1336 TypeRef::Named(_) => {
1337 format!("self.{name}.clone().map(Into::into)")
1338 }
1339 TypeRef::Duration => {
1340 format!("self.{name}.map(|v| std::time::Duration::from_millis(v as u64))")
1341 }
1342 TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Named(_)) => {
1343 format!("self.{name}.clone().map(|v| v.into_iter().map(Into::into).collect())")
1344 }
1345 TypeRef::Vec(vi) => match vi.as_ref() {
1347 TypeRef::Primitive(p) if cast_uints_to_i32 && needs_i32_cast(p) => {
1348 let core_ty = core_prim_str(p);
1349 format!("self.{name}.clone().map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
1350 }
1351 TypeRef::Primitive(p) if cast_large_ints_to_f64 && needs_f64_cast(p) => {
1353 let core_ty = core_prim_str(p);
1354 format!("self.{name}.clone().map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
1355 }
1356 _ => format!("self.{name}.clone()"),
1357 },
1358 _ => format!("self.{name}.clone()"),
1359 };
1360 if field.optional {
1361 format!("({base}).map(Some)")
1362 } else {
1363 base
1364 }
1365 }
1366 TypeRef::Map(_, v) => match v.as_ref() {
1367 TypeRef::Json => {
1368 if field.optional {
1373 format!(
1374 "self.{name}.clone().map(|m| m.into_iter().map(|(k, v)| \
1375 (k.into(), serde_json::from_str(&v).unwrap_or(serde_json::Value::String(v)))).collect())"
1376 )
1377 } else {
1378 format!(
1379 "self.{name}.clone().into_iter().map(|(k, v)| \
1380 (k.into(), serde_json::from_str(&v).unwrap_or(serde_json::Value::String(v)))).collect()"
1381 )
1382 }
1383 }
1384 TypeRef::Named(_) => {
1387 if field.optional {
1388 format!(
1389 "self.{name}.clone().map(|m| m.into_iter().map(|(k, v)| (k.into(), v.into())).collect())"
1390 )
1391 } else {
1392 format!("self.{name}.clone().into_iter().map(|(k, v)| (k.into(), v.into())).collect()")
1393 }
1394 }
1395 TypeRef::Primitive(p) if cast_uints_to_i32 && needs_i32_cast(p) => {
1397 let core_ty = core_prim_str(p);
1398 if field.optional {
1399 format!(
1400 "self.{name}.clone().map(|m| m.into_iter().map(|(k, v)| (k.into(), v as {core_ty})).collect())"
1401 )
1402 } else {
1403 format!("self.{name}.clone().into_iter().map(|(k, v)| (k.into(), v as {core_ty})).collect()")
1404 }
1405 }
1406 TypeRef::Primitive(p) if cast_large_ints_to_f64 && needs_f64_cast(p) => {
1408 let core_ty = core_prim_str(p);
1409 if field.optional {
1410 format!(
1411 "self.{name}.clone().map(|m| m.into_iter().map(|(k, v)| (k.into(), v as {core_ty})).collect())"
1412 )
1413 } else {
1414 format!("self.{name}.clone().into_iter().map(|(k, v)| (k.into(), v as {core_ty})).collect()")
1415 }
1416 }
1417 _ => {
1419 if field.optional {
1420 format!("self.{name}.clone().map(|m| m.into_iter().map(|(k, v)| (k.into(), v)).collect())")
1421 } else {
1422 format!("self.{name}.clone().into_iter().map(|(k, v)| (k.into(), v)).collect()")
1423 }
1424 }
1425 },
1426 TypeRef::Unit => format!("self.{name}.clone()"),
1427 TypeRef::Json => {
1428 if field.optional {
1430 format!("self.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok())")
1431 } else {
1432 format!("serde_json::from_str(&self.{name}).unwrap_or_default()")
1433 }
1434 }
1435 };
1436 let expr = if let Some(newtype_path) = &field.newtype_wrapper {
1441 match &field.ty {
1442 TypeRef::Optional(_) => format!("({expr}).map({newtype_path})"),
1443 TypeRef::Vec(_) => format!("({expr}).into_iter().map({newtype_path}).collect::<Vec<_>>()"),
1444 _ if field.optional => format!("({expr}).map({newtype_path})"),
1445 _ => format!("{newtype_path}({expr})"),
1446 }
1447 } else {
1448 expr
1449 };
1450 out.push_str(&crate::template_env::render(
1451 "binding_helpers/struct_field_line.jinja",
1452 minijinja::context! {
1453 name => &field.name,
1454 expr => &expr,
1455 },
1456 ));
1457 out.push('\n');
1458 }
1459 if typ.has_stripped_cfg_fields {
1461 out.push_str(" ..Default::default()\n");
1462 }
1463 out.push_str(" };\n ");
1464 out
1465}
1466
1467#[allow(clippy::too_many_arguments)]
1482pub fn gen_async_body(
1483 core_call: &str,
1484 cfg: &RustBindingConfig,
1485 has_error: bool,
1486 return_wrap: &str,
1487 is_opaque: bool,
1488 inner_clone_line: &str,
1489 is_unit_return: bool,
1490 return_type: Option<&str>,
1491) -> String {
1492 let pattern_body = match cfg.async_pattern {
1493 AsyncPattern::Pyo3FutureIntoPy => {
1494 let result_handling = if has_error {
1495 format!(
1496 "let result = {core_call}.await\n \
1497 .map_err(|e| PyErr::new::<PyRuntimeError, _>(e.to_string()))?;"
1498 )
1499 } else if is_unit_return {
1500 format!("{core_call}.await;")
1501 } else {
1502 format!("let result = {core_call}.await;")
1503 };
1504 let (ok_expr, extra_binding) = if is_unit_return && !has_error {
1505 ("()".to_string(), String::new())
1506 } else if return_wrap.contains(".into()") || return_wrap.contains("::from(") {
1507 let wrapped_var = "wrapped_result";
1511 let binding = if let Some(ret_type) = return_type {
1512 format!("let {wrapped_var}: {ret_type} = {return_wrap};\n ")
1514 } else {
1515 format!("let {wrapped_var} = {return_wrap};\n ")
1516 };
1517 (wrapped_var.to_string(), binding)
1518 } else {
1519 (return_wrap.to_string(), String::new())
1520 };
1521 crate::template_env::render(
1522 "binding_helpers/async_body_pyo3.jinja",
1523 minijinja::context! {
1524 result_handling => result_handling,
1525 extra_binding => extra_binding,
1526 ok_expr => ok_expr,
1527 },
1528 )
1529 }
1530 AsyncPattern::WasmNativeAsync => {
1531 let result_handling = if has_error {
1532 format!(
1533 "let result = {core_call}.await\n \
1534 .map_err(|e| JsValue::from_str(&e.to_string()))?;"
1535 )
1536 } else if is_unit_return {
1537 format!("{core_call}.await;")
1538 } else {
1539 format!("let result = {core_call}.await;")
1540 };
1541 let ok_expr = if is_unit_return && !has_error {
1542 "()"
1543 } else {
1544 return_wrap
1545 };
1546 crate::template_env::render(
1547 "binding_helpers/async_body_wasm.jinja",
1548 minijinja::context! {
1549 result_handling => result_handling,
1550 ok_expr => ok_expr,
1551 },
1552 )
1553 }
1554 AsyncPattern::NapiNativeAsync => {
1555 let result_handling = if has_error {
1556 format!(
1557 "let result = {core_call}.await\n \
1558 .map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))?;"
1559 )
1560 } else if is_unit_return {
1561 format!("{core_call}.await;")
1562 } else {
1563 format!("let result = {core_call}.await;")
1564 };
1565 let (needs_ok_wrapper, ok_expr) = if !has_error && !is_unit_return {
1566 (false, return_wrap.to_string())
1568 } else {
1569 let expr = if is_unit_return && !has_error {
1570 "()".to_string()
1571 } else {
1572 return_wrap.to_string()
1573 };
1574 (true, expr)
1575 };
1576 crate::template_env::render(
1577 "binding_helpers/async_body_napi.jinja",
1578 minijinja::context! {
1579 result_handling => result_handling,
1580 needs_ok_wrapper => needs_ok_wrapper,
1581 ok_expr => ok_expr,
1582 return_wrap => return_wrap,
1583 },
1584 )
1585 }
1586 AsyncPattern::TokioBlockOn => {
1587 let rt_new = "tokio::runtime::Runtime::new()\
1588 .map_err(|e| extendr_api::Error::Other(e.to_string()))?";
1589 crate::template_env::render(
1590 "binding_helpers/async_body_tokio.jinja",
1591 minijinja::context! {
1592 has_error => has_error,
1593 is_opaque => is_opaque,
1594 is_unit_return => is_unit_return,
1595 core_call => core_call,
1596 return_wrap => return_wrap,
1597 rt_new => rt_new,
1598 },
1599 )
1600 }
1601 AsyncPattern::None => "todo!(\"async not supported by backend\")".to_string(),
1602 };
1603 if inner_clone_line.is_empty() {
1604 pattern_body
1605 } else {
1606 format!("{inner_clone_line}{pattern_body}")
1607 }
1608}
1609
1610pub fn gen_unimplemented_body(
1617 return_type: &TypeRef,
1618 fn_name: &str,
1619 has_error: bool,
1620 cfg: &RustBindingConfig,
1621 params: &[ParamDef],
1622 opaque_types: &AHashSet<String>,
1623) -> String {
1624 let suppress = if params.is_empty() {
1626 String::new()
1627 } else {
1628 let names: Vec<&str> = params.iter().map(|p| p.name.as_str()).collect();
1629 if names.len() == 1 {
1630 format!("let _ = {};\n ", names[0])
1631 } else {
1632 format!("let _ = ({});\n ", names.join(", "))
1633 }
1634 };
1635 let err_msg = format!("Not implemented: {fn_name}");
1636 let body = if has_error {
1637 match cfg.async_pattern {
1639 AsyncPattern::Pyo3FutureIntoPy => {
1640 format!("Err(pyo3::exceptions::PyNotImplementedError::new_err(\"{err_msg}\"))")
1641 }
1642 AsyncPattern::NapiNativeAsync => {
1643 format!("Err(napi::Error::new(napi::Status::GenericFailure, \"{err_msg}\"))")
1644 }
1645 AsyncPattern::WasmNativeAsync => {
1646 format!("Err(JsValue::from_str(\"{err_msg}\"))")
1647 }
1648 _ if cfg.cast_uints_to_i32 => {
1651 format!("Err(extendr_api::Error::Other(\"{err_msg}\".to_string()))")
1652 }
1653 _ => format!("Err(\"{err_msg}\".to_string())"),
1654 }
1655 } else {
1656 match return_type {
1658 TypeRef::Unit => "()".to_string(),
1659 TypeRef::String | TypeRef::Char | TypeRef::Path => format!("String::from(\"[unimplemented: {fn_name}]\")"),
1660 TypeRef::Bytes => "Vec::new()".to_string(),
1661 TypeRef::Primitive(p) => match p {
1662 alef_core::ir::PrimitiveType::Bool => "false".to_string(),
1663 alef_core::ir::PrimitiveType::F32 => "0.0f32".to_string(),
1664 alef_core::ir::PrimitiveType::F64 => "0.0f64".to_string(),
1665 _ => "0".to_string(),
1666 },
1667 TypeRef::Optional(_) => "None".to_string(),
1668 TypeRef::Vec(_) => "Vec::new()".to_string(),
1669 TypeRef::Map(_, _) => "Default::default()".to_string(),
1670 TypeRef::Duration => "0".to_string(),
1671 TypeRef::Named(name) => {
1672 if opaque_types.contains(name.as_str()) {
1676 format!("todo!(\"{err_msg}\")")
1677 } else {
1678 "Default::default()".to_string()
1679 }
1680 }
1681 TypeRef::Json => {
1682 "Default::default()".to_string()
1684 }
1685 }
1686 };
1687 format!("{suppress}{body}")
1688}