tigerbeetle 0.0.36 → 0.0.38

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 (248) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -0
  3. data/ext/tb_client/extconf.rb +13 -13
  4. data/ext/tb_client/tigerbeetle/LICENSE +177 -0
  5. data/ext/tb_client/tigerbeetle/build.zig +2327 -0
  6. data/ext/tb_client/tigerbeetle/src/aof.zig +1000 -0
  7. data/ext/tb_client/tigerbeetle/src/build_multiversion.zig +808 -0
  8. data/ext/tb_client/tigerbeetle/src/cdc/amqp/protocol.zig +1283 -0
  9. data/ext/tb_client/tigerbeetle/src/cdc/amqp/spec.zig +1704 -0
  10. data/ext/tb_client/tigerbeetle/src/cdc/amqp/types.zig +341 -0
  11. data/ext/tb_client/tigerbeetle/src/cdc/amqp.zig +1450 -0
  12. data/ext/tb_client/tigerbeetle/src/cdc/runner.zig +1659 -0
  13. data/ext/tb_client/tigerbeetle/src/clients/c/samples/main.c +406 -0
  14. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/context.zig +1092 -0
  15. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/echo_client.zig +286 -0
  16. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/packet.zig +158 -0
  17. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/signal.zig +229 -0
  18. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/signal_fuzz.zig +110 -0
  19. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client.h +386 -0
  20. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client.zig +34 -0
  21. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_exports.zig +281 -0
  22. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_header.zig +312 -0
  23. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_header_test.zig +138 -0
  24. data/ext/tb_client/tigerbeetle/src/clients/c/test.zig +466 -0
  25. data/ext/tb_client/tigerbeetle/src/clients/docs_samples.zig +157 -0
  26. data/ext/tb_client/tigerbeetle/src/clients/docs_types.zig +90 -0
  27. data/ext/tb_client/tigerbeetle/src/clients/dotnet/ci.zig +203 -0
  28. data/ext/tb_client/tigerbeetle/src/clients/dotnet/docs.zig +79 -0
  29. data/ext/tb_client/tigerbeetle/src/clients/dotnet/dotnet_bindings.zig +542 -0
  30. data/ext/tb_client/tigerbeetle/src/clients/go/ci.zig +109 -0
  31. data/ext/tb_client/tigerbeetle/src/clients/go/docs.zig +86 -0
  32. data/ext/tb_client/tigerbeetle/src/clients/go/go_bindings.zig +370 -0
  33. data/ext/tb_client/tigerbeetle/src/clients/go/pkg/native/tb_client.h +386 -0
  34. data/ext/tb_client/tigerbeetle/src/clients/java/ci.zig +167 -0
  35. data/ext/tb_client/tigerbeetle/src/clients/java/docs.zig +126 -0
  36. data/ext/tb_client/tigerbeetle/src/clients/java/java_bindings.zig +996 -0
  37. data/ext/tb_client/tigerbeetle/src/clients/java/src/client.zig +748 -0
  38. data/ext/tb_client/tigerbeetle/src/clients/java/src/jni.zig +3238 -0
  39. data/ext/tb_client/tigerbeetle/src/clients/java/src/jni_tests.zig +1718 -0
  40. data/ext/tb_client/tigerbeetle/src/clients/java/src/jni_thread_cleaner.zig +190 -0
  41. data/ext/tb_client/tigerbeetle/src/clients/node/ci.zig +104 -0
  42. data/ext/tb_client/tigerbeetle/src/clients/node/docs.zig +75 -0
  43. data/ext/tb_client/tigerbeetle/src/clients/node/node.zig +522 -0
  44. data/ext/tb_client/tigerbeetle/src/clients/node/node_bindings.zig +267 -0
  45. data/ext/tb_client/tigerbeetle/src/clients/node/src/c.zig +3 -0
  46. data/ext/tb_client/tigerbeetle/src/clients/node/src/translate.zig +379 -0
  47. data/ext/tb_client/tigerbeetle/src/clients/python/ci.zig +131 -0
  48. data/ext/tb_client/tigerbeetle/src/clients/python/docs.zig +63 -0
  49. data/ext/tb_client/tigerbeetle/src/clients/python/python_bindings.zig +588 -0
  50. data/ext/tb_client/tigerbeetle/src/clients/rust/assets/tb_client.h +386 -0
  51. data/ext/tb_client/tigerbeetle/src/clients/rust/ci.zig +73 -0
  52. data/ext/tb_client/tigerbeetle/src/clients/rust/docs.zig +106 -0
  53. data/ext/tb_client/tigerbeetle/src/clients/rust/rust_bindings.zig +305 -0
  54. data/ext/tb_client/tigerbeetle/src/config.zig +296 -0
  55. data/ext/tb_client/tigerbeetle/src/constants.zig +790 -0
  56. data/ext/tb_client/tigerbeetle/src/copyhound.zig +202 -0
  57. data/ext/tb_client/tigerbeetle/src/counting_allocator.zig +72 -0
  58. data/ext/tb_client/tigerbeetle/src/direction.zig +120 -0
  59. data/ext/tb_client/tigerbeetle/src/docs_website/build.zig +158 -0
  60. data/ext/tb_client/tigerbeetle/src/docs_website/src/content.zig +156 -0
  61. data/ext/tb_client/tigerbeetle/src/docs_website/src/docs.zig +252 -0
  62. data/ext/tb_client/tigerbeetle/src/docs_website/src/file_checker.zig +313 -0
  63. data/ext/tb_client/tigerbeetle/src/docs_website/src/html.zig +87 -0
  64. data/ext/tb_client/tigerbeetle/src/docs_website/src/page_writer.zig +63 -0
  65. data/ext/tb_client/tigerbeetle/src/docs_website/src/redirects.zig +47 -0
  66. data/ext/tb_client/tigerbeetle/src/docs_website/src/search_index_writer.zig +28 -0
  67. data/ext/tb_client/tigerbeetle/src/docs_website/src/service_worker_writer.zig +61 -0
  68. data/ext/tb_client/tigerbeetle/src/docs_website/src/single_page_writer.zig +169 -0
  69. data/ext/tb_client/tigerbeetle/src/docs_website/src/website.zig +46 -0
  70. data/ext/tb_client/tigerbeetle/src/ewah.zig +445 -0
  71. data/ext/tb_client/tigerbeetle/src/ewah_benchmark.zig +128 -0
  72. data/ext/tb_client/tigerbeetle/src/ewah_fuzz.zig +171 -0
  73. data/ext/tb_client/tigerbeetle/src/fuzz_tests.zig +179 -0
  74. data/ext/tb_client/tigerbeetle/src/integration_tests.zig +662 -0
  75. data/ext/tb_client/tigerbeetle/src/io/common.zig +155 -0
  76. data/ext/tb_client/tigerbeetle/src/io/darwin.zig +1093 -0
  77. data/ext/tb_client/tigerbeetle/src/io/linux.zig +1880 -0
  78. data/ext/tb_client/tigerbeetle/src/io/test.zig +1005 -0
  79. data/ext/tb_client/tigerbeetle/src/io/windows.zig +1598 -0
  80. data/ext/tb_client/tigerbeetle/src/io.zig +34 -0
  81. data/ext/tb_client/tigerbeetle/src/iops.zig +134 -0
  82. data/ext/tb_client/tigerbeetle/src/list.zig +236 -0
  83. data/ext/tb_client/tigerbeetle/src/lsm/binary_search.zig +848 -0
  84. data/ext/tb_client/tigerbeetle/src/lsm/binary_search_benchmark.zig +179 -0
  85. data/ext/tb_client/tigerbeetle/src/lsm/cache_map.zig +424 -0
  86. data/ext/tb_client/tigerbeetle/src/lsm/cache_map_fuzz.zig +420 -0
  87. data/ext/tb_client/tigerbeetle/src/lsm/compaction.zig +2117 -0
  88. data/ext/tb_client/tigerbeetle/src/lsm/composite_key.zig +182 -0
  89. data/ext/tb_client/tigerbeetle/src/lsm/forest.zig +1119 -0
  90. data/ext/tb_client/tigerbeetle/src/lsm/forest_fuzz.zig +1102 -0
  91. data/ext/tb_client/tigerbeetle/src/lsm/forest_table_iterator.zig +200 -0
  92. data/ext/tb_client/tigerbeetle/src/lsm/groove.zig +1495 -0
  93. data/ext/tb_client/tigerbeetle/src/lsm/k_way_merge.zig +739 -0
  94. data/ext/tb_client/tigerbeetle/src/lsm/k_way_merge_benchmark.zig +166 -0
  95. data/ext/tb_client/tigerbeetle/src/lsm/manifest.zig +754 -0
  96. data/ext/tb_client/tigerbeetle/src/lsm/manifest_level.zig +1294 -0
  97. data/ext/tb_client/tigerbeetle/src/lsm/manifest_level_fuzz.zig +510 -0
  98. data/ext/tb_client/tigerbeetle/src/lsm/manifest_log.zig +1263 -0
  99. data/ext/tb_client/tigerbeetle/src/lsm/manifest_log_fuzz.zig +628 -0
  100. data/ext/tb_client/tigerbeetle/src/lsm/node_pool.zig +247 -0
  101. data/ext/tb_client/tigerbeetle/src/lsm/scan_buffer.zig +116 -0
  102. data/ext/tb_client/tigerbeetle/src/lsm/scan_builder.zig +543 -0
  103. data/ext/tb_client/tigerbeetle/src/lsm/scan_fuzz.zig +938 -0
  104. data/ext/tb_client/tigerbeetle/src/lsm/scan_lookup.zig +293 -0
  105. data/ext/tb_client/tigerbeetle/src/lsm/scan_merge.zig +359 -0
  106. data/ext/tb_client/tigerbeetle/src/lsm/scan_range.zig +99 -0
  107. data/ext/tb_client/tigerbeetle/src/lsm/scan_state.zig +17 -0
  108. data/ext/tb_client/tigerbeetle/src/lsm/scan_tree.zig +962 -0
  109. data/ext/tb_client/tigerbeetle/src/lsm/schema.zig +617 -0
  110. data/ext/tb_client/tigerbeetle/src/lsm/scratch_memory.zig +84 -0
  111. data/ext/tb_client/tigerbeetle/src/lsm/segmented_array.zig +1500 -0
  112. data/ext/tb_client/tigerbeetle/src/lsm/segmented_array_benchmark.zig +149 -0
  113. data/ext/tb_client/tigerbeetle/src/lsm/segmented_array_fuzz.zig +7 -0
  114. data/ext/tb_client/tigerbeetle/src/lsm/set_associative_cache.zig +865 -0
  115. data/ext/tb_client/tigerbeetle/src/lsm/table.zig +607 -0
  116. data/ext/tb_client/tigerbeetle/src/lsm/table_memory.zig +843 -0
  117. data/ext/tb_client/tigerbeetle/src/lsm/table_value_iterator.zig +90 -0
  118. data/ext/tb_client/tigerbeetle/src/lsm/timestamp_range.zig +40 -0
  119. data/ext/tb_client/tigerbeetle/src/lsm/tree.zig +630 -0
  120. data/ext/tb_client/tigerbeetle/src/lsm/tree_fuzz.zig +933 -0
  121. data/ext/tb_client/tigerbeetle/src/lsm/zig_zag_merge.zig +534 -0
  122. data/ext/tb_client/tigerbeetle/src/message_buffer.zig +469 -0
  123. data/ext/tb_client/tigerbeetle/src/message_bus.zig +1214 -0
  124. data/ext/tb_client/tigerbeetle/src/message_bus_fuzz.zig +936 -0
  125. data/ext/tb_client/tigerbeetle/src/message_pool.zig +343 -0
  126. data/ext/tb_client/tigerbeetle/src/multiversion.zig +2195 -0
  127. data/ext/tb_client/tigerbeetle/src/queue.zig +390 -0
  128. data/ext/tb_client/tigerbeetle/src/repl/completion.zig +201 -0
  129. data/ext/tb_client/tigerbeetle/src/repl/parser.zig +1356 -0
  130. data/ext/tb_client/tigerbeetle/src/repl/terminal.zig +496 -0
  131. data/ext/tb_client/tigerbeetle/src/repl.zig +1034 -0
  132. data/ext/tb_client/tigerbeetle/src/scripts/amqp.zig +973 -0
  133. data/ext/tb_client/tigerbeetle/src/scripts/cfo.zig +1866 -0
  134. data/ext/tb_client/tigerbeetle/src/scripts/changelog.zig +304 -0
  135. data/ext/tb_client/tigerbeetle/src/scripts/ci.zig +227 -0
  136. data/ext/tb_client/tigerbeetle/src/scripts/client_readmes.zig +658 -0
  137. data/ext/tb_client/tigerbeetle/src/scripts/devhub.zig +466 -0
  138. data/ext/tb_client/tigerbeetle/src/scripts/release.zig +1058 -0
  139. data/ext/tb_client/tigerbeetle/src/scripts.zig +105 -0
  140. data/ext/tb_client/tigerbeetle/src/shell.zig +1195 -0
  141. data/ext/tb_client/tigerbeetle/src/stack.zig +260 -0
  142. data/ext/tb_client/tigerbeetle/src/state_machine/auditor.zig +911 -0
  143. data/ext/tb_client/tigerbeetle/src/state_machine/workload.zig +2079 -0
  144. data/ext/tb_client/tigerbeetle/src/state_machine.zig +4872 -0
  145. data/ext/tb_client/tigerbeetle/src/state_machine_fuzz.zig +288 -0
  146. data/ext/tb_client/tigerbeetle/src/state_machine_tests.zig +3128 -0
  147. data/ext/tb_client/tigerbeetle/src/static_allocator.zig +82 -0
  148. data/ext/tb_client/tigerbeetle/src/stdx/bit_set.zig +157 -0
  149. data/ext/tb_client/tigerbeetle/src/stdx/bounded_array.zig +292 -0
  150. data/ext/tb_client/tigerbeetle/src/stdx/debug.zig +65 -0
  151. data/ext/tb_client/tigerbeetle/src/stdx/flags.zig +1414 -0
  152. data/ext/tb_client/tigerbeetle/src/stdx/mlock.zig +92 -0
  153. data/ext/tb_client/tigerbeetle/src/stdx/prng.zig +677 -0
  154. data/ext/tb_client/tigerbeetle/src/stdx/radix.zig +336 -0
  155. data/ext/tb_client/tigerbeetle/src/stdx/ring_buffer.zig +511 -0
  156. data/ext/tb_client/tigerbeetle/src/stdx/sort_test.zig +112 -0
  157. data/ext/tb_client/tigerbeetle/src/stdx/stdx.zig +1160 -0
  158. data/ext/tb_client/tigerbeetle/src/stdx/testing/low_level_hash_vectors.zig +142 -0
  159. data/ext/tb_client/tigerbeetle/src/stdx/testing/snaptest.zig +361 -0
  160. data/ext/tb_client/tigerbeetle/src/stdx/time_units.zig +275 -0
  161. data/ext/tb_client/tigerbeetle/src/stdx/unshare.zig +295 -0
  162. data/ext/tb_client/tigerbeetle/src/stdx/vendored/aegis.zig +436 -0
  163. data/ext/tb_client/tigerbeetle/src/stdx/windows.zig +48 -0
  164. data/ext/tb_client/tigerbeetle/src/stdx/zipfian.zig +402 -0
  165. data/ext/tb_client/tigerbeetle/src/storage.zig +489 -0
  166. data/ext/tb_client/tigerbeetle/src/storage_fuzz.zig +180 -0
  167. data/ext/tb_client/tigerbeetle/src/testing/bench.zig +146 -0
  168. data/ext/tb_client/tigerbeetle/src/testing/cluster/grid_checker.zig +53 -0
  169. data/ext/tb_client/tigerbeetle/src/testing/cluster/journal_checker.zig +61 -0
  170. data/ext/tb_client/tigerbeetle/src/testing/cluster/manifest_checker.zig +76 -0
  171. data/ext/tb_client/tigerbeetle/src/testing/cluster/message_bus.zig +110 -0
  172. data/ext/tb_client/tigerbeetle/src/testing/cluster/network.zig +412 -0
  173. data/ext/tb_client/tigerbeetle/src/testing/cluster/state_checker.zig +331 -0
  174. data/ext/tb_client/tigerbeetle/src/testing/cluster/storage_checker.zig +458 -0
  175. data/ext/tb_client/tigerbeetle/src/testing/cluster.zig +1198 -0
  176. data/ext/tb_client/tigerbeetle/src/testing/exhaustigen.zig +128 -0
  177. data/ext/tb_client/tigerbeetle/src/testing/fixtures.zig +181 -0
  178. data/ext/tb_client/tigerbeetle/src/testing/fuzz.zig +144 -0
  179. data/ext/tb_client/tigerbeetle/src/testing/id.zig +97 -0
  180. data/ext/tb_client/tigerbeetle/src/testing/io.zig +317 -0
  181. data/ext/tb_client/tigerbeetle/src/testing/marks.zig +126 -0
  182. data/ext/tb_client/tigerbeetle/src/testing/packet_simulator.zig +533 -0
  183. data/ext/tb_client/tigerbeetle/src/testing/reply_sequence.zig +154 -0
  184. data/ext/tb_client/tigerbeetle/src/testing/state_machine.zig +389 -0
  185. data/ext/tb_client/tigerbeetle/src/testing/storage.zig +1247 -0
  186. data/ext/tb_client/tigerbeetle/src/testing/table.zig +249 -0
  187. data/ext/tb_client/tigerbeetle/src/testing/time.zig +98 -0
  188. data/ext/tb_client/tigerbeetle/src/testing/tmp_tigerbeetle.zig +212 -0
  189. data/ext/tb_client/tigerbeetle/src/testing/vortex/constants.zig +26 -0
  190. data/ext/tb_client/tigerbeetle/src/testing/vortex/faulty_network.zig +580 -0
  191. data/ext/tb_client/tigerbeetle/src/testing/vortex/java_driver/ci.zig +39 -0
  192. data/ext/tb_client/tigerbeetle/src/testing/vortex/logged_process.zig +214 -0
  193. data/ext/tb_client/tigerbeetle/src/testing/vortex/rust_driver/ci.zig +34 -0
  194. data/ext/tb_client/tigerbeetle/src/testing/vortex/supervisor.zig +766 -0
  195. data/ext/tb_client/tigerbeetle/src/testing/vortex/workload.zig +543 -0
  196. data/ext/tb_client/tigerbeetle/src/testing/vortex/zig_driver.zig +181 -0
  197. data/ext/tb_client/tigerbeetle/src/tidy.zig +1448 -0
  198. data/ext/tb_client/tigerbeetle/src/tigerbeetle/benchmark_driver.zig +227 -0
  199. data/ext/tb_client/tigerbeetle/src/tigerbeetle/benchmark_load.zig +1069 -0
  200. data/ext/tb_client/tigerbeetle/src/tigerbeetle/cli.zig +1422 -0
  201. data/ext/tb_client/tigerbeetle/src/tigerbeetle/inspect.zig +1658 -0
  202. data/ext/tb_client/tigerbeetle/src/tigerbeetle/inspect_integrity.zig +518 -0
  203. data/ext/tb_client/tigerbeetle/src/tigerbeetle/libtb_client.zig +36 -0
  204. data/ext/tb_client/tigerbeetle/src/tigerbeetle/main.zig +646 -0
  205. data/ext/tb_client/tigerbeetle/src/tigerbeetle.zig +958 -0
  206. data/ext/tb_client/tigerbeetle/src/time.zig +236 -0
  207. data/ext/tb_client/tigerbeetle/src/trace/event.zig +745 -0
  208. data/ext/tb_client/tigerbeetle/src/trace/statsd.zig +462 -0
  209. data/ext/tb_client/tigerbeetle/src/trace.zig +556 -0
  210. data/ext/tb_client/tigerbeetle/src/unit_tests.zig +321 -0
  211. data/ext/tb_client/tigerbeetle/src/vopr.zig +1785 -0
  212. data/ext/tb_client/tigerbeetle/src/vortex.zig +101 -0
  213. data/ext/tb_client/tigerbeetle/src/vsr/checkpoint_trailer.zig +473 -0
  214. data/ext/tb_client/tigerbeetle/src/vsr/checksum.zig +208 -0
  215. data/ext/tb_client/tigerbeetle/src/vsr/checksum_benchmark.zig +43 -0
  216. data/ext/tb_client/tigerbeetle/src/vsr/client.zig +768 -0
  217. data/ext/tb_client/tigerbeetle/src/vsr/client_replies.zig +532 -0
  218. data/ext/tb_client/tigerbeetle/src/vsr/client_sessions.zig +338 -0
  219. data/ext/tb_client/tigerbeetle/src/vsr/clock.zig +1019 -0
  220. data/ext/tb_client/tigerbeetle/src/vsr/fault_detector.zig +279 -0
  221. data/ext/tb_client/tigerbeetle/src/vsr/free_set.zig +1381 -0
  222. data/ext/tb_client/tigerbeetle/src/vsr/free_set_fuzz.zig +315 -0
  223. data/ext/tb_client/tigerbeetle/src/vsr/grid.zig +1460 -0
  224. data/ext/tb_client/tigerbeetle/src/vsr/grid_blocks_missing.zig +757 -0
  225. data/ext/tb_client/tigerbeetle/src/vsr/grid_scrubber.zig +797 -0
  226. data/ext/tb_client/tigerbeetle/src/vsr/journal.zig +2586 -0
  227. data/ext/tb_client/tigerbeetle/src/vsr/marzullo.zig +308 -0
  228. data/ext/tb_client/tigerbeetle/src/vsr/message_header.zig +1777 -0
  229. data/ext/tb_client/tigerbeetle/src/vsr/multi_batch.zig +715 -0
  230. data/ext/tb_client/tigerbeetle/src/vsr/multi_batch_fuzz.zig +185 -0
  231. data/ext/tb_client/tigerbeetle/src/vsr/repair_budget.zig +333 -0
  232. data/ext/tb_client/tigerbeetle/src/vsr/replica.zig +12355 -0
  233. data/ext/tb_client/tigerbeetle/src/vsr/replica_format.zig +416 -0
  234. data/ext/tb_client/tigerbeetle/src/vsr/replica_reformat.zig +165 -0
  235. data/ext/tb_client/tigerbeetle/src/vsr/replica_test.zig +2928 -0
  236. data/ext/tb_client/tigerbeetle/src/vsr/routing.zig +1075 -0
  237. data/ext/tb_client/tigerbeetle/src/vsr/superblock.zig +1603 -0
  238. data/ext/tb_client/tigerbeetle/src/vsr/superblock_fuzz.zig +484 -0
  239. data/ext/tb_client/tigerbeetle/src/vsr/superblock_quorums.zig +405 -0
  240. data/ext/tb_client/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +355 -0
  241. data/ext/tb_client/tigerbeetle/src/vsr/sync.zig +29 -0
  242. data/ext/tb_client/tigerbeetle/src/vsr.zig +1727 -0
  243. data/lib/tb_client/shared_lib.rb +12 -5
  244. data/lib/tigerbeetle/platforms.rb +9 -0
  245. data/lib/tigerbeetle/version.rb +2 -2
  246. data/tigerbeetle.gemspec +22 -5
  247. metadata +242 -3
  248. data/ext/tb_client/pkg.tar.gz +0 -0
