2 import os, sys, re, subprocess
5 print("USAGE: /path/to/lightning.h /path/to/bindings/output /path/to/bindings/ /path/to/bindings/output.c debug lang")
8 if sys.argv[5] == "false":
10 elif sys.argv[5] == "true":
13 print("debug should be true or false and indicates whether to track allocations and ensure we don't leak")
17 if sys.argv[6] == "java" or sys.argv[6] == "android":
19 from java_strings import Consts
20 target = java_strings.Target.JAVA
21 if sys.argv[6] == "android":
22 target = java_strings.Target.ANDROID
23 elif sys.argv[6] == "typescript":
24 import typescript_strings
25 from typescript_strings import Consts
26 target = typescript_strings.Target.NODEJS
27 if len(sys.argv) == 8 and sys.argv[7] == 'browser':
28 target = typescript_strings.Target.BROWSER
30 print("Only java or typescript can be set for lang")
34 consts = Consts(DEBUG, target=target, outdir=sys.argv[4])
36 local_git_version = os.getenv("LDK_GARBAGECOLLECTED_GIT_OVERRIDE")
37 if local_git_version is None:
38 local_git_version = subprocess.check_output(["git", "describe", '--tag', '--dirty']).decode("utf-8").strip()
40 from bindingstypes import *
47 def camel_to_snake(s):
48 # Convert camel case to snake case, in a way that appears to match cbindgen
54 if lastchar.isupper():
55 if not char.isupper() and not lastund:
60 ret = ret + lastchar.lower()
63 if char.isupper() and not lastund:
71 return (ret + lastchar.lower()).strip("_")
73 def doc_to_field_nullable(doc):
76 for line in doc.splitlines():
77 if "Note that this (or a relevant inner pointer) may be NULL or all-0s to represent None" in line:
81 def doc_to_params_ret_nullable(doc):
86 for line in doc.splitlines():
87 if "may be NULL or all-0s to represent None" not in line:
89 if "Note that the return value" in line:
91 elif "Note that " in line:
92 param = line.split("Note that ")[1].split(" ")[0]
94 return (params, ret_null)
97 # Map from enum name to "contains trait object"
99 opaque_structs = set()
104 var_is_arr_regex = re.compile("\(\* ?([A-za-z0-9_]*)\)\[([a-z0-9]*)\]")
105 var_ty_regex = re.compile("([A-za-z_0-9]*)(.*)")
106 java_c_types_none_allowed = True # Unset when we do the real pass that populates the above sets
107 def java_c_types(fn_arg, ret_arr_len):
108 fn_arg = fn_arg.strip()
109 if fn_arg.startswith("MUST_USE_RES "):
112 if fn_arg.startswith("const "):
115 if fn_arg.startswith("struct "):
117 if fn_arg.startswith("enum "):
119 nonnull_ptr = "NONNULL_PTR" in fn_arg
120 fn_arg = fn_arg.replace("NONNULL_PTR", "")
127 if fn_arg.startswith("LDKPaymentPreimage") or fn_arg.startswith("LDKPaymentSecret") or fn_arg.startswith("LDKPaymentHash"):
128 if fn_arg.startswith("LDKPaymentPreimage"):
129 fn_arg = "uint8_t (*" + fn_arg[19:] + ")[32]"
130 elif fn_arg.startswith("LDKPaymentSecret"):
131 fn_arg = "uint8_t (*" + fn_arg[17:] + ")[32]"
132 elif fn_arg.startswith("LDKPaymentHash"):
133 fn_arg = "uint8_t (*" + fn_arg[15:] + ")[32]"
134 assert var_is_arr_regex.match(fn_arg[8:])
135 rust_obj = "LDKThirtyTwoBytes"
137 elif fn_arg.startswith("LDKThirtyTwoBytes"):
138 fn_arg = "uint8_t (*" + fn_arg[18:] + ")[32]"
139 assert var_is_arr_regex.match(fn_arg[8:])
140 rust_obj = "LDKThirtyTwoBytes"
142 elif fn_arg.startswith("LDKTxid"):
143 fn_arg = "uint8_t (*" + fn_arg[8:] + ")[32]"
144 assert var_is_arr_regex.match(fn_arg[8:])
145 rust_obj = "LDKThirtyTwoBytes"
147 elif fn_arg.startswith("LDKPublicKey"):
148 fn_arg = "uint8_t (*" + fn_arg[13:] + ")[33]"
149 assert var_is_arr_regex.match(fn_arg[8:])
150 rust_obj = "LDKPublicKey"
151 arr_access = "compressed_form"
152 elif fn_arg.startswith("LDKSecretKey"):
153 fn_arg = "uint8_t (*" + fn_arg[13:] + ")[32]"
154 assert var_is_arr_regex.match(fn_arg[8:])
155 rust_obj = "LDKSecretKey"
157 elif fn_arg.startswith("LDKSignature"):
158 fn_arg = "uint8_t (*" + fn_arg[13:] + ")[64]"
159 assert var_is_arr_regex.match(fn_arg[8:])
160 rust_obj = "LDKSignature"
161 arr_access = "compact_form"
162 elif fn_arg.startswith("LDKRecoverableSignature"):
163 fn_arg = "uint8_t (*" + fn_arg[24:] + ")[68]"
164 assert var_is_arr_regex.match(fn_arg[8:])
165 rust_obj = "LDKRecoverableSignature"
166 arr_access = "serialized_form"
167 elif fn_arg.startswith("LDKThreeBytes"):
168 fn_arg = "uint8_t (*" + fn_arg[14:] + ")[3]"
169 assert var_is_arr_regex.match(fn_arg[8:])
170 rust_obj = "LDKThreeBytes"
172 elif fn_arg.startswith("LDKFourBytes"):
173 fn_arg = "uint8_t (*" + fn_arg[13:] + ")[4]"
174 assert var_is_arr_regex.match(fn_arg[8:])
175 rust_obj = "LDKFourBytes"
177 elif fn_arg.startswith("LDKSixteenBytes"):
178 fn_arg = "uint8_t (*" + fn_arg[16:] + ")[16]"
179 assert var_is_arr_regex.match(fn_arg[8:])
180 rust_obj = "LDKSixteenBytes"
182 elif fn_arg.startswith("LDKTwentyBytes"):
183 fn_arg = "uint8_t (*" + fn_arg[15:] + ")[20]"
184 assert var_is_arr_regex.match(fn_arg[8:])
185 rust_obj = "LDKTwentyBytes"
187 elif fn_arg.startswith("LDKTwelveBytes"):
188 fn_arg = "uint8_t (*" + fn_arg[15:] + ")[12]"
189 assert var_is_arr_regex.match(fn_arg[8:])
190 rust_obj = "LDKTwelveBytes"
192 elif fn_arg.startswith("LDKu8slice"):
193 fn_arg = "uint8_t (*" + fn_arg[11:] + ")[datalen]"
194 assert var_is_arr_regex.match(fn_arg[8:])
195 rust_obj = "LDKu8slice"
197 elif fn_arg.startswith("LDKCVec_u8Z"):
198 fn_arg = "uint8_t (*" + fn_arg[12:] + ")[datalen]"
199 rust_obj = "LDKCVec_u8Z"
200 assert var_is_arr_regex.match(fn_arg[8:])
202 elif fn_arg.startswith("LDKTransaction ") or fn_arg == "LDKTransaction":
203 fn_arg = "uint8_t (*" + fn_arg[15:] + ")[datalen]"
204 rust_obj = "LDKTransaction"
205 assert var_is_arr_regex.match(fn_arg[8:])
207 elif fn_arg.startswith("LDKCVec_"):
210 fn_arg = fn_arg.replace("*", "")
213 tyn = fn_arg[8:].split(" ")
214 assert tyn[0].endswith("Z")
218 new_arg = "LDK" + tyn[0][:-1]
220 new_arg = new_arg + " " + a
221 res = java_c_types(new_arg, ret_arr_len)
223 assert java_c_types_none_allowed
226 res.pass_by_ref = True
227 java_ty = consts.java_arr_ty_str(res.java_ty)
228 if res.is_native_primitive or res.passed_as_ptr:
229 return TypeInfo(rust_obj=fn_arg.split(" ")[0], java_ty=java_ty, java_hu_ty=res.java_hu_ty + "[]",
230 java_fn_ty_arg="[" + res.java_fn_ty_arg, c_ty=res.c_ty + "Array", passed_as_ptr=False, is_ptr=is_ptr,
231 nonnull_ptr=nonnull_ptr, is_const=is_const,
232 var_name=res.var_name, arr_len="datalen", arr_access="data", subty=res, is_native_primitive=False)
234 return TypeInfo(rust_obj=fn_arg.split(" ")[0], java_ty=java_ty, java_hu_ty=res.java_hu_ty + "[]",
235 java_fn_ty_arg="[" + res.java_fn_ty_arg, c_ty=consts.ptr_arr, passed_as_ptr=False, is_ptr=is_ptr,
236 nonnull_ptr=nonnull_ptr, is_const=is_const,
237 var_name=res.var_name, arr_len="datalen", arr_access="data", subty=res, is_native_primitive=False)
240 contains_trait = False
243 java_type_plural = None
245 if fn_arg.startswith("void"):
249 fn_arg = fn_arg[4:].strip()
251 elif fn_arg.startswith("bool"):
256 fn_arg = fn_arg[4:].strip()
258 elif fn_arg.startswith("uint8_t"):
259 mapped_type = consts.c_type_map['uint8_t']
260 java_ty = mapped_type[0]
264 fn_arg = fn_arg[7:].strip()
266 elif fn_arg.startswith("LDKu5"):
267 java_ty = consts.c_type_map['uint8_t'][0]
273 fn_arg = fn_arg[6:].strip()
274 elif fn_arg.startswith("uint16_t"):
275 mapped_type = consts.c_type_map['uint16_t']
276 java_ty = mapped_type[0]
280 fn_arg = fn_arg[8:].strip()
282 elif fn_arg.startswith("uint32_t"):
283 mapped_type = consts.c_type_map['uint32_t']
284 java_ty = mapped_type[0]
288 fn_arg = fn_arg[8:].strip()
290 elif fn_arg.startswith("uint64_t") or fn_arg.startswith("uintptr_t"):
291 # TODO: uintptr_t is arch-dependent :(
292 mapped_type = consts.c_type_map['uint64_t']
293 java_ty = mapped_type[0]
295 if fn_arg.startswith("uint64_t"):
298 fn_arg = fn_arg[8:].strip()
300 java_ty = consts.ptr_native_ty
301 c_ty = consts.ptr_c_ty
303 rust_obj = "uintptr_t"
304 fn_arg = fn_arg[9:].strip()
306 elif is_const and fn_arg.startswith("char *"):
307 java_ty = consts.java_type_map["String"]
308 java_hu_ty = consts.java_hu_type_map["String"]
311 fn_ty_arg = "Ljava/lang/String;"
312 fn_arg = fn_arg[6:].strip()
313 elif fn_arg.startswith("LDKStr"):
316 java_ty = consts.java_type_map["String"]
317 java_hu_ty = consts.java_hu_type_map["String"]
319 fn_ty_arg = "Ljava/lang/String;"
320 fn_arg = fn_arg[6:].strip()
324 ma = var_ty_regex.match(fn_arg)
325 arr_ty = ma.group(1).strip()
326 if ma.group(1).strip() in unitary_enums:
327 assert ma.group(1).strip().startswith("LDK")
328 java_ty = ma.group(1).strip()[3:]
330 c_ty = consts.result_c_ty
331 fn_ty_arg = "Lorg/ldk/enums/" + java_ty + ";"
332 fn_arg = ma.group(2).strip()
333 rust_obj = ma.group(1).strip()
335 c_ty = consts.ptr_c_ty
336 java_ty = consts.ptr_native_ty
337 java_hu_ty = ma.group(1).strip()
338 java_hu_ty = java_hu_ty.replace("LDKCOption", "Option")
339 java_hu_ty = java_hu_ty.replace("LDKCResult", "Result")
340 java_hu_ty = java_hu_ty.replace("LDKC2Tuple", "TwoTuple")
341 java_hu_ty = java_hu_ty.replace("LDKC3Tuple", "ThreeTuple")
342 java_hu_ty = java_hu_ty.replace("LDK", "")
344 fn_arg = ma.group(2).strip()
345 rust_obj = ma.group(1).strip()
346 if rust_obj in trait_structs:
347 contains_trait = True
348 elif rust_obj in complex_enums:
349 contains_trait = complex_enums[rust_obj]
352 if fn_arg.startswith(" *") or fn_arg.startswith("*"):
353 fn_arg = fn_arg.replace("*", "").strip()
355 c_ty = consts.ptr_c_ty
356 java_ty = consts.ptr_native_ty
359 var_is_arr = var_is_arr_regex.match(fn_arg)
361 if var_is_arr is not None or ret_arr_len is not None:
362 assert(not take_by_ptr)
364 # is there a special case for plurals?
365 if len(mapped_type) == 3:
366 java_ty = mapped_type[1]
367 java_hu_ty = mapped_type[2]
369 java_ty = java_ty + "[]"
371 c_ty = c_ty + "Array"
373 subty = java_c_types(arr_ty, None)
375 assert java_c_types_none_allowed
378 subty.pass_by_ref = True
380 if var_is_arr is not None:
381 if var_is_arr.group(1) == "":
382 return TypeInfo(rust_obj=rust_obj, java_ty=java_ty, java_hu_ty=java_hu_ty, java_fn_ty_arg="[" + fn_ty_arg, c_ty=c_ty, is_const=is_const,
383 passed_as_ptr=False, is_ptr=False, nonnull_ptr=nonnull_ptr, var_name="arg", subty=subty,
384 arr_len=var_is_arr.group(2), arr_access=arr_access, is_native_primitive=False, contains_trait=contains_trait)
385 return TypeInfo(rust_obj=rust_obj, java_ty=java_ty, java_hu_ty=java_hu_ty, java_fn_ty_arg="[" + fn_ty_arg, c_ty=c_ty, is_const=is_const,
386 passed_as_ptr=False, is_ptr=False, nonnull_ptr=nonnull_ptr, var_name=var_is_arr.group(1), subty=subty,
387 arr_len=var_is_arr.group(2), arr_access=arr_access, is_native_primitive=False, contains_trait=contains_trait)
389 if java_hu_ty is None:
391 return TypeInfo(rust_obj=rust_obj, java_ty=java_ty, java_hu_ty=java_hu_ty, java_fn_ty_arg=fn_ty_arg, c_ty=c_ty, passed_as_ptr=is_ptr or take_by_ptr,
392 is_const=is_const, is_ptr=is_ptr, nonnull_ptr=nonnull_ptr, var_name=fn_arg, arr_len=arr_len, arr_access=arr_access, is_native_primitive=is_primitive,
393 contains_trait=contains_trait, subty=subty)
395 fn_ptr_regex = re.compile("^extern const ([A-Za-z_0-9\* ]*) \(\*(.*)\)\((.*)\);$")
396 fn_ret_arr_regex = re.compile("(.*) \(\*(.*)\((.*)\)\)\[([0-9]*)\];$")
397 reg_fn_regex = re.compile("([A-Za-z_0-9\* ]* \*?)([a-zA-Z_0-9]*)\((.*)\);$")
401 from gen_type_mapping import TypeMappingGenerator
402 type_mapping_generator = TypeMappingGenerator(java_c_types, consts, opaque_structs, clone_fns, unitary_enums, trait_structs, complex_enums, result_types, tuple_types)
404 with open(sys.argv[1]) as in_h:
406 reg_fn = reg_fn_regex.match(line)
407 if reg_fn is not None:
408 if reg_fn.group(2).endswith("_clone"):
409 clone_fns.add(reg_fn.group(2))
411 rty = java_c_types(reg_fn.group(1), None)
412 if rty is not None and not rty.is_native_primitive and reg_fn.group(2) == rty.java_hu_ty + "_new":
413 constructor_fns[rty.rust_obj] = reg_fn.group(3)
415 arr_fn = fn_ret_arr_regex.match(line)
416 if arr_fn is not None:
417 if arr_fn.group(2).endswith("_clone"):
418 clone_fns.add(arr_fn.group(2))
419 # No object constructors return arrays, as then they wouldn't be an object constructor
422 # Define some manual clones...
423 clone_fns.add("ThirtyTwoBytes_clone")
424 write_c("static inline struct LDKThirtyTwoBytes ThirtyTwoBytes_clone(const struct LDKThirtyTwoBytes *orig) { struct LDKThirtyTwoBytes ret; memcpy(ret.data, orig->data, 32); return ret; }\n")
426 java_c_types_none_allowed = False # C structs created by cbindgen are declared in dependency order
428 with open(f"{sys.argv[3]}/structs/UtilMethods{consts.file_ext}", "a") as util:
429 util.write(consts.util_fn_pfx)
431 with open(sys.argv[1]) as in_h, open(f"{sys.argv[2]}/bindings{consts.file_ext}", "w") as out_java:
432 # Map a top-level function
433 def map_fn(line, re_match, ret_arr_len, c_call_string, doc_comment):
434 map_fn_with_ref_option(line, re_match, ret_arr_len, c_call_string, doc_comment, False)
435 def map_fn_with_ref_option(line, re_match, ret_arr_len, c_call_string, doc_comment, force_holds_ref):
436 method_return_type = re_match.group(1)
437 method_name = re_match.group(2)
438 method_comma_separated_arguments = re_match.group(3)
439 method_arguments = method_comma_separated_arguments.split(',')
441 if method_name.startswith("__"):
444 is_free = method_name.endswith("_free")
445 if method_name.startswith("COption") or method_name.startswith("CResult"):
446 struct_meth = method_name.rsplit("Z", 1)[0][1:] + "Z"
447 expected_struct = "LDKC" + struct_meth
448 struct_meth_name = method_name[len(struct_meth) + 1:].strip("_")
449 elif method_name.startswith("C2Tuple") or method_name.startswith("C3Tuple"):
450 tuple_name = method_name.rsplit("Z", 1)[0][2:] + "Z"
451 if method_name.startswith("C2Tuple"):
452 struct_meth = "Two" + tuple_name
453 expected_struct = "LDKC2" + tuple_name
455 struct_meth = "Three" + tuple_name
456 expected_struct = "LDKC3" + tuple_name
457 struct_meth_name = method_name[len(tuple_name) + 2:].strip("_")
459 struct_meth = method_name.split("_")[0]
460 expected_struct = "LDK" + struct_meth
461 struct_meth_name = method_name[len(struct_meth) + 1 if len(struct_meth) != 0 else 0:].strip("_")
463 (params_nullable, ret_nullable) = doc_to_params_ret_nullable(doc_comment)
465 return_type_info = type_mapping_generator.map_nullable_type(method_return_type.strip() + " ret", True, ret_arr_len, False, force_holds_ref)
467 return_type_info = type_mapping_generator.map_type(method_return_type.strip() + " ret", True, ret_arr_len, False, force_holds_ref)
469 if method_name.endswith("_clone") and expected_struct not in unitary_enums:
470 meth_line = "uintptr_t " + expected_struct.replace("LDK", "") + "_clone_ptr(" + expected_struct + " *NONNULL_PTR arg)"
471 write_c("static inline " + meth_line + " {\n")
472 write_c("\t" + return_type_info.ret_conv[0].replace("\n", "\n\t"))
473 write_c(method_name + "(arg)")
474 write_c(return_type_info.ret_conv[1])
475 write_c("\n\treturn " + return_type_info.ret_conv_name + ";\n}\n")
476 map_fn(meth_line + ";\n", re.compile("(uintptr_t) ([A-Za-z_0-9]*)\((.*)\)").match(meth_line), None, None, None)
479 default_constructor_args = {}
481 takes_self_ptr = False
484 for argument_index, argument in enumerate(method_arguments):
485 arg_ty = type_mapping_generator.java_c_types(argument, None)
486 argument_conversion_info = None
487 if argument_index == 0 and arg_ty.java_hu_ty == struct_meth:
488 argument_conversion_info = type_mapping_generator.map_type_with_info(arg_ty, False, None, is_free, True, False)
490 if argument_conversion_info.ty_info.is_ptr:
491 takes_self_ptr = True
492 elif arg_ty.var_name in params_nullable:
493 argument_conversion_info = type_mapping_generator.map_type_with_info(arg_ty, False, None, is_free, True, True)
494 if argument_conversion_info.arg_conv is not None and "WARNING" in argument_conversion_info.arg_conv:
495 arg_ty_info = java_c_types(argument, None)
496 print("WARNING: Remapping argument " + arg_ty_info.var_name + " of function " + method_name + " to a reference")
497 print(" The argument appears to require a move, or not clonable, and is nullable.")
498 print(" Normally for arguments that require a move and are not clonable, we split")
499 print(" the argument into the type's constructor's arguments and just use those to")
500 print(" construct a new object on the fly.")
501 print(" However, because the object is nullable, doing so would mean we can no")
502 print(" longer allow the user to pass null, as we now have an argument list instead.")
503 print(" Thus, we blindly assume its really an Option<&Type> instead of an Option<Type>.")
504 print(" It may or may not actually be a reference, but its the simplest mapping option")
505 print(" and also the only use of this code today.")
506 arg_ty_info.requires_clone = False
507 argument_conversion_info = type_mapping_generator.map_type_with_info(arg_ty_info, False, None, is_free, True, True)
508 assert argument_conversion_info.nullable
509 assert argument_conversion_info.arg_conv is not None and "WARNING" not in argument_conversion_info.arg_conv
511 argument_conversion_info = type_mapping_generator.map_type_with_info(arg_ty, False, None, is_free, True, False)
513 if argument_conversion_info.arg_conv is not None and "WARNING" in argument_conversion_info.arg_conv:
514 if argument_conversion_info.rust_obj in constructor_fns:
516 for explode_arg in constructor_fns[argument_conversion_info.rust_obj].split(','):
517 explode_arg_conv = type_mapping_generator.map_type(explode_arg, False, None, False, True)
518 if explode_arg_conv.c_ty == "void":
519 # We actually want to handle this case, but for now its only used in NetGraphMsgHandler::new()
520 # which ends up resulting in a redundant constructor - both without arguments for the NetworkGraph.
523 if not argument_conversion_info.arg_name in default_constructor_args:
524 default_constructor_args[argument_conversion_info.arg_name] = []
525 default_constructor_args[argument_conversion_info.arg_name].append(explode_arg_conv)
526 argument_types.append(argument_conversion_info)
528 if not takes_self and return_type_info.java_hu_ty != struct_meth:
529 if not return_type_info.java_hu_ty.startswith("Result_" + struct_meth):
530 struct_meth_name = method_name
534 impl_on_struct = (expected_struct in opaque_structs or expected_struct in trait_structs or
535 expected_struct in complex_enums or expected_struct in complex_enums or
536 expected_struct in result_types or expected_struct in tuple_types) and not is_free
537 impl_on_utils = not impl_on_struct and (not is_free and not method_name.endswith("_clone") and
538 not method_name.startswith("TxOut") and
539 not method_name.startswith("_") and
540 method_name != "check_platform" and method_name != "Result_read" and
541 not expected_struct in unitary_enums and
542 ((not method_name.startswith("C2Tuple_") and not method_name.startswith("C3Tuple_"))
543 or method_name.endswith("_read")))
545 # If we're adding a static method, and it returns a primitive or an array of primitives,
546 # and a variable conversion adds a reference on the return type (via `this`), skip the
547 # variable's conversion reference-add (as we obviously cannot need a reference).
548 if impl_on_utils and (return_type_info.is_native_primitive or
549 (return_type_info.ty_info.subty is not None and return_type_info.ty_info.subty.is_native_primitive)):
550 for arg in argument_types:
551 if arg.from_hu_conv is not None and arg.from_hu_conv[1] != "":
552 if "this" in arg.from_hu_conv[1]:
553 arg.from_hu_conv = (arg.from_hu_conv[0], "")
555 out_java.write("\t// " + line)
556 (out_java_delta, out_c_delta, out_java_struct_delta) = \
557 consts.map_function(argument_types, c_call_string, method_name, struct_meth_name, return_type_info, struct_meth, default_constructor_args, takes_self, takes_self_ptr, args_known, type_mapping_generator, doc_comment)
558 out_java.write(out_java_delta)
561 assert len(argument_types) == 1
562 assert return_type_info.c_ty == "void"
563 write_c(consts.c_fn_ty_pfx + "void " + consts.c_fn_name_define_pfx(method_name, True) + argument_types[0].c_ty + " " + argument_types[0].ty_info.var_name + ") {\n")
564 if argument_types[0].ty_info.passed_as_ptr and not argument_types[0].ty_info.rust_obj in opaque_structs:
565 write_c("\tif ((" + argument_types[0].ty_info.var_name + " & 1) != 0) return;\n")
567 for info in argument_types:
568 if info.arg_conv is not None:
569 write_c("\t" + info.arg_conv.replace('\n', "\n\t") + "\n")
570 assert c_call_string is None
571 write_c("\t" + method_name + "(")
572 if argument_types[0].arg_conv_name is not None:
573 write_c(argument_types[0].arg_conv_name)
575 for info in argument_types:
576 if info.arg_conv_cleanup is not None:
577 write_c("\n\t" + info.arg_conv_cleanup.replace("\n", "\n\t"))
582 out_java_struct = None
584 out_java_struct = open(f"{sys.argv[3]}/structs/{struct_meth}{consts.file_ext}", "a")
585 out_java_struct.write(out_java_struct_delta)
587 out_java_struct = open(f"{sys.argv[3]}/structs/UtilMethods{consts.file_ext}", "a")
588 for line in out_java_struct_delta.splitlines():
589 out_java_struct.write(line + "\n")
591 def map_unitary_enum(struct_name, field_lines, enum_doc_comment):
592 assert struct_name.startswith("LDK")
593 with open(f"{sys.argv[3]}/enums/{struct_name[3:]}{consts.file_ext}", "w") as out_java_enum:
594 unitary_enums.add(struct_name)
595 for idx, (struct_line, _) in enumerate(field_lines):
597 assert(struct_line == "typedef enum %s {" % struct_name)
598 elif idx == len(field_lines) - 3:
599 assert(struct_line.endswith("_Sentinel,"))
600 elif idx == len(field_lines) - 2:
601 assert(struct_line == "} %s;" % struct_name)
602 elif idx == len(field_lines) - 1:
603 assert(struct_line == "")
604 assert struct_name.startswith("LDK")
605 (c_out, native_file_out, native_out) = consts.native_c_unitary_enum_map(struct_name[3:], [(x.strip().strip(","), y) for x, y in field_lines[1:-3]], enum_doc_comment)
607 out_java_enum.write(native_file_out)
608 out_java.write(native_out)
610 def map_complex_enum(struct_name, union_enum_items, inline_enum_variants, enum_doc_comment):
611 java_hu_type = struct_name.replace("LDK", "").replace("COption", "Option")
614 tag_field_lines = union_enum_items["field_lines"]
615 contains_trait = False
616 for idx, (struct_line, variant_docs) in enumerate(tag_field_lines):
618 assert(struct_line == "typedef enum %s_Tag {" % struct_name)
619 elif idx == len(tag_field_lines) - 3:
620 assert(struct_line.endswith("_Sentinel,"))
621 elif idx == len(tag_field_lines) - 2:
622 assert(struct_line == "} %s_Tag;" % struct_name)
623 elif idx == len(tag_field_lines) - 1:
624 assert(struct_line == "")
626 variant_name = struct_line.strip(' ,')[len(struct_name) + 1:]
628 if "LDK" + variant_name in union_enum_items:
629 enum_var_lines = union_enum_items["LDK" + variant_name]
630 for idx, (field, field_docs) in enumerate(enum_var_lines):
631 if idx != 0 and idx < len(enum_var_lines) - 2 and field.strip() != "":
632 field_ty = type_mapping_generator.java_c_types(field.strip(' ;'), None)
633 contains_trait |= field_ty.contains_trait
634 if field_docs is not None and doc_to_field_nullable(field_docs):
635 field_conv = type_mapping_generator.map_type_with_info(field_ty, False, None, False, True, True)
637 field_conv = type_mapping_generator.map_type_with_info(field_ty, False, None, False, True, False)
638 fields.append((field_conv, field_docs))
639 enum_variants.append(ComplexEnumVariantInfo(variant_name, variant_docs, fields, False))
640 elif camel_to_snake(variant_name) in inline_enum_variants:
641 # TODO: If we ever have a rust enum Variant(Option<Struct>) we need to pipe
642 # docs through to there, and then potentially mark the field nullable.
643 mapped = type_mapping_generator.map_type(inline_enum_variants[camel_to_snake(variant_name)] + " " + camel_to_snake(variant_name), False, None, False, True)
644 contains_trait |= mapped.ty_info.contains_trait
645 fields.append((mapped, None))
646 enum_variants.append(ComplexEnumVariantInfo(variant_name, variant_docs, fields, True))
648 enum_variants.append(ComplexEnumVariantInfo(variant_name, variant_docs, fields, True))
649 complex_enums[struct_name] = contains_trait
651 with open(f"{sys.argv[3]}/structs/{java_hu_type}{consts.file_ext}", "w") as out_java_enum:
652 (out_java_addendum, out_java_enum_addendum, out_c_addendum) = consts.map_complex_enum(struct_name, enum_variants, camel_to_snake, enum_doc_comment)
654 out_java_enum.write(out_java_enum_addendum)
655 out_java.write(out_java_addendum)
656 write_c(out_c_addendum)
658 def map_trait(struct_name, field_var_lines, trait_fn_lines, trait_doc_comment):
659 with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "w") as out_java_trait:
661 flattened_field_var_convs = []
662 for var_line in field_var_lines:
663 if var_line.group(1) in trait_structs:
664 field_var_convs.append((var_line.group(1), var_line.group(2), trait_structs[var_line.group(1)]))
665 flattened_field_var_convs.append((var_line.group(1), var_line.group(2), ))
666 flattened_field_var_convs.extend(trait_structs[var_line.group(1)])
668 mapped = type_mapping_generator.map_type(var_line.group(1) + " " + var_line.group(2), False, None, False, False)
669 field_var_convs.append(mapped)
670 flattened_field_var_convs.append(mapped)
671 trait_structs[struct_name] = field_var_convs
674 for fn_docs, fn_line in trait_fn_lines:
675 if fn_line == "cloned":
676 ret_ty_info = type_mapping_generator.map_type("void", True, None, False, False)
677 field_fns.append(TraitMethInfo("cloned", False, ret_ty_info, [], fn_docs))
679 (nullable_params, ret_nullable) = doc_to_params_ret_nullable(fn_docs)
681 assert False # This isn't yet handled on the Java side
682 ret_ty_info.nullable = True
683 ret_ty_info = type_mapping_generator.map_nullable_type(fn_line.group(2).strip() + " ret", True, None, False, False)
685 ret_ty_info = type_mapping_generator.map_type(fn_line.group(2).strip() + " ret", True, None, False, False)
686 is_const = fn_line.group(4) is not None
689 for idx, arg in enumerate(fn_line.group(5).split(',')):
692 arg_ty_info = type_mapping_generator.java_c_types(arg, None)
693 if arg_ty_info.var_name in nullable_params:
694 # Types that are actually null instead of all-0s aren't yet handled on the Java side:
695 arg_conv_info = type_mapping_generator.map_type_with_info(arg_ty_info, True, None, False, False, True)
697 arg_conv_info = type_mapping_generator.map_type_with_info(arg_ty_info, True, None, False, False, False)
698 arg_tys.append(arg_conv_info)
699 field_fns.append(TraitMethInfo(fn_line.group(3), is_const, ret_ty_info, arg_tys, fn_docs))
701 (out_java_addendum, out_java_trait_addendum, out_c_addendum) = consts.native_c_map_trait(struct_name, field_var_convs, flattened_field_var_convs, field_fns, trait_doc_comment)
702 write_c(out_c_addendum)
703 out_java_trait.write(out_java_trait_addendum)
704 out_java.write(out_java_addendum)
706 for fn_docs, fn_line in trait_fn_lines:
707 if fn_line == "cloned":
709 # For now, just disable enabling the _call_log - we don't know how to inverse-map String
710 is_log = fn_line.group(3) == "log" and struct_name == "LDKLogger"
711 if fn_line.group(3) != "free" and fn_line.group(3) != "eq" and not is_log:
712 dummy_line = fn_line.group(2) + struct_name.replace("LDK", "") + "_" + fn_line.group(3) + " " + struct_name + " *NONNULL_PTR this_arg" + fn_line.group(5) + "\n"
713 map_fn(dummy_line, re.compile("([A-Za-z_0-9]*) *([A-Za-z_0-9]*) *(.*)").match(dummy_line), None, "(this_arg_conv->" + fn_line.group(3) + ")(this_arg_conv->this_arg", fn_docs)
714 for idx, var_line in enumerate(field_var_lines):
715 if var_line.group(1) not in trait_structs:
716 write_c(var_line.group(1) + " " + struct_name + "_set_get_" + var_line.group(2) + "(" + struct_name + "* this_arg) {\n")
717 write_c("\tif (this_arg->set_" + var_line.group(2) + " != NULL)\n")
718 write_c("\t\tthis_arg->set_" + var_line.group(2) + "(this_arg);\n")
719 write_c("\treturn this_arg->" + var_line.group(2) + ";\n")
721 dummy_line = var_line.group(1) + " " + struct_name.replace("LDK", "") + "_get_" + var_line.group(2) + " " + struct_name + " *NONNULL_PTR this_arg" + fn_line.group(5) + "\n"
722 map_fn(dummy_line, re.compile("([A-Za-z_0-9]*) *([A-Za-z_0-9]*) *(.*)").match(dummy_line), None, struct_name + "_set_get_" + var_line.group(2) + "(this_arg_conv", fn_docs)
724 def map_result(struct_name, res_ty, err_ty):
725 result_types.add(struct_name)
726 human_ty = struct_name.replace("LDKCResult", "Result")
727 res_map = type_mapping_generator.map_type(res_ty + " res", True, None, False, True)
728 err_map = type_mapping_generator.map_type(err_ty + " err", True, None, False, True)
729 java_hu_struct = consts.map_result(struct_name, res_map, err_map)
730 create_getter(struct_name, res_ty, "ok", ("*", "->contents.result"), ("", "->result_ok"))
731 create_getter(struct_name, err_ty, "err", ("*", "->contents.err"), ("!", "->result_ok"))
732 with open(f"{sys.argv[3]}/structs/{human_ty}{consts.file_ext}", "w") as out_java_struct:
733 out_java_struct.write(java_hu_struct)
735 def create_getter(struct_name, field_decl, field_name, accessor, check_sfx):
736 field_ty = java_c_types(field_decl + " " + field_name, None)
737 ptr_fn_defn = field_decl + " *" + struct_name.replace("LDK", "") + "_get_" + field_name + "(" + struct_name + " *NONNULL_PTR owner)"
738 owned_fn_defn = field_decl + " " + struct_name.replace("LDK", "") + "_get_" + field_name + "(" + struct_name + " *NONNULL_PTR owner)"
741 if field_ty.rust_obj is not None and field_ty.rust_obj.replace("LDK", "") + "_clone" in clone_fns:
742 fn_defn = owned_fn_defn
743 write_c("static inline " + fn_defn + "{\n")
744 if check_sfx is not None:
745 write_c("CHECK(" + check_sfx[0] + "owner" + check_sfx[1] + ");\n")
746 write_c("\treturn " + field_ty.rust_obj.replace("LDK", "") + "_clone(&" + accessor[0] + "owner" + accessor[1] + ");\n")
747 elif field_ty.arr_len is not None or field_ty.is_native_primitive or field_ty.rust_obj in unitary_enums:
748 fn_defn = owned_fn_defn
749 write_c("static inline " + fn_defn + "{\n")
750 if check_sfx is not None:
751 write_c("CHECK(" + check_sfx[0] + "owner" + check_sfx[1] + ");\n")
752 write_c("\treturn " + accessor[0] + "owner" + accessor[1] + ";\n")
755 fn_defn = ptr_fn_defn
756 write_c("static inline " + fn_defn + "{\n")
757 if check_sfx is not None:
758 write_c("CHECK(" + check_sfx[0] + "owner" + check_sfx[1] + ");\n")
759 write_c("\treturn &" + accessor[0] + "owner" + accessor[1] + ";\n")
762 dummy_line = fn_defn + ";\n"
763 map_fn_with_ref_option(dummy_line, reg_fn_regex.match(dummy_line), None, None, "", holds_ref)
765 def map_tuple(struct_name, field_lines):
766 human_ty = struct_name.replace("LDKC2Tuple", "TwoTuple").replace("LDKC3Tuple", "ThreeTuple")
767 with open(f"{sys.argv[3]}/structs/{human_ty}{consts.file_ext}", "w") as out_java_struct:
768 out_java_struct.write(consts.map_tuple(struct_name))
770 for idx, (line, _) in enumerate(field_lines):
771 if idx != 0 and idx < len(field_lines) - 2:
772 ty_list.append(java_c_types(line.strip(';'), None))
773 tuple_types[struct_name] = (ty_list, struct_name)
775 # Map virtual getter functions
776 for idx, (line, _) in enumerate(field_lines):
777 if idx != 0 and idx < len(field_lines) - 2:
778 field_name = chr(ord('a') + idx - 1)
779 assert line.endswith(" " + field_name + ";")
780 create_getter(struct_name, line[:-3].strip(), field_name, ("", "->" + field_name), None)
782 out_java.write(consts.bindings_header)
783 with open(f"{sys.argv[2]}/version{consts.file_ext}", "w") as out_java_version:
784 out_java_version.write(consts.bindings_version_file.replace('<git_version_ldk_garbagecollected>', local_git_version))
786 with open(f"{sys.argv[3]}/structs/CommonBase{consts.file_ext}", "w") as out_java_struct:
787 out_java_struct.write(consts.common_base)
790 last_block_comment = None
793 const_val_regex = re.compile("^extern const ([A-Za-z_0-9]*) ([A-Za-z_0-9]*);$")
795 line_indicates_result_regex = re.compile("^ union (LDKCResult_[A-Za-z_0-9]*Ptr) contents;$")
796 line_indicates_vec_regex = re.compile("^ (struct |enum |union )?([A-Za-z_0-9]*) \*data;$")
797 line_indicates_opaque_regex = re.compile("^ bool is_owned;$")
798 line_indicates_trait_regex = re.compile("^ (struct |enum |union )?([A-Za-z_0-9]* \*?)\(\*([A-Za-z_0-9]*)\)\((const )?void \*this_arg(.*)\);$")
799 assert(line_indicates_trait_regex.match(" uintptr_t (*send_data)(void *this_arg, LDKu8slice data, bool resume_read);"))
800 assert(line_indicates_trait_regex.match(" struct LDKCVec_MessageSendEventZ (*get_and_clear_pending_msg_events)(const void *this_arg);"))
801 assert(line_indicates_trait_regex.match(" struct LDKCVec_u8Z (*write)(const void *this_arg);"))
802 line_indicates_trait_clone_regex = re.compile("^ void \(\*cloned\)\(struct ([A-Za-z0-9])* \*NONNULL_PTR new_[A-Za-z0-9]*\);$")
803 assert(line_indicates_trait_clone_regex.match(" void (*cloned)(struct LDKSign *NONNULL_PTR new_Sign);"))
804 line_field_var_regex = re.compile("^ struct ([A-Za-z_0-9]*) ([A-Za-z_0-9]*);$")
805 assert(line_field_var_regex.match(" struct LDKMessageSendEventsProvider MessageSendEventsProvider;"))
806 assert(line_field_var_regex.match(" struct LDKChannelPublicKeys pubkeys;"))
807 struct_name_regex = re.compile("^typedef (struct|enum|union) (MUST_USE_STRUCT )?(LDK[A-Za-z_0-9]*) {$")
808 assert(struct_name_regex.match("typedef struct LDKCVec_u8Z {"))
809 assert(struct_name_regex.match("typedef enum LDKNetwork {"))
811 union_enum_items = {}
812 result_ptr_struct_items = {}
814 if block_comment is not None:
815 if line.endswith("*/\n"):
816 last_block_comment = block_comment.strip("\n")
819 block_comment = block_comment + line.strip(" /*")
820 elif cur_block_obj is not None:
821 cur_block_obj = cur_block_obj + line
822 if line.startswith("} "):
826 obj_lines = cur_block_obj.split("\n")
828 result_contents = None
829 is_unitary_enum = False
830 is_union_enum = False
835 last_struct_block_comment = None
837 for idx, struct_line in enumerate(obj_lines):
838 if struct_line.strip().startswith("/*"):
839 block_comment = struct_line.strip(" /*")
840 if block_comment is not None:
841 if struct_line.endswith("*/"):
842 last_struct_block_comment = block_comment.strip("\n")
845 block_comment = block_comment + "\n" + struct_line.strip(" /*").replace("…", "...")
847 struct_name_match = struct_name_regex.match(struct_line)
848 if struct_name_match is not None:
849 struct_name = struct_name_match.group(3)
850 if struct_name_match.group(1) == "enum":
851 if not struct_name.endswith("_Tag"):
852 is_unitary_enum = True
855 elif struct_name_match.group(1) == "union":
857 if line_indicates_opaque_regex.match(struct_line):
859 result_match = line_indicates_result_regex.match(struct_line)
860 if result_match is not None:
861 result_contents = result_match.group(1)
862 vec_ty_match = line_indicates_vec_regex.match(struct_line)
863 if vec_ty_match is not None and struct_name.startswith("LDKCVec_"):
864 vec_ty = vec_ty_match.group(2)
865 elif struct_name.startswith("LDKC2Tuple_") or struct_name.startswith("LDKC3Tuple_"):
867 trait_fn_match = line_indicates_trait_regex.match(struct_line)
868 if trait_fn_match is not None:
869 trait_fn_lines.append((last_struct_block_comment, trait_fn_match))
870 trait_clone_fn_match = line_indicates_trait_clone_regex.match(struct_line)
871 if trait_clone_fn_match is not None:
872 trait_fn_lines.append((last_struct_block_comment, "cloned"))
873 field_var_match = line_field_var_regex.match(struct_line)
874 if field_var_match is not None:
875 field_var_lines.append(field_var_match)
876 field_lines.append((struct_line, last_struct_block_comment))
877 last_struct_block_comment = None
879 assert(struct_name is not None)
880 assert(len(trait_fn_lines) == 0 or not (is_opaque or is_unitary_enum or is_union_enum or is_union or result_contents is not None or vec_ty is not None))
881 assert(not is_opaque or not (len(trait_fn_lines) != 0 or is_unitary_enum or is_union_enum or is_union or result_contents is not None or vec_ty is not None))
882 assert(not is_unitary_enum or not (len(trait_fn_lines) != 0 or is_opaque or is_union_enum or is_union or result_contents is not None or vec_ty is not None))
883 assert(not is_union_enum or not (len(trait_fn_lines) != 0 or is_unitary_enum or is_opaque or is_union or result_contents is not None or vec_ty is not None))
884 assert(not is_union or not (len(trait_fn_lines) != 0 or is_unitary_enum or is_union_enum or is_opaque or result_contents is not None or vec_ty is not None))
885 assert(result_contents is None or not (len(trait_fn_lines) != 0 or is_unitary_enum or is_union_enum or is_opaque or is_union or vec_ty is not None))
886 assert(vec_ty is None or not (len(trait_fn_lines) != 0 or is_unitary_enum or is_union_enum or is_opaque or is_union or result_contents is not None))
889 opaque_structs.add(struct_name)
890 with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "w") as out_java_struct:
891 out_opaque_struct_human = consts.map_opaque_struct(struct_name, last_block_comment)
892 last_block_comment = None
893 out_java_struct.write(out_opaque_struct_human)
894 elif result_contents is not None:
895 assert result_contents in result_ptr_struct_items
896 res_ty, err_ty = result_ptr_struct_items[result_contents]
897 map_result(struct_name, res_ty, err_ty)
898 elif struct_name.startswith("LDKCResult_") and struct_name.endswith("ZPtr"):
899 for line, _ in field_lines:
900 if line.endswith("*result;"):
901 res_ty = line[:-8].strip()
902 elif line.endswith("*err;"):
903 err_ty = line[:-5].strip()
904 result_ptr_struct_items[struct_name] = (res_ty, err_ty)
905 result_types.add(struct_name[:-3])
907 map_tuple(struct_name, field_lines)
908 elif vec_ty is not None:
909 ty_info = type_mapping_generator.map_type(vec_ty + " arr_elem", False, None, False, False)
910 if ty_info.is_native_primitive:
911 clone_fns.add(struct_name.replace("LDK", "") + "_clone")
912 write_c("static inline " + struct_name + " " + struct_name.replace("LDK", "") + "_clone(const " + struct_name + " *orig) {\n")
913 write_c("\t" + struct_name + " ret = { .data = MALLOC(sizeof(" + ty_info.c_ty + ") * orig->datalen, \"" + struct_name + " clone bytes\"), .datalen = orig->datalen };\n")
914 write_c("\tmemcpy(ret.data, orig->data, sizeof(" + ty_info.c_ty + ") * ret.datalen);\n")
915 write_c("\treturn ret;\n}\n")
916 elif (ty_info.rust_obj.replace("LDK", "") + "_clone") in clone_fns:
917 ty_name = struct_name.replace("LDK", "")
918 clone_fns.add(ty_name + "_clone")
919 write_c("static inline " + struct_name + " " + ty_name + "_clone(const " + struct_name + " *orig) {\n")
920 write_c("\t" + struct_name + " ret = { .data = MALLOC(sizeof(" + ty_info.rust_obj + ") * orig->datalen, \"" + struct_name + " clone bytes\"), .datalen = orig->datalen };\n")
921 write_c("\tfor (size_t i = 0; i < ret.datalen; i++) {\n")
922 write_c("\t\tret.data[i] = " + ty_info.rust_obj.replace("LDK", "") + "_clone(&orig->data[i]);\n")
923 write_c("\t}\n\treturn ret;\n}\n")
925 assert(struct_name.endswith("_Tag"))
926 struct_name = struct_name[:-4]
927 union_enum_items[struct_name] = {"field_lines": field_lines}
928 elif struct_name.endswith("_Body") and struct_name.split("_")[0] in union_enum_items:
929 enum_var_name = struct_name.split("_")
930 union_enum_items[enum_var_name[0]][enum_var_name[1]] = field_lines
931 elif struct_name in union_enum_items:
934 for line, _ in field_lines:
935 if line == " struct {":
939 elif elem_items > -1:
941 if line.startswith("struct "):
943 elif line.startswith("enum "):
945 split = line.split(" ")
946 assert len(split) == 2
947 tuple_variants[split[1].strip(";")] = split[0]
950 # We don't currently support tuple variant with more than one element
952 map_complex_enum(struct_name, union_enum_items[struct_name], tuple_variants, last_block_comment)
953 last_block_comment = None
954 elif is_unitary_enum:
955 map_unitary_enum(struct_name, field_lines, last_block_comment)
956 last_block_comment = None
957 elif len(trait_fn_lines) > 0:
958 map_trait(struct_name, field_var_lines, trait_fn_lines, last_block_comment)
959 elif struct_name == "LDKTxOut":
960 with open(f"{sys.argv[3]}/structs/TxOut{consts.file_ext}", "w") as out_java_struct:
961 out_java_struct.write(consts.hu_struct_file_prefix)
962 out_java_struct.write(consts.txout_defn)
963 fn_line = "struct LDKCVec_u8Z TxOut_get_script_pubkey (struct LDKTxOut* thing)"
964 write_c(fn_line + " {")
965 write_c("\treturn CVec_u8Z_clone(&thing->script_pubkey);")
967 map_fn(fn_line + "\n", re.compile("(.*) (TxOut_get_script_pubkey) \((.*)\)").match(fn_line), None, None, None)
968 fn_line = "uint64_t TxOut_get_value (struct LDKTxOut* thing)"
969 write_c(fn_line + " {")
970 write_c("\treturn thing->value;")
972 map_fn(fn_line + "\n", re.compile("(.*) (TxOut_get_value) \((.*)\)").match(fn_line), None, None, None)
974 pass # Everything remaining is a byte[] or some form
977 fn_ptr = fn_ptr_regex.match(line)
978 fn_ret_arr = fn_ret_arr_regex.match(line)
979 reg_fn = reg_fn_regex.match(line)
980 const_val = const_val_regex.match(line)
982 if line.startswith("#include <"):
984 elif line.startswith("/*"):
985 if not line.endswith("*/\n"):
986 block_comment = line.strip(" /*")
987 elif line.startswith("typedef enum "):
989 elif line.startswith("typedef struct "):
991 elif line.startswith("typedef union "):
993 elif fn_ptr is not None:
994 map_fn(line, fn_ptr, None, None, last_block_comment)
995 last_block_comment = None
996 elif fn_ret_arr is not None:
997 map_fn(line, fn_ret_arr, fn_ret_arr.group(4), None, last_block_comment)
998 last_block_comment = None
999 elif reg_fn is not None:
1000 map_fn(line, reg_fn, None, None, last_block_comment)
1001 last_block_comment = None
1002 elif const_val_regex is not None:
1003 # TODO Map const variables
1006 assert(line == "\n")
1008 out_java.write(consts.bindings_footer)
1009 for struct_name in opaque_structs:
1010 with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "a") as out_java_struct:
1011 out_java_struct.write("}\n")
1012 for struct_name in trait_structs:
1013 with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "a") as out_java_struct:
1014 out_java_struct.write("}\n")
1015 for struct_name in complex_enums:
1016 with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '').replace('COption', 'Option')}{consts.file_ext}", "a") as out_java_struct:
1017 out_java_struct.write("}\n")
1018 for struct_name in result_types:
1019 with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDKCResult', 'Result')}{consts.file_ext}", "a") as out_java_struct:
1020 out_java_struct.write("}\n")
1021 for struct_name in tuple_types:
1022 struct_hu_name = struct_name.replace("LDKC2Tuple", "TwoTuple").replace("LDKC3Tuple", "ThreeTuple")
1023 with open(f"{sys.argv[3]}/structs/{struct_hu_name}{consts.file_ext}", "a") as out_java_struct:
1024 out_java_struct.write("}\n")
1026 with open(f"{sys.argv[4]}/bindings.c.body", "w") as out_c:
1027 out_c.write(consts.c_file_pfx)
1028 out_c.write(consts.init_str())
1030 with open(f"{sys.argv[4]}/version.c", "w") as out_c:
1031 out_c.write(consts.c_version_file.replace('<git_version_ldk_garbagecollected>', local_git_version))
1032 with open(f"{sys.argv[3]}/structs/UtilMethods{consts.file_ext}", "a") as util:
1033 util.write(consts.util_fn_sfx)