gen_rust.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893
  1. # -------------------------------------------------------------------------------
  2. # Generate rust bindings.
  3. #
  4. # rust coding style:
  5. # - types are PascalCase
  6. # - otherwise snake_case
  7. # -------------------------------------------------------------------------------
  8. import gen_ir
  9. import os, shutil, sys
  10. import gen_util as util
  11. module_names = {
  12. "slog_": "log",
  13. "sg_": "gfx",
  14. "sapp_": "app",
  15. "stm_": "time",
  16. "saudio_": "audio",
  17. "sgl_": "gl",
  18. "sdtx_": "debugtext",
  19. "sshape_": "shape",
  20. "sapp_sg": "glue",
  21. "simgui_": "imgui",
  22. "sg_imgui_": "gfx_imgui",
  23. }
  24. module_requires_rust_feature = {
  25. module_names["simgui_"]: "imgui",
  26. module_names["sg_imgui_"]: "imgui",
  27. }
  28. c_source_paths = {
  29. "slog_": "sokol-rust/src/sokol/c/sokol_log.c",
  30. "sg_": "sokol-rust/src/sokol/c/sokol_gfx.c",
  31. "sapp_": "sokol-rust/src/sokol/c/sokol_app.c",
  32. "stm_": "sokol-rust/src/sokol/c/sokol_time.c",
  33. "saudio_": "sokol-rust/src/sokol/c/sokol_audio.c",
  34. "sgl_": "sokol-rust/src/sokol/c/sokol_gl.c",
  35. "sdtx_": "sokol-rust/src/sokol/c/sokol_debugtext.c",
  36. "sshape_": "sokol-rust/src/sokol/c/sokol_shape.c",
  37. "sapp_sg": "sokol-rust/src/sokol/c/sokol_glue.c",
  38. "simgui_": "sokol-rust/src/sokol/c/sokol_imgui.c",
  39. "sg_imgui_": "sokol-rust/src/sokol/c/sokol_gfx_imgui.c",
  40. }
  41. ignores = [
  42. "sdtx_printf",
  43. "sdtx_vprintf",
  44. # "sg_install_trace_hooks",
  45. # "sg_trace_hooks",
  46. ]
  47. range_struct_name = "Range"
  48. # functions that need to be exposed as 'raw' C callbacks without a rust wrapper function
  49. c_callbacks = ["slog_func"]
  50. # NOTE: syntax for function results: "func_name.RESULT"
  51. overrides = {
  52. "type": "_type",
  53. "ref": "_ref",
  54. "sg_apply_uniforms.ub_index": "uintptr_t",
  55. "sg_draw.base_element": "uintptr_t",
  56. "sg_draw.num_elements": "uintptr_t",
  57. "sg_draw.num_instances": "uintptr_t",
  58. "sshape_element_range_t.base_element": "uintptr_t",
  59. "sshape_element_range_t.num_elements": "uintptr_t",
  60. "sdtx_font.font_index": "uintptr_t",
  61. "sdtx_move": "sdtx_move_cursor",
  62. "sdtx_move_x": "sdtx_move_cursor_x",
  63. "sdtx_move_y": "sdtx_move_cursor_y",
  64. "sg_image_type::SG_IMAGETYPE_2D": "SG_IMAGEYPE_DIM2",
  65. "sg_image_type::SG_IMAGETYPE_3D": "SG_IMAGETYPE_DIM3",
  66. "sapp_keycode::SAPP_KEYCODE_0": "SAPP_KEYCODE_NUM0",
  67. "sapp_keycode::SAPP_KEYCODE_1": "SAPP_KEYCODE_NUM1",
  68. "sapp_keycode::SAPP_KEYCODE_2": "SAPP_KEYCODE_NUM2",
  69. "sapp_keycode::SAPP_KEYCODE_3": "SAPP_KEYCODE_NUM3",
  70. "sapp_keycode::SAPP_KEYCODE_4": "SAPP_KEYCODE_NUM4",
  71. "sapp_keycode::SAPP_KEYCODE_5": "SAPP_KEYCODE_NUM5",
  72. "sapp_keycode::SAPP_KEYCODE_6": "SAPP_KEYCODE_NUM6",
  73. "sapp_keycode::SAPP_KEYCODE_7": "SAPP_KEYCODE_NUM7",
  74. "sapp_keycode::SAPP_KEYCODE_8": "SAPP_KEYCODE_NUM8",
  75. "sapp_keycode::SAPP_KEYCODE_9": "SAPP_KEYCODE_NUM9",
  76. # "sgl_error": "sgl_get_error", # 'error' is reserved in zig
  77. # "sgl_deg": "sgl_as_degrees",
  78. # "sgl_rad": "sgl_as_radians",
  79. # "sg_context_desc.color_format": "int",
  80. # "SGL_NO_ERROR": "SGL_ERROR_NO_ERROR",
  81. # "sg_context_desc.depth_format": "int",
  82. }
  83. prim_types = {
  84. "int": "i32",
  85. "bool": "bool",
  86. "char": "core::ffi::c_char",
  87. "int8_t": "i8",
  88. "uint8_t": "u8",
  89. "int16_t": "i16",
  90. "uint16_t": "u16",
  91. "int32_t": "i32",
  92. "uint32_t": "u32",
  93. "int64_t": "i64",
  94. "uint64_t": "u64",
  95. "float": "f32",
  96. "double": "f64",
  97. "uintptr_t": "usize",
  98. "intptr_t": "isize",
  99. "size_t": "usize",
  100. }
  101. prim_defaults = {
  102. "int": "0",
  103. "bool": "false",
  104. "int8_t": "0",
  105. "uint8_t": "0",
  106. "int16_t": "0",
  107. "uint16_t": "0",
  108. "int32_t": "0",
  109. "uint32_t": "0",
  110. "int64_t": "0",
  111. "uint64_t": "0",
  112. "float": "0.0",
  113. "double": "0.0",
  114. "uintptr_t": "0",
  115. "intptr_t": "0",
  116. "size_t": "0",
  117. "char": "0",
  118. }
  119. special_constant_types = {
  120. "SG_INVALID_ID": "u32",
  121. "SAPP_MODIFIER_SHIFT": "u32",
  122. "SAPP_MODIFIER_CTRL": "u32",
  123. "SAPP_MODIFIER_ALT": "u32",
  124. "SAPP_MODIFIER_SUPER": "u32",
  125. "SAPP_MODIFIER_LMB": "u32",
  126. "SAPP_MODIFIER_RMB": "u32",
  127. "SAPP_MODIFIER_MMB": "u32",
  128. }
  129. struct_types = []
  130. enum_types = []
  131. enum_items = {}
  132. out_lines = ""
  133. def reset_globals():
  134. global struct_types
  135. global enum_types
  136. global enum_items
  137. global out_lines
  138. struct_types = []
  139. enum_types = []
  140. enum_items = {}
  141. out_lines = ""
  142. def l(s):
  143. global out_lines
  144. out_lines += s + "\n"
  145. def as_rust_prim_type(s):
  146. return prim_types[s]
  147. def as_upper_snake_case(s, prefix):
  148. outp = s.lower()
  149. if outp.startswith(prefix):
  150. outp = outp[len(prefix):]
  151. return outp.upper()
  152. # prefix_bla_blub(_t) => (dep::)BlaBlub
  153. def as_rust_struct_type(s, prefix):
  154. parts = s.lower().split("_")
  155. outp = "" if s.startswith(prefix) else f"{parts[0]}::"
  156. for part in parts[1:]:
  157. # ignore '_t' type postfix
  158. if part != "t":
  159. outp += part.capitalize()
  160. return outp
  161. # prefix_bla_blub(_t) => (dep::)BlaBlub
  162. def as_rust_enum_type(s, prefix):
  163. parts = s.lower().split("_")
  164. outp = "" if s.startswith(prefix) else f"{parts[0]}::"
  165. for part in parts[1:]:
  166. # ignore '_t' type postfix
  167. if part != "t":
  168. outp += part.capitalize()
  169. return outp
  170. def check_override(name, default=None):
  171. if name in overrides:
  172. return overrides[name]
  173. elif default is None:
  174. return name
  175. else:
  176. return default
  177. def check_ignore(name):
  178. return name in ignores
  179. # PREFIX_ENUM_BLA_BLA => BlaBla, _PREFIX_ENUM_BLA_BLA => BlaBla
  180. def as_enum_item_name(s):
  181. parts = s.lstrip("_").split("_")
  182. outp = ""
  183. for i, part in enumerate(parts[2:]):
  184. # TODO: What to do with enum fields starting with numbers?
  185. outp += part.capitalize()
  186. return outp
  187. def enum_default_item(enum_name):
  188. return enum_items[enum_name][0]
  189. def is_prim_type(s):
  190. return s in prim_types
  191. def is_struct_type(s):
  192. return s in struct_types
  193. def is_enum_type(s):
  194. return s in enum_types
  195. def is_const_prim_ptr(s):
  196. for prim_type in prim_types:
  197. if s == f"const {prim_type} *":
  198. return True
  199. return False
  200. def is_prim_ptr(s):
  201. for prim_type in prim_types:
  202. if s == f"{prim_type} *":
  203. return True
  204. return False
  205. def is_const_struct_ptr(s):
  206. for struct_type in struct_types:
  207. if s == f"const {struct_type} *":
  208. return True
  209. return False
  210. def is_struct_ptr(s):
  211. for struct_type in struct_types:
  212. if s == f"{struct_type} *":
  213. return True
  214. return False
  215. def type_default_value(s):
  216. return prim_defaults[s]
  217. def as_c_arg_type(arg_prefix, arg_type, prefix):
  218. # NOTE: if arg_prefix is None, the result is used as return value
  219. pre = "" if arg_prefix is None else arg_prefix
  220. if arg_type == "void":
  221. return ""
  222. elif is_prim_type(arg_type):
  223. return pre + as_rust_prim_type(arg_type)
  224. elif is_struct_type(arg_type):
  225. return pre + as_rust_struct_type(arg_type, prefix)
  226. elif is_enum_type(arg_type):
  227. return pre + as_rust_enum_type(arg_type, prefix)
  228. elif util.is_void_ptr(arg_type):
  229. return pre + "*mut core::ffi::c_void"
  230. elif util.is_const_void_ptr(arg_type):
  231. return pre + "*const core::ffi::c_void"
  232. elif util.is_string_ptr(arg_type):
  233. return pre + "*const core::ffi::c_char"
  234. elif is_const_struct_ptr(arg_type):
  235. return pre + f"*const {as_rust_struct_type(util.extract_ptr_type(arg_type), prefix)}"
  236. elif is_struct_ptr(arg_type):
  237. return pre + f"*mut {as_rust_struct_type(util.extract_ptr_type(arg_type), prefix)}"
  238. elif is_prim_ptr(arg_type):
  239. return pre + f"*mut {as_rust_prim_type(util.extract_ptr_type(arg_type))}"
  240. elif is_const_prim_ptr(arg_type):
  241. return pre + f"*const {as_rust_prim_type(util.extract_ptr_type(arg_type))}"
  242. else:
  243. sys.exit(f"ERROR as_c_arg_type(): {arg_type}")
  244. def as_rust_arg_type(arg_prefix, arg_type, prefix):
  245. # NOTE: if arg_prefix is None, the result is used as return value
  246. pre = "" if arg_prefix is None else arg_prefix
  247. if arg_type == "void":
  248. return ""
  249. elif is_prim_type(arg_type):
  250. return pre + as_rust_prim_type(arg_type)
  251. elif is_struct_type(arg_type):
  252. return pre + as_rust_struct_type(arg_type, prefix)
  253. elif is_enum_type(arg_type):
  254. return pre + as_rust_enum_type(arg_type, prefix)
  255. elif util.is_void_ptr(arg_type):
  256. return pre + "*mut core::ffi::c_void"
  257. elif util.is_const_void_ptr(arg_type):
  258. return pre + "*const core::ffi::c_void"
  259. elif util.is_string_ptr(arg_type):
  260. return pre + "&str"
  261. elif is_const_struct_ptr(arg_type):
  262. return pre + f"&{as_rust_struct_type(util.extract_ptr_type(arg_type), prefix)}"
  263. elif is_struct_ptr(arg_type):
  264. return pre + f"&mut {as_rust_struct_type(util.extract_ptr_type(arg_type), prefix)}"
  265. elif is_prim_ptr(arg_type):
  266. return pre + f"&mut {as_rust_prim_type(util.extract_ptr_type(arg_type))}"
  267. elif is_const_prim_ptr(arg_type):
  268. return pre + f"&{as_rust_prim_type(util.extract_ptr_type(arg_type))}"
  269. else:
  270. sys.exit(f"ERROR as_rust_arg_type(): {arg_type}")
  271. def is_rust_string(rust_type):
  272. return rust_type == "&str" or rust_type == " -> &'static str"
  273. # get C-style arguments of a function pointer as string
  274. def funcptr_args_c(field_type, prefix):
  275. tokens = field_type[field_type.index("(*)") + 4: -1].split(",")
  276. s = ""
  277. for token in tokens:
  278. arg_type = token.strip()
  279. if s != "":
  280. s += ", "
  281. c_arg = as_c_arg_type(None, arg_type, prefix)
  282. if c_arg == "void":
  283. return ""
  284. else:
  285. s += c_arg
  286. return s
  287. # get C-style result of a function pointer as string
  288. def funcptr_result_c(field_type):
  289. res_type = field_type[: field_type.index("(*)")].strip()
  290. if res_type == "void":
  291. return ""
  292. elif util.is_const_void_ptr(res_type):
  293. return " -> *const core::ffi::c_void"
  294. elif util.is_void_ptr(res_type):
  295. return " -> *mut core::ffi::c_void"
  296. else:
  297. sys.exit(f"ERROR funcptr_result_c(): {field_type}")
  298. def funcdecl_args_c(decl, prefix):
  299. s = ""
  300. func_name = decl["name"]
  301. for param_decl in decl["params"]:
  302. if s != "":
  303. s += ", "
  304. param_name = param_decl["name"]
  305. param_type = check_override(
  306. f"{func_name}.{param_name}", default=param_decl["type"]
  307. )
  308. s += f"{as_c_arg_type(f'{param_name}: ', param_type, prefix)}"
  309. return s
  310. def funcdecl_args_rust(decl, prefix):
  311. s = ""
  312. func_name = decl["name"]
  313. for param_decl in decl["params"]:
  314. if s != "":
  315. s += ", "
  316. param_name = param_decl["name"]
  317. param_type = check_override(
  318. f"{func_name}.{param_name}", default=param_decl["type"]
  319. )
  320. s += f"{as_rust_arg_type(f'{param_name}: ', param_type, prefix)}"
  321. return s
  322. def funcdecl_result_c(decl, prefix):
  323. func_name = decl["name"]
  324. decl_type = decl["type"]
  325. result_type = check_override(
  326. f"{func_name}.RESULT", default=decl_type[: decl_type.index("(")].strip()
  327. )
  328. it = as_c_arg_type(None, result_type, prefix)
  329. if it == "()" or it == "":
  330. return ""
  331. else:
  332. return f" -> {it}"
  333. def funcdecl_result_rust(decl, prefix):
  334. func_name = decl["name"]
  335. decl_type = decl["type"]
  336. result_type = check_override(
  337. f"{func_name}.RESULT", default=decl_type[: decl_type.index("(")].strip()
  338. )
  339. rust_res_type = as_rust_arg_type(None, result_type, prefix)
  340. if is_rust_string(rust_res_type):
  341. rust_res_type = "&'static str"
  342. if rust_res_type == "":
  343. return ""
  344. else:
  345. return f" -> {rust_res_type }"
  346. def gen_struct(decl, prefix):
  347. struct_name = check_override(decl["name"])
  348. rust_type = as_rust_struct_type(struct_name, prefix)
  349. rust_struct_type = rust_type
  350. struct_lines = []
  351. default_lines = []
  352. for field in decl["fields"]:
  353. field_name = check_override(field["name"])
  354. field_type = check_override(
  355. f"{struct_name}.{field_name}", default=field["type"]
  356. )
  357. if is_prim_type(field_type):
  358. struct_lines.append(
  359. f"pub {field_name}: {as_rust_prim_type(field_type)}"
  360. )
  361. default_lines.append(
  362. f"{field_name}: {type_default_value(field_type)}"
  363. )
  364. elif is_struct_type(field_type):
  365. struct_lines.append(
  366. f"pub {field_name}: {as_rust_struct_type(field_type, prefix)}"
  367. )
  368. default_lines.append(
  369. f"{field_name}: {as_rust_struct_type(field_type, prefix)}::new()"
  370. )
  371. elif is_enum_type(field_type):
  372. struct_lines.append(
  373. f"pub {field_name}: {as_rust_enum_type(field_type, prefix)}"
  374. )
  375. default_lines.append(
  376. f"{field_name}: {as_rust_enum_type(field_type, prefix)}::new()"
  377. )
  378. elif util.is_string_ptr(field_type):
  379. struct_lines.append(
  380. f"pub {field_name}: *const core::ffi::c_char"
  381. )
  382. default_lines.append(
  383. f"{field_name}: core::ptr::null()"
  384. )
  385. elif util.is_const_void_ptr(field_type):
  386. struct_lines.append(
  387. f"pub {field_name}: *const core::ffi::c_void"
  388. )
  389. default_lines.append(
  390. f"{field_name}: core::ptr::null()"
  391. )
  392. elif util.is_void_ptr(field_type):
  393. struct_lines.append(
  394. f"pub {field_name}: *mut core::ffi::c_void"
  395. )
  396. default_lines.append(
  397. f"{field_name}: core::ptr::null_mut()"
  398. )
  399. elif is_const_prim_ptr(field_type):
  400. struct_lines.append(
  401. f"pub {field_name}: *const {as_rust_prim_type(util.extract_ptr_type(field_type))}"
  402. )
  403. default_lines.append(
  404. f"{field_name}: core::ptr::null()"
  405. )
  406. elif is_prim_ptr(field_type):
  407. struct_lines.append(
  408. f"pub {field_name}: *mut {as_rust_prim_type(util.extract_ptr_type(field_type))}"
  409. )
  410. default_lines.append(
  411. f"{field_name}: core::ptr::null_mut()"
  412. )
  413. elif is_const_struct_ptr(field_type):
  414. struct_lines.append(
  415. f"pub {field_name}: *const {as_rust_struct_type(util.extract_ptr_type(field_type), prefix)}"
  416. )
  417. default_lines.append(
  418. f"{field_name}: core::ptr::null()"
  419. )
  420. elif is_struct_ptr(field_type):
  421. struct_lines.append(
  422. f"pub {field_name}: *mut {as_rust_struct_type(util.extract_ptr_type(field_type), prefix)}"
  423. )
  424. default_lines.append(
  425. f"{field_name}: core::ptr::null_mut()"
  426. )
  427. elif util.is_func_ptr(field_type):
  428. struct_lines.append(
  429. f"pub {field_name}: Option<extern \"C\" fn({funcptr_args_c(field_type, prefix)}){funcptr_result_c(field_type)}>"
  430. )
  431. default_lines.append(
  432. f"{field_name}: None"
  433. )
  434. elif util.is_1d_array_type(field_type):
  435. array_type = util.extract_array_type(field_type)
  436. array_sizes = util.extract_array_sizes(field_type)
  437. if is_prim_type(array_type) or is_struct_type(array_type):
  438. if is_prim_type(array_type):
  439. rust_type = as_rust_prim_type(array_type)
  440. def_val = type_default_value(array_type)
  441. elif is_struct_type(array_type) or is_enum_type(array_type):
  442. rust_type = as_rust_struct_type(array_type, prefix)
  443. def_val = f"{rust_type}::new()"
  444. else:
  445. sys.exit(f"ERROR gen_struct is_1d_array_type: {array_type}")
  446. t0 = f"[{rust_type}; {array_sizes[0]}]"
  447. # t1 = f"&{rust_type}"
  448. struct_lines.append(
  449. f"pub {field_name}: {t0}"
  450. )
  451. default_lines.append(
  452. f"{field_name}: [{def_val}; {array_sizes[0]}]"
  453. )
  454. elif util.is_const_void_ptr(array_type):
  455. struct_lines.append(
  456. f"pub {field_name}: [*const core::ffi::c_void; {array_sizes[0]}]"
  457. )
  458. default_lines.append(
  459. f"{field_name}: [core::ptr::null(); {array_sizes[0]}]"
  460. )
  461. else:
  462. sys.exit(
  463. f"ERROR gen_struct: array {field_name}: {field_type} => [{array_type}: {array_sizes[0]}]"
  464. )
  465. elif util.is_2d_array_type(field_type):
  466. array_type = util.extract_array_type(field_type)
  467. array_sizes = util.extract_array_sizes(field_type)
  468. if is_prim_type(array_type):
  469. rust_type = as_rust_prim_type(array_type)
  470. def_val = type_default_value(array_type)
  471. elif is_struct_type(array_type):
  472. rust_type = as_rust_struct_type(array_type, prefix)
  473. def_val = f"{rust_type}::new()"
  474. else:
  475. sys.exit(f"ERROR gen_struct is_2d_array_type: {array_type}")
  476. struct_lines.append(
  477. f"pub {field_name}: [[{rust_type}; {array_sizes[1]}]; {array_sizes[0]}]"
  478. )
  479. default_lines.append(
  480. f"{field_name}: [[{def_val}; {array_sizes[1]}]; {array_sizes[0]}]"
  481. )
  482. else:
  483. sys.exit(f"ERROR gen_struct: {field_name}: {field_type};")
  484. #
  485. # TODO: Is this the best way to have zero-initialization with support for constants?
  486. # core::mem::zeroed() cleaner?
  487. #
  488. l("#[repr(C)]")
  489. l("#[derive(Copy, Clone, Debug)]")
  490. l(f"pub struct {rust_struct_type} {{")
  491. for line in struct_lines:
  492. l(f" {line},")
  493. l("}")
  494. l(f"impl {rust_struct_type} {{")
  495. l(" pub const fn new() -> Self {")
  496. l(" Self {")
  497. for line in default_lines:
  498. l(f" {line},")
  499. l(" }")
  500. l(" }")
  501. l("}")
  502. l(f"impl Default for {rust_struct_type} {{")
  503. l(" fn default() -> Self {")
  504. l(" Self::new()")
  505. l(" }")
  506. l("}")
  507. def gen_consts(decl, prefix):
  508. for item in decl["items"]:
  509. #
  510. # TODO: What type should these constants have? Currently giving all `usize`
  511. # unless specifically overriden by `special_constant_types`
  512. #
  513. item_name = check_override(item["name"])
  514. if item_name in special_constant_types:
  515. special_type = special_constant_types[item_name]
  516. l(f"pub const {as_upper_snake_case(item_name, prefix)}: {special_type} = {item['value']};")
  517. else:
  518. l(f"pub const {as_upper_snake_case(item_name, prefix)}: usize = {item['value']};")
  519. def gen_enum(decl, prefix):
  520. enum_name = check_override(decl["name"])
  521. names = [
  522. as_enum_item_name(check_override(f"{decl['name']}::{item['name']}", item['name'])) for item in decl["items"]
  523. ]
  524. is_u32 = False
  525. for name in names:
  526. if name == "ForceU32":
  527. is_u32 = True
  528. break
  529. l("#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]")
  530. if is_u32:
  531. l("#[repr(u32)]")
  532. else:
  533. l("#[repr(i32)]")
  534. rust_enum_name = as_rust_enum_type(enum_name, prefix)
  535. l(f"pub enum {rust_enum_name} {{")
  536. for item_name, item in zip(names, decl["items"]):
  537. if item_name != "ForceU32":
  538. if "value" in item:
  539. l(f" {item_name} = {item['value']},")
  540. else:
  541. l(f" {item_name},")
  542. l("}")
  543. default_item = enum_default_item(enum_name)
  544. l(f"impl {rust_enum_name} {{")
  545. l(" pub const fn new() -> Self {")
  546. l(f" Self::{default_item}")
  547. l(" }")
  548. l("}")
  549. l(f"impl Default for {rust_enum_name} {{")
  550. l(" fn default() -> Self {")
  551. l(f" Self::{default_item}")
  552. l(" }")
  553. l("}")
  554. def gen_func_c(decl, prefix):
  555. l("pub extern \"C\" {")
  556. l(f" fn {decl['name']}({funcdecl_args_c(decl, prefix)}){funcdecl_result_c(decl, prefix)};")
  557. l("}")
  558. def gen_c_funcs(funcs):
  559. l("pub mod ffi {")
  560. l(" #![allow(unused_imports)]")
  561. l(" use super::*;")
  562. l(" extern \"C\" {")
  563. for decl, prefix in funcs:
  564. l(f" pub fn {decl['name']}({funcdecl_args_c(decl, prefix)}){funcdecl_result_c(decl, prefix)};")
  565. l(" }")
  566. l("}")
  567. def gen_rust_funcs(funcs):
  568. for decl, prefix in funcs:
  569. gen_func_rust(decl, prefix)
  570. def gen_func_rust(decl, prefix):
  571. c_func_name = decl["name"]
  572. rust_func_name = util.as_lower_snake_case(check_override(decl["name"]), prefix)
  573. rust_res_type = funcdecl_result_rust(decl, prefix)
  574. if c_func_name in c_callbacks:
  575. c_res_type = funcdecl_result_c(decl, prefix)
  576. l("#[inline]")
  577. l(f'pub extern "C" fn {c_func_name}({funcdecl_args_c(decl, prefix)}){c_res_type} {{')
  578. l(" unsafe {")
  579. s = f" ffi::{c_func_name}("
  580. for i, param_decl in enumerate(decl["params"]):
  581. if i > 0:
  582. s += ", "
  583. arg_name = param_decl["name"]
  584. s += arg_name
  585. s += ")"
  586. l(s)
  587. l(" }")
  588. l("}")
  589. else:
  590. l("#[inline]")
  591. l(f"pub fn {rust_func_name}({funcdecl_args_rust(decl, prefix)}){rust_res_type} {{")
  592. for i, param_decl in enumerate(decl["params"]):
  593. arg_name = param_decl["name"]
  594. arg_type = param_decl["type"]
  595. if util.is_string_ptr(arg_type):
  596. l(f" let tmp_{i} = std::ffi::CString::new({arg_name}).unwrap();")
  597. l(" unsafe {")
  598. if is_rust_string(rust_res_type):
  599. # special case: convert C string to rust string slice
  600. s = f" c_char_ptr_to_rust_str(ffi::{c_func_name}("
  601. else:
  602. s = f" ffi::{c_func_name}("
  603. for i, param_decl in enumerate(decl["params"]):
  604. if i > 0:
  605. s += ", "
  606. arg_name = param_decl["name"]
  607. arg_type = param_decl["type"]
  608. if util.is_string_ptr(arg_type):
  609. s += f"tmp_{i}.as_ptr()"
  610. else:
  611. s += arg_name
  612. if is_rust_string(rust_res_type):
  613. s += ")"
  614. s += ")"
  615. l(s)
  616. l(" }")
  617. l("}")
  618. def pre_parse(inp):
  619. global struct_types
  620. global enum_types
  621. for decl in inp["decls"]:
  622. kind = decl["kind"]
  623. if kind == "struct":
  624. struct_types.append(decl["name"])
  625. elif kind == "enum":
  626. enum_name = decl["name"]
  627. enum_types.append(enum_name)
  628. enum_items[enum_name] = []
  629. for item in decl["items"]:
  630. enum_items[enum_name].append(as_enum_item_name(item["name"]))
  631. def gen_imports(inp, dep_prefixes):
  632. for dep_prefix in dep_prefixes:
  633. dep_module_name = module_names[dep_prefix]
  634. # l(f'const {dep_prefix[:-1]} = @import("{dep_module_name}.rs");')
  635. l(f'use crate::{dep_module_name} as {dep_prefix[:-1]};')
  636. l("")
  637. def gen_helpers(inp):
  638. l("/// Helper function to convert a C string to a rust string slice")
  639. l("#[inline]")
  640. l("fn c_char_ptr_to_rust_str(c_char_ptr: *const core::ffi::c_char) -> &'static str {")
  641. l(" let c_str = unsafe { core::ffi::CStr::from_ptr(c_char_ptr) };")
  642. l(" c_str.to_str().expect(\"c_char_ptr contained invalid Utf8 Data\")")
  643. l("}")
  644. l("")
  645. if inp['prefix'] in ['sg_', 'sdtx_', 'sshape_', 'sapp_']:
  646. l("/// Helper function to cast a rust slice into a sokol Range")
  647. l(f"pub fn slice_as_range<T>(data: &[T]) -> {range_struct_name} {{")
  648. l(f" {range_struct_name} {{ size: data.len() * std::mem::size_of::<T>(), ptr: data.as_ptr() as *const _ }}")
  649. l("}")
  650. l("/// Helper function to cast a rust reference into a sokol Range")
  651. l(f"pub fn value_as_range<T>(value: &T) -> {range_struct_name} {{")
  652. l(f" {range_struct_name} {{ size: std::mem::size_of::<T>(), ptr: value as *const T as *const _ }}")
  653. l("}")
  654. l("")
  655. l(f"impl<T> From<&[T]> for {range_struct_name} {{")
  656. l(" #[inline]")
  657. l(" fn from(data: &[T]) -> Self {")
  658. l(" slice_as_range(data)")
  659. l(" }")
  660. l("}")
  661. l(f"impl<T> From<&T> for {range_struct_name} {{")
  662. l(" #[inline]")
  663. l(" fn from(value: &T) -> Self {")
  664. l(" value_as_range(value)")
  665. l(" }")
  666. l("}")
  667. l("")
  668. # if inp["prefix"] == "sdtx_":
  669. # l("/// std.fmt compatible Writer")
  670. # l("pub const Writer = struct {")
  671. # l(" pub const Error = error { };")
  672. # l(" pub fn writeAll(self: Writer, bytes: []const u8) Error!void {")
  673. # l(" _ = self;")
  674. # l(" for (bytes) |byte| {")
  675. # l(" putc(byte);")
  676. # l(" }")
  677. # l(" }")
  678. # l(" pub fn writeByteNTimes(self: Writer, byte: u8, n: u64) Error!void {")
  679. # l(" _ = self;")
  680. # l(" var i: u64 = 0;")
  681. # l(" while (i < n): (i += 1) {")
  682. # l(" putc(byte);")
  683. # l(" }")
  684. # l(" }")
  685. # l("};")
  686. # l("// std.fmt-style formatted print")
  687. # l("pub fn print(comptime fmt: anytype, args: anytype) void {")
  688. # l(" var writer: Writer = .{};")
  689. # l(' @import("std").fmt.format(writer, fmt, args) catch {};')
  690. # l("}")
  691. # l("")
  692. def gen_module(inp, dep_prefixes):
  693. module = inp['module']
  694. if module in module_requires_rust_feature:
  695. feature = module_requires_rust_feature[module]
  696. l(f"//! To use this module, enable the feature \"{feature}\"")
  697. l("// machine generated, do not edit")
  698. l("")
  699. l("#![allow(dead_code)]")
  700. l("#![allow(unused_imports)]")
  701. l("")
  702. gen_imports(inp, dep_prefixes)
  703. gen_helpers(inp)
  704. pre_parse(inp)
  705. prefix = inp["prefix"]
  706. funcs = []
  707. for decl in inp["decls"]:
  708. #
  709. # HACK: gen_ir.py accidentally marks all sg_imgui_ declarations as is_dep since sg_imgui
  710. # depends on sg_a but also starts with sg_... Fix gen_ir.py to remove this hack
  711. #
  712. dep_hack = False
  713. if module == "gfx_imgui":
  714. dep_hack = "name" in decl and decl["name"].startswith("sg_imgui_")
  715. if not decl["is_dep"] or dep_hack:
  716. kind = decl["kind"]
  717. if kind == "consts":
  718. gen_consts(decl, prefix)
  719. elif not check_ignore(decl["name"]):
  720. if kind == "struct":
  721. gen_struct(decl, prefix)
  722. elif kind == "enum":
  723. gen_enum(decl, prefix)
  724. elif kind == "func":
  725. funcs.append((decl, prefix))
  726. gen_c_funcs(funcs)
  727. gen_rust_funcs(funcs)
  728. def prepare():
  729. print("=== Generating Rust bindings:")
  730. if not os.path.isdir("sokol-rust/src/sokol"):
  731. os.makedirs("sokol-rust/src/sokol")
  732. if not os.path.isdir("sokol-rust/src/sokol/c"):
  733. os.makedirs("sokol-rust/src/sokol/c")
  734. with open("sokol-rust/src/lib.rs", "w", newline="\n") as f_outp:
  735. f_outp.write("//! Automatically generated sokol bindings for Rust\n\n")
  736. def gen(c_header_path, c_prefix, dep_c_prefixes):
  737. if c_prefix not in module_names:
  738. print(f' >> warning: skipping generation for {c_prefix} prefix...')
  739. return
  740. module_name = module_names[c_prefix]
  741. c_source_path = c_source_paths[c_prefix]
  742. print(f' {c_header_path} => {module_name}')
  743. reset_globals()
  744. c_path_in_project = f'sokol-rust/src/sokol/c/{os.path.basename(c_header_path)}'
  745. shutil.copyfile(c_header_path, c_path_in_project)
  746. ir = gen_ir.gen(c_header_path, c_source_path, module_name, c_prefix, dep_c_prefixes)
  747. gen_module(ir, dep_c_prefixes)
  748. output_path = f"sokol-rust/src/{ir['module']}.rs"
  749. with open(output_path, 'w', newline='\n') as f_outp:
  750. f_outp.write(out_lines)
  751. with open("sokol-rust/src/lib.rs", "a", newline="\n") as f_outp:
  752. module = ir['module']
  753. if module in module_requires_rust_feature:
  754. feature = module_requires_rust_feature[module]
  755. f_outp.write(f"/// Enable feature \"{feature}\" to use\n")
  756. f_outp.write(f"#[cfg(feature=\"{feature}\")]\n")
  757. f_outp.write(f"pub mod {module};\n")