@@ -0,0 +1,658 @@
1
+ //! All language clients are sufficiently similar, and, for that reason, we generate the
2
+ //! corresponding READMEs by splicing language-specific code snippets into a language agnostic
3
+ //! template. This file handles the generation process.
4
+ //!
5
+ //! Code generation is written as a test that checks that generated READMEs are fresh. If they are
6
+ //! not, the file on disk is updated, and the overall test fails.
7
+
8
+ const std = @import("std");
9
+ const assert = std.debug.assert;
10
+ const log = std.log;
11
+
12
+ const stdx = @import("stdx");
13
+ const MiB = stdx.MiB;
14
+ const Shell = @import("../shell.zig");
15
+ const Docs = @import("../clients/docs_types.zig").Docs;
16
+ const Sample = @import("../clients/docs_types.zig").Sample;
17
+ const samples = @import("../clients/docs_samples.zig").samples;
18
+
19
+ const Language = @import("./ci.zig").Language;
20
+
21
+ const LanguageDocs = .{
22
+ .go = @import("../clients/go/docs.zig").GoDocs,
23
+ .rust = @import("../clients/rust/docs.zig").RustDocs,
24
+ .node = @import("../clients/node/docs.zig").NodeDocs,
25
+ .java = @import("../clients/java/docs.zig").JavaDocs,
26
+ .dotnet = @import("../clients/dotnet/docs.zig").DotnetDocs,
27
+ .python = @import("../clients/python/docs.zig").PythonDocs,
28
+ };
29
+
30
+ pub fn test_freshness(
31
+ shell: *Shell,
32
+ gpa: std.mem.Allocator,
33
+ language: Language,
34
+ ) !void {
35
+ var arena = std.heap.ArenaAllocator.init(gpa);
36
+ defer arena.deinit();
37
+
38
+ const docs = switch (language) {
39
+ inline else => |l| @field(LanguageDocs, @tagName(l)),
40
+ };
41
+
42
+ const walkthrough_path = try shell.fmt(
43
+ "./samples/walkthrough/{s}{s}.{s}",
44
+ .{ docs.test_source_path, docs.test_file_name, docs.extension },
45
+ );
46
+ const walkthrough = try shell.cwd.readFileAlloc(
47
+ arena.allocator(),
48
+ walkthrough_path,
49
+ 1 * MiB,
50
+ );
51
+
52
+ var ctx = Context{
53
+ .shell = shell,
54
+ .arena = arena.allocator(),
55
+ .buffer = std.ArrayList(u8).init(arena.allocator()),
56
+ .docs = docs,
57
+ .walkthrough = walkthrough,
58
+ };
59
+
60
+ var updated_any = false;
61
+
62
+ { // Root README.md.
63
+ ctx.buffer.clearRetainingCapacity();
64
+ try readme_root(&ctx);
65
+
66
+ const update = try shell.file_ensure_content("README.md", ctx.buffer.items, .{});
67
+ updated_any = updated_any or (update == .updated);
68
+ }
69
+
70
+ for (samples) |sample| { // Per-sample README.md
71
+
72
+ ctx.buffer.clearRetainingCapacity();
73
+ try readme_sample(&ctx, sample);
74
+
75
+ const sample_readme = try shell.fmt("samples/{s}/README.md", .{sample.directory});
76
+ const update = try shell.file_ensure_content(sample_readme, ctx.buffer.items, .{});
77
+ updated_any = updated_any or (update == .updated);
78
+ }
79
+
80
+ if (updated_any) return error.DocsUpdated;
81
+ }
82
+
83
+ // Can't use `@src().file` here as it uses forward slashes on Windows.
84
+ const this_file = "/src/scripts/client_readmes.zig";
85
+
86
+ fn readme_root(ctx: *Context) !void {
87
+ assert(ctx.buffer.items.len == 0);
88
+
89
+ ctx.print(
90
+ \\<!-- This file is generated by [{s}]({s}). -->
91
+ \\
92
+ , .{ this_file, this_file });
93
+
94
+ ctx.header(1, ctx.docs.name);
95
+ ctx.paragraph(ctx.docs.description);
96
+
97
+ {
98
+ ctx.header(2, "Prerequisites");
99
+ ctx.print(
100
+ \\Linux >= 5.6 is the only production environment we
101
+ \\support. But for ease of development we also support macOS and Windows.
102
+ \\
103
+ , .{});
104
+ ctx.paragraph(ctx.docs.prerequisites);
105
+ }
106
+
107
+ {
108
+ ctx.header(2, "Setup");
109
+ ctx.paragraph("First, create a directory for your project and `cd` into the directory.");
110
+
111
+ if (ctx.docs.project_file.len > 0) {
112
+ ctx.print(
113
+ "Then create `{s}` and copy this into it:\n\n",
114
+ .{ctx.docs.project_file_name},
115
+ );
116
+ _, const project_file_language = stdx.cut(ctx.docs.project_file_name, ".").?;
117
+ ctx.code(project_file_language, ctx.docs.project_file);
118
+ }
119
+
120
+ if (ctx.docs.install_commands.len > 0) {
121
+ ctx.paragraph("Then, install the TigerBeetle client:");
122
+ ctx.commands(ctx.docs.install_commands);
123
+ }
124
+
125
+ ctx.print("Now, create `{s}{s}.{s}` and copy this into it:\n\n", .{
126
+ ctx.docs.test_source_path,
127
+ ctx.docs.test_file_name,
128
+ ctx.docs.extension,
129
+ });
130
+ ctx.code_section("imports");
131
+ ctx.paragraph("Finally, build and run:");
132
+ ctx.commands(ctx.docs.run_commands);
133
+
134
+ ctx.paragraph(
135
+ \\Now that all prerequisites and dependencies are correctly set
136
+ \\up, let's dig into using TigerBeetle.
137
+ ,
138
+ );
139
+ }
140
+
141
+ {
142
+ ctx.header(2, "Sample projects");
143
+ ctx.paragraph(
144
+ \\This document is primarily a reference guide to
145
+ \\the client. Below are various sample projects demonstrating
146
+ \\features of TigerBeetle.
147
+ ,
148
+ );
149
+ // Absolute paths here are necessary for resolving within the docs site.
150
+ for (samples) |sample| {
151
+ if (try ctx.sample_exists(sample)) {
152
+ ctx.print("* [{s}](/src/clients/{s}/samples/{s}/): {s}\n", .{
153
+ sample.proper_name,
154
+ ctx.docs.directory,
155
+ sample.directory,
156
+ sample.short_description,
157
+ });
158
+ }
159
+ }
160
+
161
+ if (ctx.docs.examples.len != 0) {
162
+ ctx.paragraph(ctx.docs.examples);
163
+ }
164
+ }
165
+
166
+ {
167
+ ctx.header(2, "Creating a Client");
168
+ ctx.paragraph(
169
+ \\A client is created with a cluster ID and replica
170
+ \\addresses for all replicas in the cluster. The cluster
171
+ \\ID and replica addresses are both chosen by the system that
172
+ \\starts the TigerBeetle cluster.
173
+ \\
174
+ \\Clients are thread-safe and a single instance should be shared
175
+ \\between multiple concurrent tasks. This allows events to be
176
+ \\[automatically batched](https://docs.tigerbeetle.com/coding/requests/#batching-events).
177
+ \\
178
+ \\Multiple clients are useful when connecting to more than
179
+ \\one TigerBeetle cluster.
180
+ \\
181
+ \\In this example the cluster ID is `0` and there is one
182
+ \\replica. The address is read from the `TB_ADDRESS`
183
+ \\environment variable and defaults to port `3000`.
184
+ );
185
+ ctx.code_section("client");
186
+ ctx.paragraph(ctx.docs.client_object_documentation);
187
+
188
+ ctx.paragraph(
189
+ \\The following are valid addresses:
190
+ \\* `3000` (interpreted as `127.0.0.1:3000`)
191
+ \\* `127.0.0.1:3000` (interpreted as `127.0.0.1:3000`)
192
+ \\* `127.0.0.1` (interpreted as `127.0.0.1:3001`, `3001` is the default port)
193
+ );
194
+ }
195
+
196
+ {
197
+ ctx.header(2, "Creating Accounts");
198
+ ctx.paragraph(
199
+ \\See details for account fields in the [Accounts
200
+ \\reference](https://docs.tigerbeetle.com/reference/account).
201
+ );
202
+ ctx.code_section("create-accounts");
203
+
204
+ ctx.paragraph(
205
+ \\See details for the recommended ID scheme in
206
+ \\[time-based identifiers](https://docs.tigerbeetle.com/coding/data-modeling#tigerbeetle-time-based-identifiers-recommended).
207
+ );
208
+
209
+ ctx.paragraph(ctx.docs.create_accounts_documentation);
210
+
211
+ ctx.header(3, "Account Flags");
212
+ ctx.paragraph(
213
+ \\The account flags value is a bitfield. See details for
214
+ \\these flags in the [Accounts
215
+ \\reference](https://docs.tigerbeetle.com/reference/account#flags).
216
+ );
217
+ ctx.paragraph(ctx.docs.account_flags_documentation);
218
+
219
+ ctx.paragraph(
220
+ \\For example, to link two accounts where the first account
221
+ \\additionally has the `debits_must_not_exceed_credits` constraint:
222
+ );
223
+ ctx.code_section("account-flags");
224
+
225
+ ctx.header(3, "Response and Errors");
226
+ ctx.paragraph(
227
+ \\The response is an empty array if all accounts were
228
+ \\created successfully. If the response is non-empty, each
229
+ \\object in the response array contains error information
230
+ \\for an account that failed. The error object contains an
231
+ \\error code and the index of the account in the request
232
+ \\batch.
233
+ \\
234
+ \\See all error conditions in the [create_accounts
235
+ \\reference](https://docs.tigerbeetle.com/reference/requests/create_accounts).
236
+ );
237
+
238
+ ctx.code_section("create-accounts-errors");
239
+
240
+ ctx.paragraph(ctx.docs.create_accounts_errors_documentation);
241
+ }
242
+
243
+ {
244
+ ctx.header(2, "Account Lookup");
245
+ ctx.paragraph(
246
+ \\Account lookup is batched, like account creation. Pass
247
+ \\in all IDs to fetch. The account for each matched ID is returned.
248
+ \\
249
+ \\If no account matches an ID, no object is returned for
250
+ \\that account. So the order of accounts in the response is
251
+ \\not necessarily the same as the order of IDs in the
252
+ \\request. You can refer to the ID field in the response to
253
+ \\distinguish accounts.
254
+ );
255
+ ctx.code_section("lookup-accounts");
256
+ }
257
+
258
+ {
259
+ ctx.header(2, "Create Transfers");
260
+ ctx.paragraph(
261
+ \\This creates a journal entry between two accounts.
262
+ \\
263
+ \\See details for transfer fields in the [Transfers
264
+ \\reference](https://docs.tigerbeetle.com/reference/transfer).
265
+ );
266
+ ctx.code_section("create-transfers");
267
+
268
+ ctx.paragraph(
269
+ \\See details for the recommended ID scheme in
270
+ \\[time-based identifiers](https://docs.tigerbeetle.com/coding/data-modeling#tigerbeetle-time-based-identifiers-recommended).
271
+ );
272
+
273
+ ctx.header(3, "Response and Errors");
274
+ ctx.paragraph(
275
+ \\The response is an empty array if all transfers were created
276
+ \\successfully. If the response is non-empty, each object in the
277
+ \\response array contains error information for a transfer that
278
+ \\failed. The error object contains an error code and the index of the
279
+ \\transfer in the request batch.
280
+ \\
281
+ \\See all error conditions in the [create_transfers
282
+ \\reference](https://docs.tigerbeetle.com/reference/requests/create_transfers).
283
+ );
284
+ ctx.code_section("create-transfers-errors");
285
+
286
+ ctx.paragraph(ctx.docs.create_transfers_errors_documentation);
287
+ }
288
+
289
+ {
290
+ ctx.header(2, "Batching");
291
+ ctx.paragraph(
292
+ \\TigerBeetle performance is maximized when you batch
293
+ \\API requests.
294
+ \\A client instance shared across multiple threads/tasks can automatically
295
+ \\batch concurrent requests, but the application must still send as many events
296
+ \\as possible in a single call.
297
+ \\For example, if you insert 1 million transfers sequentially, one at a time,
298
+ \\the insert rate will be a *fraction* of the potential, because the client will
299
+ \\wait for a reply between each one.
300
+ );
301
+ ctx.code_section("no-batch");
302
+ ctx.paragraph(
303
+ \\Instead, **always batch as much as you can**.
304
+ \\The maximum batch size is set in the TigerBeetle server. The default is 8189.
305
+ );
306
+ ctx.code_section("batch");
307
+
308
+ ctx.header(3, "Queues and Workers");
309
+ ctx.paragraph(
310
+ \\If you are making requests to TigerBeetle from workers
311
+ \\pulling jobs from a queue, you can batch requests to
312
+ \\TigerBeetle by having the worker act on multiple jobs from
313
+ \\the queue at once rather than one at a time. i.e. pulling
314
+ \\multiple jobs from the queue rather than just one.
315
+ );
316
+ }
317
+
318
+ {
319
+ ctx.header(2, "Transfer Flags");
320
+ ctx.paragraph(
321
+ \\The transfer `flags` value is a bitfield. See details for these flags in
322
+ \\the [Transfers
323
+ \\reference](https://docs.tigerbeetle.com/reference/transfer#flags).
324
+ );
325
+ ctx.paragraph(ctx.docs.transfer_flags_documentation);
326
+ ctx.paragraph("For example, to link `transfer0` and `transfer1`:");
327
+ ctx.code_section("transfer-flags-link");
328
+
329
+ ctx.header(3, "Two-Phase Transfers");
330
+ ctx.paragraph(
331
+ \\Two-phase transfers are supported natively by toggling the appropriate
332
+ \\flag. TigerBeetle will then adjust the `credits_pending` and
333
+ \\`debits_pending` fields of the appropriate accounts. A corresponding
334
+ \\post pending transfer then needs to be sent to post or void the
335
+ \\transfer.
336
+ );
337
+ ctx.header(4, "Post a Pending Transfer");
338
+ ctx.paragraph(
339
+ \\With `flags` set to `post_pending_transfer`,
340
+ \\TigerBeetle will post the transfer. TigerBeetle will atomically roll
341
+ \\back the changes to `debits_pending` and `credits_pending` of the
342
+ \\appropriate accounts and apply them to the `debits_posted` and
343
+ \\`credits_posted` balances.
344
+ );
345
+ ctx.code_section("transfer-flags-post");
346
+
347
+ ctx.header(4, "Void a Pending Transfer");
348
+ ctx.paragraph(
349
+ \\In contrast, with `flags` set to `void_pending_transfer`,
350
+ \\TigerBeetle will void the transfer. TigerBeetle will roll
351
+ \\back the changes to `debits_pending` and `credits_pending` of the
352
+ \\appropriate accounts and **not** apply them to the `debits_posted` and
353
+ \\`credits_posted` balances.
354
+ );
355
+ ctx.code_section("transfer-flags-void");
356
+ }
357
+
358
+ {
359
+ ctx.header(2, "Transfer Lookup");
360
+ ctx.paragraph(
361
+ \\NOTE: While transfer lookup exists, it is not a flexible query API. We
362
+ \\are developing query APIs and there will be new methods for querying
363
+ \\transfers in the future.
364
+ \\
365
+ \\Transfer lookup is batched, like transfer creation. Pass in all `id`s to
366
+ \\fetch, and matched transfers are returned.
367
+ \\
368
+ \\If no transfer matches an `id`, no object is returned for that
369
+ \\transfer. So the order of transfers in the response is not necessarily
370
+ \\the same as the order of `id`s in the request. You can refer to the
371
+ \\`id` field in the response to distinguish transfers.
372
+ );
373
+ ctx.code_section("lookup-transfers");
374
+ }
375
+
376
+ {
377
+ ctx.header(2, "Get Account Transfers");
378
+ ctx.paragraph(
379
+ \\NOTE: This is a preview API that is subject to breaking changes once we have
380
+ \\a stable querying API.
381
+ \\
382
+ \\Fetches the transfers involving a given account, allowing basic filter and pagination
383
+ \\capabilities.
384
+ \\
385
+ \\The transfers in the response are sorted by `timestamp` in chronological or
386
+ \\reverse-chronological order.
387
+ );
388
+ ctx.code_section("get-account-transfers");
389
+ }
390
+
391
+ {
392
+ ctx.header(2, "Get Account Balances");
393
+ ctx.paragraph(
394
+ \\NOTE: This is a preview API that is subject to breaking changes once we have
395
+ \\a stable querying API.
396
+ \\
397
+ \\Fetches the point-in-time balances of a given account, allowing basic filter and
398
+ \\pagination capabilities.
399
+ \\
400
+ \\Only accounts created with the flag
401
+ \\[`history`](https://docs.tigerbeetle.com/reference/account#flagshistory) set retain
402
+ \\[historical balances](https://docs.tigerbeetle.com/reference/requests/get_account_balances).
403
+ \\
404
+ \\The balances in the response are sorted by `timestamp` in chronological or
405
+ \\reverse-chronological order.
406
+ );
407
+ ctx.code_section("get-account-balances");
408
+ }
409
+
410
+ {
411
+ ctx.header(2, "Query Accounts");
412
+ ctx.paragraph(
413
+ \\NOTE: This is a preview API that is subject to breaking changes once we have
414
+ \\a stable querying API.
415
+ \\
416
+ \\Query accounts by the intersection of some fields and by timestamp range.
417
+ \\
418
+ \\The accounts in the response are sorted by `timestamp` in chronological or
419
+ \\reverse-chronological order.
420
+ );
421
+ ctx.code_section("query-accounts");
422
+ }
423
+
424
+ {
425
+ ctx.header(2, "Query Transfers");
426
+ ctx.paragraph(
427
+ \\NOTE: This is a preview API that is subject to breaking changes once we have
428
+ \\a stable querying API.
429
+ \\
430
+ \\Query transfers by the intersection of some fields and by timestamp range.
431
+ \\
432
+ \\The transfers in the response are sorted by `timestamp` in chronological or
433
+ \\reverse-chronological order.
434
+ );
435
+ ctx.code_section("query-transfers");
436
+ }
437
+
438
+ {
439
+ ctx.header(2, "Linked Events");
440
+ ctx.paragraph(
441
+ \\When the `linked` flag is specified for an account when creating accounts or
442
+ \\a transfer when creating transfers, it links that event with the next event in the
443
+ \\batch, to create a chain of events, of arbitrary length, which all
444
+ \\succeed or fail together. The tail of a chain is denoted by the first
445
+ \\event without this flag. The last event in a batch may therefore never
446
+ \\have the `linked` flag set as this would leave a chain
447
+ \\open-ended. Multiple chains or individual events may coexist within a
448
+ \\batch to succeed or fail independently.
449
+ \\
450
+ \\Events within a chain are executed within order, or are rolled back on
451
+ \\error, so that the effect of each event in the chain is visible to the
452
+ \\next, and so that the chain is either visible or invisible as a unit
453
+ \\to subsequent events after the chain. The event that was the first to
454
+ \\break the chain will have a unique error result. Other events in the
455
+ \\chain will have their error result set to `linked_event_failed`.
456
+ );
457
+ ctx.code_section("linked-events");
458
+ }
459
+
460
+ {
461
+ ctx.header(2, "Imported Events");
462
+ ctx.paragraph(
463
+ \\When the `imported` flag is specified for an account when creating accounts or
464
+ \\a transfer when creating transfers, it allows importing historical events with
465
+ \\a user-defined timestamp.
466
+ \\
467
+ \\The entire batch of events must be set with the flag `imported`.
468
+ \\
469
+ \\It's recommended to submit the whole batch as a `linked` chain of events, ensuring that
470
+ \\if any event fails, none of them are committed, preserving the last timestamp unchanged.
471
+ \\This approach gives the application a chance to correct failed imported events, re-submitting
472
+ \\the batch again with the same user-defined timestamps.
473
+ );
474
+ ctx.code_section("imported-events");
475
+ }
476
+
477
+ {
478
+ ctx.header(2, "Timeouts And Cancellation");
479
+ ctx.paragraph(
480
+ \\The Client retries indefinitely and doesn't impose any per-request timeout. Cancellation is
481
+ \\provided as a mechanism, and the specific cancellation policy is left to the
482
+ \\application. A Client instance can be closed at any time. On close, all in-flight
483
+ \\requests are canceled and return an error to the caller. Even if an error is returned,
484
+ \\a request might still be processed by the TigerBeetle server.
485
+ \\[Reliable transaction submission](https://docs.tigerbeetle.com/coding/reliable-transaction-submission/)
486
+ \\explains how to make transfers retry-proof using IDs for end-to-end idempotency.
487
+ );
488
+ }
489
+
490
+ ctx.ensure_final_newline();
491
+ }
492
+
493
+ fn readme_sample(ctx: *Context, sample: Sample) !void {
494
+ ctx.print(
495
+ \\<!-- This file is generated by [{s}]({s}). -->
496
+ \\
497
+ , .{ this_file, this_file });
498
+
499
+ ctx.print(
500
+ \\# {s} {s} Sample
501
+ \\
502
+ \\Code for this sample is in [./{s}{s}.{s}](./{s}{s}.{s}).
503
+ \\
504
+ \\
505
+ , .{
506
+ sample.proper_name,
507
+ ctx.docs.proper_name,
508
+ ctx.docs.test_source_path,
509
+ ctx.docs.test_file_name,
510
+ ctx.docs.extension,
511
+ ctx.docs.test_source_path,
512
+ ctx.docs.test_file_name,
513
+ ctx.docs.extension,
514
+ });
515
+
516
+ {
517
+ ctx.header(2, "Prerequisites");
518
+ ctx.print(
519
+ \\Linux >= 5.6 is the only production environment we
520
+ \\support. But for ease of development we also support macOS and Windows.
521
+ \\
522
+ , .{});
523
+ ctx.paragraph(ctx.docs.prerequisites);
524
+ }
525
+
526
+ {
527
+ ctx.header(2, "Setup");
528
+ ctx.paragraph(try ctx.shell.fmt(
529
+ \\First, clone this repo and `cd` into `tigerbeetle/src/clients/{s}/samples/{s}`.
530
+ , .{ ctx.docs.directory, sample.directory }));
531
+
532
+ ctx.paragraph("Then, install the TigerBeetle client:");
533
+ ctx.commands(ctx.docs.install_commands);
534
+ }
535
+
536
+ {
537
+ ctx.header(2, "Start the TigerBeetle server");
538
+ ctx.paragraph(
539
+ \\Follow steps in the repo README to [run
540
+ \\TigerBeetle](/README.md#running-tigerbeetle).
541
+ \\
542
+ \\If you are not running on port `localhost:3000`, set
543
+ \\the environment variable `TB_ADDRESS` to the full
544
+ \\address of the TigerBeetle server you started.
545
+ );
546
+ }
547
+
548
+ {
549
+ ctx.header(2, "Run this sample");
550
+ ctx.paragraph("Now you can run this sample:");
551
+ ctx.commands(ctx.docs.run_commands);
552
+ }
553
+
554
+ {
555
+ ctx.header(2, "Walkthrough");
556
+ ctx.paragraph("Here's what this project does.");
557
+ ctx.paragraph(sample.long_description);
558
+ }
559
+
560
+ ctx.ensure_final_newline();
561
+ }
562
+
563
+ const Context = struct {
564
+ shell: *Shell,
565
+ arena: std.mem.Allocator,
566
+ buffer: std.ArrayList(u8),
567
+ docs: Docs,
568
+ walkthrough: []const u8,
569
+
570
+ fn sample_exists(ctx: *Context, sample: @TypeOf(samples[0])) !bool {
571
+ const sample_directory = try ctx.shell.fmt("samples/{s}/", .{sample.directory});
572
+ return try ctx.shell.dir_exists(sample_directory);
573
+ }
574
+
575
+ // Pulls a single "section" of code out of the entire walkthrough sample.
576
+ //
577
+ // A section is delimited by a pair of `section:SECTION_NAME` and `endsection:SECTION_NAME`
578
+ // comments. If there are several such pairs, their contents is concatenated (see the `imports`
579
+ // section in the Java sample for a motivational example for concatenation behavior).
580
+ fn read_section(ctx: *Context, section_name: []const u8) []const u8 {
581
+ var section_content = std.ArrayList(u8).init(ctx.arena);
582
+ const section_start =
583
+ ctx.shell.fmt("section:{s}\n", .{section_name}) catch @panic("OOM");
584
+ const section_end =
585
+ ctx.shell.fmt("endsection:{s}\n", .{section_name}) catch @panic("OOM");
586
+
587
+ var rest = ctx.walkthrough;
588
+ for (0..10) |_| {
589
+ _, rest = stdx.cut(rest, section_start) orelse break;
590
+
591
+ var section, rest = stdx.cut(rest, section_end).?;
592
+ const newline_index = std.mem.lastIndexOfScalar(u8, section, '\n') orelse {
593
+ log.warn("empty section fragment: {s}", .{section_name});
594
+ @panic("empty section fragement");
595
+ };
596
+ section = section[0..newline_index];
597
+
598
+ var indent_min: usize = std.math.maxInt(usize);
599
+ var lines = std.mem.splitScalar(u8, section, '\n');
600
+ while (lines.next()) |line| {
601
+ if (line.len == 0) continue;
602
+ var indent_line: usize = 0;
603
+ while (line[indent_line] == ' ' or line[indent_line] == '\t') indent_line += 1;
604
+ indent_min = @min(indent_min, indent_line);
605
+ }
606
+ assert(indent_min < 18);
607
+
608
+ lines = std.mem.splitScalar(u8, section, '\n');
609
+ while (lines.next()) |line| {
610
+ if (line.len > 0) {
611
+ assert(line.len > indent_min);
612
+ section_content.appendSlice(line[indent_min..]) catch unreachable;
613
+ }
614
+ section_content.append('\n') catch unreachable;
615
+ }
616
+ } else @panic("too many parts in a section");
617
+ assert(section_content.pop() == '\n');
618
+
619
+ const result = section_content.items;
620
+ assert(result.len > 0);
621
+ return result;
622
+ }
623
+
624
+ fn header(ctx: *Context, comptime level: u8, content: []const u8) void {
625
+ ctx.print(("#" ** level) ++ " {s}\n\n", .{content});
626
+ }
627
+
628
+ fn paragraph(ctx: *Context, content: []const u8) void {
629
+ // Don't print empty lines.
630
+ if (content.len == 0) return;
631
+ ctx.print("{s}\n\n", .{content});
632
+ }
633
+
634
+ fn code(ctx: *Context, language: []const u8, content: []const u8) void {
635
+ // Don't print empty lines.
636
+ if (content.len == 0) return;
637
+ ctx.print("```{s}\n{s}\n```\n\n", .{ language, content });
638
+ }
639
+
640
+ fn code_section(ctx: *Context, section_name: []const u8) void {
641
+ const section_content = ctx.read_section(section_name);
642
+ ctx.code(ctx.docs.markdown_name, section_content);
643
+ }
644
+
645
+ fn commands(ctx: *Context, content: []const u8) void {
646
+ ctx.code("console", content);
647
+ }
648
+
649
+ fn print(ctx: *Context, comptime fmt: []const u8, args: anytype) void {
650
+ ctx.buffer.writer().print(fmt, args) catch @panic("OOM");
651
+ }
652
+
653
+ fn ensure_final_newline(ctx: *Context) void {
654
+ assert(ctx.buffer.pop() == '\n');
655
+ assert(std.mem.endsWith(u8, ctx.buffer.items, "\n"));
656
+ assert(!std.mem.endsWith(u8, ctx.buffer.items, "\n\n"));
657
+ }
658
+ };