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 | TypeRef::Bytes => {
178 if returns_ref {
179 format!("{expr}.into_iter().map(Into::into).collect()")
180 } else {
181 expr.to_string()
182 }
183 }
184 _ => expr.to_string(),
185 },
186 _ => expr.to_string(),
187 }
188}
189
190pub fn wrap_return(
195 expr: &str,
196 return_type: &TypeRef,
197 type_name: &str,
198 opaque_types: &AHashSet<String>,
199 self_is_opaque: bool,
200 returns_ref: bool,
201 returns_cow: bool,
202) -> String {
203 wrap_return_with_mutex(
204 expr,
205 return_type,
206 type_name,
207 opaque_types,
208 &AHashSet::new(),
209 self_is_opaque,
210 returns_ref,
211 returns_cow,
212 )
213}
214
215pub fn apply_return_newtype_unwrap(expr: &str, return_newtype_wrapper: &Option<String>) -> String {
220 match return_newtype_wrapper {
221 Some(_) => crate::template_env::render(
222 "binding_helpers/return_newtype_unwrap.jinja",
223 minijinja::context! {
224 expr => expr,
225 },
226 )
227 .trim_end_matches('\n')
228 .to_string(),
229 None => expr.to_string(),
230 }
231}
232
233pub fn gen_call_args(params: &[ParamDef], opaque_types: &AHashSet<String>) -> String {
244 params
245 .iter()
246 .enumerate()
247 .map(|(idx, p)| {
248 let promoted = crate::shared::is_promoted_optional(params, idx);
249 let unwrap_suffix = if promoted && p.optional {
254 format!(".expect(\"'{}' is required\")", p.name)
255 } else {
256 String::new()
257 };
258 if let Some(newtype_path) = &p.newtype_wrapper {
261 return if p.optional {
262 format!("{}.map({newtype_path})", p.name)
263 } else if promoted {
264 format!("{newtype_path}({}{})", p.name, unwrap_suffix)
265 } else {
266 format!("{newtype_path}({})", p.name)
267 };
268 }
269 match &p.ty {
270 TypeRef::Named(name) if opaque_types.contains(name.as_str()) => {
271 if p.optional {
273 format!("{}.as_ref().map(|v| &v.inner)", p.name)
274 } else if promoted {
275 format!("{}{}.inner.as_ref()", p.name, unwrap_suffix)
276 } else {
277 format!("&{}.inner", p.name)
278 }
279 }
280 TypeRef::Named(_) => {
281 if p.optional {
282 if p.is_ref {
283 format!("{}.as_ref()", p.name)
286 } else {
287 format!("{}.map(Into::into)", p.name)
288 }
289 } else if promoted {
290 format!("{}{}.into()", p.name, unwrap_suffix)
291 } else {
292 format!("{}.into()", p.name)
293 }
294 }
295 TypeRef::String | TypeRef::Char => {
299 if p.optional {
300 if p.is_ref {
301 format!("{}.as_deref()", p.name)
302 } else {
303 p.name.clone()
304 }
305 } else if promoted {
306 if p.is_ref {
307 format!("&{}{}", p.name, unwrap_suffix)
308 } else {
309 format!("{}{}", p.name, unwrap_suffix)
310 }
311 } else if p.is_ref {
312 format!("&{}", p.name)
313 } else {
314 p.name.clone()
315 }
316 }
317 TypeRef::Path => {
319 if p.optional && p.is_ref {
320 format!("{}.as_deref().map(std::path::Path::new)", p.name)
321 } else if p.optional {
322 format!("{}.map(std::path::PathBuf::from)", p.name)
323 } else if promoted {
324 format!("std::path::PathBuf::from({}{})", p.name, unwrap_suffix)
325 } else if p.is_ref {
326 format!("std::path::Path::new(&{})", p.name)
327 } else {
328 format!("std::path::PathBuf::from({})", p.name)
329 }
330 }
331 TypeRef::Bytes => {
332 if p.optional {
333 if p.is_ref {
334 format!("{}.as_deref()", p.name)
335 } else {
336 p.name.clone()
337 }
338 } else if promoted {
339 if p.is_ref {
342 format!("&{}{}", p.name, unwrap_suffix)
343 } else {
344 format!("{}{}", p.name, unwrap_suffix)
345 }
346 } else {
347 if p.is_ref {
350 format!("&{}", p.name)
351 } else {
352 p.name.clone()
353 }
354 }
355 }
356 TypeRef::Duration => {
358 if p.optional {
359 format!("{}.map(std::time::Duration::from_millis)", p.name)
360 } else if promoted {
361 format!("std::time::Duration::from_millis({}{})", p.name, unwrap_suffix)
362 } else {
363 format!("std::time::Duration::from_millis({})", p.name)
364 }
365 }
366 TypeRef::Json => {
367 if p.optional {
369 format!("{}.as_ref().and_then(|s| serde_json::from_str(s).ok())", p.name)
370 } else if promoted {
371 format!("serde_json::from_str(&{}{}).unwrap_or_default()", p.name, unwrap_suffix)
372 } else {
373 format!("serde_json::from_str(&{}).unwrap_or_default()", p.name)
374 }
375 }
376 TypeRef::Vec(inner) => {
377 if matches!(inner.as_ref(), TypeRef::Named(_)) {
379 if p.optional {
380 if p.is_ref {
381 format!("{}.as_deref()", p.name)
382 } else {
383 p.name.clone()
384 }
385 } else if promoted {
386 if p.is_ref {
387 format!("&{}{}", p.name, unwrap_suffix)
388 } else {
389 format!("{}{}", p.name, unwrap_suffix)
390 }
391 } else if p.is_ref {
392 format!("&{}", p.name)
393 } else {
394 p.name.clone()
395 }
396 } else if promoted {
397 format!("{}{}", p.name, unwrap_suffix)
398 } else if p.is_mut && p.optional {
399 format!("{}.as_deref_mut()", p.name)
400 } else if p.is_mut {
401 format!("&mut {}", p.name)
402 } else if p.is_ref && p.optional {
403 format!("{}.as_deref()", p.name)
404 } else if p.is_ref {
405 format!("&{}", p.name)
406 } else {
407 p.name.clone()
408 }
409 }
410 _ => {
411 if promoted {
412 format!("{}{}", p.name, unwrap_suffix)
413 } else if p.is_mut && p.optional {
414 format!("{}.as_deref_mut()", p.name)
415 } else if p.is_mut {
416 format!("&mut {}", p.name)
417 } else if p.is_ref && p.optional {
418 format!("{}.as_deref()", p.name)
421 } else if p.is_ref {
422 format!("&{}", p.name)
423 } else {
424 p.name.clone()
425 }
426 }
427 }
428 })
429 .collect::<Vec<_>>()
430 .join(", ")
431}
432
433pub fn gen_call_args_cfg(
440 params: &[ParamDef],
441 opaque_types: &AHashSet<String>,
442 cast_uints_to_i32: bool,
443 cast_large_ints_to_f64: bool,
444) -> String {
445 params
446 .iter()
447 .enumerate()
448 .map(|(idx, p)| {
449 let promoted = crate::shared::is_promoted_optional(params, idx);
450 let unwrap_suffix = if promoted && p.optional {
451 format!(".expect(\"'{}' is required\")", p.name)
452 } else {
453 String::new()
454 };
455 if p.newtype_wrapper.is_some() {
457 return gen_call_args(std::slice::from_ref(p), opaque_types);
460 }
461 if let TypeRef::Primitive(prim) = &p.ty {
463 let core_ty = core_prim_str(prim);
464 let needs_cast =
465 (cast_uints_to_i32 && needs_i32_cast(prim)) || (cast_large_ints_to_f64 && needs_f64_cast(prim));
466 if needs_cast {
467 return if p.optional {
468 format!("{}.map(|v| v as {core_ty})", p.name)
469 } else if promoted {
470 format!("({}{}) as {core_ty}", p.name, unwrap_suffix)
471 } else {
472 format!("{} as {core_ty}", p.name)
473 };
474 }
475 }
476 gen_call_args(std::slice::from_ref(p), opaque_types)
478 })
479 .collect::<Vec<_>>()
480 .join(", ")
481}
482
483pub fn gen_call_args_with_let_bindings(params: &[ParamDef], opaque_types: &AHashSet<String>) -> String {
486 params
487 .iter()
488 .enumerate()
489 .map(|(idx, p)| {
490 let promoted = crate::shared::is_promoted_optional(params, idx);
491 let unwrap_suffix = if promoted {
492 format!(".expect(\"'{}' is required\")", p.name)
493 } else {
494 String::new()
495 };
496 if let Some(newtype_path) = &p.newtype_wrapper {
498 return if p.optional {
499 format!("{}.map({newtype_path})", p.name)
500 } else if promoted {
501 format!("{newtype_path}({}{})", p.name, unwrap_suffix)
502 } else {
503 format!("{newtype_path}({})", p.name)
504 };
505 }
506 match &p.ty {
507 TypeRef::Named(name) if opaque_types.contains(name.as_str()) => {
508 if p.optional {
509 format!("{}.as_ref().map(|v| &v.inner)", p.name)
510 } else if promoted {
511 format!("{}{}.inner.as_ref()", p.name, unwrap_suffix)
512 } else {
513 format!("&{}.inner", p.name)
514 }
515 }
516 TypeRef::Named(_) => {
517 if p.optional && p.is_ref {
518 format!("{}_core", p.name)
520 } else if p.is_ref {
521 format!("&{}_core", p.name)
523 } else {
524 format!("{}_core", p.name)
525 }
526 }
527 TypeRef::String | TypeRef::Char => {
528 if p.optional {
529 if p.is_ref {
530 format!("{}.as_deref()", p.name)
531 } else {
532 p.name.clone()
533 }
534 } else if promoted {
535 if p.is_ref {
536 format!("&{}{}", p.name, unwrap_suffix)
537 } else {
538 format!("{}{}", p.name, unwrap_suffix)
539 }
540 } else if p.is_ref {
541 format!("&{}", p.name)
542 } else {
543 p.name.clone()
544 }
545 }
546 TypeRef::Path => {
547 if promoted {
548 format!("std::path::PathBuf::from({}{})", p.name, unwrap_suffix)
549 } else if p.optional && p.is_ref {
550 format!("{}.as_deref().map(std::path::Path::new)", p.name)
551 } else if p.optional {
552 format!("{}.map(std::path::PathBuf::from)", p.name)
553 } else if p.is_ref {
554 format!("std::path::Path::new(&{})", p.name)
555 } else {
556 format!("std::path::PathBuf::from({})", p.name)
557 }
558 }
559 TypeRef::Bytes => {
560 if p.optional {
561 if p.is_ref {
562 format!("{}.as_deref()", p.name)
563 } else {
564 p.name.clone()
565 }
566 } else if promoted {
567 if p.is_ref {
570 format!("&{}{}", p.name, unwrap_suffix)
571 } else {
572 format!("{}{}", p.name, unwrap_suffix)
573 }
574 } else {
575 if p.is_ref {
578 format!("&{}", p.name)
579 } else {
580 p.name.clone()
581 }
582 }
583 }
584 TypeRef::Duration => {
585 if p.optional {
586 format!("{}.map(std::time::Duration::from_millis)", p.name)
587 } else if promoted {
588 format!("std::time::Duration::from_millis({}{})", p.name, unwrap_suffix)
589 } else {
590 format!("std::time::Duration::from_millis({})", p.name)
591 }
592 }
593 TypeRef::Vec(inner) => {
594 if matches!(inner.as_ref(), TypeRef::String) && p.sanitized && p.original_type.is_some() {
597 if p.optional && p.is_ref {
598 format!("{}_core.as_deref()", p.name)
599 } else if p.optional {
600 format!("{}_core", p.name)
601 } else if p.is_ref {
602 format!("&{}_core", p.name)
603 } else {
604 format!("{}_core", p.name)
605 }
606 } else if matches!(inner.as_ref(), TypeRef::Named(_)) {
607 if p.optional && p.is_ref {
609 format!("{}_core.as_deref()", p.name)
611 } else if p.optional {
612 format!("{}_core", p.name)
614 } else if p.is_ref {
615 format!("&{}_core", p.name)
616 } else {
617 format!("{}_core", p.name)
618 }
619 } else if matches!(inner.as_ref(), TypeRef::String | TypeRef::Char) && p.is_ref {
620 if p.optional {
623 format!("{}.as_deref()", p.name)
624 } else {
625 format!("&{}_refs", p.name)
626 }
627 } else if promoted {
628 format!("{}{}", p.name, unwrap_suffix)
629 } else if p.is_ref && p.optional {
630 format!("{}.as_deref()", p.name)
631 } else if p.is_ref {
632 format!("&{}", p.name)
633 } else {
634 p.name.clone()
635 }
636 }
637 _ => {
638 if promoted {
639 format!("{}{}", p.name, unwrap_suffix)
640 } else if p.is_ref && p.optional {
641 format!("{}.as_deref()", p.name)
642 } else if p.is_ref {
643 format!("&{}", p.name)
644 } else {
645 p.name.clone()
646 }
647 }
648 }
649 })
650 .collect::<Vec<_>>()
651 .join(", ")
652}
653
654pub fn gen_named_let_bindings_pub(params: &[ParamDef], opaque_types: &AHashSet<String>, core_import: &str) -> String {
656 gen_named_let_bindings(params, opaque_types, core_import)
657}
658
659pub fn gen_named_let_bindings_no_promote(
662 params: &[ParamDef],
663 opaque_types: &AHashSet<String>,
664 core_import: &str,
665) -> String {
666 gen_named_let_bindings_inner(params, opaque_types, core_import, false)
667}
668
669pub(super) fn gen_named_let_bindings(
670 params: &[ParamDef],
671 opaque_types: &AHashSet<String>,
672 core_import: &str,
673) -> String {
674 gen_named_let_bindings_inner(params, opaque_types, core_import, true)
675}
676
677pub(super) fn gen_named_let_bindings_by_ref(
681 params: &[ParamDef],
682 opaque_types: &AHashSet<String>,
683 core_import: &str,
684) -> String {
685 let mut bindings = String::new();
686 for (idx, p) in params.iter().enumerate() {
687 match &p.ty {
688 TypeRef::Named(name) if !opaque_types.contains(name.as_str()) => {
689 let promoted = crate::shared::is_promoted_optional(params, idx);
690 let core_type_path = format!("{core_import}::{name}");
691 let binding = if p.optional {
692 crate::template_env::render(
693 "binding_helpers/named_let_binding_by_ref_optional.jinja",
694 minijinja::context! {
695 name => &p.name,
696 core_type_path => &core_type_path,
697 },
698 )
699 } else if promoted {
700 crate::template_env::render(
701 "binding_helpers/named_let_binding_by_ref_promoted.jinja",
702 minijinja::context! {
703 name => &p.name,
704 core_type_path => &core_type_path,
705 },
706 )
707 } else {
708 crate::template_env::render(
709 "binding_helpers/named_let_binding_by_ref_simple.jinja",
710 minijinja::context! {
711 name => &p.name,
712 core_type_path => &core_type_path,
713 },
714 )
715 };
716 bindings.push_str(&binding);
717 bindings.push_str("\n ");
718 }
719 TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Named(n) if !opaque_types.contains(n.as_str())) => {
720 let binding = if p.optional {
721 crate::template_env::render(
722 "binding_helpers/vec_named_let_binding_by_ref_optional.jinja",
723 minijinja::context! {
724 name => &p.name,
725 },
726 )
727 } else {
728 let promoted = crate::shared::is_promoted_optional(params, idx);
729 if promoted {
730 crate::template_env::render(
731 "binding_helpers/vec_named_let_binding_by_ref_promoted.jinja",
732 minijinja::context! {
733 name => &p.name,
734 },
735 )
736 } else {
737 crate::template_env::render(
738 "binding_helpers/vec_named_let_binding_by_ref_simple.jinja",
739 minijinja::context! {
740 name => &p.name,
741 },
742 )
743 }
744 };
745 bindings.push_str(&binding);
746 bindings.push_str("\n ");
747 }
748 TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::String | TypeRef::Char) && p.is_ref => {
749 let binding = if p.optional {
750 crate::template_env::render(
751 "binding_helpers/vec_string_refs_binding_optional.jinja",
752 minijinja::context! {
753 name => &p.name,
754 },
755 )
756 } else {
757 crate::template_env::render(
758 "binding_helpers/vec_string_refs_binding_simple.jinja",
759 minijinja::context! {
760 name => &p.name,
761 },
762 )
763 };
764 bindings.push_str(&binding);
765 bindings.push_str("\n ");
766 }
767 _ => {}
768 }
769 }
770 bindings
771}
772
773fn gen_named_let_bindings_inner(
774 params: &[ParamDef],
775 opaque_types: &AHashSet<String>,
776 core_import: &str,
777 promote: bool,
778) -> String {
779 let mut bindings = String::new();
780 for (idx, p) in params.iter().enumerate() {
781 match &p.ty {
782 TypeRef::Named(name) if !opaque_types.contains(name.as_str()) => {
783 let promoted = promote && crate::shared::is_promoted_optional(params, idx);
784 let core_type_path = format!("{}::{}", core_import, name);
785 let binding = if p.optional {
786 if p.is_ref {
787 crate::template_env::render(
788 "binding_helpers/named_let_binding_optional_ref.jinja",
789 minijinja::context! {
790 name => &p.name,
791 core_type_path => &core_type_path,
792 },
793 )
794 } else {
795 crate::template_env::render(
796 "binding_helpers/named_let_binding_optional.jinja",
797 minijinja::context! {
798 name => &p.name,
799 core_type_path => &core_type_path,
800 },
801 )
802 }
803 } else if promoted {
804 crate::template_env::render(
805 "binding_helpers/named_let_binding_promoted.jinja",
806 minijinja::context! {
807 name => &p.name,
808 core_type_path => &core_type_path,
809 },
810 )
811 } else {
812 crate::template_env::render(
813 "binding_helpers/named_let_binding_simple.jinja",
814 minijinja::context! {
815 name => &p.name,
816 core_type_path => &core_type_path,
817 },
818 )
819 };
820 bindings.push_str(&binding);
821 bindings.push_str("\n ");
822 }
823 TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Named(n) if !opaque_types.contains(n.as_str())) => {
824 let promoted = promote && crate::shared::is_promoted_optional(params, idx);
825 let binding = if p.optional && p.is_ref {
826 crate::template_env::render(
827 "binding_helpers/vec_named_let_binding_optional.jinja",
828 minijinja::context! {
829 name => &p.name,
830 },
831 )
832 } else if p.optional {
833 crate::template_env::render(
834 "binding_helpers/vec_named_let_binding_optional_no_ref.jinja",
835 minijinja::context! {
836 name => &p.name,
837 },
838 )
839 } else if promoted {
840 crate::template_env::render(
841 "binding_helpers/vec_named_let_binding_promoted.jinja",
842 minijinja::context! {
843 name => &p.name,
844 },
845 )
846 } else {
847 crate::template_env::render(
848 "binding_helpers/vec_named_let_binding_simple.jinja",
849 minijinja::context! {
850 name => &p.name,
851 },
852 )
853 };
854 bindings.push_str(&binding);
855 bindings.push_str("\n ");
856 }
857 TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::String | TypeRef::Char) && p.is_ref => {
860 let binding = if p.optional {
861 crate::template_env::render(
862 "binding_helpers/vec_string_refs_binding_optional.jinja",
863 minijinja::context! {
864 name => &p.name,
865 },
866 )
867 } else {
868 crate::template_env::render(
869 "binding_helpers/vec_string_refs_binding_simple.jinja",
870 minijinja::context! {
871 name => &p.name,
872 },
873 )
874 };
875 bindings.push_str(&binding);
876 bindings.push_str("\n ");
877 }
878 TypeRef::Vec(inner)
880 if matches!(inner.as_ref(), TypeRef::String) && p.sanitized && p.original_type.is_some() =>
881 {
882 let template = if p.optional {
883 "binding_helpers/sanitized_vec_string_filter_optional.jinja"
884 } else {
885 "binding_helpers/sanitized_vec_string_filter_simple.jinja"
886 };
887 bindings.push_str(&crate::template_env::render(
888 template,
889 minijinja::context! {
890 name => &p.name,
891 },
892 ));
893 }
894 _ => {}
895 }
896 }
897 bindings
898}
899
900pub fn gen_serde_let_bindings(
907 params: &[ParamDef],
908 opaque_types: &AHashSet<String>,
909 core_import: &str,
910 err_conv: &str,
911 indent: &str,
912) -> String {
913 let mut bindings = String::new();
914 for (idx, p) in params.iter().enumerate() {
915 let promoted = crate::shared::is_promoted_optional(params, idx);
916 match &p.ty {
917 TypeRef::Named(name) if !opaque_types.contains(name.as_str()) => {
918 let core_path = format!("{}::{}", core_import, name);
919 if p.optional {
920 bindings.push_str(&crate::template_env::render(
921 "binding_helpers/serde_named_let_binding_optional.jinja",
922 minijinja::context! {
923 name => &p.name,
924 core_path => core_path,
925 err_conv => err_conv,
926 indent => indent,
927 },
928 ));
929 } else if promoted {
930 bindings.push_str(&crate::template_env::render(
934 "binding_helpers/serde_named_let_binding_promoted.jinja",
935 minijinja::context! {
936 name => &p.name,
937 core_path => core_path,
938 err_conv => err_conv,
939 indent => indent,
940 },
941 ));
942 } else {
943 bindings.push_str(&crate::template_env::render(
944 "binding_helpers/serde_named_let_binding_simple.jinja",
945 minijinja::context! {
946 name => &p.name,
947 core_path => core_path,
948 err_conv => err_conv,
949 indent => indent,
950 },
951 ));
952 }
953 }
954 TypeRef::Vec(inner) => {
955 if let TypeRef::Named(name) = inner.as_ref() {
956 if !opaque_types.contains(name.as_str()) {
957 let core_path = format!("{}::{}", core_import, name);
958 if p.optional {
959 bindings.push_str(&crate::template_env::render(
960 "binding_helpers/serde_vec_named_optional.jinja",
961 minijinja::context! {
962 name => &p.name,
963 core_path => core_path,
964 err_conv => err_conv,
965 indent => indent,
966 },
967 ));
968 } else {
969 bindings.push_str(&crate::template_env::render(
970 "binding_helpers/serde_vec_named_simple.jinja",
971 minijinja::context! {
972 name => &p.name,
973 core_path => core_path,
974 err_conv => err_conv,
975 indent => indent,
976 },
977 ));
978 }
979 }
980 } else if matches!(inner.as_ref(), TypeRef::String) && p.sanitized && p.original_type.is_some() {
981 let template = if p.optional {
984 "binding_helpers/serde_sanitized_vec_string_optional.jinja"
985 } else {
986 "binding_helpers/serde_sanitized_vec_string_simple.jinja"
987 };
988 bindings.push_str(&crate::template_env::render(
989 template,
990 minijinja::context! {
991 name => &p.name,
992 err_conv => err_conv,
993 indent => indent,
994 },
995 ));
996 }
997 }
998 _ => {}
999 }
1000 }
1001 bindings
1002}
1003
1004pub fn has_named_params(params: &[ParamDef], opaque_types: &AHashSet<String>) -> bool {
1009 params.iter().any(|p| match &p.ty {
1010 TypeRef::Named(name) if !opaque_types.contains(name.as_str()) => true,
1011 TypeRef::Vec(inner) => {
1012 matches!(inner.as_ref(), TypeRef::Named(name) if !opaque_types.contains(name.as_str()))
1016 || (matches!(inner.as_ref(), TypeRef::String | TypeRef::Char) && p.is_ref)
1017 || (matches!(inner.as_ref(), TypeRef::String) && p.sanitized && p.original_type.is_some())
1018 }
1019 _ => false,
1020 })
1021}
1022
1023pub fn is_simple_non_opaque_param(ty: &TypeRef) -> bool {
1026 match ty {
1027 TypeRef::Primitive(_)
1028 | TypeRef::String
1029 | TypeRef::Char
1030 | TypeRef::Bytes
1031 | TypeRef::Path
1032 | TypeRef::Unit
1033 | TypeRef::Duration => true,
1034 TypeRef::Optional(inner) => is_simple_non_opaque_param(inner),
1035 _ => false,
1036 }
1037}
1038
1039pub fn gen_lossy_binding_to_core_fields(
1052 typ: &TypeDef,
1053 core_import: &str,
1054 option_duration_on_defaults: bool,
1055 opaque_types: &AHashSet<String>,
1056 cast_uints_to_i32: bool,
1057 cast_large_ints_to_f64: bool,
1058 skip_types: &[String],
1059) -> String {
1060 gen_lossy_binding_to_core_fields_inner(
1061 typ,
1062 core_import,
1063 false,
1064 option_duration_on_defaults,
1065 opaque_types,
1066 cast_uints_to_i32,
1067 cast_large_ints_to_f64,
1068 skip_types,
1069 )
1070}
1071
1072pub fn gen_lossy_binding_to_core_fields_mut(
1074 typ: &TypeDef,
1075 core_import: &str,
1076 option_duration_on_defaults: bool,
1077 opaque_types: &AHashSet<String>,
1078 cast_uints_to_i32: bool,
1079 cast_large_ints_to_f64: bool,
1080 skip_types: &[String],
1081) -> String {
1082 gen_lossy_binding_to_core_fields_inner(
1083 typ,
1084 core_import,
1085 true,
1086 option_duration_on_defaults,
1087 opaque_types,
1088 cast_uints_to_i32,
1089 cast_large_ints_to_f64,
1090 skip_types,
1091 )
1092}
1093
1094#[allow(clippy::too_many_arguments)]
1095fn gen_lossy_binding_to_core_fields_inner(
1096 typ: &TypeDef,
1097 core_import: &str,
1098 needs_mut: bool,
1099 option_duration_on_defaults: bool,
1100 opaque_types: &AHashSet<String>,
1101 cast_uints_to_i32: bool,
1102 cast_large_ints_to_f64: bool,
1103 skip_types: &[String],
1104) -> String {
1105 let core_path = crate::conversions::core_type_path(typ, core_import);
1106 let mut_kw = if needs_mut { "mut " } else { "" };
1107 let allow = if typ.has_stripped_cfg_fields {
1112 "#[allow(clippy::needless_update)]\n "
1113 } else {
1114 ""
1115 };
1116 let mut out = format!("{allow}let {mut_kw}core_self = {core_path} {{\n");
1117 for field in &typ.fields {
1118 if field.cfg.is_some() {
1121 continue;
1122 }
1123 let name = &field.name;
1124 if field.sanitized && field.core_wrapper != CoreWrapper::Cow {
1125 out.push_str(&crate::template_env::render(
1126 "binding_helpers/struct_field_default.jinja",
1127 minijinja::context! {
1128 name => &field.name,
1129 },
1130 ));
1131 out.push('\n');
1132 continue;
1133 }
1134 let is_opaque_named = match &field.ty {
1139 TypeRef::Named(n) => opaque_types.contains(n.as_str()),
1140 TypeRef::Optional(inner) => {
1141 matches!(inner.as_ref(), TypeRef::Named(n) if opaque_types.contains(n.as_str()))
1142 }
1143 _ => false,
1144 };
1145 if is_opaque_named {
1146 out.push_str(&crate::template_env::render(
1147 "binding_helpers/struct_field_default.jinja",
1148 minijinja::context! {
1149 name => &field.name,
1150 },
1151 ));
1152 out.push('\n');
1153 continue;
1154 }
1155 let is_skip_named = match &field.ty {
1158 TypeRef::Named(n) => skip_types.contains(n),
1159 TypeRef::Optional(inner) => {
1160 matches!(inner.as_ref(), TypeRef::Named(n) if skip_types.contains(n))
1161 }
1162 _ => false,
1163 };
1164 if is_skip_named {
1165 out.push_str(&crate::template_env::render(
1166 "binding_helpers/default_field.jinja",
1167 minijinja::context! {
1168 name => &name,
1169 },
1170 ));
1171 continue;
1172 }
1173 let expr = match &field.ty {
1174 TypeRef::Primitive(p) if cast_uints_to_i32 && needs_i32_cast(p) => {
1175 let core_ty = core_prim_str(p);
1176 if field.optional {
1177 format!("self.{name}.map(|v| v as {core_ty})")
1178 } else {
1179 format!("self.{name} as {core_ty}")
1180 }
1181 }
1182 TypeRef::Primitive(p) if cast_large_ints_to_f64 && needs_f64_cast(p) => {
1183 let core_ty = core_prim_str(p);
1184 if field.optional {
1185 format!("self.{name}.map(|v| v as {core_ty})")
1186 } else {
1187 format!("self.{name} as {core_ty}")
1188 }
1189 }
1190 TypeRef::Primitive(_) => format!("self.{name}"),
1191 TypeRef::Duration => {
1192 if field.optional {
1193 format!("self.{name}.map(std::time::Duration::from_millis)")
1194 } else if option_duration_on_defaults && typ.has_default {
1195 format!("self.{name}.map(std::time::Duration::from_millis).unwrap_or_default()")
1200 } else {
1201 format!("std::time::Duration::from_millis(self.{name})")
1202 }
1203 }
1204 TypeRef::String => {
1205 if field.core_wrapper == CoreWrapper::Cow {
1206 format!("self.{name}.clone().into()")
1207 } else {
1208 format!("self.{name}.clone()")
1209 }
1210 }
1211 TypeRef::Bytes => {
1215 if field.core_wrapper == CoreWrapper::Bytes {
1216 format!("self.{name}.clone().into()")
1217 } else {
1218 format!("self.{name}.clone()")
1219 }
1220 }
1221 TypeRef::Char => {
1222 if field.optional {
1223 format!("self.{name}.as_ref().and_then(|s| s.chars().next())")
1224 } else {
1225 format!("self.{name}.chars().next().unwrap_or('*')")
1226 }
1227 }
1228 TypeRef::Path => {
1229 if field.optional {
1230 format!("self.{name}.clone().map(Into::into)")
1231 } else {
1232 format!("self.{name}.clone().into()")
1233 }
1234 }
1235 TypeRef::Named(_) => {
1236 if field.optional {
1237 format!("self.{name}.clone().map(Into::into)")
1238 } else {
1239 format!("self.{name}.clone().into()")
1240 }
1241 }
1242 TypeRef::Vec(inner) => match inner.as_ref() {
1243 TypeRef::Named(_) => {
1244 if field.optional {
1245 format!("self.{name}.clone().map(|v| v.into_iter().map(Into::into).collect())")
1247 } else {
1248 format!("self.{name}.clone().into_iter().map(Into::into).collect()")
1249 }
1250 }
1251 TypeRef::Primitive(p) if cast_uints_to_i32 && needs_i32_cast(p) => {
1253 let core_ty = core_prim_str(p);
1254 if field.optional {
1255 format!("self.{name}.clone().map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
1256 } else {
1257 format!("self.{name}.clone().into_iter().map(|v| v as {core_ty}).collect()")
1258 }
1259 }
1260 TypeRef::Primitive(p) if cast_large_ints_to_f64 && needs_f64_cast(p) => {
1262 let core_ty = core_prim_str(p);
1263 if field.optional {
1264 format!("self.{name}.clone().map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
1265 } else {
1266 format!("self.{name}.clone().into_iter().map(|v| v as {core_ty}).collect()")
1267 }
1268 }
1269 _ => format!("self.{name}.clone()"),
1270 },
1271 TypeRef::Optional(inner) => {
1272 let base = match inner.as_ref() {
1276 TypeRef::Named(_) => {
1277 format!("self.{name}.clone().map(Into::into)")
1278 }
1279 TypeRef::Duration => {
1280 format!("self.{name}.map(|v| std::time::Duration::from_millis(v as u64))")
1281 }
1282 TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Named(_)) => {
1283 format!("self.{name}.clone().map(|v| v.into_iter().map(Into::into).collect())")
1284 }
1285 TypeRef::Vec(vi) => match vi.as_ref() {
1287 TypeRef::Primitive(p) if cast_uints_to_i32 && needs_i32_cast(p) => {
1288 let core_ty = core_prim_str(p);
1289 format!("self.{name}.clone().map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
1290 }
1291 TypeRef::Primitive(p) if cast_large_ints_to_f64 && needs_f64_cast(p) => {
1293 let core_ty = core_prim_str(p);
1294 format!("self.{name}.clone().map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
1295 }
1296 _ => format!("self.{name}.clone()"),
1297 },
1298 _ => format!("self.{name}.clone()"),
1299 };
1300 if field.optional {
1301 format!("({base}).map(Some)")
1302 } else {
1303 base
1304 }
1305 }
1306 TypeRef::Map(_, v) => match v.as_ref() {
1307 TypeRef::Json => {
1308 if field.optional {
1313 format!(
1314 "self.{name}.clone().map(|m| m.into_iter().map(|(k, v)| \
1315 (k.into(), serde_json::from_str(&v).unwrap_or(serde_json::Value::String(v)))).collect())"
1316 )
1317 } else {
1318 format!(
1319 "self.{name}.clone().into_iter().map(|(k, v)| \
1320 (k.into(), serde_json::from_str(&v).unwrap_or(serde_json::Value::String(v)))).collect()"
1321 )
1322 }
1323 }
1324 TypeRef::Named(_) => {
1327 if field.optional {
1328 format!(
1329 "self.{name}.clone().map(|m| m.into_iter().map(|(k, v)| (k.into(), v.into())).collect())"
1330 )
1331 } else {
1332 format!("self.{name}.clone().into_iter().map(|(k, v)| (k.into(), v.into())).collect()")
1333 }
1334 }
1335 TypeRef::Primitive(p) if cast_uints_to_i32 && needs_i32_cast(p) => {
1337 let core_ty = core_prim_str(p);
1338 if field.optional {
1339 format!(
1340 "self.{name}.clone().map(|m| m.into_iter().map(|(k, v)| (k.into(), v as {core_ty})).collect())"
1341 )
1342 } else {
1343 format!("self.{name}.clone().into_iter().map(|(k, v)| (k.into(), v as {core_ty})).collect()")
1344 }
1345 }
1346 TypeRef::Primitive(p) if cast_large_ints_to_f64 && needs_f64_cast(p) => {
1348 let core_ty = core_prim_str(p);
1349 if field.optional {
1350 format!(
1351 "self.{name}.clone().map(|m| m.into_iter().map(|(k, v)| (k.into(), v as {core_ty})).collect())"
1352 )
1353 } else {
1354 format!("self.{name}.clone().into_iter().map(|(k, v)| (k.into(), v as {core_ty})).collect()")
1355 }
1356 }
1357 _ => {
1359 if field.optional {
1360 format!("self.{name}.clone().map(|m| m.into_iter().map(|(k, v)| (k.into(), v)).collect())")
1361 } else {
1362 format!("self.{name}.clone().into_iter().map(|(k, v)| (k.into(), v)).collect()")
1363 }
1364 }
1365 },
1366 TypeRef::Unit => format!("self.{name}.clone()"),
1367 TypeRef::Json => {
1368 if field.optional {
1370 format!("self.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok())")
1371 } else {
1372 format!("serde_json::from_str(&self.{name}).unwrap_or_default()")
1373 }
1374 }
1375 };
1376 let expr = if let Some(newtype_path) = &field.newtype_wrapper {
1381 match &field.ty {
1382 TypeRef::Optional(_) => format!("({expr}).map({newtype_path})"),
1383 TypeRef::Vec(_) => format!("({expr}).into_iter().map({newtype_path}).collect::<Vec<_>>()"),
1384 _ if field.optional => format!("({expr}).map({newtype_path})"),
1385 _ => format!("{newtype_path}({expr})"),
1386 }
1387 } else {
1388 expr
1389 };
1390 out.push_str(&crate::template_env::render(
1391 "binding_helpers/struct_field_line.jinja",
1392 minijinja::context! {
1393 name => &field.name,
1394 expr => &expr,
1395 },
1396 ));
1397 out.push('\n');
1398 }
1399 if typ.has_stripped_cfg_fields {
1401 out.push_str(" ..Default::default()\n");
1402 }
1403 out.push_str(" };\n ");
1404 out
1405}
1406
1407#[allow(clippy::too_many_arguments)]
1422pub fn gen_async_body(
1423 core_call: &str,
1424 cfg: &RustBindingConfig,
1425 has_error: bool,
1426 return_wrap: &str,
1427 is_opaque: bool,
1428 inner_clone_line: &str,
1429 is_unit_return: bool,
1430 return_type: Option<&str>,
1431) -> String {
1432 let pattern_body = match cfg.async_pattern {
1433 AsyncPattern::Pyo3FutureIntoPy => {
1434 let result_handling = if has_error {
1435 format!(
1436 "let result = {core_call}.await\n \
1437 .map_err(|e| PyErr::new::<PyRuntimeError, _>(e.to_string()))?;"
1438 )
1439 } else if is_unit_return {
1440 format!("{core_call}.await;")
1441 } else {
1442 format!("let result = {core_call}.await;")
1443 };
1444 let (ok_expr, extra_binding) = if is_unit_return && !has_error {
1445 ("()".to_string(), String::new())
1446 } else if return_wrap.contains(".into()") || return_wrap.contains("::from(") {
1447 let wrapped_var = "wrapped_result";
1451 let binding = if let Some(ret_type) = return_type {
1452 format!("let {wrapped_var}: {ret_type} = {return_wrap};\n ")
1454 } else {
1455 format!("let {wrapped_var} = {return_wrap};\n ")
1456 };
1457 (wrapped_var.to_string(), binding)
1458 } else {
1459 (return_wrap.to_string(), String::new())
1460 };
1461 crate::template_env::render(
1462 "binding_helpers/async_body_pyo3.jinja",
1463 minijinja::context! {
1464 result_handling => result_handling,
1465 extra_binding => extra_binding,
1466 ok_expr => ok_expr,
1467 },
1468 )
1469 }
1470 AsyncPattern::WasmNativeAsync => {
1471 let result_handling = if has_error {
1472 format!(
1473 "let result = {core_call}.await\n \
1474 .map_err(|e| JsValue::from_str(&e.to_string()))?;"
1475 )
1476 } else if is_unit_return {
1477 format!("{core_call}.await;")
1478 } else {
1479 format!("let result = {core_call}.await;")
1480 };
1481 let ok_expr = if is_unit_return && !has_error {
1482 "()"
1483 } else {
1484 return_wrap
1485 };
1486 crate::template_env::render(
1487 "binding_helpers/async_body_wasm.jinja",
1488 minijinja::context! {
1489 result_handling => result_handling,
1490 ok_expr => ok_expr,
1491 },
1492 )
1493 }
1494 AsyncPattern::NapiNativeAsync => {
1495 let result_handling = if has_error {
1496 format!(
1497 "let result = {core_call}.await\n \
1498 .map_err(|e| napi::Error::new(napi::Status::GenericFailure, e.to_string()))?;"
1499 )
1500 } else if is_unit_return {
1501 format!("{core_call}.await;")
1502 } else {
1503 format!("let result = {core_call}.await;")
1504 };
1505 let (needs_ok_wrapper, ok_expr) = if !has_error && !is_unit_return {
1506 (false, return_wrap.to_string())
1508 } else {
1509 let expr = if is_unit_return && !has_error {
1510 "()".to_string()
1511 } else {
1512 return_wrap.to_string()
1513 };
1514 (true, expr)
1515 };
1516 crate::template_env::render(
1517 "binding_helpers/async_body_napi.jinja",
1518 minijinja::context! {
1519 result_handling => result_handling,
1520 needs_ok_wrapper => needs_ok_wrapper,
1521 ok_expr => ok_expr,
1522 return_wrap => return_wrap,
1523 },
1524 )
1525 }
1526 AsyncPattern::TokioBlockOn => {
1527 let rt_new = "tokio::runtime::Runtime::new()\
1528 .map_err(|e| extendr_api::Error::Other(e.to_string()))?";
1529 crate::template_env::render(
1530 "binding_helpers/async_body_tokio.jinja",
1531 minijinja::context! {
1532 has_error => has_error,
1533 is_opaque => is_opaque,
1534 is_unit_return => is_unit_return,
1535 core_call => core_call,
1536 return_wrap => return_wrap,
1537 rt_new => rt_new,
1538 },
1539 )
1540 }
1541 AsyncPattern::None => "todo!(\"async not supported by backend\")".to_string(),
1542 };
1543 if inner_clone_line.is_empty() {
1544 pattern_body
1545 } else {
1546 format!("{inner_clone_line}{pattern_body}")
1547 }
1548}
1549
1550pub fn gen_unimplemented_body(
1557 return_type: &TypeRef,
1558 fn_name: &str,
1559 has_error: bool,
1560 cfg: &RustBindingConfig,
1561 params: &[ParamDef],
1562 opaque_types: &AHashSet<String>,
1563) -> String {
1564 let suppress = if params.is_empty() {
1566 String::new()
1567 } else {
1568 let names: Vec<&str> = params.iter().map(|p| p.name.as_str()).collect();
1569 if names.len() == 1 {
1570 format!("let _ = {};\n ", names[0])
1571 } else {
1572 format!("let _ = ({});\n ", names.join(", "))
1573 }
1574 };
1575 let err_msg = format!("Not implemented: {fn_name}");
1576 let body = if has_error {
1577 match cfg.async_pattern {
1579 AsyncPattern::Pyo3FutureIntoPy => {
1580 format!("Err(pyo3::exceptions::PyNotImplementedError::new_err(\"{err_msg}\"))")
1581 }
1582 AsyncPattern::NapiNativeAsync => {
1583 format!("Err(napi::Error::new(napi::Status::GenericFailure, \"{err_msg}\"))")
1584 }
1585 AsyncPattern::WasmNativeAsync => {
1586 format!("Err(JsValue::from_str(\"{err_msg}\"))")
1587 }
1588 _ => format!("Err(\"{err_msg}\".to_string())"),
1589 }
1590 } else {
1591 match return_type {
1593 TypeRef::Unit => "()".to_string(),
1594 TypeRef::String | TypeRef::Char | TypeRef::Path => format!("String::from(\"[unimplemented: {fn_name}]\")"),
1595 TypeRef::Bytes => "Vec::new()".to_string(),
1596 TypeRef::Primitive(p) => match p {
1597 alef_core::ir::PrimitiveType::Bool => "false".to_string(),
1598 alef_core::ir::PrimitiveType::F32 => "0.0f32".to_string(),
1599 alef_core::ir::PrimitiveType::F64 => "0.0f64".to_string(),
1600 _ => "0".to_string(),
1601 },
1602 TypeRef::Optional(_) => "None".to_string(),
1603 TypeRef::Vec(_) => "Vec::new()".to_string(),
1604 TypeRef::Map(_, _) => "Default::default()".to_string(),
1605 TypeRef::Duration => "0".to_string(),
1606 TypeRef::Named(name) => {
1607 if opaque_types.contains(name.as_str()) {
1611 format!("todo!(\"{err_msg}\")")
1612 } else {
1613 "Default::default()".to_string()
1614 }
1615 }
1616 TypeRef::Json => {
1617 "Default::default()".to_string()
1619 }
1620 }
1621 };
1622 format!("{suppress}{body}")
1623}