tigerbeetle 0.0.40 → 0.17.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (293) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +0 -25
  3. data/README.md +670 -80
  4. data/docs/migration.md +201 -0
  5. data/sig/tigerbeetle.rbs +271 -0
  6. data/src/ext/tigerbeetle/extconf.rb +47 -0
  7. data/src/ext/tigerbeetle/lib/aarch64-linux-gnu.2.27/libtb_client.so +0 -0
  8. data/src/ext/tigerbeetle/lib/aarch64-linux-musl/libtb_client.so +0 -0
  9. data/src/ext/tigerbeetle/lib/aarch64-macos/libtb_client.dylib +0 -0
  10. data/src/ext/tigerbeetle/lib/x86_64-linux-gnu.2.27/libtb_client.so +0 -0
  11. data/src/ext/tigerbeetle/lib/x86_64-linux-musl/libtb_client.so +0 -0
  12. data/src/ext/tigerbeetle/lib/x86_64-macos/libtb_client.dylib +0 -0
  13. data/src/ext/tigerbeetle/lib/x86_64-windows/tb_client.dll +0 -0
  14. data/src/ext/tigerbeetle/rb_tb_gen.h +458 -0
  15. data/{ext/tb_client/tigerbeetle/src/clients/rust/assets → src/ext/tigerbeetle}/tb_client.h +18 -16
  16. data/src/ext/tigerbeetle/tigerbeetle.c +310 -0
  17. data/src/tigerbeetle/bindings.rb +347 -0
  18. data/src/tigerbeetle/client.rb +129 -0
  19. data/src/tigerbeetle/completion_dispatcher.rb +108 -0
  20. data/src/tigerbeetle/id.rb +40 -0
  21. data/src/tigerbeetle/tb.rb +3 -0
  22. data/src/tigerbeetle/version.rb +3 -0
  23. data/src/tigerbeetle.rb +39 -0
  24. metadata +33 -350
  25. data/CHANGELOG.md +0 -162
  26. data/ext/tb_client/extconf.rb +0 -41
  27. data/ext/tb_client/tigerbeetle/LICENSE +0 -177
  28. data/ext/tb_client/tigerbeetle/build.zig +0 -2296
  29. data/ext/tb_client/tigerbeetle/src/aof.zig +0 -1000
  30. data/ext/tb_client/tigerbeetle/src/build/fetch.zig +0 -112
  31. data/ext/tb_client/tigerbeetle/src/build_multiversion.zig +0 -808
  32. data/ext/tb_client/tigerbeetle/src/cdc/amqp/protocol.zig +0 -1283
  33. data/ext/tb_client/tigerbeetle/src/cdc/amqp/spec.zig +0 -1704
  34. data/ext/tb_client/tigerbeetle/src/cdc/amqp/types.zig +0 -341
  35. data/ext/tb_client/tigerbeetle/src/cdc/amqp.zig +0 -1450
  36. data/ext/tb_client/tigerbeetle/src/cdc/runner.zig +0 -1659
  37. data/ext/tb_client/tigerbeetle/src/clients/c/samples/main.c +0 -406
  38. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/context.zig +0 -1092
  39. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/echo_client.zig +0 -286
  40. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/packet.zig +0 -158
  41. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/signal.zig +0 -229
  42. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/signal_fuzz.zig +0 -110
  43. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client.h +0 -386
  44. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client.zig +0 -34
  45. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_exports.zig +0 -281
  46. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_header.zig +0 -312
  47. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_header_test.zig +0 -138
  48. data/ext/tb_client/tigerbeetle/src/clients/c/test.zig +0 -466
  49. data/ext/tb_client/tigerbeetle/src/clients/docs_samples.zig +0 -157
  50. data/ext/tb_client/tigerbeetle/src/clients/docs_types.zig +0 -90
  51. data/ext/tb_client/tigerbeetle/src/clients/dotnet/ci.zig +0 -203
  52. data/ext/tb_client/tigerbeetle/src/clients/dotnet/docs.zig +0 -79
  53. data/ext/tb_client/tigerbeetle/src/clients/dotnet/dotnet_bindings.zig +0 -542
  54. data/ext/tb_client/tigerbeetle/src/clients/go/ci.zig +0 -109
  55. data/ext/tb_client/tigerbeetle/src/clients/go/docs.zig +0 -86
  56. data/ext/tb_client/tigerbeetle/src/clients/go/go_bindings.zig +0 -370
  57. data/ext/tb_client/tigerbeetle/src/clients/go/pkg/native/tb_client.h +0 -386
  58. data/ext/tb_client/tigerbeetle/src/clients/java/ci.zig +0 -167
  59. data/ext/tb_client/tigerbeetle/src/clients/java/docs.zig +0 -126
  60. data/ext/tb_client/tigerbeetle/src/clients/java/java_bindings.zig +0 -996
  61. data/ext/tb_client/tigerbeetle/src/clients/java/src/client.zig +0 -748
  62. data/ext/tb_client/tigerbeetle/src/clients/java/src/jni.zig +0 -3238
  63. data/ext/tb_client/tigerbeetle/src/clients/java/src/jni_tests.zig +0 -1718
  64. data/ext/tb_client/tigerbeetle/src/clients/java/src/jni_thread_cleaner.zig +0 -190
  65. data/ext/tb_client/tigerbeetle/src/clients/node/ci.zig +0 -104
  66. data/ext/tb_client/tigerbeetle/src/clients/node/docs.zig +0 -75
  67. data/ext/tb_client/tigerbeetle/src/clients/node/node.zig +0 -522
  68. data/ext/tb_client/tigerbeetle/src/clients/node/node_bindings.zig +0 -267
  69. data/ext/tb_client/tigerbeetle/src/clients/node/src/c.zig +0 -3
  70. data/ext/tb_client/tigerbeetle/src/clients/node/src/translate.zig +0 -379
  71. data/ext/tb_client/tigerbeetle/src/clients/python/ci.zig +0 -131
  72. data/ext/tb_client/tigerbeetle/src/clients/python/docs.zig +0 -63
  73. data/ext/tb_client/tigerbeetle/src/clients/python/python_bindings.zig +0 -588
  74. data/ext/tb_client/tigerbeetle/src/clients/rust/ci.zig +0 -73
  75. data/ext/tb_client/tigerbeetle/src/clients/rust/docs.zig +0 -106
  76. data/ext/tb_client/tigerbeetle/src/clients/rust/rust_bindings.zig +0 -305
  77. data/ext/tb_client/tigerbeetle/src/config.zig +0 -296
  78. data/ext/tb_client/tigerbeetle/src/constants.zig +0 -790
  79. data/ext/tb_client/tigerbeetle/src/copyhound.zig +0 -202
  80. data/ext/tb_client/tigerbeetle/src/counting_allocator.zig +0 -72
  81. data/ext/tb_client/tigerbeetle/src/direction.zig +0 -120
  82. data/ext/tb_client/tigerbeetle/src/docs_website/build.zig +0 -158
  83. data/ext/tb_client/tigerbeetle/src/docs_website/src/content.zig +0 -156
  84. data/ext/tb_client/tigerbeetle/src/docs_website/src/docs.zig +0 -252
  85. data/ext/tb_client/tigerbeetle/src/docs_website/src/file_checker.zig +0 -313
  86. data/ext/tb_client/tigerbeetle/src/docs_website/src/html.zig +0 -87
  87. data/ext/tb_client/tigerbeetle/src/docs_website/src/page_writer.zig +0 -63
  88. data/ext/tb_client/tigerbeetle/src/docs_website/src/redirects.zig +0 -47
  89. data/ext/tb_client/tigerbeetle/src/docs_website/src/search_index_writer.zig +0 -28
  90. data/ext/tb_client/tigerbeetle/src/docs_website/src/service_worker_writer.zig +0 -61
  91. data/ext/tb_client/tigerbeetle/src/docs_website/src/single_page_writer.zig +0 -169
  92. data/ext/tb_client/tigerbeetle/src/docs_website/src/website.zig +0 -46
  93. data/ext/tb_client/tigerbeetle/src/ewah.zig +0 -445
  94. data/ext/tb_client/tigerbeetle/src/ewah_benchmark.zig +0 -128
  95. data/ext/tb_client/tigerbeetle/src/ewah_fuzz.zig +0 -171
  96. data/ext/tb_client/tigerbeetle/src/fuzz_tests.zig +0 -179
  97. data/ext/tb_client/tigerbeetle/src/integration_tests.zig +0 -662
  98. data/ext/tb_client/tigerbeetle/src/io/common.zig +0 -155
  99. data/ext/tb_client/tigerbeetle/src/io/darwin.zig +0 -1093
  100. data/ext/tb_client/tigerbeetle/src/io/linux.zig +0 -1880
  101. data/ext/tb_client/tigerbeetle/src/io/test.zig +0 -1005
  102. data/ext/tb_client/tigerbeetle/src/io/windows.zig +0 -1598
  103. data/ext/tb_client/tigerbeetle/src/io.zig +0 -34
  104. data/ext/tb_client/tigerbeetle/src/iops.zig +0 -134
  105. data/ext/tb_client/tigerbeetle/src/list.zig +0 -236
  106. data/ext/tb_client/tigerbeetle/src/lsm/binary_search.zig +0 -848
  107. data/ext/tb_client/tigerbeetle/src/lsm/binary_search_benchmark.zig +0 -179
  108. data/ext/tb_client/tigerbeetle/src/lsm/cache_map.zig +0 -424
  109. data/ext/tb_client/tigerbeetle/src/lsm/cache_map_fuzz.zig +0 -420
  110. data/ext/tb_client/tigerbeetle/src/lsm/compaction.zig +0 -2114
  111. data/ext/tb_client/tigerbeetle/src/lsm/composite_key.zig +0 -185
  112. data/ext/tb_client/tigerbeetle/src/lsm/forest.zig +0 -1146
  113. data/ext/tb_client/tigerbeetle/src/lsm/forest_fuzz.zig +0 -1102
  114. data/ext/tb_client/tigerbeetle/src/lsm/forest_table_iterator.zig +0 -200
  115. data/ext/tb_client/tigerbeetle/src/lsm/groove.zig +0 -1495
  116. data/ext/tb_client/tigerbeetle/src/lsm/k_way_merge.zig +0 -739
  117. data/ext/tb_client/tigerbeetle/src/lsm/k_way_merge_benchmark.zig +0 -166
  118. data/ext/tb_client/tigerbeetle/src/lsm/manifest.zig +0 -754
  119. data/ext/tb_client/tigerbeetle/src/lsm/manifest_level.zig +0 -1294
  120. data/ext/tb_client/tigerbeetle/src/lsm/manifest_level_fuzz.zig +0 -510
  121. data/ext/tb_client/tigerbeetle/src/lsm/manifest_log.zig +0 -1241
  122. data/ext/tb_client/tigerbeetle/src/lsm/manifest_log_fuzz.zig +0 -628
  123. data/ext/tb_client/tigerbeetle/src/lsm/node_pool.zig +0 -247
  124. data/ext/tb_client/tigerbeetle/src/lsm/scan_buffer.zig +0 -116
  125. data/ext/tb_client/tigerbeetle/src/lsm/scan_builder.zig +0 -543
  126. data/ext/tb_client/tigerbeetle/src/lsm/scan_fuzz.zig +0 -938
  127. data/ext/tb_client/tigerbeetle/src/lsm/scan_lookup.zig +0 -293
  128. data/ext/tb_client/tigerbeetle/src/lsm/scan_merge.zig +0 -359
  129. data/ext/tb_client/tigerbeetle/src/lsm/scan_range.zig +0 -99
  130. data/ext/tb_client/tigerbeetle/src/lsm/scan_state.zig +0 -17
  131. data/ext/tb_client/tigerbeetle/src/lsm/scan_tree.zig +0 -962
  132. data/ext/tb_client/tigerbeetle/src/lsm/schema.zig +0 -617
  133. data/ext/tb_client/tigerbeetle/src/lsm/scratch_memory.zig +0 -84
  134. data/ext/tb_client/tigerbeetle/src/lsm/segmented_array.zig +0 -1500
  135. data/ext/tb_client/tigerbeetle/src/lsm/segmented_array_benchmark.zig +0 -149
  136. data/ext/tb_client/tigerbeetle/src/lsm/segmented_array_fuzz.zig +0 -7
  137. data/ext/tb_client/tigerbeetle/src/lsm/set_associative_cache.zig +0 -865
  138. data/ext/tb_client/tigerbeetle/src/lsm/table.zig +0 -607
  139. data/ext/tb_client/tigerbeetle/src/lsm/table_memory.zig +0 -843
  140. data/ext/tb_client/tigerbeetle/src/lsm/table_value_iterator.zig +0 -90
  141. data/ext/tb_client/tigerbeetle/src/lsm/timestamp_range.zig +0 -40
  142. data/ext/tb_client/tigerbeetle/src/lsm/tree.zig +0 -629
  143. data/ext/tb_client/tigerbeetle/src/lsm/tree_fuzz.zig +0 -933
  144. data/ext/tb_client/tigerbeetle/src/lsm/zig_zag_merge.zig +0 -534
  145. data/ext/tb_client/tigerbeetle/src/message_buffer.zig +0 -469
  146. data/ext/tb_client/tigerbeetle/src/message_bus.zig +0 -1219
  147. data/ext/tb_client/tigerbeetle/src/message_bus_fuzz.zig +0 -936
  148. data/ext/tb_client/tigerbeetle/src/message_pool.zig +0 -343
  149. data/ext/tb_client/tigerbeetle/src/multiversion.zig +0 -2195
  150. data/ext/tb_client/tigerbeetle/src/queue.zig +0 -390
  151. data/ext/tb_client/tigerbeetle/src/repl/completion.zig +0 -201
  152. data/ext/tb_client/tigerbeetle/src/repl/parser.zig +0 -1356
  153. data/ext/tb_client/tigerbeetle/src/repl/terminal.zig +0 -496
  154. data/ext/tb_client/tigerbeetle/src/repl.zig +0 -1034
  155. data/ext/tb_client/tigerbeetle/src/scripts/amqp.zig +0 -973
  156. data/ext/tb_client/tigerbeetle/src/scripts/cfo.zig +0 -1866
  157. data/ext/tb_client/tigerbeetle/src/scripts/changelog.zig +0 -304
  158. data/ext/tb_client/tigerbeetle/src/scripts/ci.zig +0 -227
  159. data/ext/tb_client/tigerbeetle/src/scripts/client_readmes.zig +0 -658
  160. data/ext/tb_client/tigerbeetle/src/scripts/devhub.zig +0 -466
  161. data/ext/tb_client/tigerbeetle/src/scripts/release.zig +0 -1058
  162. data/ext/tb_client/tigerbeetle/src/scripts.zig +0 -105
  163. data/ext/tb_client/tigerbeetle/src/shell.zig +0 -1195
  164. data/ext/tb_client/tigerbeetle/src/stack.zig +0 -260
  165. data/ext/tb_client/tigerbeetle/src/state_machine/auditor.zig +0 -911
  166. data/ext/tb_client/tigerbeetle/src/state_machine/workload.zig +0 -2079
  167. data/ext/tb_client/tigerbeetle/src/state_machine.zig +0 -4872
  168. data/ext/tb_client/tigerbeetle/src/state_machine_fuzz.zig +0 -288
  169. data/ext/tb_client/tigerbeetle/src/state_machine_tests.zig +0 -3128
  170. data/ext/tb_client/tigerbeetle/src/static_allocator.zig +0 -82
  171. data/ext/tb_client/tigerbeetle/src/stdx/bit_set.zig +0 -157
  172. data/ext/tb_client/tigerbeetle/src/stdx/bounded_array.zig +0 -292
  173. data/ext/tb_client/tigerbeetle/src/stdx/debug.zig +0 -65
  174. data/ext/tb_client/tigerbeetle/src/stdx/flags.zig +0 -1414
  175. data/ext/tb_client/tigerbeetle/src/stdx/huge_page_allocator.zig +0 -115
  176. data/ext/tb_client/tigerbeetle/src/stdx/mlock.zig +0 -92
  177. data/ext/tb_client/tigerbeetle/src/stdx/prng.zig +0 -677
  178. data/ext/tb_client/tigerbeetle/src/stdx/radix.zig +0 -336
  179. data/ext/tb_client/tigerbeetle/src/stdx/ring_buffer.zig +0 -511
  180. data/ext/tb_client/tigerbeetle/src/stdx/sort_test.zig +0 -112
  181. data/ext/tb_client/tigerbeetle/src/stdx/stdx.zig +0 -1163
  182. data/ext/tb_client/tigerbeetle/src/stdx/testing/low_level_hash_vectors.zig +0 -142
  183. data/ext/tb_client/tigerbeetle/src/stdx/testing/snaptest.zig +0 -361
  184. data/ext/tb_client/tigerbeetle/src/stdx/time_units.zig +0 -275
  185. data/ext/tb_client/tigerbeetle/src/stdx/unshare.zig +0 -295
  186. data/ext/tb_client/tigerbeetle/src/stdx/vendored/aegis.zig +0 -436
  187. data/ext/tb_client/tigerbeetle/src/stdx/windows.zig +0 -48
  188. data/ext/tb_client/tigerbeetle/src/stdx/zipfian.zig +0 -402
  189. data/ext/tb_client/tigerbeetle/src/storage.zig +0 -489
  190. data/ext/tb_client/tigerbeetle/src/storage_fuzz.zig +0 -180
  191. data/ext/tb_client/tigerbeetle/src/testing/bench.zig +0 -146
  192. data/ext/tb_client/tigerbeetle/src/testing/cluster/grid_checker.zig +0 -53
  193. data/ext/tb_client/tigerbeetle/src/testing/cluster/journal_checker.zig +0 -61
  194. data/ext/tb_client/tigerbeetle/src/testing/cluster/manifest_checker.zig +0 -76
  195. data/ext/tb_client/tigerbeetle/src/testing/cluster/message_bus.zig +0 -110
  196. data/ext/tb_client/tigerbeetle/src/testing/cluster/network.zig +0 -412
  197. data/ext/tb_client/tigerbeetle/src/testing/cluster/state_checker.zig +0 -331
  198. data/ext/tb_client/tigerbeetle/src/testing/cluster/storage_checker.zig +0 -458
  199. data/ext/tb_client/tigerbeetle/src/testing/cluster.zig +0 -1198
  200. data/ext/tb_client/tigerbeetle/src/testing/exhaustigen.zig +0 -128
  201. data/ext/tb_client/tigerbeetle/src/testing/fixtures.zig +0 -181
  202. data/ext/tb_client/tigerbeetle/src/testing/fuzz.zig +0 -144
  203. data/ext/tb_client/tigerbeetle/src/testing/id.zig +0 -97
  204. data/ext/tb_client/tigerbeetle/src/testing/io.zig +0 -317
  205. data/ext/tb_client/tigerbeetle/src/testing/marks.zig +0 -126
  206. data/ext/tb_client/tigerbeetle/src/testing/packet_simulator.zig +0 -533
  207. data/ext/tb_client/tigerbeetle/src/testing/reply_sequence.zig +0 -154
  208. data/ext/tb_client/tigerbeetle/src/testing/state_machine.zig +0 -389
  209. data/ext/tb_client/tigerbeetle/src/testing/storage.zig +0 -1247
  210. data/ext/tb_client/tigerbeetle/src/testing/table.zig +0 -249
  211. data/ext/tb_client/tigerbeetle/src/testing/time.zig +0 -98
  212. data/ext/tb_client/tigerbeetle/src/testing/tmp_tigerbeetle.zig +0 -212
  213. data/ext/tb_client/tigerbeetle/src/testing/vortex/constants.zig +0 -26
  214. data/ext/tb_client/tigerbeetle/src/testing/vortex/faulty_network.zig +0 -579
  215. data/ext/tb_client/tigerbeetle/src/testing/vortex/java_driver/ci.zig +0 -39
  216. data/ext/tb_client/tigerbeetle/src/testing/vortex/logged_process.zig +0 -214
  217. data/ext/tb_client/tigerbeetle/src/testing/vortex/rust_driver/ci.zig +0 -34
  218. data/ext/tb_client/tigerbeetle/src/testing/vortex/supervisor.zig +0 -785
  219. data/ext/tb_client/tigerbeetle/src/testing/vortex/workload.zig +0 -543
  220. data/ext/tb_client/tigerbeetle/src/testing/vortex/zig_driver.zig +0 -181
  221. data/ext/tb_client/tigerbeetle/src/tidy.zig +0 -1449
  222. data/ext/tb_client/tigerbeetle/src/tigerbeetle/benchmark_driver.zig +0 -227
  223. data/ext/tb_client/tigerbeetle/src/tigerbeetle/benchmark_load.zig +0 -1069
  224. data/ext/tb_client/tigerbeetle/src/tigerbeetle/cli.zig +0 -1422
  225. data/ext/tb_client/tigerbeetle/src/tigerbeetle/inspect.zig +0 -1658
  226. data/ext/tb_client/tigerbeetle/src/tigerbeetle/inspect_integrity.zig +0 -518
  227. data/ext/tb_client/tigerbeetle/src/tigerbeetle/libtb_client.zig +0 -36
  228. data/ext/tb_client/tigerbeetle/src/tigerbeetle/main.zig +0 -646
  229. data/ext/tb_client/tigerbeetle/src/tigerbeetle.zig +0 -958
  230. data/ext/tb_client/tigerbeetle/src/time.zig +0 -236
  231. data/ext/tb_client/tigerbeetle/src/trace/event.zig +0 -745
  232. data/ext/tb_client/tigerbeetle/src/trace/statsd.zig +0 -462
  233. data/ext/tb_client/tigerbeetle/src/trace.zig +0 -556
  234. data/ext/tb_client/tigerbeetle/src/unit_tests.zig +0 -321
  235. data/ext/tb_client/tigerbeetle/src/vopr.zig +0 -1785
  236. data/ext/tb_client/tigerbeetle/src/vortex.zig +0 -101
  237. data/ext/tb_client/tigerbeetle/src/vsr/checkpoint_trailer.zig +0 -473
  238. data/ext/tb_client/tigerbeetle/src/vsr/checksum.zig +0 -208
  239. data/ext/tb_client/tigerbeetle/src/vsr/checksum_benchmark.zig +0 -43
  240. data/ext/tb_client/tigerbeetle/src/vsr/client.zig +0 -768
  241. data/ext/tb_client/tigerbeetle/src/vsr/client_replies.zig +0 -532
  242. data/ext/tb_client/tigerbeetle/src/vsr/client_sessions.zig +0 -338
  243. data/ext/tb_client/tigerbeetle/src/vsr/clock.zig +0 -1019
  244. data/ext/tb_client/tigerbeetle/src/vsr/fault_detector.zig +0 -279
  245. data/ext/tb_client/tigerbeetle/src/vsr/free_set.zig +0 -1381
  246. data/ext/tb_client/tigerbeetle/src/vsr/free_set_fuzz.zig +0 -315
  247. data/ext/tb_client/tigerbeetle/src/vsr/grid.zig +0 -1460
  248. data/ext/tb_client/tigerbeetle/src/vsr/grid_blocks_missing.zig +0 -757
  249. data/ext/tb_client/tigerbeetle/src/vsr/grid_scrubber.zig +0 -797
  250. data/ext/tb_client/tigerbeetle/src/vsr/journal.zig +0 -2586
  251. data/ext/tb_client/tigerbeetle/src/vsr/marzullo.zig +0 -308
  252. data/ext/tb_client/tigerbeetle/src/vsr/message_header.zig +0 -1777
  253. data/ext/tb_client/tigerbeetle/src/vsr/multi_batch.zig +0 -715
  254. data/ext/tb_client/tigerbeetle/src/vsr/multi_batch_fuzz.zig +0 -185
  255. data/ext/tb_client/tigerbeetle/src/vsr/repair_budget.zig +0 -333
  256. data/ext/tb_client/tigerbeetle/src/vsr/replica.zig +0 -12356
  257. data/ext/tb_client/tigerbeetle/src/vsr/replica_format.zig +0 -416
  258. data/ext/tb_client/tigerbeetle/src/vsr/replica_reformat.zig +0 -165
  259. data/ext/tb_client/tigerbeetle/src/vsr/replica_test.zig +0 -2928
  260. data/ext/tb_client/tigerbeetle/src/vsr/routing.zig +0 -1075
  261. data/ext/tb_client/tigerbeetle/src/vsr/superblock.zig +0 -1603
  262. data/ext/tb_client/tigerbeetle/src/vsr/superblock_fuzz.zig +0 -484
  263. data/ext/tb_client/tigerbeetle/src/vsr/superblock_quorums.zig +0 -405
  264. data/ext/tb_client/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +0 -355
  265. data/ext/tb_client/tigerbeetle/src/vsr/sync.zig +0 -29
  266. data/ext/tb_client/tigerbeetle/src/vsr.zig +0 -1727
  267. data/lib/tb_client/shared_lib.rb +0 -66
  268. data/lib/tb_client.rb +0 -282
  269. data/lib/tigerbeetle/account.rb +0 -38
  270. data/lib/tigerbeetle/account_balance.rb +0 -23
  271. data/lib/tigerbeetle/account_filter.rb +0 -31
  272. data/lib/tigerbeetle/atomic_counter.rb +0 -14
  273. data/lib/tigerbeetle/client.rb +0 -214
  274. data/lib/tigerbeetle/converters/account.rb +0 -63
  275. data/lib/tigerbeetle/converters/account_balance.rb +0 -31
  276. data/lib/tigerbeetle/converters/account_filter.rb +0 -32
  277. data/lib/tigerbeetle/converters/base.rb +0 -35
  278. data/lib/tigerbeetle/converters/create_accounts_result.rb +0 -21
  279. data/lib/tigerbeetle/converters/create_transfers_result.rb +0 -21
  280. data/lib/tigerbeetle/converters/query_filter.rb +0 -33
  281. data/lib/tigerbeetle/converters/time.rb +0 -23
  282. data/lib/tigerbeetle/converters/transfer.rb +0 -64
  283. data/lib/tigerbeetle/converters/uint_128.rb +0 -24
  284. data/lib/tigerbeetle/converters.rb +0 -12
  285. data/lib/tigerbeetle/error.rb +0 -4
  286. data/lib/tigerbeetle/id.rb +0 -30
  287. data/lib/tigerbeetle/platforms.rb +0 -9
  288. data/lib/tigerbeetle/query_filter.rb +0 -31
  289. data/lib/tigerbeetle/request.rb +0 -7
  290. data/lib/tigerbeetle/transfer.rb +0 -40
  291. data/lib/tigerbeetle/version.rb +0 -4
  292. data/lib/tigerbeetle.rb +0 -13
  293. data/tigerbeetle.gemspec +0 -60
