1mod ty;
24
25pub use ty::{Family, Prim, Ty, WireType};
26
27use crate::ir::{
28 CallbackInterfaceDef, EnumDef, ErrorDomain, Function, InterfaceDef, Module, StructDef,
29 StructField, TypeRef,
30};
31use heck::ToUpperCamelCase;
32
33use crate::abi::{
34 async_callback_params, async_input_params, callback_method_signature, context_param,
35 error_out_param, lower_param, lower_return, sync_signature, AbiParam, CType, ConstPos,
36};
37use crate::resolved::ResolvedApi;
38
39pub const RUNTIME_SYMBOLS: &[&str] = &[
43 "str",
44 "bytes",
45 "reader",
46 "writer",
47 "abi_version",
48 "error",
49 "error_set",
50 "error_clear",
51 "error_free",
52 "free_bytes",
53 "cancel_token",
54 "cancel_token_create",
55 "cancel_token_cancel",
56 "cancel_token_is_cancelled",
57 "cancel_token_destroy",
58 "alloc",
59 "dealloc",
60 "debug_live",
61];
62
63pub const RESERVED_SYMBOL_FAMILIES: &[&str] = &[
68 "str_", "bytes_", "reader_", "writer_", "list_", "map_", "opt_",
69];
70
71pub const ABI_VERSION: u32 = 3;
74
75#[must_use]
78pub fn checksum_symbol(prefix: &str, module: &str) -> String {
79 format!("{prefix}_{module}_checksum")
80}
81
82#[derive(Debug, Clone, PartialEq, Eq)]
85pub struct AbiFn {
86 pub symbol: String,
88 pub params: Vec<AbiParam>,
90 pub ret: CType,
92}
93
94#[derive(Debug, Clone, PartialEq, Eq)]
97pub enum CallShape {
98 Sync(AbiFn),
100 Async(AsyncBinding),
102 Iterator(IteratorBinding),
104}
105
106#[derive(Debug, Clone, PartialEq, Eq)]
108pub struct AsyncBinding {
109 pub launch: AbiFn,
112 pub callback_type: String,
115 pub callback_params: Vec<AbiParam>,
118}
119
120#[derive(Debug, Clone, PartialEq, Eq)]
122pub struct IteratorBinding {
123 pub elem: Ty,
125 pub iter_tag: String,
127 pub launch: AbiFn,
129 pub next: AbiFn,
131 pub destroy_symbol: String,
133}
134
135#[derive(Debug, Clone, PartialEq, Eq)]
137pub struct ParamBinding {
138 pub name: String,
140 pub ty: Ty,
142 pub doc: Option<String>,
144 pub abi: Vec<AbiParam>,
146}
147
148impl ParamBinding {
149 pub fn new(name: impl Into<String>, ty: Ty, doc: Option<String>, module: &str) -> Self {
152 let name = name.into();
153 let abi = lower_param(&name, &ty, module);
154 Self { name, ty, doc, abi }
155 }
156}
157
158#[derive(Debug, Clone, PartialEq, Eq)]
166pub struct FnBinding {
167 pub name: String,
169 pub doc: Option<String>,
171 pub deprecated: Option<String>,
173 pub cancellable: bool,
175 pub is_async: bool,
177 pub throws: bool,
183 pub has_self: bool,
186 pub params: Vec<ParamBinding>,
188 pub ret: Option<Ty>,
193 pub c_base: String,
197 pub shape: CallShape,
199}
200
201#[derive(Debug, Clone, PartialEq, Eq)]
207pub struct FieldBinding {
208 pub name: String,
210 pub doc: Option<String>,
212 pub ty: Ty,
214}
215
216#[derive(Debug, Clone, PartialEq, Eq)]
220pub struct StructBinding {
221 pub name: String,
223 pub doc: Option<String>,
225 pub deprecated: Option<String>,
227 pub fields: Vec<FieldBinding>,
229}
230
231#[derive(Debug, Clone, PartialEq, Eq)]
241pub struct EnumBinding {
242 pub name: String,
244 pub doc: Option<String>,
246 pub deprecated: Option<String>,
248 pub c_tag: String,
250 pub variants: Vec<EnumVariantBinding>,
252 pub rich: bool,
255}
256
257impl EnumBinding {
258 pub fn is_rich(&self) -> bool {
260 self.rich
261 }
262}
263
264#[derive(Debug, Clone, PartialEq, Eq)]
267pub struct EnumVariantBinding {
268 pub name: String,
270 pub value: i32,
273 pub doc: Option<String>,
275 pub c_const: String,
277 pub fields: Vec<FieldBinding>,
280}
281
282#[derive(Debug, Clone, PartialEq, Eq)]
290pub struct InterfaceBinding {
291 pub name: String,
293 pub doc: Option<String>,
295 pub deprecated: Option<String>,
297 pub c_tag: String,
299 pub constructors: Vec<FnBinding>,
301 pub methods: Vec<FnBinding>,
303 pub statics: Vec<FnBinding>,
305 pub clone_symbol: String,
308 pub destroy_symbol: String,
310}
311
312#[derive(Debug, Clone, PartialEq, Eq)]
319pub struct CallbackMethodBinding {
320 pub name: String,
322 pub doc: Option<String>,
324 pub deprecated: Option<String>,
326 pub params: Vec<ParamBinding>,
328 pub ret: Option<Ty>,
330 pub abi_params: Vec<AbiParam>,
333 pub abi_ret: CType,
335}
336
337#[derive(Debug, Clone, PartialEq, Eq)]
344pub struct CallbackInterfaceBinding {
345 pub name: String,
347 pub doc: Option<String>,
349 pub deprecated: Option<String>,
352 pub c_tag: String,
354 pub vtable_tag: String,
356 pub methods: Vec<CallbackMethodBinding>,
358}
359
360#[derive(Debug, Clone, PartialEq, Eq)]
362pub struct ErrorCodeBinding {
363 pub name: String,
365 pub value: i32,
367 pub message: String,
369 pub doc: Option<String>,
371 pub c_const: String,
373 pub fields: Vec<FieldBinding>,
378}
379
380#[derive(Debug, Clone, PartialEq, Eq)]
388pub struct ErrorBinding {
389 pub name: String,
391 pub type_name: String,
395 pub owner_path: String,
397 pub declared_here: bool,
400 pub c_tag: String,
403 pub codes: Vec<ErrorCodeBinding>,
405}
406
407#[derive(Debug, Clone, PartialEq, Eq)]
409pub struct ModuleBinding {
410 pub name: String,
412 pub checksum: Option<u64>,
416 pub segments: Vec<String>,
418 pub path: String,
420 pub dot_path: String,
423 pub doc: Option<String>,
426 pub error: Option<ErrorBinding>,
430 pub enums: Vec<EnumBinding>,
432 pub structs: Vec<StructBinding>,
434 pub interfaces: Vec<InterfaceBinding>,
436 pub callback_interfaces: Vec<CallbackInterfaceBinding>,
438 pub functions: Vec<FnBinding>,
440}
441
442impl ModuleBinding {
443 pub fn callback_interface(&self, name: &str) -> Option<&CallbackInterfaceBinding> {
445 self.callback_interfaces.iter().find(|c| c.name == name)
446 }
447
448 pub fn is_empty(&self) -> bool {
450 self.enums.is_empty()
451 && self.structs.is_empty()
452 && self.interfaces.is_empty()
453 && self.callback_interfaces.is_empty()
454 && self.functions.is_empty()
455 && !self.declares_error()
456 }
457
458 pub fn declares_error(&self) -> bool {
461 self.error.as_ref().is_some_and(|e| e.declared_here)
462 }
463
464 pub fn callables(&self) -> impl Iterator<Item = &FnBinding> {
467 self.functions
468 .iter()
469 .chain(self.interfaces.iter().flat_map(|i| {
470 i.constructors
471 .iter()
472 .chain(i.methods.iter())
473 .chain(i.statics.iter())
474 }))
475 }
476
477 pub fn has_async(&self) -> bool {
479 self.callables().any(|f| f.is_async)
480 }
481
482 pub fn has_iterators(&self) -> bool {
484 self.callables()
485 .any(|f| matches!(f.shape, CallShape::Iterator(_)))
486 }
487}
488
489#[derive(Debug, Clone, PartialEq, Eq)]
491pub struct BindingModel {
492 pub prefix: String,
494 pub version: String,
496 pub modules: Vec<ModuleBinding>,
498}
499
500impl BindingModel {
501 pub fn build(api: &ResolvedApi) -> Self {
505 let prefix = api.identity().prefix.as_str();
506 let mut modules = Vec::new();
507 let lowerer = Lowerer { api, prefix };
508 for m in &api.modules {
509 lowerer.module(m, &[], None, &mut modules);
510 }
511 Self {
512 prefix: prefix.to_string(),
513 version: api.version.clone(),
514 modules,
515 }
516 }
517
518 pub fn roots(&self) -> impl Iterator<Item = &ModuleBinding> {
520 self.modules.iter().filter(|m| m.segments.len() == 1)
521 }
522
523 pub fn children<'a>(
527 &'a self,
528 parent: &'a ModuleBinding,
529 ) -> impl Iterator<Item = &'a ModuleBinding> + 'a {
530 self.modules.iter().filter(move |m| {
531 m.segments.len() == parent.segments.len() + 1
532 && m.segments[..parent.segments.len()] == parent.segments[..]
533 })
534 }
535
536 pub fn functions(&self) -> impl Iterator<Item = (&ModuleBinding, &FnBinding)> {
538 self.modules
539 .iter()
540 .flat_map(|m| m.functions.iter().map(move |f| (m, f)))
541 }
542
543 pub fn callables(&self) -> impl Iterator<Item = (&ModuleBinding, &FnBinding)> {
546 self.modules
547 .iter()
548 .flat_map(|m| m.callables().map(move |f| (m, f)))
549 }
550
551 pub fn callback_interfaces(
554 &self,
555 ) -> impl Iterator<Item = (&ModuleBinding, &CallbackInterfaceBinding)> {
556 self.modules
557 .iter()
558 .flat_map(|m| m.callback_interfaces.iter().map(move |c| (m, c)))
559 }
560
561 pub fn interface(&self, name: &str, current: &str) -> Option<&InterfaceBinding> {
565 let (dot_path, bare) = match name.rsplit_once('.') {
566 Some((p, n)) => (p, n),
567 None => (current, name),
568 };
569 self.modules
570 .iter()
571 .find(|m| m.dot_path == dot_path)
572 .and_then(|m| m.interfaces.iter().find(|i| i.name == bare))
573 }
574
575 pub fn c_symbols(&self) -> Vec<(String, String)> {
587 let p = &self.prefix;
588 let mut out: Vec<(String, String)> = RUNTIME_SYMBOLS
589 .iter()
590 .map(|s| (format!("{p}_{s}"), format!("the runtime symbol '{p}_{s}'")))
591 .collect();
592 for m in self.roots() {
593 out.push((
594 checksum_symbol(p, &m.name),
595 format!("the checksum of module '{}'", m.name),
596 ));
597 }
598 let callable = |out: &mut Vec<(String, String)>, f: &FnBinding, owner: &str| {
599 let what = format!("'{owner}.{}'", f.name);
600 match &f.shape {
601 CallShape::Sync(abi) => out.push((abi.symbol.clone(), format!("function {what}"))),
602 CallShape::Async(a) => {
603 out.push((a.launch.symbol.clone(), format!("async function {what}")));
604 out.push((
605 a.callback_type.clone(),
606 format!("the completion type of {what}"),
607 ));
608 }
609 CallShape::Iterator(it) => {
610 out.push((it.launch.symbol.clone(), format!("function {what}")));
611 out.push((it.iter_tag.clone(), format!("the iterator type of {what}")));
612 out.push((
613 it.next.symbol.clone(),
614 format!("the iterator step of {what}"),
615 ));
616 out.push((
617 it.destroy_symbol.clone(),
618 format!("the iterator destructor of {what}"),
619 ));
620 }
621 }
622 };
623 for m in &self.modules {
624 let dot = &m.dot_path;
625 if let Some(e) = m.error.as_ref().filter(|e| e.declared_here) {
626 out.push((e.c_tag.clone(), format!("error domain '{dot}.{}'", e.name)));
627 for c in &e.codes {
628 out.push((
629 c.c_const.clone(),
630 format!("error code '{dot}.{}.{}'", e.name, c.name),
631 ));
632 }
633 }
634 for e in &m.enums {
635 out.push((e.c_tag.clone(), format!("enum '{dot}.{}'", e.name)));
636 if e.rich {
637 out.push((
638 format!("{}_Tag", e.c_tag),
639 format!("the tag type of enum '{dot}.{}'", e.name),
640 ));
641 }
642 for v in &e.variants {
643 out.push((
644 v.c_const.clone(),
645 format!("enum variant '{dot}.{}.{}'", e.name, v.name),
646 ));
647 }
648 }
649 for s in &m.structs {
650 out.push((
651 format!("{p}_{}_{}", m.path, s.name),
652 format!("record '{dot}.{}'", s.name),
653 ));
654 }
655 for c in &m.callback_interfaces {
656 out.push((
657 c.vtable_tag.clone(),
658 format!("the vtable of callback interface '{dot}.{}'", c.name),
659 ));
660 }
661 for i in &m.interfaces {
662 let owner = format!("{dot}.{}", i.name);
663 out.push((i.c_tag.clone(), format!("interface '{owner}'")));
664 out.push((i.clone_symbol.clone(), format!("the clone of '{owner}'")));
665 out.push((
666 i.destroy_symbol.clone(),
667 format!("the destructor of '{owner}'"),
668 ));
669 for f in i.constructors.iter().chain(&i.methods).chain(&i.statics) {
670 callable(&mut out, f, &owner);
671 }
672 }
673 for f in &m.functions {
674 callable(&mut out, f, dot);
675 }
676 }
677 out
678 }
679
680 pub fn has_async(&self) -> bool {
682 self.modules.iter().any(ModuleBinding::has_async)
683 }
684
685 pub fn has_iterators(&self) -> bool {
687 self.modules.iter().any(ModuleBinding::has_iterators)
688 }
689
690 pub fn has_callback_interfaces(&self) -> bool {
692 self.modules
693 .iter()
694 .any(|m| !m.callback_interfaces.is_empty())
695 }
696
697 pub fn has_interfaces(&self) -> bool {
699 self.modules.iter().any(|m| !m.interfaces.is_empty())
700 }
701
702 pub fn has_buffers(&self) -> bool {
705 self.modules.iter().any(|m| {
706 !m.structs.is_empty()
707 || m.enums.iter().any(|e| e.rich)
708 || m.error
709 .as_ref()
710 .is_some_and(|e| e.codes.iter().any(|c| !c.fields.is_empty()))
711 }) || self.any_type(&|t| t.is_buffered())
712 }
713
714 pub fn has_buffered_objects(&self) -> bool {
718 self.any_top_type(&|ty, position| match position {
719 TypePosition::Field => ty.contains_object(),
722 TypePosition::Boundary => ty.is_buffered() && ty.contains_object(),
725 })
726 }
727
728 pub fn any_type(&self, pred: &dyn Fn(&Ty) -> bool) -> bool {
734 self.any_top_type(&|ty, _| ty.any(pred))
735 }
736
737 fn any_top_type(&self, pred: &dyn Fn(&Ty, TypePosition) -> bool) -> bool {
741 let boundary = |ty: &Ty| pred(ty, TypePosition::Boundary);
742 let field = |ty: &Ty| pred(ty, TypePosition::Field);
743 self.modules.iter().any(|m| {
744 m.callables().any(|f| {
745 f.params.iter().any(|p| boundary(&p.ty)) || f.ret.as_ref().is_some_and(boundary)
746 }) || m.callback_interfaces.iter().any(|c| {
747 c.methods.iter().any(|f| {
748 f.params.iter().any(|p| boundary(&p.ty)) || f.ret.as_ref().is_some_and(boundary)
749 })
750 }) || m
751 .structs
752 .iter()
753 .any(|s| s.fields.iter().any(|f| field(&f.ty)))
754 || m.enums.iter().any(|e| {
755 e.variants
756 .iter()
757 .any(|v| v.fields.iter().any(|f| field(&f.ty)))
758 })
759 || m.error.as_ref().is_some_and(|e| {
760 e.declared_here
761 && e.codes
762 .iter()
763 .any(|c| c.fields.iter().any(|f| field(&f.ty)))
764 })
765 })
766 }
767}
768
769#[derive(Clone, Copy)]
771enum TypePosition {
772 Boundary,
774 Field,
776}
777
778struct Lowerer<'a> {
780 api: &'a ResolvedApi,
781 prefix: &'a str,
782}
783
784struct Scope<'a> {
786 path: &'a str,
788}
789
790impl Lowerer<'_> {
791 fn ty(&self, ty: &TypeRef) -> Ty {
792 self.api.resolve(ty)
793 }
794
795 fn module(
801 &self,
802 module: &Module,
803 parent: &[String],
804 inherited_error: Option<&ErrorBinding>,
805 out: &mut Vec<ModuleBinding>,
806 ) {
807 let mut segments = parent.to_vec();
808 segments.push(module.name.clone());
809 let path = segments.join("_");
810 let dot_path = segments.join(".");
811 let scope = Scope { path: &path };
812 let prefix = self.prefix;
813
814 let error = match &module.errors {
815 Some(domain) => Some(self.error_domain(domain, &scope)),
816 None => inherited_error.cloned().map(|mut e| {
817 e.declared_here = false;
818 e
819 }),
820 };
821
822 let enums = module
823 .enums
824 .iter()
825 .map(|e| self.enum_def(e, &scope))
826 .collect();
827 let structs = module.structs.iter().map(|s| self.struct_def(s)).collect();
828 let interfaces = module
829 .interfaces
830 .iter()
831 .map(|i| self.interface(i, &scope))
832 .collect();
833 let callback_interfaces = module
834 .callback_interfaces
835 .iter()
836 .map(|c| self.callback_interface(c, &scope))
837 .collect();
838 let functions = module
839 .functions
840 .iter()
841 .map(|f| {
842 let c_base = format!("{prefix}_{path}_{}", f.name);
843 self.callable(f, &scope, &c_base, None)
844 })
845 .collect();
846
847 let doc = module
848 .doc
849 .clone()
850 .or_else(|| module.functions.iter().find_map(|f| f.doc.clone()));
851
852 out.push(ModuleBinding {
853 name: module.name.clone(),
854 checksum: parent
855 .is_empty()
856 .then(|| crate::checksum::module_checksum(module)),
857 segments: segments.clone(),
858 path: path.clone(),
859 dot_path: dot_path.clone(),
860 doc,
861 error: error.clone(),
862 enums,
863 structs,
864 interfaces,
865 callback_interfaces,
866 functions,
867 });
868
869 for child in &module.modules {
870 self.module(child, &segments, error.as_ref(), out);
871 }
872 }
873
874 fn error_domain(&self, domain: &ErrorDomain, scope: &Scope<'_>) -> ErrorBinding {
875 let c_tag = format!("{}_{}_{}", self.prefix, scope.path, domain.name);
876 ErrorBinding {
877 name: domain.name.clone(),
878 type_name: crate::errors::type_name(&domain.name, "Error"),
879 owner_path: scope.path.to_string(),
880 declared_here: true,
881 c_tag: c_tag.clone(),
882 codes: domain
883 .codes
884 .iter()
885 .map(|c| ErrorCodeBinding {
886 name: c.name.clone(),
887 value: c.code,
888 message: c.message.clone(),
889 doc: c.doc.clone(),
890 c_const: format!("{c_tag}_{}", c.name),
891 fields: self.fields(&c.fields),
892 })
893 .collect(),
894 }
895 }
896
897 fn fields(&self, fields: &[StructField]) -> Vec<FieldBinding> {
898 fields
899 .iter()
900 .map(|f| FieldBinding {
901 name: f.name.clone(),
902 doc: f.doc.clone(),
903 ty: self.ty(&f.ty),
904 })
905 .collect()
906 }
907
908 fn struct_def(&self, s: &StructDef) -> StructBinding {
909 StructBinding {
910 name: s.name.clone(),
911 doc: s.doc.clone(),
912 deprecated: s.deprecated.clone(),
913 fields: self.fields(&s.fields),
914 }
915 }
916
917 fn enum_def(&self, e: &EnumDef, scope: &Scope<'_>) -> EnumBinding {
918 let c_tag = format!("{}_{}_{}", self.prefix, scope.path, e.name);
919 let variants = e
920 .variants
921 .iter()
922 .map(|v| EnumVariantBinding {
923 name: v.name.clone(),
924 value: v.value,
925 doc: v.doc.clone(),
926 c_const: format!("{c_tag}_{}", v.name),
927 fields: self.fields(&v.fields),
928 })
929 .collect();
930 EnumBinding {
931 name: e.name.clone(),
932 doc: e.doc.clone(),
933 deprecated: e.deprecated.clone(),
934 c_tag,
935 variants,
936 rich: e.is_rich(),
937 }
938 }
939
940 fn params(&self, params: &[crate::ir::Param], scope: &Scope<'_>) -> Vec<ParamBinding> {
941 params
942 .iter()
943 .map(|p| ParamBinding::new(&p.name, self.ty(&p.ty), p.doc.clone(), scope.path))
944 .collect()
945 }
946
947 fn callback_interface(
948 &self,
949 c: &CallbackInterfaceDef,
950 scope: &Scope<'_>,
951 ) -> CallbackInterfaceBinding {
952 let c_tag = format!("{}_{}_{}", self.prefix, scope.path, c.name);
953 let methods = c
954 .methods
955 .iter()
956 .map(|m| {
957 let params = self.params(&m.params, scope);
958 let ret = m.returns.as_ref().map(|r| self.ty(r));
959 let sig = callback_method_signature(¶ms, ret.as_ref(), scope.path);
960 CallbackMethodBinding {
961 name: m.name.clone(),
962 doc: m.doc.clone(),
963 deprecated: m.deprecated.clone(),
964 params,
965 ret,
966 abi_params: sig.params,
967 abi_ret: sig.ret,
968 }
969 })
970 .collect();
971 CallbackInterfaceBinding {
972 name: c.name.clone(),
973 doc: c.doc.clone(),
974 deprecated: c.deprecated.clone(),
975 vtable_tag: format!("{c_tag}_vtable"),
976 c_tag,
977 methods,
978 }
979 }
980
981 fn interface(&self, iface: &InterfaceDef, scope: &Scope<'_>) -> InterfaceBinding {
985 let c_tag = format!("{}_{}_{}", self.prefix, scope.path, iface.name);
986 let self_slot = AbiParam::new(
987 "self",
988 CType::Ptr {
989 konst: ConstPos::West,
990 pointee: Box::new(CType::StructTag {
991 module: scope.path.to_string(),
992 name: iface.name.clone(),
993 }),
994 },
995 );
996 let member = |name: &str| format!("{c_tag}_{name}");
997 let constructors = iface
998 .constructors
999 .iter()
1000 .map(|c| {
1001 let mut f = c.clone();
1004 f.returns = Some(TypeRef::Named(iface.name.clone()));
1005 self.callable(&f, scope, &member(&c.name), None)
1006 })
1007 .collect();
1008 let methods = iface
1009 .methods
1010 .iter()
1011 .map(|m| self.callable(m, scope, &member(&m.name), Some(self_slot.clone())))
1012 .collect();
1013 let statics = iface
1014 .statics
1015 .iter()
1016 .map(|s| self.callable(s, scope, &member(&s.name), None))
1017 .collect();
1018 InterfaceBinding {
1019 name: iface.name.clone(),
1020 doc: iface.doc.clone(),
1021 deprecated: iface.deprecated.clone(),
1022 c_tag: c_tag.clone(),
1023 constructors,
1024 methods,
1025 statics,
1026 clone_symbol: format!("{c_tag}_clone"),
1027 destroy_symbol: format!("{c_tag}_destroy"),
1028 }
1029 }
1030
1031 fn callable(
1036 &self,
1037 f: &Function,
1038 scope: &Scope<'_>,
1039 c_base: &str,
1040 self_slot: Option<AbiParam>,
1041 ) -> FnBinding {
1042 let prefix = self.prefix;
1043 let path = scope.path;
1044 let params = self.params(&f.params, scope);
1045 let ret = f.returns.as_ref().map(|r| self.ty(r));
1046 let core_base = c_base
1050 .strip_prefix(&format!("{prefix}_"))
1051 .expect("c_base always starts with the symbol prefix")
1052 .to_string();
1053 let with_self = |mut params: Vec<AbiParam>| {
1054 if let Some(s) = &self_slot {
1055 params.insert(0, s.clone());
1056 }
1057 params
1058 };
1059
1060 let shape = if let Some(elem) = ret.as_ref().and_then(Ty::iterator_elem) {
1061 let pascal = f.name.to_upper_camel_case();
1062 let owner = &core_base[..core_base.len() - f.name.len() - 1];
1065 let iter_core = format!("{owner}_{pascal}Iterator");
1066 let iter_tag = format!("{prefix}_{iter_core}");
1067
1068 let mut launch_params: Vec<AbiParam> =
1069 params.iter().flat_map(|p| p.abi.iter().cloned()).collect();
1070 launch_params.push(error_out_param());
1071 let launch = AbiFn {
1072 symbol: c_base.to_string(),
1073 params: with_self(launch_params),
1074 ret: CType::ptr(CType::Named(iter_core.clone())),
1075 };
1076
1077 let item = lower_return(elem, path);
1078 let mut next_params = vec![
1079 AbiParam::new("iter", CType::ptr(CType::Named(iter_core.clone()))),
1080 AbiParam::new("out_item", CType::ptr(item.ret)),
1081 ];
1082 next_params.extend(item.out_params);
1083 next_params.push(error_out_param());
1084 let next = AbiFn {
1085 symbol: format!("{iter_tag}_next"),
1086 params: next_params,
1087 ret: CType::Int32,
1088 };
1089
1090 CallShape::Iterator(IteratorBinding {
1091 elem: elem.clone(),
1092 iter_tag: iter_tag.clone(),
1093 launch,
1094 next,
1095 destroy_symbol: format!("{iter_tag}_destroy"),
1096 })
1097 } else if f.r#async {
1098 let callback_type = format!("{c_base}_callback");
1099 let mut launch_params = async_input_params(¶ms, f.cancellable);
1100 launch_params.push(AbiParam::new(
1101 "callback",
1102 CType::Named(format!("{core_base}_callback")),
1103 ));
1104 launch_params.push(context_param());
1105 let launch = AbiFn {
1106 symbol: c_base.to_string(),
1107 params: with_self(launch_params),
1108 ret: CType::Void,
1109 };
1110 CallShape::Async(AsyncBinding {
1111 launch,
1112 callback_type,
1113 callback_params: async_callback_params(ret.as_ref(), path),
1114 })
1115 } else {
1116 let sig = sync_signature(¶ms, ret.as_ref(), path);
1117 CallShape::Sync(AbiFn {
1118 symbol: c_base.to_string(),
1119 params: with_self(sig.params),
1120 ret: sig.ret,
1121 })
1122 };
1123
1124 FnBinding {
1125 name: f.name.clone(),
1126 doc: f.doc.clone(),
1127 deprecated: f.deprecated.clone(),
1128 cancellable: f.cancellable,
1129 is_async: f.r#async,
1130 throws: f.throws,
1131 has_self: self_slot.is_some(),
1132 params,
1133 ret,
1134 c_base: c_base.to_string(),
1135 shape,
1136 }
1137 }
1138}
1139
1140pub fn iterator_item_ctype(elem: &Ty, module: &str) -> CType {
1143 lower_return(elem, module).ret
1144}
1145
1146#[cfg(test)]
1147mod tests {
1148 use super::*;
1149 use crate::ir::{
1150 Api, CallbackInterfaceDef, EnumDef, EnumVariant, Function, InterfaceDef, Module, Param,
1151 StructDef, StructField,
1152 };
1153
1154 fn param(name: &str, ty: TypeRef) -> Param {
1155 Param {
1156 name: name.into(),
1157 ty,
1158 doc: None,
1159 }
1160 }
1161
1162 fn func(name: &str, params: Vec<Param>, returns: Option<TypeRef>) -> Function {
1163 Function {
1164 name: name.into(),
1165 params,
1166 returns,
1167 doc: None,
1168 throws: false,
1169 r#async: false,
1170 cancellable: false,
1171 deprecated: None,
1172 }
1173 }
1174
1175 fn module(name: &str) -> Module {
1176 Module {
1177 name: name.into(),
1178 doc: None,
1179 functions: vec![],
1180 interfaces: vec![],
1181 callback_interfaces: vec![],
1182 structs: vec![],
1183 enums: vec![],
1184 errors: None,
1185 modules: vec![],
1186 }
1187 }
1188
1189 fn api(modules: Vec<Module>) -> ResolvedApi {
1190 ResolvedApi::assume_valid(Api {
1191 version: crate::ir::CURRENT_SCHEMA_VERSION.into(),
1192 modules,
1193 })
1194 }
1195
1196 fn rendered(abi: &AbiFn) -> Vec<String> {
1197 abi.params
1198 .iter()
1199 .map(|p| format!("{} {}", p.ty.render_c("weaveffi"), p.name))
1200 .collect()
1201 }
1202
1203 #[test]
1204 fn sync_function_symbol_and_sig() {
1205 let m = Module {
1206 functions: vec![func(
1207 "add",
1208 vec![param("a", TypeRef::I32), param("b", TypeRef::I32)],
1209 Some(TypeRef::I32),
1210 )],
1211 ..module("math")
1212 };
1213 let model = BindingModel::build(
1214 &api(vec![m]).with_identity(crate::pkg::Identity::named("weaveffi")),
1215 );
1216 let f = &model.modules[0].functions[0];
1217 assert_eq!(f.c_base, "weaveffi_math_add");
1218 let CallShape::Sync(abi) = &f.shape else {
1219 panic!("expected sync")
1220 };
1221 assert_eq!(abi.symbol, "weaveffi_math_add");
1222 assert_eq!(abi.ret, CType::Int32);
1223 assert_eq!(
1224 rendered(abi),
1225 ["int32_t a", "int32_t b", "weaveffi_error* out_err"]
1226 );
1227
1228 let model = BindingModel::build(
1229 &api(vec![module("net")]).with_identity(crate::pkg::Identity::named("acme")),
1230 );
1231 assert_eq!(model.prefix, "acme");
1232 }
1233
1234 #[test]
1235 fn async_function_has_launch_and_callback() {
1236 let m = Module {
1237 functions: vec![Function {
1238 cancellable: true,
1239 r#async: true,
1240 ..func(
1241 "fetch",
1242 vec![param("id", TypeRef::I64)],
1243 Some(TypeRef::StringUtf8),
1244 )
1245 }],
1246 ..module("net")
1247 };
1248 let model = BindingModel::build(
1249 &api(vec![m]).with_identity(crate::pkg::Identity::named("weaveffi")),
1250 );
1251 let CallShape::Async(a) = &model.modules[0].functions[0].shape else {
1252 panic!("expected async")
1253 };
1254 assert_eq!(a.launch.symbol, "weaveffi_net_fetch");
1255 assert_eq!(a.callback_type, "weaveffi_net_fetch_callback");
1256 let names: Vec<&str> = a.launch.params.iter().map(|p| p.name.as_str()).collect();
1257 assert_eq!(names, ["id", "cancel_token", "callback", "context"]);
1258 assert_eq!(a.callback_params[0].name, "context");
1259 assert_eq!(a.callback_params[1].name, "err");
1260 assert_eq!(a.callback_params[2].name, "result_ptr");
1261 assert!(model.has_async());
1262 }
1263
1264 #[test]
1265 fn iterator_function_has_next_and_destroy() {
1266 let m = Module {
1267 functions: vec![func(
1268 "get_messages",
1269 vec![],
1270 Some(TypeRef::Iterator(Box::new(TypeRef::StringUtf8))),
1271 )],
1272 ..module("events")
1273 };
1274 let model = BindingModel::build(
1275 &api(vec![m]).with_identity(crate::pkg::Identity::named("weaveffi")),
1276 );
1277 let CallShape::Iterator(it) = &model.modules[0].functions[0].shape else {
1278 panic!("expected iterator")
1279 };
1280 assert_eq!(it.iter_tag, "weaveffi_events_GetMessagesIterator");
1281 assert_eq!(it.launch.symbol, "weaveffi_events_get_messages");
1282 assert_eq!(it.next.symbol, "weaveffi_events_GetMessagesIterator_next");
1283 assert_eq!(
1284 it.destroy_symbol,
1285 "weaveffi_events_GetMessagesIterator_destroy"
1286 );
1287 assert_eq!(it.elem, Ty::StringUtf8);
1288 assert_eq!(it.next.ret, CType::Int32);
1289 assert_eq!(it.next.params[1].ty.render_c("weaveffi"), "const uint8_t**");
1290 assert!(model.has_iterators());
1291 }
1292
1293 #[test]
1294 fn user_types_resolve_to_kinds_and_buffers() {
1295 let shared = Module {
1296 structs: vec![StructDef {
1297 name: "Contact".into(),
1298 doc: None,
1299 deprecated: Some("use Person".into()),
1300 fields: vec![
1301 StructField {
1302 name: "name".into(),
1303 ty: TypeRef::StringUtf8,
1304 doc: None,
1305 },
1306 StructField {
1307 name: "status".into(),
1308 ty: TypeRef::Named("Status".into()),
1309 doc: None,
1310 },
1311 StructField {
1312 name: "store".into(),
1313 ty: TypeRef::Optional(Box::new(TypeRef::Named("Store".into()))),
1314 doc: None,
1315 },
1316 ],
1317 }],
1318 enums: vec![EnumDef {
1319 name: "Status".into(),
1320 doc: None,
1321 deprecated: None,
1322 variants: vec![EnumVariant {
1323 name: "Ok".into(),
1324 value: 0,
1325 doc: None,
1326 fields: vec![],
1327 }],
1328 }],
1329 interfaces: vec![InterfaceDef {
1330 name: "Store".into(),
1331 doc: None,
1332 deprecated: None,
1333 constructors: vec![func("open", vec![], None)],
1334 methods: vec![func(
1335 "save",
1336 vec![param("contact", TypeRef::Named("Contact".into()))],
1337 Some(TypeRef::List(Box::new(TypeRef::Named("Contact".into())))),
1338 )],
1339 statics: vec![],
1340 }],
1341 ..module("contacts")
1342 };
1343 let other = Module {
1344 functions: vec![func(
1345 "status_of",
1346 vec![param("store", TypeRef::Named("Store".into()))],
1347 Some(TypeRef::Named("Status".into())),
1348 )],
1349 ..module("ops")
1350 };
1351 let model = BindingModel::build(
1352 &api(vec![shared, other]).with_identity(crate::pkg::Identity::named("weaveffi")),
1353 );
1354 let contacts = &model.modules[0];
1355 let s = &contacts.structs[0];
1356 assert_eq!(s.deprecated.as_deref(), Some("use Person"));
1357 assert_eq!(s.fields[1].ty, Ty::Enum("contacts.Status".into()));
1358 assert_eq!(
1359 s.fields[2].ty,
1360 Ty::Optional(Box::new(Ty::Interface("contacts.Store".into())))
1361 );
1362 assert_eq!(contacts.enums[0].c_tag, "weaveffi_contacts_Status");
1363 assert_eq!(
1364 contacts.enums[0].variants[0].c_const,
1365 "weaveffi_contacts_Status_Ok"
1366 );
1367
1368 let iface = &contacts.interfaces[0];
1369 assert_eq!(iface.c_tag, "weaveffi_contacts_Store");
1370 assert_eq!(iface.clone_symbol, "weaveffi_contacts_Store_clone");
1371 assert_eq!(iface.destroy_symbol, "weaveffi_contacts_Store_destroy");
1372 assert_eq!(
1373 iface.constructors[0].ret,
1374 Some(Ty::Interface("contacts.Store".into()))
1375 );
1376 let save = &iface.methods[0];
1377 assert!(save.has_self);
1378 assert_eq!(save.params[0].ty, Ty::Record("contacts.Contact".into()));
1379 let CallShape::Sync(abi) = &save.shape else {
1380 panic!("expected sync")
1381 };
1382 assert_eq!(
1383 rendered(abi),
1384 [
1385 "const weaveffi_contacts_Store* self",
1386 "const uint8_t* contact_ptr",
1387 "size_t contact_len",
1388 "size_t* out_len",
1389 "weaveffi_error* out_err"
1390 ]
1391 );
1392 assert_eq!(abi.ret.render_c("weaveffi"), "const uint8_t*");
1393
1394 let ops = &model.modules[1];
1395 let f = &ops.functions[0];
1396 assert_eq!(f.params[0].ty, Ty::Interface("contacts.Store".into()));
1397 assert_eq!(f.ret, Some(Ty::Enum("contacts.Status".into())));
1398 let CallShape::Sync(abi) = &f.shape else {
1399 panic!("expected sync")
1400 };
1401 assert_eq!(
1402 rendered(abi),
1403 [
1404 "const weaveffi_contacts_Store* store",
1405 "weaveffi_error* out_err"
1406 ]
1407 );
1408 assert_eq!(abi.ret.render_c("weaveffi"), "weaveffi_contacts_Status");
1409 assert!(model.has_buffers());
1410 assert!(model.has_buffered_objects());
1411 assert!(model.has_interfaces());
1412 assert_eq!(
1413 model.interface("contacts.Store", "ops").map(|i| &i.c_tag),
1414 Some(&"weaveffi_contacts_Store".to_string())
1415 );
1416 assert_eq!(
1417 model.interface("Store", "contacts").map(|i| &i.c_tag),
1418 Some(&"weaveffi_contacts_Store".to_string())
1419 );
1420 }
1421
1422 #[test]
1423 fn callback_interfaces_lower_to_vtables() {
1424 let m = Module {
1425 callback_interfaces: vec![CallbackInterfaceDef {
1426 name: "Listener".into(),
1427 doc: Some("Receives messages.".into()),
1428 deprecated: None,
1429 methods: vec![
1430 func("on_message", vec![param("text", TypeRef::StringUtf8)], None),
1431 func("should_stop", vec![], Some(TypeRef::Bool)),
1432 ],
1433 }],
1434 functions: vec![func(
1435 "subscribe",
1436 vec![param("listener", TypeRef::Named("Listener".into()))],
1437 None,
1438 )],
1439 ..module("events")
1440 };
1441 let model = BindingModel::build(
1442 &api(vec![m]).with_identity(crate::pkg::Identity::named("weaveffi")),
1443 );
1444 let mb = &model.modules[0];
1445 let cb = &mb.callback_interfaces[0];
1446 assert_eq!(cb.c_tag, "weaveffi_events_Listener");
1447 assert_eq!(cb.vtable_tag, "weaveffi_events_Listener_vtable");
1448 let on_message = &cb.methods[0];
1449 let slots: Vec<String> = on_message
1450 .abi_params
1451 .iter()
1452 .map(|p| format!("{} {}", p.ty.render_c("weaveffi"), p.name))
1453 .collect();
1454 assert_eq!(
1455 slots,
1456 [
1457 "void* ctx",
1458 "const uint8_t* text_ptr",
1459 "size_t text_len",
1460 "weaveffi_error* out_err"
1461 ]
1462 );
1463 assert_eq!(on_message.abi_ret, CType::Void);
1464 assert_eq!(cb.methods[1].abi_ret, CType::Bool);
1465 assert!(mb.callback_interface("Listener").is_some());
1466 assert!(model.has_callback_interfaces());
1467
1468 let f = &mb.functions[0];
1469 assert_eq!(
1470 f.params[0].ty,
1471 Ty::CallbackInterface("events.Listener".into())
1472 );
1473 let CallShape::Sync(abi) = &f.shape else {
1474 panic!("expected sync")
1475 };
1476 assert_eq!(
1477 rendered(abi),
1478 [
1479 "void* listener_ctx",
1480 "const weaveffi_events_Listener_vtable* listener_vtable",
1481 "weaveffi_error* out_err"
1482 ]
1483 );
1484 assert!(!model.has_buffers());
1485 assert!(!model.has_buffered_objects());
1486 }
1487
1488 #[test]
1489 fn nested_modules_flatten_pre_order_with_paths_and_docs() {
1490 let inner = Module {
1491 functions: vec![Function {
1492 doc: Some("Leaf.".into()),
1493 ..func("leaf_fn", vec![], None)
1494 }],
1495 ..module("inner")
1496 };
1497 let outer = Module {
1498 doc: Some("Outer module.".into()),
1499 functions: vec![func("outer_fn", vec![], None)],
1500 modules: vec![inner],
1501 ..module("outer")
1502 };
1503 let model = BindingModel::build(
1504 &api(vec![outer]).with_identity(crate::pkg::Identity::named("weaveffi")),
1505 );
1506 let paths: Vec<&str> = model.modules.iter().map(|m| m.path.as_str()).collect();
1507 assert_eq!(paths, ["outer", "outer_inner"]);
1508 assert_eq!(model.modules[1].dot_path, "outer.inner");
1509 assert_eq!(
1510 model.modules[1].functions[0].c_base,
1511 "weaveffi_outer_inner_leaf_fn"
1512 );
1513 assert_eq!(model.modules[0].doc.as_deref(), Some("Outer module."));
1514 assert_eq!(model.modules[1].doc.as_deref(), Some("Leaf."));
1515 }
1516}