nandi/frqpublic Fork 0
484abf82feb9e705cf5578fc764895fa87075a13
Commits
Clone
git clone https://git.rickub.com/nandi/frq.git
git clone ssh://git@rickub.com/nandi/frq.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

py2jolt.py · 133 lines · 5.2 KBPython Blame HistoryRaw
Bind libmoq_ffi from the object's own metadata, not from its header 9f081a1 nandi 9d ago1#!/usr/bin/env python3
2"""Turn uniffi-bindgen's Python output into jolt `defcfn` declarations.
3
4The Python backend emits, for every entry point in the library, a pair of
5lines that together are an exact ABI description:
6
7 _UniffiLib.uniffi_moq_ffi_fn_method_moqclient_connect.argtypes = (
8 ctypes.c_uint64, _UniffiRustBuffer, ctypes.POINTER(_UniffiRustCallStatus),
9 )
10 _UniffiLib.uniffi_moq_ffi_fn_method_moqclient_connect.restype = ctypes.c_uint64
11
12That is generated from the metadata embedded in the .so itself, so it matches
13the object being bound rather than a header shipped alongside it -- which for
14this release is a different build (the Apple artifact carries audio/ video, the
15Linux and Android ones do not).
16"""
17import re, sys, collections
18
19CTYPE = {
20 "ctypes.c_int8": ":int8", "ctypes.c_uint8": ":uint8",
21 "ctypes.c_int16": ":int16", "ctypes.c_uint16": ":uint16",
22 "ctypes.c_int32": ":int32", "ctypes.c_uint32": ":uint32",
23 "ctypes.c_int64": ":int64", "ctypes.c_uint64": ":uint64",
24 "ctypes.c_float": ":float", "ctypes.c_double": ":double",
25 "ctypes.c_void_p": ":pointer", "ctypes.c_size_t": ":uint64",
26 "None": ":void",
27}
28# A RustBuffer crosses by value; a status is always a pointer out-param.
29RB = "[:by-value [:struct [[:capacity :uint64] [:len :uint64] [:data :pointer]]]]"
30STATUS = ":pointer"
31
32def conv(t):
33 t = t.strip().rstrip(",").strip()
34 if not t:
35 return None
36 if t in CTYPE:
37 return CTYPE[t]
38 if "RustCallStatus" in t:
39 return STATUS
40 if "RustBuffer" in t:
41 return RB
42 if "ForeignBytes" in t:
43 return "[:by-value [:struct [[:len :int32] [:data :pointer]]]]"
44 if t.startswith("ctypes.POINTER"):
45 return ":pointer"
46 # A function-pointer typedef (the future continuation) -- jolt lowers an
47 # ffi/callback to a plain pointer, so that is what the slot takes.
48 if "callback" in t.lower() or "struct" in t.lower():
49 return ":pointer"
50 return None # unknown -- reported, never guessed
51
52def split_args(s):
53 out, depth, cur = [], 0, ""
54 for ch in s:
55 if ch == "(":
56 depth += 1
57 elif ch == ")":
58 depth -= 1
59 if ch == "," and depth == 0:
60 out.append(cur); cur = ""
61 else:
62 cur += ch
63 if cur.strip():
64 out.append(cur)
65 return out
66
67def main(path):
68 src = open(path).read()
69 args = dict(re.findall(r"_UniffiLib\.(\w+)\.argtypes\s*=\s*\(([^)]*(?:\([^)]*\)[^)]*)*)\)", src))
70 rets = dict(re.findall(r"_UniffiLib\.(\w+)\.restype\s*=\s*(.+)", src))
71 names = sorted(set(args) | set(rets))
72 unknown = collections.Counter()
73 rows = []
74 for n in names:
75 a = [conv(x) for x in split_args(args.get(n, ""))]
76 r = conv(rets.get(n, "None"))
77 if None in a or r is None:
78 for x in split_args(args.get(n, "")) + [rets.get(n, "None")]:
79 if conv(x) is None:
80 unknown[x.strip()] += 1
81 continue
82 rows.append((n, a, r))
83 return rows, unknown, len(names)
84
Build libmoq_ffi ourselves, for the codecs the release leaves out eab184b nandi 9d ago85def error_variants(path):
86 """The MoqError variant table, read from the same generated bindings.
87
88 Hand-maintaining this is a trap: the variants are not appended to, they are
89 INSERTED into. Turning moq-ffi\'s audio/video features on adds Audio and
90 Video at 5 and 6 and shifts every later variant down two, so a stale table
91 does not report an unknown variant -- it reports a confidently wrong name
92 for a real one.
93 """
94 src = open(path).read()
95 i = src.index("class _UniffiFfiConverterTypeMoqError")
96 blk = src[i:]
97 blk = blk[:blk.index("\nclass ", 10)]
98 pairs = re.findall(r"if variant == (\d+):\s*\n\s*return MoqError\.(\w+)\(", blk)
99 def kebab(n):
100 return re.sub(r"(?<!^)(?=[A-Z])", "-", n).lower()
101 return [(int(n), kebab(name)) for n, name in pairs]
102
103
Bind libmoq_ffi from the object's own metadata, not from its header 9f081a1 nandi 9d ago104if __name__ == "__main__":
105 rows, unknown, total = main(sys.argv[1])
106 print(f";; {len(rows)} of {total} entry points bound", file=sys.stderr)
107 if unknown:
108 print(";; UNMAPPED (left unbound rather than guessed):", file=sys.stderr)
109 for t, c in unknown.most_common():
110 print(f";; {t} x{c}", file=sys.stderr)
Build libmoq_ffi ourselves, for the codecs the release leaves out eab184b nandi 9d ago111 variants = error_variants(sys.argv[1])
112 print(";; MoqError, as UniFFI numbers it in THIS object. Generated with the")
113 print(";; entry points above, and for the same reason: the variants are")
114 print(";; inserted into rather than appended to, so a table written by hand")
115 print(";; against one build names the wrong error in the next.")
116 print("(def moq-error-variants")
117 print(" {" + "\n ".join("%d :%s" % (n, k) for n, k in variants) + "})")
118 print()
Bind libmoq_ffi from the object's own metadata, not from its header 9f081a1 nandi 9d ago119 for n, a, r in rows:
120 # Anchored, longest-first: "uniffi_moq_ffi_checksum_" must not be
121 # eaten by the "ffi_moq_ffi_" that also matches inside it.
122 for pre in ("uniffi_moq_ffi_fn_", "uniffi_moq_ffi_checksum_",
123 "ffi_moq_ffi_", "uniffi_moq_ffi_"):
124 if n.startswith(pre):
125 stem = n[len(pre):]
126 if pre.endswith("checksum_"):
127 stem = "checksum-" + stem
128 break
129 else:
130 stem = n
131 jolt_name = stem.replace("_", "-")
132 argv = " ".join(a) if a else ""
133 print(f'(ffi/defcfn {jolt_name} "{n}" [{argv}] {r})')