@@ -1,1449 +0,0 @@
1
- //! Checks for various non-functional properties of the code itself.
2
-
3
- const std = @import("std");
4
- const Allocator = std.mem.Allocator;
5
- const assert = std.debug.assert;
6
- const fs = std.fs;
7
- const mem = std.mem;
8
- const Ast = std.zig.Ast;
9
-
10
- const stdx = @import("stdx");
11
- const Shell = @import("./shell.zig");
12
-
13
- const Snap = stdx.Snap;
14
- const module_path = "src";
15
- const snap = Snap.snap_fn(module_path);
16
-
17
- const MiB = stdx.MiB;
18
-
19
- test "tidy" {
20
- const gpa = std.testing.allocator;
21
-
22
- var errors: Errors = .{};
23
-
24
- const shell = try Shell.create(gpa);
25
- defer shell.destroy();
26
-
27
- var counter: IdentifierCounter = try .init(gpa);
28
- defer counter.deinit(gpa);
29
-
30
- var dead_files_detector = DeadFilesDetector.init(gpa);
31
- defer dead_files_detector.deinit(gpa);
32
-
33
- // NB: all checks are intentionally implemented in a streaming fashion,
34
- // such that we only need to read the files once.
35
- const file_buffer = try gpa.alloc(u8, 1 * MiB);
36
- defer gpa.free(file_buffer);
37
-
38
- const paths = try list_file_paths(shell);
39
- for (paths) |file_path| {
40
- const source_file = try SourceFile.read(file_path, file_buffer);
41
- try tidy_file(gpa, &counter, source_file, &errors);
42
-
43
- if (source_file.has_extension(".zig")) {
44
- try dead_files_detector.visit(source_file);
45
- }
46
- }
47
-
48
- dead_files_detector.finish(&errors);
49
-
50
- if (errors.count > 0) return error.Untidy;
51
- assert(errors.count == 0);
52
- }
53
-
54
- const Errors = struct {
55
- count: u32 = 0,
56
- captured: ?std.ArrayListUnmanaged(u8) = null, // For tests.
57
-
58
- pub fn add_control_character(
59
- errors: *Errors,
60
- file: SourceFile,
61
- offset: usize,
62
- character: u8,
63
- ) void {
64
- errors.emit(
65
- "{s}:{d}: error: control character code={}\n",
66
- .{ file.path, file.line_number(offset), character },
67
- );
68
- }
69
-
70
- pub fn add_banned(
71
- errors: *Errors,
72
- file: SourceFile,
73
- offset: usize,
74
- banned_item: []const u8,
75
- replacement: []const u8,
76
- ) void {
77
- errors.emit(
78
- "{s}:{d}: error: {s} is banned, use {s}\n",
79
- .{ file.path, file.line_number(offset), banned_item, replacement },
80
- );
81
- }
82
-
83
- pub fn add_banned_reminder(
84
- errors: *Errors,
85
- file: SourceFile,
86
- offset: usize,
87
- banned_item: []const u8,
88
- ) void {
89
- errors.emit(
90
- "{s}:{d}: error: leftover {s}, remove before merge\n",
91
- .{ file.path, file.line_number(offset), banned_item },
92
- );
93
- }
94
-
95
- pub fn add_long_line(errors: *Errors, file: SourceFile, line_index: usize) void {
96
- const line_number = line_index + 1;
97
- errors.emit(
98
- "{s}:{d}: error: line exceeds 100 columns\n",
99
- .{ file.path, line_number },
100
- );
101
- }
102
-
103
- pub fn add_trailing_whitespace(errors: *Errors, file: SourceFile, line_index: usize) void {
104
- errors.emit(
105
- "{s}:{d}: error: trailing whitespace\n",
106
- .{ file.path, line_index + 1 },
107
- );
108
- }
109
-
110
- pub fn add_bad_type_function_name(
111
- errors: *Errors,
112
- file: SourceFile,
113
- line_index: usize,
114
- function_name: []const u8,
115
- ) void {
116
- const line_number = line_index + 1;
117
- errors.emit(
118
- "{s}:{d}: error: type function name '{s}' should end in 'Type'\n",
119
- .{ file.path, line_number, function_name },
120
- );
121
- }
122
-
123
- pub fn add_long_function(errors: *Errors, file: SourceFile, line_index: usize) void {
124
- const line_number = line_index + 1;
125
- errors.emit(
126
- "{s}:{d}: error: functions exceeds 70 lines\n",
127
- .{ file.path, line_number },
128
- );
129
- }
130
-
131
- pub fn add_ambiguous_precedence(errors: *Errors, file: SourceFile, line_index: usize) void {
132
- const line_number = line_index + 1;
133
- errors.emit(
134
- "{s}:{d}: error: ambiguous operator precedence, add parenthesis\n",
135
- .{ file.path, line_number },
136
- );
137
- }
138
-
139
- pub fn add_dead_declaration(errors: *Errors, file: SourceFile, declaration: []const u8) void {
140
- errors.emit("{s}: error: '{s}' is dead code\n", .{ file.path, declaration });
141
- }
142
-
143
- pub fn add_defer_newline(errors: *Errors, file: SourceFile, line_index: usize) void {
144
- const line_number = line_index + 1;
145
- errors.emit(
146
- "{s}:{d}: error: defer must be followed by a blank line\n",
147
- .{ file.path, line_number },
148
- );
149
- }
150
-
151
- pub fn add_invalid_markdown_title(errors: *Errors, file: SourceFile) void {
152
- errors.emit(
153
- "{s}: error: document should have exactly one top-level '# Title'\n",
154
- .{file.path},
155
- );
156
- }
157
-
158
- pub fn add_file_untracked(errors: *Errors, file: []const u8) void {
159
- errors.emit(
160
- "{s}: error: imported file untracked by git\n",
161
- .{file},
162
- );
163
- }
164
-
165
- pub fn add_file_dead(errors: *Errors, file: []const u8) void {
166
- errors.emit(
167
- "{s}: error: file never imported\n",
168
- .{file},
169
- );
170
- }
171
-
172
- pub fn add_tracking(errors: *Errors, file: SourceFile, line_index: usize) void {
173
- errors.emit(
174
- "{s}:{d}: error: remove '?si=...' tracking parameter from URL\n",
175
- .{ file.path, line_index },
176
- );
177
- }
178
-
179
- fn emit(errors: *Errors, comptime fmt: []const u8, args: anytype) void {
180
- comptime assert(fmt[fmt.len - 1] == '\n');
181
- errors.count += 1;
182
- if (errors.captured) |*captured| {
183
- captured.writer(std.testing.allocator).print(fmt, args) catch @panic("OOM");
184
- } else {
185
- std.debug.print(fmt, args);
186
- }
187
- }
188
- };
189
-
190
- const SourceFile = struct {
191
- path: []const u8,
192
- text: [:0]const u8,
193
-
194
- // NB: The return value borrows both path and buffer.
195
- fn read(path: []const u8, buffer: []u8) !SourceFile {
196
- const bytes_read = (try std.fs.cwd().readFile(path, buffer)).len;
197
- if (bytes_read >= buffer.len - 1) return error.FileTooLong;
198
- buffer[bytes_read] = 0;
199
- return .{
200
- .path = path,
201
- .text = buffer[0..bytes_read :0],
202
- };
203
- }
204
-
205
- fn has_extension(file: SourceFile, extension: []const u8) bool {
206
- assert(extension.len > 0);
207
- assert(extension[0] == '.');
208
- return std.mem.endsWith(u8, file.path, extension);
209
- }
210
-
211
- // O(N), but only invoked on the cold path (when there are errors).
212
- fn line_number(file: SourceFile, offset: usize) usize {
213
- assert(offset <= file.text.len);
214
- // +1: Line _index_ is zero-based, line _number_ is one-based.
215
- return std.mem.count(u8, file.text[0..offset], "\n") + 1;
216
- }
217
- };
218
-
219
- fn tidy_file(
220
- gpa: Allocator,
221
- counter: *IdentifierCounter,
222
- file: SourceFile,
223
- errors: *Errors,
224
- ) Allocator.Error!void {
225
- tidy_control_characters(file, errors);
226
- if (file.has_extension(".zig")) {
227
- tidy_banned(file, errors);
228
- tidy_lines(file, errors);
229
- tidy_type_functions(file, errors);
230
-
231
- var tree = try std.zig.Ast.parse(gpa, file.text, .zig);
232
- defer tree.deinit(gpa);
233
-
234
- tidy_dead_declarations(file, &tree, counter, errors);
235
- tidy_ast(file, &tree, errors);
236
- }
237
- if (file.has_extension(".md")) {
238
- tidy_markdown_title(file, errors);
239
- }
240
- }
241
-
242
- fn check_tidy_file(file_path: []const u8, file_text: [:0]const u8, want: Snap) !void {
243
- const gpa = std.testing.allocator;
244
-
245
- var counter: IdentifierCounter = try .init(gpa);
246
- defer counter.deinit(gpa);
247
-
248
- var errors: Errors = .{ .captured = .{} };
249
- defer errors.captured.?.deinit(std.testing.allocator);
250
-
251
- try tidy_file(gpa, &counter, .{ .path = file_path, .text = file_text }, &errors);
252
- const got = errors.captured.?.items;
253
-
254
- try want.diff(got);
255
- assert(errors.count == std.mem.count(u8, got, "\n"));
256
- }
257
-
258
- fn tidy_control_characters(file: SourceFile, errors: *Errors) void {
259
- const binary_file_extensions: []const []const u8 = &.{ ".ico", ".png", ".webp" };
260
- for (binary_file_extensions) |extension| {
261
- if (file.has_extension(extension)) return;
262
- }
263
-
264
- const allowed = .{
265
- .@"\r" = file.has_extension(".bat"),
266
-
267
- // Visual Studio insists on \t, taking the best from `make`.
268
- // Go uses tabs.
269
- .@"\t" = file.has_extension(".sln") or
270
- (file.has_extension(".go") or
271
- (file.has_extension(".md") and mem.indexOf(u8, file.text, "```go") != null)),
272
- };
273
-
274
- var remaining = file.text;
275
- while (mem.indexOfAny(u8, remaining, "\r\t")) |index| {
276
- const offset = index + (file.text.len - remaining.len);
277
- inline for (comptime std.meta.fieldNames(@TypeOf(allowed))) |field| {
278
- if (remaining[index] == field[0]) {
279
- if (!@field(allowed, field)) {
280
- errors.add_control_character(file, offset, field[0]);
281
- }
282
- break;
283
- }
284
- } else unreachable;
285
-
286
- remaining = remaining[index + 1 ..];
287
- }
288
- }
289
-
290
- test tidy_control_characters {
291
- try check_tidy_file(
292
- "hello.txt",
293
- "Hello\t\nWorld\r\n",
294
- snap(@src(),
295
- \\hello.txt:1: error: control character code=9
296
- \\hello.txt:2: error: control character code=13
297
- \\
298
- ),
299
- );
300
- }
301
-
302
- fn tidy_banned(file: SourceFile, errors: *Errors) void {
303
- // Vendored code is exempt from bans.
304
- if (std.mem.eql(u8, file.path, "src/stdx/vendored/aegis.zig")) return;
305
- // Don't ban ourselves!
306
- if (std.mem.eql(u8, file.path, "src/tidy.zig")) return;
307
-
308
- const ban_list: []const struct { []const u8, []const u8 } = &.{
309
- // Functionality provided by stdx:
310
- .{ "std.BoundedArray", "stdx.BoundedArrayType" },
311
- .{ "StaticBitSet", "stdx.BitSetType" },
312
- .{ "std.time.Duration", "stdx.Duration" },
313
- .{ "std.time.Instant", "stdx.Instant" },
314
- .{ "hasUniqueRepresentation", "stdx.has_unique_representation" },
315
- .{ "@memcpy(", "stdx.copy_disjoint" },
316
- .{ "mem.copyForwards(", "stdx.copy_left" },
317
- .{ "mem.copyBackwards(", "stdx.copy_right" },
318
- .{ "uintLessThan", "stdx.PRNG" },
319
- .{ "intRangeLessThan", "stdx.PRNG" },
320
- .{ "intRangeAtMost", "stdx.PRNG" },
321
- .{ "intRangeAtMostBiased", "stdx.PRNG" },
322
-
323
- // Library footguns:
324
- .{ "unexpectedErrno", "stdx.unexpected_errno" },
325
- .{ "posix.send(", "posix.sendto to avoid connection race condition" },
326
-
327
- // Language footguns:
328
- .{ "== error.", "switch to avoid silent anyerror upcast" },
329
- .{ "!= error.", "switch to avoid silent anyerror upcast" },
330
-
331
- // Everything else:
332
- .{ "debug.assert(", "unqualified assert" },
333
- .{ "Self = @This()", "proper type name" },
334
- .{ "!comptime", "! inside comptime" },
335
- .{ "usingnamespace", "something else" },
336
- };
337
-
338
- for (ban_list) |ban_item| {
339
- const banned, const replacement = ban_item;
340
- if (std.mem.indexOf(u8, file.text, banned)) |offset| {
341
- errors.add_banned(file, offset, banned, replacement);
342
- }
343
- }
344
-
345
- // Reminders:
346
- // Do use FIXME comments proactively while iterating on the code when you want to make sure
347
- // something is revisited before getting into the main branch.
348
- inline for (.{ "FIXME", "dbg(" }) |banned| {
349
- if (std.mem.indexOf(u8, file.text, banned)) |offset| {
350
- if (std.mem.startsWith(u8, file.text[offset..], "dbg(prefix: []const u8")) {
351
- // Allow fn dbg( function definition.
352
-
353
- } else {
354
- errors.add_banned_reminder(file, offset, banned);
355
- }
356
- }
357
- }
358
- }
359
-
360
- test tidy_banned {
361
- try check_tidy_file(
362
- \\banned.zig
363
- ,
364
- \\//FIXME: use copy_disjoint:
365
- \\@memcpy(foo, bar)
366
- ,
367
- snap(@src(),
368
- \\banned.zig:2: error: @memcpy( is banned, use stdx.copy_disjoint
369
- \\banned.zig:1: error: leftover FIXME, remove before merge
370
- \\
371
- ),
372
- );
373
- }
374
-
375
- fn tidy_lines(file: SourceFile, errors: *Errors) void {
376
- if (std.mem.endsWith(u8, file.path, "low_level_hash_vectors.zig")) return;
377
-
378
- var line_iterator = mem.splitScalar(u8, file.text, '\n');
379
- var line_index: u32 = 0;
380
- while (line_iterator.next()) |line| : (line_index += 1) {
381
- tidy_line(file, line, line_index, errors);
382
- }
383
- }
384
-
385
- fn tidy_line(file: SourceFile, line: []const u8, line_index: usize, errors: *Errors) void {
386
- const line_length = tidy_line_length(line);
387
- if (line_length <= 100) return;
388
-
389
- if (tidy_line_link(line)) return;
390
-
391
- // Journal recovery table
392
- if (std.mem.indexOf(u8, line, "Case.init(") != null) return;
393
-
394
- // For multiline strings, we care that the _result_ fits 100 characters,
395
- // but we don't mind indentation in the source.
396
- if (tidy_line_raw_literal(line)) |string_value| {
397
- const string_value_length = tidy_line_length(string_value);
398
- if (string_value_length <= 100) return;
399
-
400
- if (std.mem.endsWith(u8, file.path, "state_machine_tests.zig") and
401
- (std.mem.startsWith(u8, string_value, " account A") or
402
- std.mem.startsWith(u8, string_value, " transfer T") or
403
- std.mem.startsWith(u8, string_value, " transfer ")))
404
- {
405
- // Table tests from state_machine.zig. They are intentionally wide.
406
- return;
407
- }
408
-
409
- // vsr.zig's Checkpoint ops diagram.
410
- if (std.mem.endsWith(u8, file.path, "vsr.zig") and
411
- std.mem.startsWith(u8, string_value, "OPS: ")) return;
412
-
413
- // trace.zig's JSON snapshot test.
414
- if (std.mem.endsWith(u8, file.path, "trace.zig") and
415
- std.mem.startsWith(u8, string_value, "{\"pid\":1,\"tid\":")) return;
416
-
417
- // AMQP JSON snapshot test.
418
- if (std.mem.endsWith(u8, file.path, "cdc/runner.zig") and
419
- std.mem.startsWith(u8, string_value, "{\"timestamp\":")) return;
420
-
421
- // Message formatting tests.
422
- if (std.mem.endsWith(u8, file.path, "message_header.zig") and
423
- std.mem.startsWith(u8, string_value, "Prepare{")) return;
424
-
425
- // Flag snapshot test.
426
- if (std.mem.endsWith(u8, file.path, "flags.zig") and
427
- std.mem.startsWith(u8, string_value, "error: subcommand required")) return;
428
- }
429
-
430
- errors.add_long_line(file, line_index);
431
- }
432
-
433
- fn tidy_line_length(line: []const u8) usize {
434
- // Count codepoints for simplicity, even if it is wrong.
435
- return std.unicode.utf8CountCodepoints(line) catch @panic("invalid utf-8");
436
- }
437
-
438
- /// Heuristically checks if a `line` contains an URL.
439
- fn tidy_line_link(line: []const u8) bool {
440
- return std.mem.indexOf(u8, line, "https://") != null;
441
- }
442
-
443
- /// If a line is a `\\` string literal, extract its value.
444
- fn tidy_line_raw_literal(line: []const u8) ?[]const u8 {
445
- const indentation, const value = stdx.cut(line, "\\\\") orelse return null;
446
- for (indentation) |c| if (c != ' ') return null;
447
- return value;
448
- }
449
-
450
- test tidy_lines {
451
- try check_tidy_file(
452
- \\lines.zig
453
- ,
454
- "" ++
455
- "pub const x = 92;\n" ++
456
- "pub const x = " ++ ("9" ** 199) ++ ";\n" ++
457
- "pub const url = \"https://example." ++ ("0" ** 199) ++ " \";\n" ++
458
- " \\\\" ++ ("9" ** 99) ++ "\n" ++
459
- " \"" ++ ("9" ** 99) ++ "\"\n",
460
- snap(@src(),
461
- \\lines.zig:2: error: line exceeds 100 columns
462
- \\lines.zig:5: error: line exceeds 100 columns
463
- \\
464
- ),
465
- );
466
- }
467
-
468
- /// All functions using the `CamelCase` naming convention return a type,
469
- /// so we enforce that the function name also ends with the `Type` suffix.
470
- fn tidy_type_functions(file: SourceFile, errors: *Errors) void {
471
- var line_index: u32 = 0;
472
- var it = std.mem.splitScalar(u8, file.text, '\n');
473
- while (it.next()) |line| : (line_index += 1) {
474
- // Zig fmt enforces that the pattern `fn Foo(` is not split across multiple lines.
475
-
476
- const prefix, const suffix = stdx.cut(line, "fn ") orelse continue;
477
- // Not all `fn ` tokens are functions, some may be `callback_fn` for example.
478
- // Functions appear at the beginning of a line or after a whitespace.
479
- if (prefix.len > 0 and prefix[prefix.len - 1] != ' ') continue;
480
- const function_name, _ = stdx.cut(suffix, "(") orelse continue;
481
- if (function_name.len == 0) continue; // E.g: `*const fn (*anyopaque) void`.
482
- assert(function_name.len > 0);
483
-
484
- // Skipping naming convention that requires upper-case functions.
485
- if (std.mem.startsWith(u8, function_name, "JNI_")) continue;
486
- // Windows use CamelCase functions.
487
- if (std.mem.indexOf(u8, line, "extern \"kernel32\"") != null) continue;
488
-
489
- if (std.ascii.isUpper(function_name[0])) {
490
- if (!std.mem.endsWith(u8, function_name, "Type")) {
491
- errors.add_bad_type_function_name(file, line_index, function_name);
492
- }
493
- }
494
- }
495
- }
496
-
497
- test tidy_type_functions {
498
- try check_tidy_file(
499
- \\type_functions.zig
500
- ,
501
- \\pub fn MyArrayType() type { }
502
- ++ "\npub fn" ++ " MyArray() type { }" ++ "\n" ++
503
- \\ pub const callback = *const fn (*anyopaque) void;
504
- ,
505
- snap(@src(),
506
- \\type_functions.zig:2: error: type function name 'MyArray' should end in 'Type'
507
- \\
508
- ),
509
- );
510
- }
511
-
512
- const IdentifierCounter = struct {
513
- const file_identifier_count_max = 100_000;
514
-
515
- map: std.StringHashMapUnmanaged(struct { count: u32, offset: u32 }) = .{},
516
-
517
- pub fn init(gpa: Allocator) !IdentifierCounter {
518
- var counter: IdentifierCounter = .{};
519
- try counter.map.ensureTotalCapacity(gpa, file_identifier_count_max + 1);
520
- return counter;
521
- }
522
-
523
- pub fn deinit(counter: *IdentifierCounter, gpa: Allocator) void {
524
- counter.map.deinit(gpa);
525
- counter.* = undefined;
526
- }
527
-
528
- pub fn empty(counter: *const IdentifierCounter) bool {
529
- return counter.map.count() == 0;
530
- }
531
-
532
- pub fn clear(counter: *IdentifierCounter) void {
533
- counter.map.clearRetainingCapacity();
534
- }
535
-
536
- pub fn record(
537
- counter: *IdentifierCounter,
538
- tree: *const Ast,
539
- token_text: []const u8,
540
- token_offset: u32,
541
- ) void {
542
- const gop = counter.map.getOrPutAssumeCapacity(token_text);
543
- if (counter.map.count() > file_identifier_count_max) @panic("file too large");
544
-
545
- if (gop.found_existing) {
546
- // Count occurrences on a single line as one, as a special case for imports:
547
- // const foo = std.foo;
548
- const between_tokens_text = tree.source[gop.value_ptr.offset..token_offset];
549
- const same_line_occurrence = mem.indexOfScalar(u8, between_tokens_text, '\n') == null;
550
- if (same_line_occurrence) return;
551
- }
552
-
553
- if (!gop.found_existing) gop.value_ptr.* = .{ .count = 0, .offset = 0 };
554
- gop.value_ptr.count += 1;
555
- gop.value_ptr.offset = token_offset;
556
- }
557
-
558
- pub fn get(counter: *const IdentifierCounter, token_text: []const u8) u32 {
559
- return counter.map.get(token_text).?.count;
560
- }
561
- };
562
-
563
- /// Detects unused constants and functions.
564
- ///
565
- /// This is a one-side heuristic: there might be false negatives, but no false positives.
566
- ///
567
- /// Current algorithm:
568
- /// - Two passes.
569
- /// - Pass 1: count how many times each identifier is mentioned in the file.
570
- /// - Pass 2: warn about any unique identifier which is a non-public declaration.
571
- ///
572
- /// At the moment, this is implemented using only the lexer, without looking at the AST, as that
573
- /// seemed simpler.
574
- fn tidy_dead_declarations(
575
- file: SourceFile,
576
- tree: *const Ast,
577
- counter: *IdentifierCounter,
578
- errors: *Errors,
579
- ) void {
580
- assert(counter.empty());
581
- defer counter.clear();
582
-
583
- var identifier_start: ?Ast.ByteOffset = 0;
584
- inline for (.{ .fill, .check }) |phase| {
585
- next_token: for (
586
- tree.tokens.items(.tag),
587
- tree.tokens.items(.start),
588
- 0..,
589
- ) |tag, start, index_usize| {
590
- const index: Ast.TokenIndex = @intCast(index_usize);
591
- const identifier_start_previous = identifier_start;
592
- identifier_start = switch (tag) {
593
- .identifier => start,
594
- else => null,
595
- };
596
-
597
- const start_previous = identifier_start_previous orelse continue :next_token;
598
- const token_text = std.mem.trim(
599
- u8,
600
- tree.source[start_previous..start],
601
- &std.ascii.whitespace,
602
- );
603
-
604
- switch (phase) {
605
- .fill => counter.record(tree, token_text, start),
606
- .check => {
607
- const usages = counter.get(token_text);
608
- assert(usages >= 1);
609
- if (usages == 1) {
610
- if (tidy_dead_declarations_is_private_declaration(tree, index - 1)) {
611
- errors.add_dead_declaration(file, token_text);
612
- }
613
- }
614
- },
615
- else => comptime unreachable,
616
- }
617
- }
618
- }
619
- }
620
-
621
- // Checks if the given identifier token refers to non-public declaration.
622
- fn tidy_dead_declarations_is_private_declaration(
623
- tree: *const Ast,
624
- token_index: Ast.TokenIndex,
625
- ) bool {
626
- assert(tree.tokens.items(.tag)[token_index] == .identifier);
627
- var declaration_keyword = false;
628
- for (0..4) |context_offset| {
629
- const context_tag = if (token_index - context_offset < 1)
630
- .eof
631
- else
632
- tree.tokens.get(token_index - context_offset - 1).tag;
633
-
634
- if (!declaration_keyword) {
635
- switch (context_tag) {
636
- .keyword_fn, .keyword_const => declaration_keyword = true,
637
- // Not a declaration.
638
- else => return false,
639
- }
640
- } else {
641
- switch (context_tag) {
642
- .keyword_inline, .keyword_extern, .string_literal => {},
643
- // Public declaration can be used in a different file.
644
- .keyword_pub, .keyword_export => return false,
645
- // []const u8 or *const u8, not a declaration.
646
- .r_bracket, .asterisk => return false,
647
- // Non public declarations, never used.
648
- else => return true,
649
- }
650
- }
651
- } else unreachable;
652
- }
653
-
654
- test tidy_dead_declarations {
655
- try check_tidy_file(
656
- \\dead.zig
657
- ,
658
- \\ const std = @import("std");
659
- \\ const import_unused = std.import_unused;
660
- \\ pub fn public_used() void { private_used(); }
661
- \\ fn private_used() void {}
662
- \\ fn private_unused() void {}
663
- ,
664
- snap(@src(),
665
- \\dead.zig: error: 'import_unused' is dead code
666
- \\dead.zig: error: 'private_unused' is dead code
667
- \\
668
- ),
669
- );
670
- }
671
-
672
- fn tidy_ast(
673
- file: SourceFile,
674
- tree: *const Ast,
675
- errors: *Errors,
676
- ) void {
677
- if (std.mem.eql(u8, file.path, "build.zig")) return;
678
- if (std.mem.endsWith(u8, file.path, "build_multiversion.zig")) return;
679
- if (std.mem.endsWith(u8, file.path, "bindings.zig")) return;
680
-
681
- const tags = tree.nodes.items(.tag);
682
- const datas = tree.nodes.items(.data);
683
- // We can implement this in a streaming fashion, but its more convenient to materialize all
684
- // functions. 1k functions per file should be enough even for TigerBeetle!
685
- var functions: [1024]struct {
686
- line_opening: usize,
687
- line_closing: usize,
688
- } = undefined;
689
- var functions_count: u32 = 0;
690
-
691
- for (tags, datas, 0..) |tag, data, node| {
692
- if (tag == .fn_decl) { // Check function length.
693
- const node_body = data.rhs;
694
-
695
- const token_opening = tree.firstToken(@intCast(node));
696
- const token_closing = tree.lastToken(@intCast(node_body));
697
-
698
- const line_opening = tree.tokenLocation(0, token_opening).line;
699
- const line_closing = tree.tokenLocation(0, token_closing).line;
700
-
701
- functions[functions_count] = .{
702
- .line_opening = line_opening,
703
- .line_closing = line_closing,
704
- };
705
- functions_count += 1;
706
- }
707
- if (is_bin_op(tag)) { // Forbid mixing bitops and arithmetics without parentheses.
708
- inline for (.{ data.lhs, data.rhs }) |child| {
709
- const tag_child = tags[child];
710
- if ((is_bin_op_bitwise(tag) and is_bin_op_arithmetic(tag_child)) or
711
- (is_bin_op_arithmetic(tag) and is_bin_op_bitwise(tag_child)))
712
- {
713
- const token_opening = tree.firstToken(@intCast(node));
714
- const line_opening = tree.tokenLocation(0, token_opening).line;
715
- errors.add_ambiguous_precedence(file, line_opening);
716
- }
717
- }
718
- }
719
- }
720
-
721
- tidy_defer_newlines(file, tree, errors);
722
-
723
- // We ratchet 70-lines-per-function TigerStyle rule from the bottom up. Some functions want
724
- // to be really long, and that is big. The most values is in preventing originally small
725
- // functions to grow over time.
726
- const function_length_red_zone = .{
727
- .min = 70, // NB: both are exclusive, so red zone is intentionally empty to start!
728
- .max = 70,
729
- };
730
-
731
- for (functions[0..functions_count], 0..) |f, index| {
732
- // Functions are sorted by the start line.
733
- if (index > 0) assert(functions[index - 1].line_opening < f.line_opening);
734
-
735
- if (index == functions_count - 1 or
736
- functions[index + 1].line_opening > f.line_closing)
737
- {
738
- const function_length = f.line_closing - f.line_opening + 1;
739
- if (function_length_red_zone.min < function_length and
740
- function_length < function_length_red_zone.max)
741
- {
742
- errors.add_long_function(file, f.line_opening);
743
- }
744
- }
745
- }
746
- }
747
-
748
- fn tidy_defer_newlines(file: SourceFile, tree: *const Ast, errors: *Errors) void {
749
- const tags = tree.tokens.items(.tag);
750
-
751
- var index: usize = 0;
752
- while (index < tags.len) : (index += 1) {
753
- const tag = tags[index];
754
- if (tag != .keyword_defer) continue;
755
-
756
- const semicolon_index = tidy_defer_statement_end(tags, index) orelse continue;
757
- const semicolon_line = tree.tokenLocation(0, @intCast(semicolon_index)).line;
758
-
759
- var next_index = semicolon_index + 1;
760
- while (next_index < tags.len and
761
- tidy_is_comment_token(tags[next_index])) : (next_index += 1)
762
- {}
763
-
764
- if (next_index >= tags.len) break;
765
- if (tags[next_index] == .eof) continue;
766
-
767
- const next_line = tree.tokenLocation(0, @intCast(next_index)).line;
768
- const next_tag = tags[next_index];
769
- if (next_tag == .r_brace or next_tag == .keyword_errdefer or next_tag == .keyword_defer) {
770
- continue;
771
- }
772
-
773
- if (next_line <= semicolon_line + 1) {
774
- errors.add_defer_newline(file, semicolon_line);
775
- }
776
- }
777
- }
778
-
779
- fn tidy_defer_statement_end(tags: []const std.zig.Token.Tag, defer_index: usize) ?usize {
780
- var depth: i32 = 0;
781
-
782
- var index: usize = defer_index + 1;
783
- while (index < tags.len) : (index += 1) {
784
- switch (tags[index]) {
785
- .l_paren, .l_brace, .l_bracket => depth += 1,
786
- .r_paren, .r_brace, .r_bracket => depth -= 1,
787
- else => {},
788
- }
789
-
790
- if (depth == 0) {
791
- switch (tags[index]) {
792
- .semicolon => return index,
793
- .r_brace => {
794
- if (index + 1 < tags.len) {
795
- if (tags[index + 1] == .semicolon) return index + 1;
796
- if (tags[index + 1] == .keyword_else) continue;
797
- }
798
- return index; // defer { ... } without trailing semicolon.
799
- },
800
- else => {},
801
- }
802
- } else if (depth < 0) {
803
- // Unterminated defer; bail out.
804
- return null;
805
- }
806
- }
807
-
808
- return null;
809
- }
810
-
811
- fn tidy_is_comment_token(tag: std.zig.Token.Tag) bool {
812
- return switch (tag) {
813
- .doc_comment, .container_doc_comment => true,
814
- else => false,
815
- };
816
- }
817
-
818
- fn is_bin_op(tag: Ast.Node.Tag) bool {
819
- return is_bin_op_bitwise(tag) or is_bin_op_arithmetic(tag);
820
- }
821
-
822
- fn is_bin_op_bitwise(tag: Ast.Node.Tag) bool {
823
- return switch (tag) {
824
- .shl, .shl_sat => true,
825
- .shr => true,
826
- .bit_xor, .bit_or, .bit_and => true,
827
- else => false,
828
- };
829
- }
830
-
831
- fn is_bin_op_arithmetic(tag: Ast.Node.Tag) bool {
832
- return switch (tag) {
833
- .add, .add_sat, .add_wrap => true,
834
- .sub, .sub_sat, .sub_wrap => true,
835
- .mul, .mul_sat, .mul_wrap => true,
836
- .div, .mod => true,
837
- else => false,
838
- };
839
- }
840
-
841
- test tidy_ast {
842
- try check_tidy_file(
843
- \\precedence.zig
844
- ,
845
- \\ pub const confusing = 1 + foo << 3;
846
- \\ pub const ok = 1 + (foo << 3);
847
- ,
848
- snap(@src(),
849
- \\precedence.zig:1: error: ambiguous operator precedence, add parenthesis
850
- \\
851
- ),
852
- );
853
- }
854
-
855
- test tidy_defer_newlines {
856
- try check_tidy_file(
857
- \\defer_newline.zig
858
- ,
859
- \\pub fn foo() void {
860
- \\ defer bar();
861
- \\ baz();
862
- \\}
863
- \\pub fn bar() void {}
864
- \\pub fn baz() void {}
865
- ,
866
- snap(@src(),
867
- \\defer_newline.zig:2: error: defer must be followed by a blank line
868
- \\
869
- ),
870
- );
871
-
872
- try check_tidy_file(
873
- \\defer_newline_ok.zig
874
- ,
875
- \\pub fn foo() void {
876
- \\ defer bar();
877
- \\
878
- \\ baz();
879
- \\}
880
- \\pub fn bar() void {}
881
- \\pub fn baz() void {}
882
- ,
883
- snap(@src(),
884
- \\
885
- ),
886
- );
887
-
888
- try check_tidy_file(
889
- \\defer_end_of_scope_ok.zig
890
- ,
891
- \\pub fn foo() void {
892
- \\ defer bar();
893
- \\}
894
- \\pub fn bar() void {}
895
- ,
896
- snap(@src(),
897
- \\
898
- ),
899
- );
900
-
901
- try check_tidy_file(
902
- \\defer_block_ok.zig
903
- ,
904
- \\pub fn foo() void {
905
- \\ defer {
906
- \\ _ = bar() catch {};
907
- \\ }
908
- \\
909
- \\ baz();
910
- \\}
911
- \\pub fn bar() anyerror!void { return; }
912
- \\pub fn baz() void {}
913
- ,
914
- snap(@src(),
915
- \\
916
- ),
917
- );
918
-
919
- try check_tidy_file(
920
- \\defer_block_missing_blank_line.zig
921
- ,
922
- \\pub fn foo() void {
923
- \\ defer {
924
- \\ _ = bar() catch {};
925
- \\ }
926
- \\ baz();
927
- \\}
928
- \\pub fn bar() anyerror!void { return; }
929
- \\pub fn baz() void {}
930
- ,
931
- snap(@src(),
932
- \\defer_block_missing_blank_line.zig:4: error: defer must be followed by a blank line
933
- \\
934
- ),
935
- );
936
-
937
- try check_tidy_file(
938
- \\defer_followed_by_errdefer_ok.zig
939
- ,
940
- \\pub fn foo() !void {
941
- \\ var tmp: i32 = undefined;
942
- \\ defer tmp_deinit();
943
- \\ errdefer tmp_log();
944
- \\ return tmp_use(tmp);
945
- \\}
946
- \\pub fn tmp_deinit() void {}
947
- \\pub fn tmp_log() void {}
948
- \\pub fn tmp_use(_: i32) !void { return; }
949
- ,
950
- snap(@src(),
951
- \\
952
- ),
953
- );
954
-
955
- try check_tidy_file(
956
- \\defer_group_ok.zig
957
- ,
958
- \\pub fn foo() void {
959
- \\ defer stdout();
960
- \\ defer stderr();
961
- \\
962
- \\ baz();
963
- \\}
964
- \\pub fn stdout() void {}
965
- \\pub fn stderr() void {}
966
- \\pub fn baz() void {}
967
- ,
968
- snap(@src(),
969
- \\
970
- ),
971
- );
972
-
973
- try check_tidy_file(
974
- \\defer_inline_if_blank_line_ok.zig
975
- ,
976
- \\pub fn foo() void {
977
- \\ defer if (bar()) {
978
- \\ baz();
979
- \\ };
980
- \\
981
- \\ baz();
982
- \\}
983
- \\pub fn bar() bool { return true; }
984
- \\pub fn baz() void {}
985
- ,
986
- snap(@src(),
987
- \\
988
- ),
989
- );
990
-
991
- try check_tidy_file(
992
- \\defer_inline_if_else_no_blank_line_ok.zig
993
- ,
994
- \\pub fn foo() void {
995
- \\ defer if (bar()) {
996
- \\ baz();
997
- \\ } else {
998
- \\ qux();
999
- \\ };
1000
- \\ // else must be part of the defer statement; no blank line required here.
1001
- \\ zap();
1002
- \\}
1003
- \\pub fn bar() bool { return true; }
1004
- \\pub fn baz() void {}
1005
- \\pub fn qux() void {}
1006
- \\pub fn zap() void {}
1007
- ,
1008
- snap(@src(),
1009
- \\
1010
- ),
1011
- );
1012
-
1013
- try check_tidy_file(
1014
- \\defer_inline_block_with_comment_ok.zig
1015
- ,
1016
- \\pub fn foo() void {
1017
- \\ defer if (bar()) {
1018
- \\ baz();
1019
- \\ };
1020
- \\
1021
- \\ // next statements
1022
- \\ qux();
1023
- \\}
1024
- \\pub fn bar() bool { return true; }
1025
- \\pub fn baz() void {}
1026
- \\pub fn qux() void {}
1027
- ,
1028
- snap(@src(),
1029
- \\
1030
- ),
1031
- );
1032
-
1033
- try check_tidy_file(
1034
- \\defer_switch_ok.zig
1035
- ,
1036
- \\pub fn foo() void {
1037
- \\ defer switch (bar()) {
1038
- \\ .ok => {},
1039
- \\ .leak => {},
1040
- \\ };
1041
- \\
1042
- \\ baz();
1043
- \\}
1044
- \\pub fn bar() enum { ok, leak } { return .ok; }
1045
- \\pub fn baz() void {}
1046
- ,
1047
- snap(@src(),
1048
- \\
1049
- ),
1050
- );
1051
-
1052
- try check_tidy_file(
1053
- \\defer_inline_for_ok.zig
1054
- ,
1055
- \\pub fn foo() void {
1056
- \\ defer inline for (.{1, 2}) |s| {
1057
- \\ _ = s;
1058
- \\ };
1059
- \\
1060
- \\ baz();
1061
- \\}
1062
- \\pub fn baz() void {}
1063
- ,
1064
- snap(@src(),
1065
- \\
1066
- ),
1067
- );
1068
-
1069
- try check_tidy_file(
1070
- \\defer_with_catch_ok.zig
1071
- ,
1072
- \\pub fn foo() void {
1073
- \\ defer bar() catch {};
1074
- \\
1075
- \\ baz();
1076
- \\}
1077
- \\pub fn bar() anyerror!void { return; }
1078
- \\pub fn baz() void {}
1079
- ,
1080
- snap(@src(),
1081
- \\
1082
- ),
1083
- );
1084
- }
1085
-
1086
- /// Checks that each markdown document has exactly one h1.
1087
- ///
1088
- /// There are two schools of thought regarding largest (`# War and Peace`)
1089
- /// headings in markdown. One school says that they are _section_ titles, so
1090
- /// you could have multiple #'s in the document. But another option is to
1091
- /// say that a single # signifies document _title_, and there should be only
1092
- /// one in a document.
1093
- ///
1094
- /// We use markdown to create HTML, so # turns into h1. MDN recommends that
1095
- /// there's only a single h1 in a page:
1096
- ///
1097
- /// <https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Heading_Elements#avoid_using_multiple_h1_elements_on_one_page>
1098
- ///
1099
- /// For this reason, we follow the second convention.
1100
- fn tidy_markdown_title(file: SourceFile, errors: *Errors) void {
1101
- var fenced_block = false; // Avoid interpreting `# ` shell comments as titles.
1102
- var heading_count: u32 = 0;
1103
- var line_count: u32 = 0;
1104
- var it = std.mem.splitScalar(u8, file.text, '\n');
1105
- while (it.next()) |line| {
1106
- line_count += 1;
1107
- if (mem.startsWith(u8, line, "```")) fenced_block = !fenced_block;
1108
- if (!fenced_block and mem.startsWith(u8, line, "# ")) heading_count += 1;
1109
- }
1110
- assert(!fenced_block);
1111
- switch (heading_count) {
1112
- // No need for a title for a short note.
1113
- 0 => if (line_count > 2) errors.add_invalid_markdown_title(file),
1114
- 1 => {},
1115
- else => errors.add_invalid_markdown_title(file),
1116
- }
1117
- }
1118
-
1119
- test tidy_markdown_title {
1120
- try check_tidy_file(
1121
- \\ok.md
1122
- ,
1123
- \\# TigerStyle
1124
- \\
1125
- \\Style applies everywhere!
1126
- \\For example, we check that markdowns contains exactly one top-level title:
1127
- \\
1128
- \\```
1129
- \\# Good Document
1130
- \\```
1131
- ,
1132
- snap(@src(),
1133
- \\
1134
- ),
1135
- );
1136
- try check_tidy_file(
1137
- \\bad.md
1138
- ,
1139
- \\# Top Level Header
1140
- \\
1141
- \\Lorem Ipsum
1142
- \\
1143
- \\# And Another Top Level Header
1144
- ,
1145
- snap(@src(),
1146
- \\bad.md: error: document should have exactly one top-level '# Title'
1147
- \\
1148
- ),
1149
- );
1150
- }
1151
-
1152
- // Zig's lazy compilation model makes it too easy to forget to include a file into the build --- if
1153
- // nothing imports a file, compiler just doesn't see it and can't flag it as unused.
1154
- //
1155
- // DeadFilesDetector implements heuristic detection of unused files, by "grepping" for import
1156
- // statements and flagging file which are never imported. This gives false negatives for unreachable
1157
- // cycles of files, as well as for identically-named files, but it should be good enough in
1158
- // practice.
1159
- const DeadFilesDetector = struct {
1160
- const FileName = [64]u8;
1161
- const FileState = struct { import_count: u32, definition_count: u32 };
1162
- const FileMap = std.AutoArrayHashMap(FileName, FileState);
1163
-
1164
- files: FileMap,
1165
-
1166
- fn init(gpa: Allocator) DeadFilesDetector {
1167
- return .{ .files = FileMap.init(gpa) };
1168
- }
1169
-
1170
- fn deinit(detector: *DeadFilesDetector, _: Allocator) void {
1171
- detector.files.deinit();
1172
- }
1173
-
1174
- fn visit(detector: *DeadFilesDetector, file: SourceFile) Allocator.Error!void {
1175
- assert(file.has_extension(".zig"));
1176
- (try detector.file_state(file.path)).definition_count += 1;
1177
-
1178
- var rest: []const u8 = file.text;
1179
- for (0..1024) |_| {
1180
- _, rest = stdx.cut(rest, "@import(\"") orelse break;
1181
- const import_path, rest = stdx.cut(rest, "\")").?;
1182
- if (std.mem.endsWith(u8, import_path, ".zig")) {
1183
- (try detector.file_state(import_path)).import_count += 1;
1184
- }
1185
- } else {
1186
- std.debug.panic("file with more than 1024 imports: {s}", .{file.path});
1187
- }
1188
- }
1189
-
1190
- fn finish(detector: *DeadFilesDetector, errors: *Errors) void {
1191
- defer detector.files.clearRetainingCapacity();
1192
-
1193
- for (detector.files.keys(), detector.files.values()) |name, state| {
1194
- if (state.definition_count == 0) {
1195
- errors.add_file_untracked(&name);
1196
- }
1197
- if (state.import_count == 0 and !is_entry_point(name)) {
1198
- errors.add_file_dead(&name);
1199
- }
1200
- }
1201
- }
1202
-
1203
- fn file_state(detector: *DeadFilesDetector, path: []const u8) !*FileState {
1204
- const gop = try detector.files.getOrPut(path_to_name(path));
1205
- if (!gop.found_existing) gop.value_ptr.* = .{ .import_count = 0, .definition_count = 0 };
1206
- return gop.value_ptr;
1207
- }
1208
-
1209
- fn path_to_name(path: []const u8) FileName {
1210
- assert(std.mem.endsWith(u8, path, ".zig"));
1211
- const basename = std.fs.path.basename(path);
1212
- var file_name: FileName = @splat(0);
1213
- assert(basename.len <= file_name.len);
1214
- stdx.copy_disjoint(.inexact, u8, &file_name, basename);
1215
- return file_name;
1216
- }
1217
-
1218
- fn is_entry_point(file: FileName) bool {
1219
- const entry_points: []const []const u8 = &.{
1220
- "build_multiversion.zig",
1221
- "build.zig",
1222
- "dotnet_bindings.zig",
1223
- "fetch.zig",
1224
- "file_checker.zig",
1225
- "fuzz_tests.zig",
1226
- "go_bindings.zig",
1227
- "integration_tests.zig",
1228
- "java_bindings.zig",
1229
- "jni_tests.zig",
1230
- "libtb_client.zig",
1231
- "main.zig",
1232
- "node_bindings.zig",
1233
- "node.zig",
1234
- "page_writer.zig",
1235
- "python_bindings.zig",
1236
- "rust_bindings.zig",
1237
- "scripts.zig",
1238
- "search_index_writer.zig",
1239
- "service_worker_writer.zig",
1240
- "single_page_writer.zig",
1241
- "tb_client_header.zig",
1242
- "unit_tests.zig",
1243
- "vopr.zig",
1244
- "vortex.zig",
1245
- "zig_driver.zig",
1246
- };
1247
- for (entry_points) |entry_point| {
1248
- if (std.mem.startsWith(u8, &file, entry_point)) return true;
1249
- }
1250
- return false;
1251
- }
1252
- };
1253
-
1254
- test "tidy changelog" {
1255
- const gpa = std.testing.allocator;
1256
-
1257
- var errors: Errors = .{};
1258
-
1259
- const changelog_buffer = try gpa.alloc(u8, 1 * MiB);
1260
- defer gpa.free(changelog_buffer);
1261
-
1262
- const changelog = try SourceFile.read("CHANGELOG.md", changelog_buffer);
1263
-
1264
- var line_iterator = mem.splitScalar(u8, changelog.text, '\n');
1265
- var line_index: usize = 0;
1266
- while (line_iterator.next()) |line| : (line_index += 1) {
1267
- if (std.mem.endsWith(u8, line, " ")) {
1268
- errors.add_trailing_whitespace(changelog, line_index);
1269
- }
1270
- const line_length = tidy_line_length(line);
1271
- if (line_length > 100 and !tidy_line_link(line)) {
1272
- errors.add_long_line(changelog, line_index);
1273
- }
1274
-
1275
- if (std.mem.indexOf(u8, line, "?si=") != null) {
1276
- errors.add_tracking(changelog, line_index);
1277
- }
1278
- }
1279
- if (errors.count > 0) return error.Untidy;
1280
- assert(errors.count == 0);
1281
- }
1282
-
1283
- test "tidy no large blobs" {
1284
- const allocator = std.testing.allocator;
1285
- const shell = try Shell.create(allocator);
1286
- defer shell.destroy();
1287
-
1288
- // Run `git rev-list | git cat-file` to find large blobs. This is better than looking at the
1289
- // files in the working tree, because it catches the cases where a large file is "removed" by
1290
- // reverting the commit.
1291
- //
1292
- // Zig's std doesn't provide a cross platform abstraction for piping two commands together, so
1293
- // we begrudgingly pass the data through this intermediary process.
1294
- const shallow = try shell.exec_stdout("git rev-parse --is-shallow-repository", .{});
1295
- if (!std.mem.eql(u8, shallow, "false")) {
1296
- return error.ShallowRepository;
1297
- }
1298
-
1299
- const rev_list = try shell.exec_stdout("git rev-list --objects HEAD", .{});
1300
- const objects = try shell.exec_stdout_options(
1301
- .{ .stdin_slice = rev_list },
1302
- "git cat-file --batch-check={format}",
1303
- .{ .format = "%(objecttype) %(objectsize) %(rest)" },
1304
- );
1305
-
1306
- var has_large_blobs = false;
1307
- var lines = std.mem.splitScalar(u8, objects, '\n');
1308
- while (lines.next()) |line| {
1309
- // Parsing lines like
1310
- // blob 1032 client/package.json
1311
- const blob = stdx.cut_prefix(line, "blob ") orelse continue;
1312
-
1313
- const size_string, const path = stdx.cut(blob, " ").?;
1314
- const size = try std.fmt.parseInt(u64, size_string, 10);
1315
-
1316
- if (std.mem.eql(u8, path, "src/vsr/replica.zig")) continue; // :-)
1317
- if (std.mem.eql(u8, path, "src/state_machine.zig")) continue; // :-|
1318
- if (std.mem.eql(u8, path, "src/docs_website/package-lock.json")) continue; // :-(
1319
- if (size > @divExact(MiB, 4)) {
1320
- has_large_blobs = true;
1321
- std.debug.print("{s}\n", .{line});
1322
- }
1323
- }
1324
- if (has_large_blobs) return error.HasLargeBlobs;
1325
- }
1326
-
1327
- test "tidy unix permissions" {
1328
- const executable_files = [_][]const u8{
1329
- "zig/download.ps1",
1330
- "zig/download.sh",
1331
- ".github/ci/test_aof.sh",
1332
- "src/scripts/cfo_supervisor.sh",
1333
- };
1334
-
1335
- const allocator = std.testing.allocator;
1336
- const shell = try Shell.create(allocator);
1337
- defer shell.destroy();
1338
-
1339
- const files = try shell.exec_stdout("git ls-files -z --format {format}", .{
1340
- .format = "%(objectmode) %(path)",
1341
- });
1342
- assert(files[files.len - 1] == 0);
1343
- var lines = std.mem.splitScalar(u8, files[0 .. files.len - 1], 0);
1344
- while (lines.next()) |line| {
1345
- const mode, const path = stdx.cut(line, " ").?;
1346
- errdefer std.debug.print("{s}: error: unexpected mode={s}\n", .{ path, mode });
1347
-
1348
- if (std.mem.eql(u8, mode, "100644")) {
1349
- // Expected for most files.
1350
- } else if (std.mem.eql(u8, mode, "100755")) {
1351
- const expected = for (executable_files) |executable_file| {
1352
- if (std.mem.eql(u8, path, executable_file)) break true;
1353
- } else false;
1354
-
1355
- if (!expected) return error.UnexpectedExecutable;
1356
- } else {
1357
- return error.UnexpectedMode;
1358
- }
1359
- }
1360
- }
1361
-
1362
- // Sanity check for "unexpected" files in the repository.
1363
- test "tidy extensions" {
1364
- const allowed_extensions = std.StaticStringMap(void).initComptime(.{
1365
- .{".c"}, .{".cs"}, .{".csproj"}, .{".css"}, .{".go"},
1366
- .{".h"}, .{".hcl"}, .{".html"}, .{".java"}, .{".js"},
1367
- .{".json"}, .{".md"}, .{".mod"}, .{".props"}, .{".py"},
1368
- .{".rs"}, .{".service"}, .{".sln"}, .{".sum"}, .{".svg"},
1369
- .{".toml"}, .{".ts"}, .{".txt"}, .{".xml"}, .{".yml"},
1370
- .{".zig"}, .{".zon"},
1371
- });
1372
-
1373
- const exceptions = std.StaticStringMap(void).initComptime(.{
1374
- .{".editorconfig"},
1375
- .{".gitignore"},
1376
- .{".nojekyll"},
1377
- .{"CNAME"},
1378
- .{"exclude-pmd.properties"},
1379
- .{"favicon.png"},
1380
- .{"notfound-light.webp"},
1381
- .{"notfound-dark.webp"},
1382
- .{"preview.webp"},
1383
- .{"LICENSE"},
1384
- .{"module-info.test"},
1385
- .{"anchor-links.lua"},
1386
- .{"markdown-links.lua"},
1387
- .{"table-wrapper.lua"},
1388
- .{"code-block-buttons.lua"},
1389
- .{"edit-link-footer.lua"},
1390
- .{"src/docs_website/.vale.ini"},
1391
- .{"zig/download.sh"},
1392
- .{"zig/download.ps1"},
1393
- .{"zig/download.win.ps1"},
1394
- .{"src/scripts/cfo_supervisor.sh"},
1395
- .{".github/ci/test_aof.sh"},
1396
- .{"src/clients/python/pyproject.toml"},
1397
- .{"src/clients/python/src/tigerbeetle/py.typed"},
1398
- });
1399
-
1400
- const allocator = std.testing.allocator;
1401
- const shell = try Shell.create(allocator);
1402
- defer shell.destroy();
1403
-
1404
- const paths = try list_file_paths(shell);
1405
-
1406
- for (exceptions.keys()) |exception| {
1407
- for (paths) |path| {
1408
- const basename = std.fs.path.basename(path);
1409
- if (std.mem.eql(u8, exception, basename) or std.mem.eql(u8, exception, path)) {
1410
- break;
1411
- }
1412
- } else {
1413
- std.debug.panic("exception (or basename) doesn't exist: {s} ({s})", .{
1414
- exception,
1415
- std.fs.path.basename(exception),
1416
- });
1417
- }
1418
- }
1419
-
1420
- var bad_extension = false;
1421
- for (paths) |path| {
1422
- if (path.len == 0) continue;
1423
- const extension = std.fs.path.extension(path);
1424
- if (!allowed_extensions.has(extension)) {
1425
- const basename = std.fs.path.basename(path);
1426
- if (!exceptions.has(basename) and !exceptions.has(path)) {
1427
- std.debug.print("bad extension: {s}\n", .{path});
1428
- bad_extension = true;
1429
- }
1430
- }
1431
- }
1432
- if (bad_extension) return error.BadExtension;
1433
- }
1434
-
1435
- /// Lists all files in the repository.
1436
- fn list_file_paths(shell: *Shell) ![]const []const u8 {
1437
- var result = std.ArrayList([]const u8).init(shell.arena.allocator());
1438
-
1439
- const files = try shell.exec_stdout("git ls-files -z", .{});
1440
- assert(files.len > 0);
1441
- assert(files[files.len - 1] == 0);
1442
- var lines = std.mem.splitScalar(u8, files[0 .. files.len - 1], 0);
1443
- while (lines.next()) |line| {
1444
- assert(line.len > 0);
1445
- try result.append(line);
1446
- }
1447
-
1448
- return result.items;
1449
- }