tigerbeetle 0.0.34 → 0.0.37

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 (249) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -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 +1084 -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 +11 -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 +362 -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 +1036 -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 +105 -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 +557 -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 +2910 -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/client.rb +1 -1
  245. data/lib/tigerbeetle/platforms.rb +9 -0
  246. data/lib/tigerbeetle/version.rb +2 -2
  247. data/tigerbeetle.gemspec +22 -5
  248. metadata +242 -3
  249. data/ext/tb_client/pkg.tar.gz +0 -0
@@ -0,0 +1,1356 @@
1
+ const std = @import("std");
2
+ const assert = std.debug.assert;
3
+
4
+ const vsr = @import("../vsr.zig");
5
+ const constants = vsr.constants;
6
+ const IO = vsr.io.IO;
7
+ const Storage = vsr.storage.StorageType(IO);
8
+ const StateMachine = vsr.state_machine.StateMachineType(Storage);
9
+ const tb = vsr.tigerbeetle;
10
+
11
+ const Terminal = @import("terminal.zig").Terminal;
12
+
13
+ pub const Parser = struct {
14
+ input: []const u8,
15
+ offset: usize = 0,
16
+ terminal: *const Terminal,
17
+
18
+ pub const Error = error{
19
+ IdentifierBad,
20
+ OperationBad,
21
+ ValueBad,
22
+ KeyValuePairBad,
23
+ KeyValuePairEqualMissing,
24
+ SyntaxMatchNone,
25
+ SliceOperationUnsupported,
26
+ };
27
+
28
+ pub const Operation = enum {
29
+ none,
30
+ help,
31
+ create_accounts,
32
+ create_transfers,
33
+ lookup_accounts,
34
+ lookup_transfers,
35
+ get_account_transfers,
36
+ get_account_balances,
37
+ query_accounts,
38
+ query_transfers,
39
+
40
+ pub fn state_machine_op(operation: Operation) StateMachine.Operation {
41
+ return switch (operation) {
42
+ .none, .help => unreachable,
43
+ .create_accounts => .create_accounts,
44
+ .create_transfers => .create_transfers,
45
+ .lookup_accounts => .lookup_accounts,
46
+ .lookup_transfers => .lookup_transfers,
47
+ .get_account_transfers => .get_account_transfers,
48
+ .get_account_balances => .get_account_balances,
49
+ .query_accounts => .query_accounts,
50
+ .query_transfers => .query_transfers,
51
+ };
52
+ }
53
+ };
54
+
55
+ pub const LookupSyntaxTree = struct {
56
+ id: u128,
57
+ };
58
+
59
+ pub const ObjectSyntaxTree = union(enum) {
60
+ account: tb.Account,
61
+ transfer: tb.Transfer,
62
+ id: LookupSyntaxTree,
63
+ account_filter: tb.AccountFilter,
64
+ query_filter: tb.QueryFilter,
65
+ };
66
+
67
+ pub const Statement = struct {
68
+ operation: Operation,
69
+ arguments: *std.ArrayListUnmanaged(u8),
70
+ };
71
+
72
+ fn print_current_position(parser: *const Parser) !void {
73
+ const target = target: {
74
+ var position_cursor: usize = 0;
75
+ var position_line: usize = 1;
76
+ var lines = std.mem.splitScalar(u8, parser.input, '\n');
77
+ while (lines.next()) |line| {
78
+ if (position_cursor + line.len >= parser.offset) {
79
+ break :target .{
80
+ .line = line,
81
+ .position_line = position_line,
82
+ .position_column = parser.offset - position_cursor,
83
+ };
84
+ } else {
85
+ position_line += 1;
86
+ position_cursor += line.len + 1; // +1 for trailing newline.
87
+ }
88
+ } else unreachable;
89
+ };
90
+
91
+ try parser.terminal.print_error("Fail near line {}, column {}:\n\n{s}\n", .{
92
+ target.position_line,
93
+ target.position_column,
94
+ target.line,
95
+ });
96
+ var column = target.position_column;
97
+ while (column > 0) {
98
+ try parser.terminal.print_error(" ", .{});
99
+ column -= 1;
100
+ }
101
+ try parser.terminal.print_error("^ Near here.\n\n", .{});
102
+ }
103
+
104
+ fn eat_whitespace(parser: *Parser) void {
105
+ while (parser.offset < parser.input.len and
106
+ std.ascii.isWhitespace(parser.input[parser.offset]))
107
+ {
108
+ parser.offset += 1;
109
+ }
110
+ }
111
+
112
+ fn parse_identifier(parser: *Parser) []const u8 {
113
+ parser.eat_whitespace();
114
+ const after_whitespace = parser.offset;
115
+
116
+ while (parser.offset < parser.input.len) {
117
+ const char_is_valid = switch (parser.input[parser.offset]) {
118
+ // Identifiers can contain any letter and `_`.
119
+ 'A'...'Z', 'a'...'z', '_' => true,
120
+ // It also may contain numbers, but not start with a number.
121
+ '0'...'9' => parser.offset > after_whitespace,
122
+ else => false,
123
+ };
124
+
125
+ if (!char_is_valid) break;
126
+ parser.offset += 1;
127
+ }
128
+
129
+ return parser.input[after_whitespace..parser.offset];
130
+ }
131
+
132
+ fn parse_syntax_char(parser: *Parser, syntax_char: u8) !void {
133
+ parser.eat_whitespace();
134
+
135
+ if (parser.offset < parser.input.len and
136
+ parser.input[parser.offset] == syntax_char)
137
+ {
138
+ parser.offset += 1;
139
+ return;
140
+ }
141
+
142
+ return Error.SyntaxMatchNone;
143
+ }
144
+
145
+ fn parse_value(parser: *Parser) []const u8 {
146
+ parser.eat_whitespace();
147
+ const after_whitespace = parser.offset;
148
+
149
+ while (parser.offset < parser.input.len) {
150
+ const c = parser.input[parser.offset];
151
+ if (!(std.ascii.isAlphanumeric(c) or c == '_' or c == '|' or c == '-')) {
152
+ // Allows flag fields to have whitespace before a '|'.
153
+ var copy = Parser{
154
+ .input = parser.input,
155
+ .offset = parser.offset,
156
+ .terminal = parser.terminal,
157
+ };
158
+ copy.eat_whitespace();
159
+ if (copy.offset < parser.input.len and parser.input[copy.offset] == '|') {
160
+ parser.offset = copy.offset;
161
+ continue;
162
+ }
163
+
164
+ // Allow flag fields to have whitespace after a '|'.
165
+ if (copy.offset < parser.input.len and
166
+ parser.offset > 0 and
167
+ parser.input[parser.offset - 1] == '|')
168
+ {
169
+ parser.offset = copy.offset;
170
+ continue;
171
+ }
172
+
173
+ break;
174
+ }
175
+
176
+ parser.offset += 1;
177
+ }
178
+
179
+ return parser.input[after_whitespace..parser.offset];
180
+ }
181
+
182
+ fn match_arg(
183
+ out: *ObjectSyntaxTree,
184
+ key_to_validate: []const u8,
185
+ value_to_validate: []const u8,
186
+ ) !void {
187
+ inline for (@typeInfo(ObjectSyntaxTree).@"union".fields) |object_syntax_tree_field| {
188
+ if (std.mem.eql(u8, @tagName(out.*), object_syntax_tree_field.name)) {
189
+ const active_value = @field(out, object_syntax_tree_field.name);
190
+ const ActiveValue = @TypeOf(active_value);
191
+
192
+ inline for (@typeInfo(ActiveValue).@"struct".fields) |active_value_field| {
193
+ if (std.mem.eql(u8, active_value_field.name, key_to_validate)) {
194
+ // Handle everything but flags, and skip reserved.
195
+ if (comptime (!std.mem.eql(u8, active_value_field.name, "flags") and
196
+ !std.mem.eql(u8, active_value_field.name, "reserved")))
197
+ {
198
+ @field(
199
+ @field(out.*, object_syntax_tree_field.name),
200
+ active_value_field.name,
201
+ ) = try parse_int(
202
+ active_value_field.type,
203
+ value_to_validate,
204
+ );
205
+ }
206
+
207
+ // Handle flags, specific to Account and Transfer fields.
208
+ if (comptime std.mem.eql(u8, active_value_field.name, "flags") and
209
+ @hasField(ActiveValue, "flags"))
210
+ {
211
+ var flags_to_validate = std.mem.splitScalar(u8, value_to_validate, '|');
212
+ var validated_flags =
213
+ std.mem.zeroInit(active_value_field.type, .{});
214
+ while (flags_to_validate.next()) |flag_to_validate| {
215
+ const flag_to_validate_trimmed = std.mem.trim(
216
+ u8,
217
+ flag_to_validate,
218
+ std.ascii.whitespace[0..],
219
+ );
220
+ inline for (@typeInfo(
221
+ active_value_field.type,
222
+ ).@"struct".fields) |known_flag_field| {
223
+ if (std.mem.eql(
224
+ u8,
225
+ known_flag_field.name,
226
+ flag_to_validate_trimmed,
227
+ )) {
228
+ if (comptime !std.mem.eql(
229
+ u8,
230
+ known_flag_field.name,
231
+ "padding",
232
+ )) {
233
+ @field(validated_flags, known_flag_field.name) = true;
234
+ }
235
+ }
236
+ }
237
+ }
238
+ @field(
239
+ @field(out.*, object_syntax_tree_field.name),
240
+ "flags",
241
+ ) = validated_flags;
242
+ }
243
+ }
244
+ }
245
+ }
246
+ }
247
+ }
248
+
249
+ fn parse_int(comptime T: type, input: []const u8) !T {
250
+ const info = @typeInfo(T);
251
+ comptime assert(info == .int);
252
+
253
+ // When base is zero the string prefix is examined to detect the true base:
254
+ // "0b", "0o" or "0x", otherwise base=10 is assumed.
255
+ const base_unknown = 0;
256
+
257
+ assert(input.len > 0);
258
+ const input_negative = input[0] == '-';
259
+
260
+ if (info.int.signedness == .unsigned and input_negative) {
261
+ // Negative input means `maxInt - input`.
262
+ // Useful for representing sentinels such as `AMOUNT_MAX`, as `-0`.
263
+ const max = std.math.maxInt(T);
264
+ return max - try std.fmt.parseUnsigned(T, input[1..], base_unknown);
265
+ }
266
+
267
+ return try std.fmt.parseUnsigned(T, input, base_unknown);
268
+ }
269
+
270
+ fn parse_arguments(
271
+ parser: *Parser,
272
+ operation: Operation,
273
+ arguments: *std.ArrayListUnmanaged(u8),
274
+ ) !void {
275
+ const default: ObjectSyntaxTree = switch (operation) {
276
+ .help, .none => return,
277
+ .create_accounts => .{ .account = std.mem.zeroInit(tb.Account, .{}) },
278
+ .create_transfers => .{ .transfer = std.mem.zeroInit(tb.Transfer, .{}) },
279
+ .lookup_accounts, .lookup_transfers => .{ .id = .{ .id = 0 } },
280
+ inline .get_account_transfers,
281
+ .get_account_balances,
282
+ => |operation_comptime| .{ .account_filter = tb.AccountFilter{
283
+ .account_id = 0,
284
+ .user_data_128 = 0,
285
+ .user_data_64 = 0,
286
+ .user_data_32 = 0,
287
+ .code = 0,
288
+ .timestamp_min = 0,
289
+ .timestamp_max = 0,
290
+ .limit = operation_comptime.state_machine_op().result_max(
291
+ constants.message_body_size_max,
292
+ ),
293
+ .flags = .{
294
+ .credits = true,
295
+ .debits = true,
296
+ .reversed = false,
297
+ },
298
+ } },
299
+ inline .query_accounts,
300
+ .query_transfers,
301
+ => |operation_comptime| .{ .query_filter = tb.QueryFilter{
302
+ .user_data_128 = 0,
303
+ .user_data_64 = 0,
304
+ .user_data_32 = 0,
305
+ .ledger = 0,
306
+ .code = 0,
307
+ .timestamp_min = 0,
308
+ .timestamp_max = 0,
309
+ .limit = operation_comptime.state_machine_op().result_max(
310
+ constants.message_body_size_max,
311
+ ),
312
+ .flags = .{
313
+ .reversed = false,
314
+ },
315
+ } },
316
+ };
317
+ var object = default;
318
+
319
+ var object_has_fields = false;
320
+ while (parser.offset < parser.input.len) {
321
+ parser.eat_whitespace();
322
+ // Always need to check i against length in case we've hit the end.
323
+ if (parser.offset >= parser.input.len or parser.input[parser.offset] == ';') {
324
+ break;
325
+ }
326
+
327
+ // Expect comma separating objects.
328
+ if (parser.offset < parser.input.len and parser.input[parser.offset] == ',') {
329
+ parser.offset += 1;
330
+ inline for (@typeInfo(ObjectSyntaxTree).@"union".fields) |object_tree_field| {
331
+ if (std.mem.eql(u8, @tagName(object), object_tree_field.name)) {
332
+ const unwrapped_field = @field(object, object_tree_field.name);
333
+ arguments.appendSliceAssumeCapacity(std.mem.asBytes(&unwrapped_field));
334
+ }
335
+ }
336
+
337
+ const state_machine_op = operation.state_machine_op();
338
+ if (!state_machine_op.is_batchable()) {
339
+ try parser.print_current_position();
340
+ try parser.terminal.print_error(
341
+ "{s} expects a single {s} but received multiple.\n",
342
+ .{ @tagName(operation), @tagName(object) },
343
+ );
344
+ return error.SliceOperationUnsupported;
345
+ }
346
+
347
+ // Reset object.
348
+ object = default;
349
+ object_has_fields = false;
350
+ }
351
+
352
+ // Grab key.
353
+ const id_result = parser.parse_identifier();
354
+
355
+ if (id_result.len == 0) {
356
+ try parser.print_current_position();
357
+ try parser.terminal.print_error(
358
+ "Expected key starting key-value pair. e.g. `id=1`\n",
359
+ .{},
360
+ );
361
+ return Error.IdentifierBad;
362
+ }
363
+
364
+ // Grab =.
365
+ parser.parse_syntax_char('=') catch {
366
+ try parser.print_current_position();
367
+ try parser.terminal.print_error(
368
+ "Expected equal sign after key '{s}' in key-value" ++
369
+ " pair. e.g. `id=1`.\n",
370
+ .{id_result},
371
+ );
372
+ return Error.KeyValuePairEqualMissing;
373
+ };
374
+
375
+ // Grab value.
376
+ const value_result = parser.parse_value();
377
+
378
+ if (value_result.len == 0) {
379
+ try parser.print_current_position();
380
+ try parser.terminal.print_error(
381
+ "Expected value after equal sign in key-value pair. e.g. `id=1`.\n",
382
+ .{},
383
+ );
384
+ return Error.ValueBad;
385
+ }
386
+
387
+ // Match key to a field in the struct.
388
+ match_arg(&object, id_result, value_result) catch {
389
+ try parser.print_current_position();
390
+ try parser.terminal.print_error(
391
+ "'{s}'='{s}' is not a valid pair for {s}.\n",
392
+ .{ id_result, value_result, @tagName(object) },
393
+ );
394
+ return Error.KeyValuePairBad;
395
+ };
396
+
397
+ object_has_fields = true;
398
+ }
399
+
400
+ // Add final object.
401
+ if (object_has_fields) {
402
+ inline for (@typeInfo(ObjectSyntaxTree).@"union".fields) |object_tree_field| {
403
+ if (std.mem.eql(u8, @tagName(object), object_tree_field.name)) {
404
+ const unwrapped_field = @field(object, object_tree_field.name);
405
+ arguments.appendSliceAssumeCapacity(std.mem.asBytes(&unwrapped_field));
406
+ }
407
+ }
408
+ }
409
+ }
410
+
411
+ // Statement grammar parsed here.
412
+ // STATEMENT: OPERATION ARGUMENTS [;]
413
+ // OPERATION: create_accounts | lookup_accounts | create_transfers | lookup_transfers
414
+ // ARGUMENTS: ARG [, ARG]
415
+ // ARG: KEY = VALUE
416
+ // KEY: string
417
+ // VALUE: string [| VALUE]
418
+ //
419
+ // For example:
420
+ // create_accounts id=1 code=2 ledger=3, id = 2 code= 2 ledger =3;
421
+ // create_accounts flags=linked | debits_must_not_exceed_credits ;
422
+ pub fn parse_statement(
423
+ input: []const u8,
424
+ terminal: *const Terminal,
425
+ arguments: *std.ArrayListUnmanaged(u8),
426
+ ) (error{OutOfMemory} || std.fs.File.WriteError || Error)!Statement {
427
+ var parser = Parser{ .input = input, .terminal = terminal };
428
+ parser.eat_whitespace();
429
+ const after_whitespace = parser.offset;
430
+ const operation_identifier = parser.parse_identifier();
431
+
432
+ const operation = operation: {
433
+ if (std.meta.stringToEnum(Operation, operation_identifier)) |valid_operation| {
434
+ break :operation valid_operation;
435
+ }
436
+
437
+ if (operation_identifier.len == 0) {
438
+ break :operation .none;
439
+ }
440
+
441
+ // Set up the offset to after the whitespace so the
442
+ // print_current_position function points at where we actually expected the
443
+ // token.
444
+ parser.offset = after_whitespace;
445
+ try parser.print_current_position();
446
+ try parser.terminal.print_error(
447
+ "Operation must be " ++
448
+ comptime operations: {
449
+ var names: []const u8 = "";
450
+ for (std.enums.values(Operation), 0..) |operation, index| {
451
+ if (operation == .none) continue;
452
+ names = names ++
453
+ (if (names.len > 0) ", " else "") ++
454
+ (if (index == std.enums.values(Operation).len - 1) "or " else "") ++
455
+ @tagName(operation);
456
+ }
457
+ break :operations names;
458
+ } ++ ". Got: '{s}'.\n",
459
+ .{operation_identifier},
460
+ );
461
+ return Error.OperationBad;
462
+ };
463
+
464
+ try parser.parse_arguments(operation, arguments);
465
+
466
+ return Statement{
467
+ .operation = operation,
468
+ .arguments = arguments,
469
+ };
470
+ }
471
+ };
472
+
473
+ const null_terminal = Terminal{
474
+ .mode_start = null,
475
+ .stdin = undefined,
476
+ .stderr = null,
477
+ .stdout = null,
478
+ };
479
+
480
+ test "parser.zig: Parser single transfer successfully" {
481
+ const vectors = [_]struct {
482
+ string: []const u8 = "",
483
+ result: tb.Transfer,
484
+ }{
485
+ .{
486
+ .string = "create_transfers id=1",
487
+ .result = tb.Transfer{
488
+ .id = 1,
489
+ .debit_account_id = 0,
490
+ .credit_account_id = 0,
491
+ .amount = 0,
492
+ .pending_id = 0,
493
+ .user_data_128 = 0,
494
+ .user_data_64 = 0,
495
+ .user_data_32 = 0,
496
+ .timeout = 0,
497
+ .ledger = 0,
498
+ .code = 0,
499
+ .flags = .{},
500
+ .timestamp = 0,
501
+ },
502
+ },
503
+ .{
504
+ .string = "create_transfers timestamp=1",
505
+ .result = tb.Transfer{
506
+ .id = 0,
507
+ .debit_account_id = 0,
508
+ .credit_account_id = 0,
509
+ .amount = 0,
510
+ .pending_id = 0,
511
+ .user_data_128 = 0,
512
+ .user_data_64 = 0,
513
+ .user_data_32 = 0,
514
+ .timeout = 0,
515
+ .ledger = 0,
516
+ .code = 0,
517
+ .flags = .{},
518
+ .timestamp = 1,
519
+ },
520
+ },
521
+ .{
522
+ .string =
523
+ \\create_transfers id=32 amount=65 ledger=12 code=9999 pending_id=7
524
+ \\ credit_account_id=2121 debit_account_id=77 user_data_128=2
525
+ \\ user_data_64=3 user_data_32=4 flags=linked
526
+ ,
527
+ .result = tb.Transfer{
528
+ .id = 32,
529
+ .debit_account_id = 77,
530
+ .credit_account_id = 2121,
531
+ .amount = 65,
532
+ .pending_id = 7,
533
+ .user_data_128 = 2,
534
+ .user_data_64 = 3,
535
+ .user_data_32 = 4,
536
+ .timeout = 0,
537
+ .ledger = 12,
538
+ .code = 9999,
539
+ .flags = .{ .linked = true },
540
+ .timestamp = 0,
541
+ },
542
+ },
543
+ .{
544
+ .string =
545
+ \\create_transfers flags=
546
+ \\ post_pending_transfer |
547
+ \\ balancing_credit |
548
+ \\ balancing_debit |
549
+ \\ void_pending_transfer |
550
+ \\ pending |
551
+ \\ linked
552
+ ,
553
+ .result = tb.Transfer{
554
+ .id = 0,
555
+ .debit_account_id = 0,
556
+ .credit_account_id = 0,
557
+ .amount = 0,
558
+ .pending_id = 0,
559
+ .user_data_128 = 0,
560
+ .user_data_64 = 0,
561
+ .user_data_32 = 0,
562
+ .timeout = 0,
563
+ .ledger = 0,
564
+ .code = 0,
565
+ .flags = .{
566
+ .post_pending_transfer = true,
567
+ .balancing_credit = true,
568
+ .balancing_debit = true,
569
+ .void_pending_transfer = true,
570
+ .pending = true,
571
+ .linked = true,
572
+ },
573
+ .timestamp = 0,
574
+ },
575
+ },
576
+ .{
577
+ .string =
578
+ \\create_transfers amount=-0
579
+ ,
580
+ .result = tb.Transfer{
581
+ .id = 0,
582
+ .debit_account_id = 0,
583
+ .credit_account_id = 0,
584
+ .amount = std.math.maxInt(u128),
585
+ .pending_id = 0,
586
+ .user_data_128 = 0,
587
+ .user_data_64 = 0,
588
+ .user_data_32 = 0,
589
+ .timeout = 0,
590
+ .ledger = 0,
591
+ .code = 0,
592
+ .flags = .{},
593
+ .timestamp = 0,
594
+ },
595
+ },
596
+ .{
597
+ .string =
598
+ \\create_transfers amount=-1
599
+ ,
600
+ .result = tb.Transfer{
601
+ .id = 0,
602
+ .debit_account_id = 0,
603
+ .credit_account_id = 0,
604
+ .amount = std.math.maxInt(u128) - 1,
605
+ .pending_id = 0,
606
+ .user_data_128 = 0,
607
+ .user_data_64 = 0,
608
+ .user_data_32 = 0,
609
+ .timeout = 0,
610
+ .ledger = 0,
611
+ .code = 0,
612
+ .flags = .{},
613
+ .timestamp = 0,
614
+ },
615
+ },
616
+ .{
617
+ .string =
618
+ \\create_transfers amount=0xbee71e
619
+ ,
620
+ .result = tb.Transfer{
621
+ .id = 0,
622
+ .debit_account_id = 0,
623
+ .credit_account_id = 0,
624
+ .amount = 0xbee71e,
625
+ .pending_id = 0,
626
+ .user_data_128 = 0,
627
+ .user_data_64 = 0,
628
+ .user_data_32 = 0,
629
+ .timeout = 0,
630
+ .ledger = 0,
631
+ .code = 0,
632
+ .flags = .{},
633
+ .timestamp = 0,
634
+ },
635
+ },
636
+ .{
637
+ .string =
638
+ \\create_transfers amount=1_000_000
639
+ ,
640
+ .result = tb.Transfer{
641
+ .id = 0,
642
+ .debit_account_id = 0,
643
+ .credit_account_id = 0,
644
+ .amount = 1_000_000,
645
+ .pending_id = 0,
646
+ .user_data_128 = 0,
647
+ .user_data_64 = 0,
648
+ .user_data_32 = 0,
649
+ .timeout = 0,
650
+ .ledger = 0,
651
+ .code = 0,
652
+ .flags = .{},
653
+ .timestamp = 0,
654
+ },
655
+ },
656
+ .{
657
+ .string =
658
+ \\create_transfers id=0xa1a2a3a4_b1b2_c1c2_d1d2_e1e2e3e4e5e6
659
+ ,
660
+ .result = tb.Transfer{
661
+ .id = 0xa1a2a3a4_b1b2_c1c2_d1d2_e1e2e3e4e5e6,
662
+ .debit_account_id = 0,
663
+ .credit_account_id = 0,
664
+ .amount = 0,
665
+ .pending_id = 0,
666
+ .user_data_128 = 0,
667
+ .user_data_64 = 0,
668
+ .user_data_32 = 0,
669
+ .timeout = 0,
670
+ .ledger = 0,
671
+ .code = 0,
672
+ .flags = .{},
673
+ .timestamp = 0,
674
+ },
675
+ },
676
+ };
677
+
678
+ for (vectors) |vector| {
679
+ var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
680
+ defer arena.deinit();
681
+
682
+ const allocator = arena.allocator();
683
+
684
+ var arguments = try std.ArrayListUnmanaged(u8).initCapacity(
685
+ allocator,
686
+ constants.message_size_max,
687
+ );
688
+ errdefer arguments.deinit(allocator);
689
+
690
+ const statement = try Parser.parse_statement(
691
+ vector.string,
692
+ &null_terminal,
693
+ &arguments,
694
+ );
695
+
696
+ try std.testing.expectEqual(statement.operation, .create_transfers);
697
+ try std.testing.expectEqualSlices(
698
+ u8,
699
+ statement.arguments.items,
700
+ std.mem.asBytes(&vector.result),
701
+ );
702
+ }
703
+ }
704
+
705
+ test "parser.zig: Parser multiple transfers successfully" {
706
+ const vectors = [_]struct {
707
+ string: []const u8 = "",
708
+ result: [2]tb.Transfer,
709
+ }{
710
+ .{
711
+ .string = "create_transfers id=1 debit_account_id=2, id=2 credit_account_id = 1;",
712
+ .result = [2]tb.Transfer{
713
+ tb.Transfer{
714
+ .id = 1,
715
+ .debit_account_id = 2,
716
+ .credit_account_id = 0,
717
+ .amount = 0,
718
+ .pending_id = 0,
719
+ .user_data_128 = 0,
720
+ .user_data_64 = 0,
721
+ .user_data_32 = 0,
722
+ .timeout = 0,
723
+ .ledger = 0,
724
+ .code = 0,
725
+ .flags = .{},
726
+ .timestamp = 0,
727
+ },
728
+ tb.Transfer{
729
+ .id = 2,
730
+ .debit_account_id = 0,
731
+ .credit_account_id = 1,
732
+ .amount = 0,
733
+ .pending_id = 0,
734
+ .user_data_128 = 0,
735
+ .user_data_64 = 0,
736
+ .user_data_32 = 0,
737
+ .timeout = 0,
738
+ .ledger = 0,
739
+ .code = 0,
740
+ .flags = .{},
741
+ .timestamp = 0,
742
+ },
743
+ },
744
+ },
745
+ };
746
+
747
+ for (vectors) |vector| {
748
+ var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
749
+ defer arena.deinit();
750
+
751
+ const allocator = arena.allocator();
752
+
753
+ var arguments = try std.ArrayListUnmanaged(u8).initCapacity(
754
+ allocator,
755
+ constants.message_size_max,
756
+ );
757
+ errdefer arguments.deinit(allocator);
758
+
759
+ const statement = try Parser.parse_statement(
760
+ vector.string,
761
+ &null_terminal,
762
+ &arguments,
763
+ );
764
+
765
+ try std.testing.expectEqual(statement.operation, .create_transfers);
766
+ try std.testing.expectEqualSlices(
767
+ u8,
768
+ statement.arguments.items,
769
+ std.mem.sliceAsBytes(&vector.result),
770
+ );
771
+ }
772
+ }
773
+
774
+ test "parser.zig: Parser single account successfully" {
775
+ const vectors = [_]struct {
776
+ string: []const u8,
777
+ result: tb.Account,
778
+ }{
779
+ .{
780
+ .string = "create_accounts id=1",
781
+ .result = tb.Account{
782
+ .id = 1,
783
+ .debits_pending = 0,
784
+ .debits_posted = 0,
785
+ .credits_pending = 0,
786
+ .credits_posted = 0,
787
+ .user_data_128 = 0,
788
+ .user_data_64 = 0,
789
+ .user_data_32 = 0,
790
+ .reserved = 0,
791
+ .ledger = 0,
792
+ .code = 0,
793
+ .flags = .{},
794
+ .timestamp = 0,
795
+ },
796
+ },
797
+ .{
798
+ .string =
799
+ \\create_accounts id=32 credits_posted=344 ledger=12 credits_pending=18
800
+ \\ code=9999 flags=linked | debits_must_not_exceed_credits debits_posted=3390
801
+ \\ debits_pending=3212 user_data_128=2 user_data_64=3 user_data_32=4
802
+ ,
803
+ .result = tb.Account{
804
+ .id = 32,
805
+ .debits_pending = 3212,
806
+ .debits_posted = 3390,
807
+ .credits_pending = 18,
808
+ .credits_posted = 344,
809
+ .user_data_128 = 2,
810
+ .user_data_64 = 3,
811
+ .user_data_32 = 4,
812
+ .reserved = 0,
813
+ .ledger = 12,
814
+ .code = 9999,
815
+ .flags = .{ .linked = true, .debits_must_not_exceed_credits = true },
816
+ .timestamp = 0,
817
+ },
818
+ },
819
+ .{
820
+ .string =
821
+ \\create_accounts flags=credits_must_not_exceed_debits|
822
+ \\ linked|debits_must_not_exceed_credits id =1
823
+ ,
824
+ .result = tb.Account{
825
+ .id = 1,
826
+ .debits_pending = 0,
827
+ .debits_posted = 0,
828
+ .credits_pending = 0,
829
+ .credits_posted = 0,
830
+ .user_data_128 = 0,
831
+ .user_data_64 = 0,
832
+ .user_data_32 = 0,
833
+ .reserved = 0,
834
+ .ledger = 0,
835
+ .code = 0,
836
+ .flags = .{
837
+ .credits_must_not_exceed_debits = true,
838
+ .linked = true,
839
+ .debits_must_not_exceed_credits = true,
840
+ },
841
+ .timestamp = 0,
842
+ },
843
+ },
844
+ };
845
+
846
+ for (vectors) |vector| {
847
+ var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
848
+ defer arena.deinit();
849
+
850
+ const allocator = arena.allocator();
851
+
852
+ var arguments = try std.ArrayListUnmanaged(u8).initCapacity(
853
+ allocator,
854
+ constants.message_size_max,
855
+ );
856
+ errdefer arguments.deinit(allocator);
857
+
858
+ const statement = try Parser.parse_statement(vector.string, &null_terminal, &arguments);
859
+
860
+ try std.testing.expectEqual(statement.operation, .create_accounts);
861
+ try std.testing.expectEqualSlices(
862
+ u8,
863
+ statement.arguments.items,
864
+ std.mem.asBytes(&vector.result),
865
+ );
866
+ }
867
+ }
868
+
869
+ test "parser.zig: Parser account filter successfully" {
870
+ const vectors = [_]struct {
871
+ string: []const u8,
872
+ operation: Parser.Operation,
873
+ result: tb.AccountFilter,
874
+ }{
875
+ .{
876
+ .string = "get_account_transfers account_id=1",
877
+ .operation = .get_account_transfers,
878
+ .result = tb.AccountFilter{
879
+ .account_id = 1,
880
+ .user_data_128 = 0,
881
+ .user_data_64 = 0,
882
+ .user_data_32 = 0,
883
+ .code = 0,
884
+ .timestamp_min = 0,
885
+ .timestamp_max = 0,
886
+ .limit = StateMachine.Operation.get_account_transfers.result_max(
887
+ constants.message_body_size_max,
888
+ ),
889
+ .flags = .{
890
+ .credits = true,
891
+ .debits = true,
892
+ .reversed = false,
893
+ },
894
+ },
895
+ },
896
+ .{
897
+ .string =
898
+ \\get_account_balances account_id=1000
899
+ \\user_data_128=128 user_data_64=64 user_data_32=32
900
+ \\code=2
901
+ \\flags=debits|reversed limit=10
902
+ \\timestamp_min=1 timestamp_max=9999;
903
+ \\
904
+ ,
905
+ .operation = .get_account_balances,
906
+ .result = tb.AccountFilter{
907
+ .account_id = 1000,
908
+ .user_data_128 = 128,
909
+ .user_data_64 = 64,
910
+ .user_data_32 = 32,
911
+ .code = 2,
912
+ .timestamp_min = 1,
913
+ .timestamp_max = 9999,
914
+ .limit = 10,
915
+ .flags = .{
916
+ .credits = false,
917
+ .debits = true,
918
+ .reversed = true,
919
+ },
920
+ },
921
+ },
922
+ };
923
+
924
+ for (vectors) |vector| {
925
+ var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
926
+ defer arena.deinit();
927
+
928
+ const allocator = arena.allocator();
929
+
930
+ var arguments = try std.ArrayListUnmanaged(u8).initCapacity(
931
+ allocator,
932
+ constants.message_size_max,
933
+ );
934
+ errdefer arguments.deinit(allocator);
935
+
936
+ const statement = try Parser.parse_statement(vector.string, &null_terminal, &arguments);
937
+
938
+ try std.testing.expectEqual(statement.operation, vector.operation);
939
+ try std.testing.expectEqualSlices(
940
+ u8,
941
+ statement.arguments.items,
942
+ std.mem.asBytes(&vector.result),
943
+ );
944
+ }
945
+ }
946
+
947
+ test "parser.zig: Parser query filter successfully" {
948
+ const vectors = [_]struct {
949
+ string: []const u8,
950
+ operation: Parser.Operation,
951
+ result: tb.QueryFilter,
952
+ }{
953
+ .{
954
+ .string = "query_transfers user_data_128=1",
955
+ .operation = .query_transfers,
956
+ .result = tb.QueryFilter{
957
+ .user_data_128 = 1,
958
+ .user_data_64 = 0,
959
+ .user_data_32 = 0,
960
+ .ledger = 0,
961
+ .code = 0,
962
+ .timestamp_min = 0,
963
+ .timestamp_max = 0,
964
+ .limit = StateMachine.Operation.query_transfers.result_max(
965
+ constants.message_body_size_max,
966
+ ),
967
+ .flags = .{
968
+ .reversed = false,
969
+ },
970
+ },
971
+ },
972
+ .{
973
+ .string =
974
+ \\query_accounts user_data_128=1000
975
+ \\user_data_64=100 user_data_32=10
976
+ \\ledger=1 code=2
977
+ \\flags=reversed limit=10
978
+ \\timestamp_min=1 timestamp_max=9999;
979
+ \\
980
+ ,
981
+ .operation = .query_accounts,
982
+ .result = tb.QueryFilter{
983
+ .user_data_128 = 1000,
984
+ .user_data_64 = 100,
985
+ .user_data_32 = 10,
986
+ .ledger = 1,
987
+ .code = 2,
988
+ .timestamp_min = 1,
989
+ .timestamp_max = 9999,
990
+ .limit = 10,
991
+ .flags = .{
992
+ .reversed = true,
993
+ },
994
+ },
995
+ },
996
+ };
997
+
998
+ for (vectors) |vector| {
999
+ var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
1000
+ defer arena.deinit();
1001
+
1002
+ const allocator = arena.allocator();
1003
+
1004
+ var arguments = try std.ArrayListUnmanaged(u8).initCapacity(
1005
+ allocator,
1006
+ constants.message_size_max,
1007
+ );
1008
+ errdefer arguments.deinit(allocator);
1009
+
1010
+ const statement = try Parser.parse_statement(
1011
+ vector.string,
1012
+ &null_terminal,
1013
+ &arguments,
1014
+ );
1015
+
1016
+ try std.testing.expectEqual(statement.operation, vector.operation);
1017
+ try std.testing.expectEqualSlices(
1018
+ u8,
1019
+ statement.arguments.items,
1020
+ std.mem.asBytes(&vector.result),
1021
+ );
1022
+ }
1023
+ }
1024
+
1025
+ test "parser.zig: Parser multiple accounts successfully" {
1026
+ const vectors = [_]struct {
1027
+ string: []const u8,
1028
+ result: [2]tb.Account,
1029
+ }{
1030
+ .{
1031
+ .string = "create_accounts id=1, id=2",
1032
+ .result = [2]tb.Account{
1033
+ tb.Account{
1034
+ .id = 1,
1035
+ .debits_pending = 0,
1036
+ .debits_posted = 0,
1037
+ .credits_pending = 0,
1038
+ .credits_posted = 0,
1039
+ .user_data_128 = 0,
1040
+ .user_data_64 = 0,
1041
+ .user_data_32 = 0,
1042
+ .reserved = 0,
1043
+ .ledger = 0,
1044
+ .code = 0,
1045
+ .flags = .{},
1046
+ .timestamp = 0,
1047
+ },
1048
+ tb.Account{
1049
+ .id = 2,
1050
+ .debits_pending = 0,
1051
+ .debits_posted = 0,
1052
+ .credits_pending = 0,
1053
+ .credits_posted = 0,
1054
+ .user_data_128 = 0,
1055
+ .user_data_64 = 0,
1056
+ .user_data_32 = 0,
1057
+ .reserved = 0,
1058
+ .ledger = 0,
1059
+ .code = 0,
1060
+ .flags = .{},
1061
+ .timestamp = 0,
1062
+ },
1063
+ },
1064
+ },
1065
+ };
1066
+
1067
+ for (vectors) |vector| {
1068
+ var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
1069
+ defer arena.deinit();
1070
+
1071
+ const allocator = arena.allocator();
1072
+
1073
+ var arguments = try std.ArrayListUnmanaged(u8).initCapacity(
1074
+ allocator,
1075
+ constants.message_size_max,
1076
+ );
1077
+ errdefer arguments.deinit(allocator);
1078
+
1079
+ const statement = try Parser.parse_statement(
1080
+ vector.string,
1081
+ &null_terminal,
1082
+ &arguments,
1083
+ );
1084
+
1085
+ try std.testing.expectEqual(statement.operation, .create_accounts);
1086
+ try std.testing.expectEqualSlices(
1087
+ u8,
1088
+ statement.arguments.items,
1089
+ std.mem.sliceAsBytes(&vector.result),
1090
+ );
1091
+ }
1092
+ }
1093
+
1094
+ test "parser.zig: Parser odd but correct formatting" {
1095
+ const vectors = [_]struct {
1096
+ string: []const u8 = "",
1097
+ result: tb.Transfer,
1098
+ }{
1099
+ // Space between key-value pair and equality
1100
+ .{
1101
+ .string = "create_transfers id = 1",
1102
+ .result = tb.Transfer{
1103
+ .id = 1,
1104
+ .debit_account_id = 0,
1105
+ .credit_account_id = 0,
1106
+ .amount = 0,
1107
+ .pending_id = 0,
1108
+ .user_data_128 = 0,
1109
+ .user_data_64 = 0,
1110
+ .user_data_32 = 0,
1111
+ .timeout = 0,
1112
+ .ledger = 0,
1113
+ .code = 0,
1114
+ .flags = .{},
1115
+ .timestamp = 0,
1116
+ },
1117
+ },
1118
+ // Space only before equals sign
1119
+ .{
1120
+ .string = "create_transfers id =1",
1121
+ .result = tb.Transfer{
1122
+ .id = 1,
1123
+ .debit_account_id = 0,
1124
+ .credit_account_id = 0,
1125
+ .amount = 0,
1126
+ .pending_id = 0,
1127
+ .user_data_128 = 0,
1128
+ .user_data_64 = 0,
1129
+ .user_data_32 = 0,
1130
+ .timeout = 0,
1131
+ .ledger = 0,
1132
+ .code = 0,
1133
+ .flags = .{},
1134
+ .timestamp = 0,
1135
+ },
1136
+ },
1137
+ // Whitespace before command
1138
+ .{
1139
+ .string = " \t \n create_transfers id=1",
1140
+ .result = tb.Transfer{
1141
+ .id = 1,
1142
+ .debit_account_id = 0,
1143
+ .credit_account_id = 0,
1144
+ .amount = 0,
1145
+ .pending_id = 0,
1146
+ .user_data_128 = 0,
1147
+ .user_data_64 = 0,
1148
+ .user_data_32 = 0,
1149
+ .timeout = 0,
1150
+ .ledger = 0,
1151
+ .code = 0,
1152
+ .flags = .{},
1153
+ .timestamp = 0,
1154
+ },
1155
+ },
1156
+ // Trailing semicolon
1157
+ .{
1158
+ .string = "create_transfers id=1;",
1159
+ .result = tb.Transfer{
1160
+ .id = 1,
1161
+ .debit_account_id = 0,
1162
+ .credit_account_id = 0,
1163
+ .amount = 0,
1164
+ .pending_id = 0,
1165
+ .user_data_128 = 0,
1166
+ .user_data_64 = 0,
1167
+ .user_data_32 = 0,
1168
+ .timeout = 0,
1169
+ .ledger = 0,
1170
+ .code = 0,
1171
+ .flags = .{},
1172
+ .timestamp = 0,
1173
+ },
1174
+ },
1175
+ // Spaces everywhere
1176
+ .{
1177
+ .string =
1178
+ \\
1179
+ \\
1180
+ \\ create_transfers
1181
+ \\ id = 1
1182
+ \\ user_data_128 = 12
1183
+ \\ debit_account_id=1 credit_account_id = 10
1184
+ \\ ;
1185
+ \\
1186
+ \\
1187
+ ,
1188
+ .result = tb.Transfer{
1189
+ .id = 1,
1190
+ .debit_account_id = 1,
1191
+ .credit_account_id = 10,
1192
+ .amount = 0,
1193
+ .pending_id = 0,
1194
+ .user_data_128 = 12,
1195
+ .user_data_64 = 0,
1196
+ .user_data_32 = 0,
1197
+ .timeout = 0,
1198
+ .ledger = 0,
1199
+ .code = 0,
1200
+ .flags = .{},
1201
+ .timestamp = 0,
1202
+ },
1203
+ },
1204
+ };
1205
+
1206
+ for (vectors) |vector| {
1207
+ var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
1208
+ defer arena.deinit();
1209
+
1210
+ const allocator = arena.allocator();
1211
+
1212
+ var arguments = try std.ArrayListUnmanaged(u8).initCapacity(
1213
+ allocator,
1214
+ constants.message_size_max,
1215
+ );
1216
+ errdefer arguments.deinit(allocator);
1217
+
1218
+ const statement = try Parser.parse_statement(
1219
+ vector.string,
1220
+ &null_terminal,
1221
+ &arguments,
1222
+ );
1223
+
1224
+ try std.testing.expectEqual(statement.operation, .create_transfers);
1225
+ try std.testing.expectEqualSlices(
1226
+ u8,
1227
+ statement.arguments.items,
1228
+ std.mem.asBytes(&vector.result),
1229
+ );
1230
+ }
1231
+ }
1232
+
1233
+ test "parser.zig: Handle parsing errors" {
1234
+ const vectors = [_]struct {
1235
+ string: []const u8 = "",
1236
+ result: anyerror,
1237
+ }{
1238
+ .{
1239
+ .string = "create_trans",
1240
+ .result = error.OperationBad,
1241
+ },
1242
+ .{
1243
+ .string =
1244
+ \\
1245
+ \\
1246
+ \\ create
1247
+ ,
1248
+ .result = error.OperationBad,
1249
+ },
1250
+ .{
1251
+ .string = "create_transfers 12",
1252
+ .result = error.IdentifierBad,
1253
+ },
1254
+ .{
1255
+ .string = "create_transfers =12",
1256
+ .result = error.IdentifierBad,
1257
+ },
1258
+ .{
1259
+ .string = "create_transfers x",
1260
+ .result = error.KeyValuePairEqualMissing,
1261
+ },
1262
+ .{
1263
+ .string = "create_transfers x=",
1264
+ .result = error.ValueBad,
1265
+ },
1266
+ .{
1267
+ .string = "create_transfers x= ",
1268
+ .result = error.ValueBad,
1269
+ },
1270
+ .{
1271
+ .string = "create_transfers x= ;",
1272
+ .result = error.ValueBad,
1273
+ },
1274
+ .{
1275
+ .string = "create_transfers x=[]",
1276
+ .result = error.ValueBad,
1277
+ },
1278
+ .{
1279
+ .string = "create_transfers id=abcd",
1280
+ .result = error.KeyValuePairBad,
1281
+ },
1282
+ .{
1283
+ .string = "create_transfers amount=0y1234",
1284
+ .result = error.KeyValuePairBad,
1285
+ },
1286
+ .{
1287
+ .string = "create_transfers amount=--0",
1288
+ .result = error.KeyValuePairBad,
1289
+ },
1290
+ };
1291
+
1292
+ for (vectors) |vector| {
1293
+ var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
1294
+ defer arena.deinit();
1295
+
1296
+ const allocator = arena.allocator();
1297
+
1298
+ var arguments = try std.ArrayListUnmanaged(u8).initCapacity(
1299
+ allocator,
1300
+ constants.message_size_max,
1301
+ );
1302
+ errdefer arguments.deinit(allocator);
1303
+
1304
+ const result = Parser.parse_statement(
1305
+ vector.string,
1306
+ &null_terminal,
1307
+ &arguments,
1308
+ );
1309
+ try std.testing.expectError(vector.result, result);
1310
+ }
1311
+ }
1312
+
1313
+ test "parser.zig: Parser fails for operations not supporting multiple objects" {
1314
+ const vectors = [_]struct {
1315
+ string: []const u8,
1316
+ result: anyerror,
1317
+ }{
1318
+ .{
1319
+ .string = "get_account_transfers account_id=1, account_id=2",
1320
+ .result = error.SliceOperationUnsupported,
1321
+ },
1322
+ .{
1323
+ .string = "get_account_balances account_id=1, account_id=2",
1324
+ .result = error.SliceOperationUnsupported,
1325
+ },
1326
+ .{
1327
+ .string = "query_accounts account_id=1, account_id=2",
1328
+ .result = error.SliceOperationUnsupported,
1329
+ },
1330
+ .{
1331
+ .string = "query_transfers account_id=1, account_id=2",
1332
+ .result = error.SliceOperationUnsupported,
1333
+ },
1334
+ };
1335
+
1336
+ for (vectors) |vector| {
1337
+ var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
1338
+ defer arena.deinit();
1339
+
1340
+ const allocator = arena.allocator();
1341
+
1342
+ var arguments = try std.ArrayListUnmanaged(u8).initCapacity(
1343
+ allocator,
1344
+ constants.message_size_max,
1345
+ );
1346
+ errdefer arguments.deinit(allocator);
1347
+
1348
+ const result = Parser.parse_statement(
1349
+ vector.string,
1350
+ &null_terminal,
1351
+ &arguments,
1352
+ );
1353
+
1354
+ try std.testing.expectError(vector.result, result);
1355
+ }
1356
+ }