1use crate::conversions::helpers::{core_prim_str, needs_f64_cast, needs_i32_cast};
2use crate::generators::{AsyncPattern, RustBindingConfig};
3use ahash::AHashSet;
4use alef_core::ir::{CoreWrapper, ParamDef, TypeDef, TypeRef};
5
6fn arc_wrap(val: &str, name: &str, mutex_types: &AHashSet<String>) -> String {
11 let needs_mutex = mutex_types.contains(name);
12 crate::template_env::render(
13 "binding_helpers/arc_wrap.jinja",
14 minijinja::context! {
15 val => val,
16 needs_mutex => needs_mutex,
17 },
18 )
19 .trim_end_matches('\n')
20 .to_string()
21}
22
23#[allow(clippy::too_many_arguments)]
37pub fn wrap_return_with_mutex(
38 expr: &str,
39 return_type: &TypeRef,
40 type_name: &str,
41 opaque_types: &AHashSet<String>,
42 mutex_types: &AHashSet<String>,
43 self_is_opaque: bool,
44 returns_ref: bool,
45 returns_cow: bool,
46) -> String {
47 let self_arc = arc_wrap("", type_name, mutex_types); let _ = self_arc; match return_type {
50 TypeRef::Named(n) if n == type_name && self_is_opaque => {
51 let inner = if returns_cow {
52 format!("{expr}.into_owned()")
53 } else if returns_ref {
54 format!("{expr}.clone()")
55 } else {
56 expr.to_string()
57 };
58 format!("Self {{ inner: {} }}", arc_wrap(&inner, type_name, mutex_types))
59 }
60 TypeRef::Named(n) if opaque_types.contains(n.as_str()) => {
61 let inner = if returns_cow {
62 format!("{expr}.into_owned()")
63 } else if returns_ref {
64 format!("{expr}.clone()")
65 } else {
66 expr.to_string()
67 };
68 format!("{n} {{ inner: {} }}", arc_wrap(&inner, n, mutex_types))
69 }
70 TypeRef::Named(_) => {
71 if returns_cow {
78 format!("{expr}.into_owned().into()")
79 } else if returns_ref {
80 format!("{expr}.clone().into()")
81 } else {
82 format!("{expr}.into()")
83 }
84 }
85 TypeRef::String => {
87 if returns_ref {
88 format!("{expr}.into()")
89 } else {
90 expr.to_string()
91 }
92 }
93 TypeRef::Bytes => format!("{expr}.to_vec()"),
96 TypeRef::Path => format!("{expr}.to_string_lossy().to_string()"),
98 TypeRef::Duration => format!("{expr}.as_millis() as u64"),
100 TypeRef::Json => format!("{expr}.to_string()"),
102 TypeRef::Optional(inner) => match inner.as_ref() {
104 TypeRef::Named(n) if opaque_types.contains(n.as_str()) => {
105 let wrap = arc_wrap("v", n, mutex_types);
106 if returns_ref {
107 format!(
108 "{expr}.map(|v| {n} {{ inner: {} }})",
109 arc_wrap("v.clone()", n, mutex_types)
110 )
111 } else {
112 format!("{expr}.map(|v| {n} {{ inner: {wrap} }})")
113 }
114 }
115 TypeRef::Named(_) => {
116 if returns_ref {
117 format!("{expr}.map(|v| v.clone().into())")
118 } else {
119 format!("{expr}.map(Into::into)")
120 }
121 }
122 TypeRef::Path => {
123 format!("{expr}.map(Into::into)")
124 }
125 TypeRef::String | TypeRef::Bytes => {
126 if returns_ref {
127 format!("{expr}.map(Into::into)")
128 } else {
129 expr.to_string()
130 }
131 }
132 TypeRef::Duration => format!("{expr}.map(|d| d.as_millis() as u64)"),
133 TypeRef::Json => format!("{expr}.map(ToString::to_string)"),
134 TypeRef::Vec(vec_inner) => match vec_inner.as_ref() {
136 TypeRef::Named(n) if opaque_types.contains(n.as_str()) => {
137 if returns_ref {
138 let wrap = arc_wrap("x.clone()", n, mutex_types);
139 format!("{expr}.map(|v| v.into_iter().map(|x| {n} {{ inner: {wrap} }}).collect())")
140 } else {
141 let wrap = arc_wrap("x", n, mutex_types);
142 format!("{expr}.map(|v| v.into_iter().map(|x| {n} {{ inner: {wrap} }}).collect())")
143 }
144 }
145 TypeRef::Named(_) => {
146 if returns_ref {
147 format!("{expr}.map(|v| v.into_iter().map(|x| x.clone().into()).collect())")
148 } else {
149 format!("{expr}.map(|v| v.into_iter().map(Into::into).collect())")
150 }
151 }
152 _ => expr.to_string(),
153 },
154 _ => expr.to_string(),
155 },
156 TypeRef::Vec(inner) => match inner.as_ref() {
158 TypeRef::Named(n) if opaque_types.contains(n.as_str()) => {
159 if returns_ref {
160 let wrap = arc_wrap("v.clone()", n, mutex_types);
161 format!("{expr}.into_iter().map(|v| {n} {{ inner: {wrap} }}).collect()")
162 } else {
163 let wrap = arc_wrap("v", n, mutex_types);
164 format!("{expr}.into_iter().map(|v| {n} {{ inner: {wrap} }}).collect()")
165 }
166 }
167 TypeRef::Named(_) => {
168 if returns_ref {
169 format!("{expr}.into_iter().map(|v| v.clone().into()).collect()")
170 } else {
171 format!("{expr}.into_iter().map(Into::into).collect()")
172 }
173 }
174 TypeRef::Path => {
175 format!("{expr}.into_iter().map(Into::into).collect()")
176 }
177 TypeRef::String => {
178 if returns_ref {
179 format!("{expr}.iter().map(|s| s.to_string()).collect()")
183 } else {
184 expr.to_string()
185 }
186 }
187 TypeRef::Bytes => {
188 if returns_ref {
189 format!("{expr}.iter().map(|b| b.to_vec()).collect()")
190 } else {
191 expr.to_string()
192 }
193 }
194 _ => expr.to_string(),
195 },
196 _ => expr.to_string(),
197 }
198}
199
200pub fn wrap_return(
205 expr: &str,
206 return_type: &TypeRef,
207 type_name: &str,
208 opaque_types: &AHashSet<String>,
209 self_is_opaque: bool,
210 returns_ref: bool,
211 returns_cow: bool,
212) -> String {
213 wrap_return_with_mutex(
214 expr,
215 return_type,
216 type_name,
217 opaque_types,
218 &AHashSet::new(),
219 self_is_opaque,
220 returns_ref,
221 returns_cow,
222 )
223}
224
225pub fn apply_return_newtype_unwrap(expr: &str, return_newtype_wrapper: &Option<String>) -> String {
230 match return_newtype_wrapper {
231 Some(_) => crate::template_env::render(
232 "binding_helpers/return_newtype_unwrap.jinja",
233 minijinja::context! {
234 expr => expr,
235 },
236 )
237 .trim_end_matches('\n')
238 .to_string(),
239 None => expr.to_string(),
240 }
241}
242
243pub fn gen_call_args(params: &[ParamDef], opaque_types: &AHashSet<String>) -> String {
254 params
255 .iter()
256 .enumerate()
257 .map(|(idx, p)| {
258 let promoted = crate::shared::is_promoted_optional(params, idx);
259 let unwrap_suffix = if promoted && p.optional {
264 format!(".expect(\"'{}' is required\")", p.name)
265 } else {
266 String::new()
267 };
268 if let Some(newtype_path) = &p.newtype_wrapper {
271 return if p.optional {
272 format!("{}.map({newtype_path})", p.name)
273 } else if promoted {
274 format!("{newtype_path}({}{})", p.name, unwrap_suffix)
275 } else {
276 format!("{newtype_path}({})", p.name)
277 };
278 }
279 match &p.ty {
280 TypeRef::Named(name) if opaque_types.contains(name.as_str()) => {
281 if p.optional {
283 format!("{}.as_ref().map(|v| &v.inner)", p.name)
284 } else if promoted {
285 format!("{}{}.inner.as_ref()", p.name, unwrap_suffix)
286 } else {
287 format!("&{}.inner", p.name)
288 }
289 }
290 TypeRef::Named(_) => {
291 if p.optional {
292 if p.is_ref {
293 format!("{}.as_ref()", p.name)
296 } else {
297 format!("{}.map(Into::into)", p.name)
298 }
299 } else if promoted {
300 format!("{}{}.into()", p.name, unwrap_suffix)
301 } else {
302 format!("{}.into()", p.name)
303 }
304 }
305 TypeRef::String | TypeRef::Char => {
309 if p.optional {
310 if p.is_ref {
311 format!("{}.as_deref()", p.name)
312 } else {
313 p.name.clone()
314 }
315 } else if promoted {
316 if p.is_ref {
317 format!("&{}{}", p.name, unwrap_suffix)
318 } else {
319 format!("{}{}", p.name, unwrap_suffix)
320 }
321 } else if p.is_ref {
322 format!("&{}", p.name)
323 } else {
324 p.name.clone()
325 }
326 }
327 TypeRef::Path => {
329 if p.optional && p.is_ref {
330 format!("{}.as_deref().map(std::path::Path::new)", p.name)
331 } else if p.optional {
332 format!("{}.map(std::path::PathBuf::from)", p.name)
333 } else if promoted {
334 format!("std::path::PathBuf::from({}{})", p.name, unwrap_suffix)
335 } else if p.is_ref {
336 format!("std::path::Path::new(&{})", p.name)
337 } else {
338 format!("std::path::PathBuf::from({})", p.name)
339 }
340 }
341 TypeRef::Bytes => {
342 if p.optional {
343 if p.is_ref {
344 format!("{}.as_deref()", p.name)
345 } else {
346 p.name.clone()
347 }
348 } else if promoted {
349 if p.is_ref {
352 format!("&{}{}", p.name, unwrap_suffix)
353 } else {
354 format!("{}{}", p.name, unwrap_suffix)
355 }
356 } else {
357 if p.is_ref {
360 format!("&{}", p.name)
361 } else {
362 p.name.clone()
363 }
364 }
365 }
366 TypeRef::Duration => {
368 if p.optional {
369 format!("{}.map(std::time::Duration::from_millis)", p.name)
370 } else if promoted {
371 format!("std::time::Duration::from_millis({}{})", p.name, unwrap_suffix)
372 } else {
373 format!("std::time::Duration::from_millis({})", p.name)
374 }
375 }
376 TypeRef::Json => {
377 if p.optional {
379 format!("{}.as_ref().and_then(|s| serde_json::from_str(s).ok())", p.name)
380 } else if promoted {
381 format!("serde_json::from_str(&{}{}).unwrap_or_default()", p.name, unwrap_suffix)
382 } else {
383 format!("serde_json::from_str(&{}).unwrap_or_default()", p.name)
384 }
385 }
386 TypeRef::Vec(inner) => {
387 if matches!(inner.as_ref(), TypeRef::Named(_)) {
389 if p.optional {
390 if p.is_ref {
391 format!("{}.as_deref()", p.name)
392 } else {
393 p.name.clone()
394 }
395 } else if promoted {
396 if p.is_ref {
397 format!("&{}{}", p.name, unwrap_suffix)
398 } else {
399 format!("{}{}", p.name, unwrap_suffix)
400 }
401 } else if p.is_ref {
402 format!("&{}", p.name)
403 } else {
404 p.name.clone()
405 }
406 } else if promoted {
407 format!("{}{}", p.name, unwrap_suffix)
408 } else if p.is_mut && p.optional {
409 format!("{}.as_deref_mut()", p.name)
410 } else if p.is_mut {
411 format!("&mut {}", p.name)
412 } else if p.is_ref && p.optional {
413 format!("{}.as_deref()", p.name)
414 } else if p.is_ref {
415 format!("&{}", p.name)
416 } else {
417 p.name.clone()
418 }
419 }
420 _ => {
421 if promoted {
422 format!("{}{}", p.name, unwrap_suffix)
423 } else if p.is_mut && p.optional {
424 format!("{}.as_deref_mut()", p.name)
425 } else if p.is_mut {
426 format!("&mut {}", p.name)
427 } else if p.is_ref && p.optional {
428 format!("{}.as_deref()", p.name)
431 } else if p.is_ref {
432 format!("&{}", p.name)
433 } else {
434 p.name.clone()
435 }
436 }
437 }
438 })
439 .collect::<Vec<_>>()
440 .join(", ")
441}
442
443pub fn gen_call_args_cfg(
450 params: &[ParamDef],
451 opaque_types: &AHashSet<String>,
452 cast_uints_to_i32: bool,
453 cast_large_ints_to_f64: bool,
454) -> String {
455 params
456 .iter()
457 .enumerate()
458 .map(|(idx, p)| {
459 let promoted = crate::shared::is_promoted_optional(params, idx);
460 let unwrap_suffix = if promoted && p.optional {
461 format!(".expect(\"'{}' is required\")", p.name)
462 } else {
463 String::new()
464 };
465 if p.newtype_wrapper.is_some() {
467 return gen_call_args(std::slice::from_ref(p), opaque_types);
470 }
471 if let TypeRef::Primitive(prim) = &p.ty {
473 let core_ty = core_prim_str(prim);
474 let needs_cast =
475 (cast_uints_to_i32 && needs_i32_cast(prim)) || (cast_large_ints_to_f64 && needs_f64_cast(prim));
476 if needs_cast {
477 return if p.optional {
478 format!("{}.map(|v| v as {core_ty})", p.name)
479 } else if promoted {
480 format!("({}{}) as {core_ty}", p.name, unwrap_suffix)
481 } else {
482 format!("{} as {core_ty}", p.name)
483 };
484 }
485 }
486 gen_call_args(std::slice::from_ref(p), opaque_types)
488 })
489 .collect::<Vec<_>>()
490 .join(", ")
491}
492
493pub fn gen_call_args_with_let_bindings(params: &[ParamDef], opaque_types: &AHashSet<String>) -> String {
496 params
497 .iter()
498 .enumerate()
499 .map(|(idx, p)| {
500 let promoted = crate::shared::is_promoted_optional(params, idx);
501 let unwrap_suffix = if promoted {
502 format!(".expect(\"'{}' is required\")", p.name)
503 } else {
504 String::new()
505 };
506 if let Some(newtype_path) = &p.newtype_wrapper {
508 return if p.optional {
509 format!("{}.map({newtype_path})", p.name)
510 } else if promoted {
511 format!("{newtype_path}({}{})", p.name, unwrap_suffix)
512 } else {
513 format!("{newtype_path}({})", p.name)
514 };
515 }
516 match &p.ty {
517 TypeRef::Named(name) if opaque_types.contains(name.as_str()) => {
518 if p.optional {
519 format!("{}.as_ref().map(|v| &v.inner)", p.name)
520 } else if promoted {
521 format!("{}{}.inner.as_ref()", p.name, unwrap_suffix)
522 } else {
523 format!("&{}.inner", p.name)
524 }
525 }
526 TypeRef::Named(_) => {
527 if p.optional && p.is_ref {
528 format!("{}_core", p.name)
530 } else if p.is_ref {
531 format!("&{}_core", p.name)
533 } else {
534 format!("{}_core", p.name)
535 }
536 }
537 TypeRef::String | TypeRef::Char => {
538 if p.optional {
539 if p.is_ref {
540 format!("{}.as_deref()", p.name)
541 } else {
542 p.name.clone()
543 }
544 } else if promoted {
545 if p.is_ref {
546 format!("&{}{}", p.name, unwrap_suffix)
547 } else {
548 format!("{}{}", p.name, unwrap_suffix)
549 }
550 } else if p.is_ref {
551 format!("&{}", p.name)
552 } else {
553 p.name.clone()
554 }
555 }
556 TypeRef::Path => {
557 if promoted {
558 format!("std::path::PathBuf::from({}{})", p.name, unwrap_suffix)
559 } else if p.optional && p.is_ref {
560 format!("{}.as_deref().map(std::path::Path::new)", p.name)
561 } else if p.optional {
562 format!("{}.map(std::path::PathBuf::from)", p.name)
563 } else if p.is_ref {
564 format!("std::path::Path::new(&{})", p.name)
565 } else {
566 format!("std::path::PathBuf::from({})", p.name)
567 }
568 }
569 TypeRef::Bytes => {
570 if p.optional {
571 if p.is_ref {
572 format!("{}.as_deref()", p.name)
573 } else {
574 p.name.clone()
575 }
576 } else if promoted {
577 if p.is_ref {
580 format!("&{}{}", p.name, unwrap_suffix)
581 } else {
582 format!("{}{}", p.name, unwrap_suffix)
583 }
584 } else {
585 if p.is_ref {
588 format!("&{}", p.name)
589 } else {
590 p.name.clone()
591 }
592 }
593 }
594 TypeRef::Duration => {
595 if p.optional {
596 format!("{}.map(std::time::Duration::from_millis)", p.name)
597 } else if promoted {
598 format!("std::time::Duration::from_millis({}{})", p.name, unwrap_suffix)
599 } else {
600 format!("std::time::Duration::from_millis({})", p.name)
601 }
602 }
603 TypeRef::Vec(inner) => {
604 if matches!(inner.as_ref(), TypeRef::String) && p.sanitized && p.original_type.is_some() {
607 if p.optional && p.is_ref {
608 format!("{}_core.as_deref()", p.name)
609 } else if p.optional {
610 format!("{}_core", p.name)
611 } else if p.is_ref {
612 format!("&{}_core", p.name)
613 } else {
614 format!("{}_core", p.name)
615 }
616 } else if matches!(inner.as_ref(), TypeRef::Named(_)) {
617 if p.optional && p.is_ref {
619 format!("{}_core.as_deref()", p.name)
621 } else if p.optional {
622 format!("{}_core", p.name)
624 } else if p.is_ref {
625 format!("&{}_core", p.name)
626 } else {
627 format!("{}_core", p.name)
628 }
629 } else if matches!(inner.as_ref(), TypeRef::String | TypeRef::Char) && p.is_ref {
630 if p.optional {
633 format!("{}.as_deref()", p.name)
634 } else {
635 format!("&{}_refs", p.name)
636 }
637 } else if promoted {
638 format!("{}{}", p.name, unwrap_suffix)
639 } else if p.is_ref && p.optional {
640 format!("{}.as_deref()", p.name)
641 } else if p.is_ref {
642 format!("&{}", p.name)
643 } else {
644 p.name.clone()
645 }
646 }
647 _ => {
648 if promoted {
649 format!("{}{}", p.name, unwrap_suffix)
650 } else if p.is_ref && p.optional {
651 format!("{}.as_deref()", p.name)
652 } else if p.is_ref {
653 format!("&{}", p.name)
654 } else {
655 p.name.clone()
656 }
657 }
658 }
659 })
660 .collect::<Vec<_>>()
661 .join(", ")
662}
663
664pub fn gen_named_let_bindings_pub(params: &[ParamDef], opaque_types: &AHashSet<String>, core_import: &str) -> String {
666 gen_named_let_bindings(params, opaque_types, core_import)
667}
668
669pub fn gen_named_let_bindings_no_promote(
672 params: &[ParamDef],
673 opaque_types: &AHashSet<String>,
674 core_import: &str,
675) -> String {
676 gen_named_let_bindings_inner(params, opaque_types, core_import, false)
677}
678
679pub(super) fn gen_named_let_bindings(
680 params: &[ParamDef],
681 opaque_types: &AHashSet<String>,
682 core_import: &str,
683) -> String {
684 gen_named_let_bindings_inner(params, opaque_types, core_import, true)
685}
686
687pub(super) fn gen_named_let_bindings_by_ref(
691 params: &[ParamDef],
692 opaque_types: &AHashSet<String>,
693 core_import: &str,
694) -> String {
695 let mut bindings = String::new();
696 for (idx, p) in params.iter().enumerate() {
697 match &p.ty {
698 TypeRef::Named(name) if !opaque_types.contains(name.as_str()) => {
699 let promoted = crate::shared::is_promoted_optional(params, idx);
700 let core_type_path = format!("{core_import}::{name}");
701 let binding = if p.optional {
702 crate::template_env::render(
703 "binding_helpers/named_let_binding_by_ref_optional.jinja",
704 minijinja::context! {
705 name => &p.name,
706 core_type_path => &core_type_path,
707 },
708 )
709 } else if promoted {
710 crate::template_env::render(
711 "binding_helpers/named_let_binding_by_ref_promoted.jinja",
712 minijinja::context! {
713 name => &p.name,
714 core_type_path => &core_type_path,
715 },
716 )
717 } else {
718 crate::template_env::render(
719 "binding_helpers/named_let_binding_by_ref_simple.jinja",
720 minijinja::context! {
721 name => &p.name,
722 core_type_path => &core_type_path,
723 },
724 )
725 };
726 bindings.push_str(&binding);
727 bindings.push_str("\n ");
728 }
729 TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Named(n) if !opaque_types.contains(n.as_str())) => {
730 let binding = if p.optional {
731 crate::template_env::render(
732 "binding_helpers/vec_named_let_binding_by_ref_optional.jinja",
733 minijinja::context! {
734 name => &p.name,
735 },
736 )
737 } else {
738 let promoted = crate::shared::is_promoted_optional(params, idx);
739 if promoted {
740 crate::template_env::render(
741 "binding_helpers/vec_named_let_binding_by_ref_promoted.jinja",
742 minijinja::context! {
743 name => &p.name,
744 },
745 )
746 } else {
747 crate::template_env::render(
748 "binding_helpers/vec_named_let_binding_by_ref_simple.jinja",
749 minijinja::context! {
750 name => &p.name,
751 },
752 )
753 }
754 };
755 bindings.push_str(&binding);
756 bindings.push_str("\n ");
757 }
758 TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::String | TypeRef::Char) && p.is_ref => {
759 let binding = if p.optional {
760 crate::template_env::render(
761 "binding_helpers/vec_string_refs_binding_optional.jinja",
762 minijinja::context! {
763 name => &p.name,
764 },
765 )
766 } else {
767 crate::template_env::render(
768 "binding_helpers/vec_string_refs_binding_simple.jinja",
769 minijinja::context! {
770 name => &p.name,
771 },
772 )
773 };
774 bindings.push_str(&binding);
775 bindings.push_str("\n ");
776 }
777 _ => {}
778 }
779 }
780 bindings
781}
782
783fn gen_named_let_bindings_inner(
784 params: &[ParamDef],
785 opaque_types: &AHashSet<String>,
786 core_import: &str,
787 promote: bool,
788) -> String {
789 let mut bindings = String::new();
790 for (idx, p) in params.iter().enumerate() {
791 match &p.ty {
792 TypeRef::Named(name) if !opaque_types.contains(name.as_str()) => {
793 let promoted = promote && crate::shared::is_promoted_optional(params, idx);
794 let core_type_path = format!("{}::{}", core_import, name);
795 let binding = if p.optional {
796 if p.is_ref {
797 crate::template_env::render(
798 "binding_helpers/named_let_binding_optional_ref.jinja",
799 minijinja::context! {
800 name => &p.name,
801 core_type_path => &core_type_path,
802 },
803 )
804 } else {
805 crate::template_env::render(
806 "binding_helpers/named_let_binding_optional.jinja",
807 minijinja::context! {
808 name => &p.name,
809 core_type_path => &core_type_path,
810 },
811 )
812 }
813 } else if promoted {
814 crate::template_env::render(
815 "binding_helpers/named_let_binding_promoted.jinja",
816 minijinja::context! {
817 name => &p.name,
818 core_type_path => &core_type_path,
819 },
820 )
821 } else {
822 crate::template_env::render(
823 "binding_helpers/named_let_binding_simple.jinja",
824 minijinja::context! {
825 name => &p.name,
826 core_type_path => &core_type_path,
827 },
828 )
829 };
830 bindings.push_str(&binding);
831 bindings.push_str("\n ");
832 }
833 TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Named(n) if !opaque_types.contains(n.as_str())) => {
834 let promoted = promote && crate::shared::is_promoted_optional(params, idx);
835 let binding = if p.optional && p.is_ref {
836 crate::template_env::render(
837 "binding_helpers/vec_named_let_binding_optional.jinja",
838 minijinja::context! {
839 name => &p.name,
840 },
841 )
842 } else if p.optional {
843 crate::template_env::render(
844 "binding_helpers/vec_named_let_binding_optional_no_ref.jinja",
845 minijinja::context! {
846 name => &p.name,
847 },
848 )
849 } else if promoted {
850 crate::template_env::render(
851 "binding_helpers/vec_named_let_binding_promoted.jinja",
852 minijinja::context! {
853 name => &p.name,
854 },
855 )
856 } else {
857 crate::template_env::render(
858 "binding_helpers/vec_named_let_binding_simple.jinja",
859 minijinja::context! {
860 name => &p.name,
861 },
862 )
863 };
864 bindings.push_str(&binding);
865 bindings.push_str("\n ");
866 }
867 TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::String | TypeRef::Char) && p.is_ref => {
870 let binding = if p.optional {
871 crate::template_env::render(
872 "binding_helpers/vec_string_refs_binding_optional.jinja",
873 minijinja::context! {
874 name => &p.name,
875 },
876 )
877 } else {
878 crate::template_env::render(
879 "binding_helpers/vec_string_refs_binding_simple.jinja",
880 minijinja::context! {
881 name => &p.name,
882 },
883 )
884 };
885 bindings.push_str(&binding);
886 bindings.push_str("\n ");
887 }
888 TypeRef::Vec(inner)
890 if matches!(inner.as_ref(), TypeRef::String) && p.sanitized && p.original_type.is_some() =>
891 {
892 let template = if p.optional {
893 "binding_helpers/sanitized_vec_string_filter_optional.jinja"
894 } else {
895 "binding_helpers/sanitized_vec_string_filter_simple.jinja"
896 };
897 bindings.push_str(&crate::template_env::render(
898 template,
899 minijinja::context! {
900 name => &p.name,
901 },
902 ));
903 }
904 _ => {}
905 }
906 }
907 bindings
908}
909
910pub fn gen_serde_let_bindings(
917 params: &[ParamDef],
918 opaque_types: &AHashSet<String>,
919 core_import: &str,
920 err_conv: &str,
921 indent: &str,
922) -> String {
923 let mut bindings = String::new();
924 for (idx, p) in params.iter().enumerate() {
925 let promoted = crate::shared::is_promoted_optional(params, idx);
926 match &p.ty {
927 TypeRef::Named(name) if !opaque_types.contains(name.as_str()) => {
928 let core_path = format!("{}::{}", core_import, name);
929 if p.optional {
930 bindings.push_str(&crate::template_env::render(
931 "binding_helpers/serde_named_let_binding_optional.jinja",
932 minijinja::context! {
933 name => &p.name,
934 core_path => core_path,
935 err_conv => err_conv,
936 indent => indent,
937 },
938 ));
939 } else if promoted {
940 bindings.push_str(&crate::template_env::render(
944 "binding_helpers/serde_named_let_binding_promoted.jinja",
945 minijinja::context! {
946 name => &p.name,
947 core_path => core_path,
948 err_conv => err_conv,
949 indent => indent,
950 },
951 ));
952 } else {
953 bindings.push_str(&crate::template_env::render(
954 "binding_helpers/serde_named_let_binding_simple.jinja",
955 minijinja::context! {
956 name => &p.name,
957 core_path => core_path,
958 err_conv => err_conv,
959 indent => indent,
960 },
961 ));
962 }
963 }
964 TypeRef::Vec(inner) => {
965 if let TypeRef::Named(name) = inner.as_ref() {
966 if !opaque_types.contains(name.as_str()) {
967 let core_path = format!("{}::{}", core_import, name);
968 if p.optional {
969 bindings.push_str(&crate::template_env::render(
970 "binding_helpers/serde_vec_named_optional.jinja",
971 minijinja::context! {
972 name => &p.name,
973 core_path => core_path,
974 err_conv => err_conv,
975 indent => indent,
976 },
977 ));
978 } else {
979 bindings.push_str(&crate::template_env::render(
980 "binding_helpers/serde_vec_named_simple.jinja",
981 minijinja::context! {
982 name => &p.name,
983 core_path => core_path,
984 err_conv => err_conv,
985 indent => indent,
986 },
987 ));
988 }
989 }
990 } else if matches!(inner.as_ref(), TypeRef::String) && p.sanitized && p.original_type.is_some() {
991 let template = if p.optional {
994 "binding_helpers/serde_sanitized_vec_string_optional.jinja"
995 } else {
996 "binding_helpers/serde_sanitized_vec_string_simple.jinja"
997 };
998 bindings.push_str(&crate::template_env::render(
999 template,
1000 minijinja::context! {
1001 name => &p.name,
1002 err_conv => err_conv,
1003 indent => indent,
1004 },
1005 ));
1006 }
1007 }
1008 _ => {}
1009 }
1010 }
1011 bindings
1012}
1013
1014pub fn has_named_params(params: &[ParamDef], opaque_types: &AHashSet<String>) -> bool {
1019 params.iter().any(|p| match &p.ty {
1020 TypeRef::Named(name) if !opaque_types.contains(name.as_str()) => true,
1021 TypeRef::Vec(inner) => {
1022 matches!(inner.as_ref(), TypeRef::Named(name) if !opaque_types.contains(name.as_str()))
1026 || (matches!(inner.as_ref(), TypeRef::String | TypeRef::Char) && p.is_ref)
1027 || (matches!(inner.as_ref(), TypeRef::String) && p.sanitized && p.original_type.is_some())
1028 }
1029 _ => false,
1030 })
1031}
1032
1033pub fn is_simple_non_opaque_param(ty: &TypeRef) -> bool {
1036 match ty {
1037 TypeRef::Primitive(_)
1038 | TypeRef::String
1039 | TypeRef::Char
1040 | TypeRef::Bytes
1041 | TypeRef::Path
1042 | TypeRef::Unit
1043 | TypeRef::Duration => true,
1044 TypeRef::Optional(inner) => is_simple_non_opaque_param(inner),
1045 _ => false,
1046 }
1047}
1048
1049pub fn gen_lossy_binding_to_core_fields(
1062 typ: &TypeDef,
1063 core_import: &str,
1064 option_duration_on_defaults: bool,
1065 opaque_types: &AHashSet<String>,
1066 cast_uints_to_i32: bool,
1067 cast_large_ints_to_f64: bool,
1068 skip_types: &[String],
1069) -> String {
1070 gen_lossy_binding_to_core_fields_inner(
1071 typ,
1072 core_import,
1073 false,
1074 option_duration_on_defaults,
1075 opaque_types,
1076 cast_uints_to_i32,
1077 cast_large_ints_to_f64,
1078 skip_types,
1079 )
1080}
1081
1082pub fn gen_lossy_binding_to_core_fields_mut(
1084 typ: &TypeDef,
1085 core_import: &str,
1086 option_duration_on_defaults: bool,
1087 opaque_types: &AHashSet<String>,
1088 cast_uints_to_i32: bool,
1089 cast_large_ints_to_f64: bool,
1090 skip_types: &[String],
1091) -> String {
1092 gen_lossy_binding_to_core_fields_inner(
1093 typ,
1094 core_import,
1095 true,
1096 option_duration_on_defaults,
1097 opaque_types,
1098 cast_uints_to_i32,
1099 cast_large_ints_to_f64,
1100 skip_types,
1101 )
1102}
1103
1104#[allow(clippy::too_many_arguments)]
1105fn gen_lossy_binding_to_core_fields_inner(
1106 typ: &TypeDef,
1107 core_import: &str,
1108 needs_mut: bool,
1109 option_duration_on_defaults: bool,
1110 opaque_types: &AHashSet<String>,
1111 cast_uints_to_i32: bool,
1112 cast_large_ints_to_f64: bool,
1113 skip_types: &[String],
1114) -> String {
1115 let core_path = crate::conversions::core_type_path(typ, core_import);
1116 let mut_kw = if needs_mut { "mut " } else { "" };
1117 let allow = if typ.has_stripped_cfg_fields {
1122 "#[allow(clippy::needless_update)]\n "
1123 } else {
1124 ""
1125 };
1126 let mut out = format!("{allow}let {mut_kw}core_self = {core_path} {{\n");
1127 for field in &typ.fields {
1128 if field.cfg.is_some() {
1131 continue;
1132 }
1133 let name = &field.name;
1134 if field.sanitized && field.core_wrapper != CoreWrapper::Cow {
1135 out.push_str(&crate::template_env::render(
1136 "binding_helpers/struct_field_default.jinja",
1137 minijinja::context! {
1138 name => &field.name,
1139 },
1140 ));
1141 out.push('\n');
1142 continue;
1143 }
1144 let is_opaque_named = match &field.ty {
1149 TypeRef::Named(n) => opaque_types.contains(n.as_str()),
1150 TypeRef::Optional(inner) => {
1151 matches!(inner.as_ref(), TypeRef::Named(n) if opaque_types.contains(n.as_str()))
1152 }
1153 _ => false,
1154 };
1155 if is_opaque_named {
1156 out.push_str(&crate::template_env::render(
1157 "binding_helpers/struct_field_default.jinja",
1158 minijinja::context! {
1159 name => &field.name,
1160 },
1161 ));
1162 out.push('\n');
1163 continue;
1164 }
1165 let is_skip_named = match &field.ty {
1168 TypeRef::Named(n) => skip_types.contains(n),
1169 TypeRef::Optional(inner) => {
1170 matches!(inner.as_ref(), TypeRef::Named(n) if skip_types.contains(n))
1171 }
1172 _ => false,
1173 };
1174 if is_skip_named {
1175 out.push_str(&crate::template_env::render(
1176 "binding_helpers/default_field.jinja",
1177 minijinja::context! {
1178 name => &name,
1179 },
1180 ));
1181 continue;
1182 }
1183 let expr = match &field.ty {
1184 TypeRef::Primitive(p) if cast_uints_to_i32 && needs_i32_cast(p) => {
1185 let core_ty = core_prim_str(p);
1186 if field.optional {
1187 format!("self.{name}.map(|v| v as {core_ty})")
1188 } else {
1189 format!("self.{name} as {core_ty}")
1190 }
1191 }
1192 TypeRef::Primitive(p) if cast_large_ints_to_f64 && needs_f64_cast(p) => {
1193 let core_ty = core_prim_str(p);
1194 if field.optional {
1195 format!("self.{name}.map(|v| v as {core_ty})")
1196 } else {
1197 format!("self.{name} as {core_ty}")
1198 }
1199 }
1200 TypeRef::Primitive(_) => format!("self.{name}"),
1201 TypeRef::Duration => {
1202 if field.optional {
1203 format!("self.{name}.map(std::time::Duration::from_millis)")
1204 } else if option_duration_on_defaults && typ.has_default {
1205 format!("self.{name}.map(std::time::Duration::from_millis).unwrap_or_default()")
1210 } else {
1211 format!("std::time::Duration::from_millis(self.{name})")
1212 }
1213 }
1214 TypeRef::String => {
1215 if field.core_wrapper == CoreWrapper::Cow {
1216 format!("self.{name}.clone().into()")
1217 } else {
1218 format!("self.{name}.clone()")
1219 }
1220 }
1221 TypeRef::Bytes => {
1225 if field.core_wrapper == CoreWrapper::Bytes {
1226 format!("self.{name}.clone().into()")
1227 } else {
1228 format!("self.{name}.clone()")
1229 }
1230 }
1231 TypeRef::Char => {
1232 if field.optional {
1233 format!("self.{name}.as_ref().and_then(|s| s.chars().next())")
1234 } else {
1235 format!("self.{name}.chars().next().unwrap_or('*')")
1236 }
1237 }
1238 TypeRef::Path => {
1239 if field.optional {
1240 format!("self.{name}.clone().map(Into::into)")
1241 } else {
1242 format!("self.{name}.clone().into()")
1243 }
1244 }
1245 TypeRef::Named(_) => {
1246 if field.optional {
1247 format!("self.{name}.clone().map(Into::into)")
1248 } else {
1249 format!("self.{name}.clone().into()")
1250 }
1251 }
1252 TypeRef::Vec(inner) => match inner.as_ref() {
1253 TypeRef::Named(_) => {
1254 if field.optional {
1255 format!("self.{name}.clone().map(|v| v.into_iter().map(Into::into).collect())")
1257 } else {
1258 format!("self.{name}.clone().into_iter().map(Into::into).collect()")
1259 }
1260 }
1261 TypeRef::Primitive(p) if cast_uints_to_i32 && needs_i32_cast(p) => {
1263 let core_ty = core_prim_str(p);
1264 if field.optional {
1265 format!("self.{name}.clone().map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
1266 } else {
1267 format!("self.{name}.clone().into_iter().map(|v| v as {core_ty}).collect()")
1268 }
1269 }
1270 TypeRef::Primitive(p) if cast_large_ints_to_f64 && needs_f64_cast(p) => {
1272 let core_ty = core_prim_str(p);
1273 if field.optional {
1274 format!("self.{name}.clone().map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
1275 } else {
1276 format!("self.{name}.clone().into_iter().map(|v| v as {core_ty}).collect()")
1277 }
1278 }
1279 _ => format!("self.{name}.clone()"),
1280 },
1281 TypeRef::Optional(inner) => {
1282 let base = match inner.as_ref() {
1286 TypeRef::Named(_) => {
1287 format!("self.{name}.clone().map(Into::into)")
1288 }
1289 TypeRef::Duration => {
1290 format!("self.{name}.map(|v| std::time::Duration::from_millis(v as u64))")
1291 }
1292 TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Named(_)) => {
1293 format!("self.{name}.clone().map(|v| v.into_iter().map(Into::into).collect())")
1294 }
1295 TypeRef::Vec(vi) => match vi.as_ref() {
1297 TypeRef::Primitive(p) if cast_uints_to_i32 && needs_i32_cast(p) => {
1298 let core_ty = core_prim_str(p);
1299 format!("self.{name}.clone().map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
1300 }
1301 TypeRef::Primitive(p) if cast_large_ints_to_f64 && needs_f64_cast(p) => {
1303 let core_ty = core_prim_str(p);
1304 format!("self.{name}.clone().map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
1305 }
1306 _ => format!("self.{name}.clone()"),
1307 },
1308 _ => format!("self.{name}.clone()"),
1309 };
1310 if field.optional {
1311 format!("({base}).map(Some)")
1312 } else {
1313 base
1314 }
1315 }
1316 TypeRef::Map(_, v) => match v.as_ref() {
1317 TypeRef::Json => {
1318 if field.optional {
1323 format!(
1324 "self.{name}.clone().map(|m| m.into_iter().map(|(k, v)| \
1325 (k.into(), serde_json::from_str(&v).unwrap_or(serde_json::Value::String(v)))).collect())"
1326 )
1327 } else {
1328 format!(
1329 "self.{name}.clone().into_iter().map(|(k, v)| \
1330 (k.into(), serde_json::from_str(&v).unwrap_or(serde_json::Value::String(v)))).collect()"
1331 )
1332 }
1333 }
1334 TypeRef::Named(_) => {
1337 if field.optional {
1338 format!(
1339 "self.{name}.clone().map(|m| m.into_iter().map(|(k, v)| (k.into(), v.into())).collect())"
1340 )
1341 } else {
1342 format!("self.{name}.clone().into_iter().map(|(k, v)| (k.into(), v.into())).collect()")
1343 }
1344 }
1345 TypeRef::Primitive(p) if cast_uints_to_i32 && needs_i32_cast(p) => {
1347 let core_ty = core_prim_str(p);
1348 if field.optional {
1349 format!(
1350 "self.{name}.clone().map(|m| m.into_iter().map(|(k, v)| (k.into(), v as {core_ty})).collect())"
1351 )
1352 } else {
1353 format!("self.{name}.clone().into_iter().map(|(k, v)| (k.into(), v as {core_ty})).collect()")
1354 }
1355 }
1356 TypeRef::Primitive(p) if cast_large_ints_to_f64 && needs_f64_cast(p) => {
1358 let core_ty = core_prim_str(p);
1359 if field.optional {
1360 format!(
1361 "self.{name}.clone().map(|m| m.into_iter().map(|(k, v)| (k.into(), v as {core_ty})).collect())"
1362 )
1363 } else {
1364 format!("self.{name}.clone().into_iter().map(|(k, v)| (k.into(), v as {core_ty})).collect()")
1365 }
1366 }
1367 _ => {
1369 if field.optional {
1370 format!("self.{name}.clone().map(|m| m.into_iter().map(|(k, v)| (k.into(), v)).collect())")
1371 } else {
1372 format!("self.{name}.clone().into_iter().map(|(k, v)| (k.into(), v)).collect()")
1373 }
1374 }
1375 },
1376 TypeRef::Unit => format!("self.{name}.clone()"),
1377 TypeRef::Json => {
1378 if field.optional {
1380 format!("self.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok())")
1381 } else {
1382 format!("serde_json::from_str(&self.{name}).unwrap_or_default()")
1383 }
1384 }
1385 };
1386 let expr = if let Some(newtype_path) = &field.newtype_wrapper {
1391 match &field.ty {
1392 TypeRef::Optional(_) => format!("({expr}).map({newtype_path})"),
1393 TypeRef::Vec(_) => format!("({expr}).into_iter().map({newtype_path}).collect::<Vec<_>>()"),
1394 _ if field.optional => format!("({expr}).map({newtype_path})"),
1395 _ => format!("{newtype_path}({expr})"),
1396 }
1397 } else {
1398 expr
1399 };
1400 out.push_str(&crate::template_env::render(
1401 "binding_helpers/struct_field_line.jinja",
1402 minijinja::context! {
1403 name => &field.name,
1404 expr => &expr,
1405 },
1406 ));
1407 out.push('\n');
1408 }
1409 if typ.has_stripped_cfg_fields {
1411 out.push_str(" ..Default::default()\n");
1412 }
1413 out.push_str(" };\n ");
1414 out
1415}
1416
1417#[allow(clippy::too_many_arguments)]
1432pub fn gen_async_body(
1433 core_call: &str,
1434 cfg: &RustBindingConfig,
1435 has_error: bool,
1436 return_wrap: &str,
1437 is_opaque: bool,
1438 inner_clone_line: &str,
1439 is_unit_return: bool,
1440 return_type: Option<&str>,
1441) -> String {
1442 let pattern_body = match cfg.async_pattern {
1443 AsyncPattern::Pyo3FutureIntoPy => {
1444 let result_handling = if has_error {
1445 format!(
1446 "let result = {core_call}.await\n \
1447 .map_err(|e| PyErr::new::<PyRuntimeError, _>(e.to_string()))?;"
1448 )
1449 } else if is_unit_return {
1450 format!("{core_call}.await;")
1451 } else {
1452 format!("let result = {core_call}.await;")
1453 };
1454 let (ok_expr, extra_binding) = if is_unit_return && !has_error {
1455 ("()".to_string(), String::new())
1456 } else if return_wrap.contains(".into()") || return_wrap.contains("::from(") {
1457 let wrapped_var = "wrapped_result";
1461 let binding = if let Some(ret_type) = return_type {
1462 format!("let {wrapped_var}: {ret_type} = {return_wrap};\n ")
1464 } else {
1465 format!("let {wrapped_var} = {return_wrap};\n ")
1466 };
1467 (wrapped_var.to_string(), binding)
1468 } else {
1469 (return_wrap.to_string(), String::new())
1470 };
1471 crate::template_env::render(
1472 "binding_helpers/async_body_pyo3.jinja",
1473 minijinja::context! {
1474 result_handling => result_handling,
1475 extra_binding => extra_binding,
1476 ok_expr => ok_expr,
1477 },
1478 )
1479 }
1480 AsyncPattern::WasmNativeAsync => {
1481 let result_handling = if has_error {
1482 format!(
1483 "let result = {core_call}.await\n \
1484 .map_err(|e| JsValue::from_str(&e.to_string()))?;"
1485 )
1486 } else if is_unit_return {
1487 format!("{core_call}.await;")
1488 } else {
1489 format!("let result = {core_call}.await;")
1490 };
1491 let ok_expr = if is_unit_return && !has_error {
1492 "()"
1493 } else {
1494 return_wrap
1495 };
1496 crate::template_env::render(
1497 "binding_helpers/async_body_wasm.jinja",
1498 minijinja::context! {
1499 result_handling => result_handling,
1500 ok_expr => ok_expr,
1501 },
1502 )
1503 }
1504 AsyncPattern::NapiNativeAsync => {
1505 let result_handling = if has_error {
1506 format!(
1507 "let result = {core_call}.await\n \
1508 .map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))?;"
1509 )
1510 } else if is_unit_return {
1511 format!("{core_call}.await;")
1512 } else {
1513 format!("let result = {core_call}.await;")
1514 };
1515 let (needs_ok_wrapper, ok_expr) = if !has_error && !is_unit_return {
1516 (false, return_wrap.to_string())
1518 } else {
1519 let expr = if is_unit_return && !has_error {
1520 "()".to_string()
1521 } else {
1522 return_wrap.to_string()
1523 };
1524 (true, expr)
1525 };
1526 crate::template_env::render(
1527 "binding_helpers/async_body_napi.jinja",
1528 minijinja::context! {
1529 result_handling => result_handling,
1530 needs_ok_wrapper => needs_ok_wrapper,
1531 ok_expr => ok_expr,
1532 return_wrap => return_wrap,
1533 },
1534 )
1535 }
1536 AsyncPattern::TokioBlockOn => {
1537 let rt_new = "tokio::runtime::Runtime::new()\
1538 .map_err(|e| extendr_api::Error::Other(e.to_string()))?";
1539 crate::template_env::render(
1540 "binding_helpers/async_body_tokio.jinja",
1541 minijinja::context! {
1542 has_error => has_error,
1543 is_opaque => is_opaque,
1544 is_unit_return => is_unit_return,
1545 core_call => core_call,
1546 return_wrap => return_wrap,
1547 rt_new => rt_new,
1548 },
1549 )
1550 }
1551 AsyncPattern::None => "todo!(\"async not supported by backend\")".to_string(),
1552 };
1553 if inner_clone_line.is_empty() {
1554 pattern_body
1555 } else {
1556 format!("{inner_clone_line}{pattern_body}")
1557 }
1558}
1559
1560pub fn gen_unimplemented_body(
1567 return_type: &TypeRef,
1568 fn_name: &str,
1569 has_error: bool,
1570 cfg: &RustBindingConfig,
1571 params: &[ParamDef],
1572 opaque_types: &AHashSet<String>,
1573) -> String {
1574 let suppress = if params.is_empty() {
1576 String::new()
1577 } else {
1578 let names: Vec<&str> = params.iter().map(|p| p.name.as_str()).collect();
1579 if names.len() == 1 {
1580 format!("let _ = {};\n ", names[0])
1581 } else {
1582 format!("let _ = ({});\n ", names.join(", "))
1583 }
1584 };
1585 let err_msg = format!("Not implemented: {fn_name}");
1586 let body = if has_error {
1587 match cfg.async_pattern {
1589 AsyncPattern::Pyo3FutureIntoPy => {
1590 format!("Err(pyo3::exceptions::PyNotImplementedError::new_err(\"{err_msg}\"))")
1591 }
1592 AsyncPattern::NapiNativeAsync => {
1593 format!("Err(napi::Error::new(napi::Status::GenericFailure, \"{err_msg}\"))")
1594 }
1595 AsyncPattern::WasmNativeAsync => {
1596 format!("Err(JsValue::from_str(\"{err_msg}\"))")
1597 }
1598 _ => format!("Err(\"{err_msg}\".to_string())"),
1599 }
1600 } else {
1601 match return_type {
1603 TypeRef::Unit => "()".to_string(),
1604 TypeRef::String | TypeRef::Char | TypeRef::Path => format!("String::from(\"[unimplemented: {fn_name}]\")"),
1605 TypeRef::Bytes => "Vec::new()".to_string(),
1606 TypeRef::Primitive(p) => match p {
1607 alef_core::ir::PrimitiveType::Bool => "false".to_string(),
1608 alef_core::ir::PrimitiveType::F32 => "0.0f32".to_string(),
1609 alef_core::ir::PrimitiveType::F64 => "0.0f64".to_string(),
1610 _ => "0".to_string(),
1611 },
1612 TypeRef::Optional(_) => "None".to_string(),
1613 TypeRef::Vec(_) => "Vec::new()".to_string(),
1614 TypeRef::Map(_, _) => "Default::default()".to_string(),
1615 TypeRef::Duration => "0".to_string(),
1616 TypeRef::Named(name) => {
1617 if opaque_types.contains(name.as_str()) {
1621 format!("todo!(\"{err_msg}\")")
1622 } else {
1623 "Default::default()".to_string()
1624 }
1625 }
1626 TypeRef::Json => {
1627 "Default::default()".to_string()
1629 }
1630 }
1631 };
1632 format!("{suppress}{body}")
1633}