hecks 3.14.0 → 4.0.0

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 (419) hide show
  1. checksums.yaml +4 -4
  2. data/examples/pizzas/adapters/fake_processor.rb +34 -0
  3. data/examples/pizzas/adapters/payment_signature.rb +66 -0
  4. data/examples/pizzas/adapters/payment_webhook.rb +87 -0
  5. data/examples/pizzas/bluebook/pizzas.behaviors +33 -0
  6. data/examples/pizzas/bluebook/pizzas.hecksagon +5 -3
  7. data/examples/pizzas/config.ru +22 -0
  8. data/lib/hecks/adapters/driven/prism.rb +0 -2
  9. data/lib/hecks/adapters/driven.rb +0 -2
  10. data/lib/hecks/adapters/driving/json.rb +1 -7
  11. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +2 -2
  12. data/lib/hecks/bluebook/dsl/derivation_words.rb +58 -0
  13. data/lib/hecks/bluebook/dsl/translation_builder.rb +6 -37
  14. data/lib/hecks/bluebook/expression/ast_json.rb +0 -9
  15. data/lib/hecks/bluebook/hecksagon.rb +1 -1
  16. data/lib/hecks/bluebook/meta_validator/translation_judge/rules.rb +7 -10
  17. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +4 -8
  18. data/lib/hecks/bluebook/meta_validator.rb +0 -11
  19. data/lib/hecks/bluebook/translation.rb +18 -10
  20. data/lib/hecks/bluebook/translation_expression/evaluator.rb +63 -0
  21. data/lib/hecks/bluebook/translation_expression/parser.rb +127 -0
  22. data/lib/hecks/bluebook/translation_expression.rb +39 -0
  23. data/lib/hecks/bluebook/translation_plan.rb +70 -0
  24. data/lib/hecks/bluebook.rb +2 -0
  25. data/lib/hecks/chapters.rb +3 -3
  26. data/lib/hecks/composer/adapters/claude_composer.adapter +3 -0
  27. data/lib/hecks/composer/adapters/claude_composer.rb +83 -0
  28. data/lib/hecks/composer/adapters/fake_agent.adapter +3 -0
  29. data/lib/hecks/composer/adapters/fake_agent.rb +55 -0
  30. data/lib/hecks/composer/adapters/jev.adapter +3 -0
  31. data/lib/hecks/composer/adapters/jev.rb +66 -0
  32. data/lib/hecks/composer/agent.rb +113 -0
  33. data/lib/hecks/composer/assistant.rb +110 -0
  34. data/lib/hecks/composer/behavior_run.rb +53 -0
  35. data/lib/hecks/composer/bluebook/composer.bluebook +178 -0
  36. data/lib/hecks/composer/bluebook/composer.hecksagon +16 -0
  37. data/lib/hecks/composer/board.rb +65 -0
  38. data/lib/hecks/composer/board_edit/command_text.rb +39 -0
  39. data/lib/hecks/composer/board_edit/source.rb +84 -0
  40. data/lib/hecks/composer/board_edit.rb +144 -0
  41. data/lib/hecks/composer/check.rb +89 -0
  42. data/lib/hecks/composer/domains.rb +81 -0
  43. data/lib/hecks/composer/jobs.rb +134 -0
  44. data/lib/hecks/composer/layout.rb +37 -0
  45. data/lib/hecks/composer/live.rb +150 -0
  46. data/lib/hecks/composer/outline.rb +48 -0
  47. data/lib/hecks/composer/page.html +989 -0
  48. data/lib/hecks/composer/panels.rb +142 -0
  49. data/lib/hecks/composer/parse.rb +83 -0
  50. data/lib/hecks/composer/plain.rb +28 -0
  51. data/lib/hecks/composer/ports/composer_agent.port +7 -0
  52. data/lib/hecks/composer/session.rb +141 -0
  53. data/lib/hecks/composer/toolbox.rb +166 -0
  54. data/lib/hecks/composer/web.rb +156 -0
  55. data/lib/hecks/composer/workbench.rb +51 -0
  56. data/lib/hecks/composer/workspace.rb +81 -0
  57. data/lib/hecks/composer.rb +17 -0
  58. data/lib/hecks/corpus/adapters.rb +22 -0
  59. data/lib/hecks/corpus/rust.rb +1 -1
  60. data/lib/hecks/corpus/worlds.rb +19 -0
  61. data/lib/hecks/corpus.rb +5 -0
  62. data/lib/hecks/forms/app/commands.rb +2 -3
  63. data/lib/hecks/forms/app/queries.rb +2 -3
  64. data/lib/hecks/forms/app/records.rb +17 -5
  65. data/lib/hecks/forms/command_form_renderer.rb +42 -16
  66. data/lib/hecks/forms/field_controls.rb +2 -2
  67. data/lib/hecks/forms/field_renderer.rb +1 -1
  68. data/lib/hecks/forms/field_shape.rb +36 -17
  69. data/lib/hecks/forms/index_renderer.rb +2 -1
  70. data/lib/hecks/forms/page.rb +1 -1
  71. data/lib/hecks/forms/params.rb +0 -13
  72. data/lib/hecks/forms/query_form_renderer.rb +7 -6
  73. data/lib/hecks/forms/record_renderer.rb +11 -7
  74. data/lib/hecks/forms/rule_words.rb +100 -0
  75. data/lib/hecks/forms/value_object_shape.rb +2 -1
  76. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +0 -3
  77. data/lib/hecks/fuzzing/isolated_boot/worlds.rb +3 -6
  78. data/lib/hecks/fuzzing/replay/filters.rb +2 -6
  79. data/lib/hecks/gate/stages.yml +25 -0
  80. data/lib/hecks/grammar/evolve/seeds.rb +0 -6
  81. data/lib/hecks/grammar/evolve.rb +0 -3
  82. data/lib/hecks/grammar/translation.bluebook +1 -1
  83. data/lib/hecks/hecks/adapters/codebase/composing.rb +56 -0
  84. data/lib/hecks/hecks/adapters/codebase/source_tree.rb +3 -1
  85. data/lib/hecks/hecks/adapters/journal_store/changes.rb +0 -26
  86. data/lib/hecks/hecks/adapters/journal_store/examination.rb +2 -23
  87. data/lib/hecks/hecks/adapters/journal_store/readings.rb +3 -11
  88. data/lib/hecks/hecks/adapters/journal_store.rb +0 -11
  89. data/lib/hecks/hecks/adapters/rehearsal/inquiry.rb +4 -11
  90. data/lib/hecks/hecks/adapters/rehearsal/memory_store.rb +0 -9
  91. data/lib/hecks/hecks/adapters/rehearsal/postgres_reading.rb +0 -4
  92. data/lib/hecks/hecks/codebase.behaviors +6 -0
  93. data/lib/hecks/hecks/codebase.bluebook +21 -0
  94. data/lib/hecks/hecks/composer.behaviors +68 -0
  95. data/lib/hecks/hecks/context_map.hecksagon +1 -1
  96. data/lib/hecks/hecks/custodian.behaviors +0 -6
  97. data/lib/hecks/hecks/custodian.bluebook +3 -80
  98. data/lib/hecks/hecks/hecks.hecksagon +2 -2
  99. data/lib/hecks/hecks/hecks.world +1 -1
  100. data/lib/hecks/hecks/rehearsal.bluebook +7 -13
  101. data/lib/hecks/kernel_capabilities/templates.rb +4 -4
  102. data/lib/hecks/kernel_capabilities.rb +1 -1
  103. data/lib/hecks/language/bluebook/vocabulary.bluebook +2 -3
  104. data/lib/hecks/language/translation/translation_aggregate.bluebook +21 -24
  105. data/lib/hecks/ports/access_control.rb +2 -2
  106. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +3 -41
  107. data/lib/hecks/ports/persistence/plugins/era/lineage/coverage.rb +7 -7
  108. data/lib/hecks/ports/persistence/plugins/era/lineage/rewrite.rb +25 -2
  109. data/lib/hecks/ports/persistence/plugins/era/lineage/rules.rb +4 -4
  110. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +17 -25
  111. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/base_sql.rb +0 -12
  112. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/domain_rename.rb +1 -1
  113. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +2 -22
  114. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +0 -1
  115. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +9 -3
  116. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +3 -29
  117. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/refusals.rb +2 -26
  118. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +6 -33
  119. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +3 -3
  120. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +2 -4
  121. data/lib/hecks/ports/persistence/plugins/era/translation/expression_sql.rb +52 -0
  122. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +23 -29
  123. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +1 -1
  124. data/lib/hecks/ports/persistence/plugins/era/translation.rb +0 -1
  125. data/lib/hecks/ports/persistence/plugins/era.rb +3 -6
  126. data/lib/hecks/ports.rb +0 -1
  127. data/lib/hecks/projection_files.rb +3 -1
  128. data/lib/hecks/projections/deploy/templates/lambda/nat_comment_oauth.tmpl +1 -1
  129. data/lib/hecks/projections/glossary/entries.rb +10 -4
  130. data/lib/hecks/projections/glossary/groups.rb +42 -0
  131. data/lib/hecks/projections/glossary/html/page.rb +7 -2
  132. data/lib/hecks/projections/glossary/html/parser.rb +1 -1
  133. data/lib/hecks/projections/glossary/html/sections.rb +31 -11
  134. data/lib/hecks/projections/glossary/html.rb +1 -2
  135. data/lib/hecks/projections/glossary/markdown.rb +31 -28
  136. data/lib/hecks/projections/glossary/mermaid.rb +3 -0
  137. data/lib/hecks/projections/glossary/page.css +143 -153
  138. data/lib/hecks/projections/glossary/page.js +63 -3
  139. data/lib/hecks/projections/glossary/sentences/actions.rb +88 -0
  140. data/lib/hecks/projections/glossary/sentences/paths.rb +77 -0
  141. data/lib/hecks/projections/glossary/sentences/queries.rb +90 -0
  142. data/lib/hecks/projections/glossary/sentences/value_objects.rb +32 -5
  143. data/lib/hecks/projections/glossary/sentences.rb +13 -3
  144. data/lib/hecks/projections/glossary/slugs.rb +4 -1
  145. data/lib/hecks/projections/glossary.rb +2 -1
  146. data/lib/hecks/projections/plain_english.rb +97 -0
  147. data/lib/hecks/projections/rust_vocabulary/arguments.rb +2 -2
  148. data/lib/hecks/projections/rust_vocabulary/enum_body.rb +20 -25
  149. data/lib/hecks/projections/rust_vocabulary/tests.rb +2 -2
  150. data/lib/hecks/projections/rust_vocabulary.rb +14 -6
  151. data/lib/hecks/projections/site/cms_editor/color.rb +0 -6
  152. data/lib/hecks/projections/statements/actions.rb +99 -0
  153. data/lib/hecks/projections/statements/relationships.rb +86 -0
  154. data/lib/hecks/projections/statements.rb +15 -24
  155. data/lib/hecks/projector/exporter/adapters.rb +112 -0
  156. data/lib/hecks/projector/exporter/translations.rb +18 -35
  157. data/lib/hecks/projector/exporter/worlds.rb +103 -0
  158. data/lib/hecks/projector/exporter.rb +5 -1
  159. data/lib/hecks/quality_control/adapters/agent.rb +6 -7
  160. data/lib/hecks/quality_control/adapters/agent_profile.rb +16 -114
  161. data/lib/hecks/quality_control/cli/qa_mine_combinations.rb +1 -1
  162. data/lib/hecks/runtime/loader/boot_steps.rb +0 -20
  163. data/lib/hecks/runtime/outbox.rb +2 -9
  164. data/lib/hecks/runtime/port_operation_interpreter.rb +1 -9
  165. data/lib/hecks/runtime/remote_dispatcher.rb +2 -2
  166. data/lib/hecks/runtime/value.rb +13 -0
  167. data/lib/hecks/rust_build/project_rust/target_ir.rb +23 -4
  168. data/lib/hecks/rust_build/wasm.rb +0 -12
  169. data/lib/hecks/rust_build/write_if_changed.rb +12 -12
  170. data/lib/hecks/rust_format.rb +77 -0
  171. data/lib/hecks/tools/fuzz_sweep/findings.rb +3 -9
  172. data/lib/hecks/tools/fuzz_sweep.rb +1 -3
  173. data/lib/hecks/tools/translation_audit/verdicts.rb +3 -28
  174. data/lib/hecks/tools/translation_audit.rb +8 -10
  175. data/lib/hecks/tools/translation_scaffold/reporting.rb +1 -1
  176. data/lib/hecks/version.rb +1 -1
  177. data/lib/hecks/vocabulary.rb +2 -1
  178. data/lib/hecks.rb +3 -0
  179. data/prebuilt/{a883a7e568667aa5ac904c82daff356bdf9e996252a36af6def4a4a43efb967e.marshal → 02c09c73b272c1fb5a9ae6d87bb5e5e529208e00f6b4b597dc2c0fd70a9c0964.marshal} +0 -0
  180. data/prebuilt/{1a6f596be124228be93eba1d86dbc9d2933190fa389b284331241cb9f27115a0.marshal → bb84a835cf6a15136bad5ca03aa5bf93a5b92f5f72444618d3ef2412b987fb32.marshal} +0 -0
  181. data/prebuilt/{verdicts-872993b1aa26d73fe48f28251374b8585fed145d02aab42be0f00de386b6ff80.json → verdicts-f1f10da14c8e8f01b4c9b7e3e2d74be8cd2bf4676364ed17fa16975d2a076d75.json} +1 -1
  182. data/rust/Cargo.toml +40 -0
  183. data/rust/build/Cargo.toml +36 -0
  184. data/rust/build/clippy.toml +4 -0
  185. data/rust/build/src/adapters_pass.rs +196 -0
  186. data/rust/build/src/binding_pass.rs +54 -13
  187. data/rust/build/src/build_artifact.rs +23 -5
  188. data/rust/build/src/cargo_sync.rs +69 -26
  189. data/rust/build/src/lineage_pass.rs +101 -87
  190. data/rust/build/src/main.rs +11 -2
  191. data/rust/build/src/pipeline.rs +80 -19
  192. data/rust/build/src/reserved_names.rs +6 -50
  193. data/rust/build/src/resolve.rs +82 -35
  194. data/rust/build/src/rustfmt.rs +100 -0
  195. data/rust/build/src/seams_pass.rs +85 -16
  196. data/rust/build/src/subprocess.rs +30 -6
  197. data/rust/build/src/tmp.rs +5 -2
  198. data/rust/build/src/translation_pass.rs +72 -22
  199. data/rust/build/src/world_pass.rs +109 -0
  200. data/rust/clippy.toml +4 -0
  201. data/rust/codegen/Cargo.toml +36 -0
  202. data/rust/codegen/clippy.toml +4 -0
  203. data/rust/codegen/src/asks.rs +40 -14
  204. data/rust/codegen/src/attr.rs +2 -2
  205. data/rust/codegen/src/bridging.rs +676 -117
  206. data/rust/codegen/src/commands.rs +979 -373
  207. data/rust/codegen/src/constraints.rs +88 -25
  208. data/rust/codegen/src/dependency_planning.rs +128 -34
  209. data/rust/codegen/src/domain_generator.rs +300 -170
  210. data/rust/codegen/src/exemplar.rs +87 -32
  211. data/rust/codegen/src/expr_emitter.rs +54 -6
  212. data/rust/codegen/src/fielded.rs +193 -41
  213. data/rust/codegen/src/hecks_naming.rs +124 -16
  214. data/rust/codegen/src/hecks_naming_tests.rs +81 -0
  215. data/rust/codegen/src/json.rs +80 -44
  216. data/rust/codegen/src/json_codec.rs +585 -149
  217. data/rust/codegen/src/literal.rs +104 -138
  218. data/rust/codegen/src/main.rs +145 -52
  219. data/rust/codegen/src/manifest.rs +76 -14
  220. data/rust/codegen/src/mutations.rs +1212 -239
  221. data/rust/codegen/src/naming.rs +111 -60
  222. data/rust/codegen/src/optional_pass.rs +90 -28
  223. data/rust/codegen/src/ports.rs +5 -4
  224. data/rust/codegen/src/prelude.rs +139 -49
  225. data/rust/codegen/src/queries.rs +601 -226
  226. data/rust/codegen/src/reactions.rs +268 -74
  227. data/rust/codegen/src/read_models.rs +606 -205
  228. data/rust/codegen/src/reference_specs.rs +16 -3
  229. data/rust/codegen/src/registry.rs +128 -79
  230. data/rust/codegen/src/reserved_names.rs +6 -50
  231. data/rust/codegen/src/shared.rs +71 -22
  232. data/rust/codegen/src/sidecars.rs +15 -5
  233. data/rust/codegen/src/skip_reason.rs +8 -2
  234. data/rust/codegen/src/types.rs +306 -108
  235. data/rust/host/Cargo.lock +1 -0
  236. data/rust/host/Cargo.toml +40 -4
  237. data/rust/host/HECKS_RELEASE +1 -1
  238. data/rust/host/clippy.toml +4 -0
  239. data/rust/host/src/api.rs +698 -218
  240. data/rust/host/src/auth.rs +491 -214
  241. data/rust/host/src/b64.rs +91 -0
  242. data/rust/host/src/bin/expr_harness.rs +6 -2
  243. data/rust/host/src/bin/lineage_harness.rs +47 -11
  244. data/rust/host/src/bin/mint_harness.rs +70 -37
  245. data/rust/host/src/bin/validate_harness.rs +16 -8
  246. data/rust/host/src/boot.rs +144 -68
  247. data/rust/host/src/boundary_fuzz/api.rs +130 -32
  248. data/rust/host/src/boundary_fuzz/auth.rs +108 -23
  249. data/rust/host/src/boundary_fuzz/expr_json.rs +85 -11
  250. data/rust/host/src/boundary_fuzz/ir.rs +164 -12
  251. data/rust/host/src/boundary_fuzz/server.rs +283 -36
  252. data/rust/host/src/boundary_fuzz/web.rs +56 -8
  253. data/rust/host/src/checkout.rs +78 -32
  254. data/rust/host/src/clock.rs +8 -0
  255. data/rust/host/src/commerce_ir.rs +333 -95
  256. data/rust/host/src/crypto.rs +33 -0
  257. data/rust/host/src/dispatch.rs +990 -331
  258. data/rust/host/src/expr_json.rs +1021 -273
  259. data/rust/host/src/extension.rs +24 -4
  260. data/rust/host/src/field_hints.rs +11 -7
  261. data/rust/host/src/fuzz_support.rs +85 -13
  262. data/rust/host/src/host.rs +100 -0
  263. data/rust/host/src/http.rs +22 -0
  264. data/rust/host/src/ir.rs +326 -16
  265. data/rust/host/src/journal.rs +277 -176
  266. data/rust/host/src/lambda_client.rs +219 -53
  267. data/rust/host/src/lib.rs +16 -4
  268. data/rust/host/src/log.rs +62 -11
  269. data/rust/host/src/mint.rs +1153 -326
  270. data/rust/host/src/needs.rs +185 -44
  271. data/rust/host/src/payments/keystore.rs +197 -45
  272. data/rust/host/src/payments/roles.rs +39 -7
  273. data/rust/host/src/payments/tests.rs +1427 -282
  274. data/rust/host/src/payments.rs +391 -112
  275. data/rust/host/src/platform_signin/attack_tests.rs +5 -5
  276. data/rust/host/src/platform_signin/config.rs +11 -11
  277. data/rust/host/src/platform_signin/cookie.rs +2 -2
  278. data/rust/host/src/platform_signin/extension.rs +2 -1
  279. data/rust/host/src/platform_signin/handler.rs +9 -8
  280. data/rust/host/src/platform_signin/jti.rs +4 -2
  281. data/rust/host/src/platform_signin/jwks.rs +2 -1
  282. data/rust/host/src/platform_signin/jws.rs +2 -37
  283. data/rust/host/src/platform_signin/known_answer_tests.rs +5 -1
  284. data/rust/host/src/platform_signin/mode_tests.rs +5 -3
  285. data/rust/host/src/platform_signin/pages.rs +20 -20
  286. data/rust/host/src/platform_signin/session.rs +8 -1
  287. data/rust/host/src/platform_signin/settings.rs +6 -6
  288. data/rust/host/src/platform_signin/strict_json.rs +12 -9
  289. data/rust/host/src/platform_signin/verify.rs +41 -41
  290. data/rust/host/src/platform_signin/wiring_support.rs +13 -18
  291. data/rust/host/src/presentation.rs +207 -63
  292. data/rust/host/src/presentation_write.rs +502 -187
  293. data/rust/host/src/query_step.rs +69 -18
  294. data/rust/host/src/rate_limit.rs +541 -142
  295. data/rust/host/src/reference_transform.rs +77 -25
  296. data/rust/host/src/reference_validate.rs +443 -104
  297. data/rust/host/src/resend.rs +190 -55
  298. data/rust/host/src/secrets.rs +116 -21
  299. data/rust/host/src/server.rs +483 -104
  300. data/rust/host/src/storage_shape.rs +88 -21
  301. data/rust/host/src/test_pg.rs +17 -7
  302. data/rust/host/src/ui_schema.rs +495 -207
  303. data/rust/host/src/wasm_runner.rs +21 -6
  304. data/rust/host/src/web/commerce.rs +101 -23
  305. data/rust/host/src/web/newsletter/cooldown.rs +10 -3
  306. data/rust/host/src/web/newsletter/template.rs +26 -7
  307. data/rust/host/src/web/newsletter/tests.rs +112 -23
  308. data/rust/host/src/web/newsletter.rs +209 -59
  309. data/rust/host/src/web/newsletter_send.rs +251 -79
  310. data/rust/host/src/web/registration_receipt/tests.rs +16 -4
  311. data/rust/host/src/web/registration_receipt.rs +100 -24
  312. data/rust/host/src/web/registrations.rs +1384 -332
  313. data/rust/host/src/web/resend_webhook.rs +138 -80
  314. data/rust/host/src/web.rs +2416 -508
  315. data/rust/lsp/Cargo.toml +36 -0
  316. data/rust/lsp/clippy.toml +4 -0
  317. data/rust/lsp/src/json.rs +29 -22
  318. data/rust/lsp/src/main.rs +81 -31
  319. data/rust/lsp/src/outline.rs +23 -7
  320. data/rust/lsp/src/rpc.rs +17 -4
  321. data/rust/parser/Cargo.toml +36 -0
  322. data/rust/parser/clippy.toml +4 -0
  323. data/rust/parser/src/build/closed_sets.rs +6 -1
  324. data/rust/parser/src/build/identity.rs +4 -3
  325. data/rust/parser/src/build/mod.rs +1 -1
  326. data/rust/parser/src/build/named_pattern.rs +4 -1
  327. data/rust/parser/src/build/naming.rs +2 -80
  328. data/rust/parser/src/build/pattern_subset.rs +6 -8
  329. data/rust/parser/src/build/query_derive.rs +3 -1
  330. data/rust/parser/src/build/query_inference.rs +14 -5
  331. data/rust/parser/src/build/read_model.rs +4 -1
  332. data/rust/parser/src/build/references.rs +4 -3
  333. data/rust/parser/src/canonical.rs +34 -15
  334. data/rust/parser/src/emit.rs +187 -105
  335. data/rust/parser/src/expr/ast_json.rs +134 -42
  336. data/rust/parser/src/expr/evaluator.rs +27 -9
  337. data/rust/parser/src/expr/mod.rs +54 -13
  338. data/rust/parser/src/expr/resolver.rs +299 -92
  339. data/rust/parser/src/ir.rs +35 -3
  340. data/rust/parser/src/keywords.rs +7168 -516
  341. data/rust/parser/src/lex.rs +11 -25
  342. data/rust/parser/src/lib.rs +1 -0
  343. data/rust/parser/src/main.rs +65 -6
  344. data/rust/parser/src/parse/adapter.rs +157 -0
  345. data/rust/parser/src/parse/aggregate.rs +19 -10
  346. data/rust/parser/src/parse/chapter.rs +16 -17
  347. data/rust/parser/src/parse/command.rs +35 -16
  348. data/rust/parser/src/parse/domain_port.rs +4 -7
  349. data/rust/parser/src/parse/entity.rs +21 -11
  350. data/rust/parser/src/parse/hecksagon.rs +10 -4
  351. data/rust/parser/src/parse/lifecycle.rs +9 -5
  352. data/rust/parser/src/parse/mod.rs +22 -91
  353. data/rust/parser/src/parse/needs.rs +21 -12
  354. data/rust/parser/src/parse/policy.rs +2 -2
  355. data/rust/parser/src/parse/process_manager.rs +9 -11
  356. data/rust/parser/src/parse/query.rs +8 -4
  357. data/rust/parser/src/parse/read_model.rs +57 -16
  358. data/rust/parser/src/parse/value_object.rs +7 -2
  359. data/rust/parser/src/parse/world.rs +322 -0
  360. data/rust/parser/src/ruby_value.rs +7 -6
  361. data/rust/src/exemplar/json.rs +4 -4
  362. data/rust/src/exemplar/mutations.rs +18 -0
  363. data/rust/src/exemplar/read_models.rs +1 -10
  364. data/rust/src/kernel/attribute_shapes/composite.rs +54 -14
  365. data/rust/src/kernel/attribute_shapes/list.rs +2 -2
  366. data/rust/src/kernel/attribute_shapes/mod.rs +8 -8
  367. data/rust/src/kernel/attribute_shapes/scalar.rs +2 -2
  368. data/rust/src/kernel/cli.rs +229 -112
  369. data/rust/src/kernel/dispatch.rs +323 -115
  370. data/rust/src/kernel/dispatch_guard_tests.rs +327 -65
  371. data/rust/src/kernel/expr.rs +203 -86
  372. data/rust/src/kernel/expression_operators/arithmetic.rs +138 -25
  373. data/rust/src/kernel/expression_operators/comparison.rs +87 -2
  374. data/rust/src/kernel/expression_operators/enumeration.rs +106 -22
  375. data/rust/src/kernel/expression_operators/logical.rs +9 -3
  376. data/rust/src/kernel/expression_operators/membership.rs +3 -1
  377. data/rust/src/kernel/expression_operators/mod.rs +34 -21
  378. data/rust/src/kernel/expression_operators/pattern_match.rs +46 -11
  379. data/rust/src/kernel/expression_operators/positional.rs +84 -30
  380. data/rust/src/kernel/expression_operators/presence.rs +52 -13
  381. data/rust/src/kernel/expression_operators/sign_test.rs +3 -1
  382. data/rust/src/kernel/expression_operators/sized.rs +24 -8
  383. data/rust/src/kernel/expression_operators/text.rs +120 -27
  384. data/rust/src/kernel/expression_operators/to_string.rs +10 -4
  385. data/rust/src/kernel/json.rs +230 -148
  386. data/rust/src/kernel/mod.rs +46 -33
  387. data/rust/src/kernel/named_query.rs +63 -15
  388. data/rust/src/kernel/needs.rs +112 -25
  389. data/rust/src/kernel/orchestrate.rs +980 -581
  390. data/rust/src/kernel/orchestrate_guard_tests.rs +538 -140
  391. data/rust/src/kernel/pattern.rs +107 -34
  392. data/rust/src/kernel/query_comparators.rs +118 -41
  393. data/rust/src/kernel/query_ordering.rs +13 -5
  394. data/rust/src/kernel/read_model.rs +479 -181
  395. data/rust/src/kernel/reference_lookup.rs +122 -26
  396. data/rust/src/kernel/repository.rs +293 -66
  397. data/rust/src/kernel/routing.rs +58 -42
  398. data/rust/src/kernel/ruby_float.rs +139 -0
  399. data/rust/src/kernel/vocab/aggregate_dispatch_order.rs +57 -57
  400. data/rust/src/kernel/vocab/entity_dispatch_order.rs +54 -54
  401. data/rust/src/kernel/vocab/field_hint.rs +19 -25
  402. data/rust/src/kernel/vocab/mod.rs +6 -6
  403. data/rust/src/kernel/vocab/query_comparator.rs +23 -24
  404. data/rust/src/kernel/vocab/refusal_template.rs +501 -297
  405. data/rust/src/lib.rs +50 -0
  406. data/rust/src/main.rs +4 -1
  407. data/rust/web/Cargo.toml +36 -0
  408. data/rust/web/clippy.toml +4 -0
  409. metadata +82 -15
  410. data/examples/pizzas/bluebook/hecksagon/mock_stripe_payment_adapter.rb +0 -36
  411. data/lib/hecks/adapters/driven/google_authentication.adapter +0 -3
  412. data/lib/hecks/adapters/driven/google_authentication.rb +0 -99
  413. data/lib/hecks/adapters/driven/mock_stripe_adapter.adapter +0 -3
  414. data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +0 -32
  415. data/lib/hecks/ports/authentication.port +0 -6
  416. data/lib/hecks/ports/authentication.rb +0 -67
  417. data/lib/hecks/ports/persistence/plugins/era/translation/approval_file.rb +0 -255
  418. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +0 -22
  419. data/rust/host/src/approval.rs +0 -652
@@ -11,18 +11,29 @@ use crate::json::Json;
11
11
  ///
12
12
  /// An aggregate with no `persisted_by` bind falls back to `world_path`'s own
13
13
  /// `default_adapter` for the target chapter, then to `"Memory"`.
14
- pub fn run(ir: &mut Json, hecksagon_path: Option<&Path>, world_path: Option<&Path>, root: &Path) -> Result<(), String> {
14
+ pub fn run(
15
+ ir: &mut Json,
16
+ hecksagon_path: Option<&Path>,
17
+ world_path: Option<&Path>,
18
+ root: &Path,
19
+ ) -> Result<(), String> {
15
20
  let binds = match hecksagon_path {
16
21
  Some(path) => {
17
- let text = std::fs::read_to_string(path).map_err(|e| format!("reading {}: {e}", path.display()))?;
22
+ let text = std::fs::read_to_string(path)
23
+ .map_err(|e| format!("reading {}: {e}", path.display()))?;
18
24
  persistence_binds(&text)
19
25
  }
20
26
  None => HashMap::new(),
21
27
  };
22
- let chapter_name = ir.get("name").and_then(Json::as_str).unwrap_or_default().to_string();
28
+ let chapter_name = ir
29
+ .get("name")
30
+ .and_then(Json::as_str)
31
+ .unwrap_or_default()
32
+ .to_string();
23
33
  let fallback_adapter = match world_path {
24
34
  Some(path) => {
25
- let text = std::fs::read_to_string(path).map_err(|e| format!("reading {}: {e}", path.display()))?;
35
+ let text = std::fs::read_to_string(path)
36
+ .map_err(|e| format!("reading {}: {e}", path.display()))?;
26
37
  default_adapter_name(&text, &chapter_name)
27
38
  }
28
39
  None => None,
@@ -30,29 +41,35 @@ pub fn run(ir: &mut Json, hecksagon_path: Option<&Path>, world_path: Option<&Pat
30
41
  .unwrap_or_else(|| "Memory".to_string());
31
42
  let capable_adapters = lineage_capable_adapter_names(root)?;
32
43
 
33
- // Collected up front: Rust can't hold this borrow of `ir` alongside the
34
- // `&mut` call to `ir.set` below.
35
- let aggregate_names: Vec<String> = ir
44
+ // Collected before `ir.set` below: Rust can't hold this borrow of `ir`
45
+ // alongside the `&mut` call.
46
+ let capable_aggregates: Vec<Json> = ir
36
47
  .get("aggregates")
37
- .map(Json::each)
38
- .unwrap_or(&[])
48
+ .map_or(&[][..], Json::each)
39
49
  .iter()
40
50
  .filter_map(|a| a.get("name").and_then(Json::as_str).map(str::to_string))
41
- .collect();
42
-
43
- let capable_aggregates: Vec<Json> = aggregate_names
44
- .into_iter()
45
51
  .filter(|name| {
46
- let adapter_name = binds.get(name.as_str()).map(String::as_str).unwrap_or(fallback_adapter.as_str());
52
+ let adapter_name = binds
53
+ .get(name.as_str())
54
+ .map_or(fallback_adapter.as_str(), String::as_str);
47
55
  capable_adapters.iter().any(|a| a == adapter_name)
48
56
  })
49
57
  .map(|name| {
50
- let storage_name = snake_case(&name);
51
- Json::Object(vec![("name".to_string(), Json::String(name)), ("storage_name".to_string(), Json::String(storage_name))])
58
+ let storage_name = crate::hecks_naming::snake(&name);
59
+ Json::Object(vec![
60
+ ("name".to_string(), Json::String(name)),
61
+ ("storage_name".to_string(), Json::String(storage_name)),
62
+ ])
52
63
  })
53
64
  .collect();
54
65
 
55
- ir.set("lineage", Json::Object(vec![("capable_aggregates".to_string(), Json::Array(capable_aggregates))]));
66
+ ir.set(
67
+ "lineage",
68
+ Json::Object(vec![(
69
+ "capable_aggregates".to_string(),
70
+ Json::Array(capable_aggregates),
71
+ )]),
72
+ );
56
73
  Ok(())
57
74
  }
58
75
 
@@ -65,18 +82,31 @@ pub(crate) fn persistence_binds(text: &str) -> HashMap<String, String> {
65
82
  continue;
66
83
  }
67
84
 
68
- let Some(call_pos) = line.find(".persisted_by(") else { continue };
85
+ let Some(call_pos) = line.find(".persisted_by(") else {
86
+ continue;
87
+ };
69
88
 
70
89
  let before = &line[..call_pos];
71
- let aggregate_name: String = before.chars().rev().take_while(|c| c.is_alphanumeric() || *c == '_').collect::<String>().chars().rev().collect();
90
+ let aggregate_name: String = before
91
+ .chars()
92
+ .rev()
93
+ .take_while(|c| c.is_alphanumeric() || *c == '_')
94
+ .collect::<String>()
95
+ .chars()
96
+ .rev()
97
+ .collect();
72
98
  if aggregate_name.is_empty() {
73
99
  continue;
74
100
  }
75
101
 
76
102
  let after = &line[call_pos + ".persisted_by(".len()..];
77
- let Some(open_quote) = after.find('"') else { continue };
103
+ let Some(open_quote) = after.find('"') else {
104
+ continue;
105
+ };
78
106
  let rest = &after[open_quote + 1..];
79
- let Some(close_quote) = rest.find('"') else { continue };
107
+ let Some(close_quote) = rest.find('"') else {
108
+ continue;
109
+ };
80
110
  let adapter_name = &rest[..close_quote];
81
111
 
82
112
  binds.insert(aggregate_name, adapter_name.to_string());
@@ -100,13 +130,19 @@ pub(crate) fn default_adapter_name(text: &str, chapter: &str) -> Option<String>
100
130
  continue;
101
131
  }
102
132
 
103
- let Some(rest) = line.trim_start().strip_prefix("default_adapter") else { continue };
133
+ let Some(rest) = line.trim_start().strip_prefix("default_adapter") else {
134
+ continue;
135
+ };
104
136
  if !rest.starts_with(char::is_whitespace) {
105
137
  continue;
106
138
  }
107
- let Some(open_quote) = rest.find('"') else { continue };
139
+ let Some(open_quote) = rest.find('"') else {
140
+ continue;
141
+ };
108
142
  let quoted = &rest[open_quote + 1..];
109
- let Some(close_quote) = quoted.find('"') else { continue };
143
+ let Some(close_quote) = quoted.find('"') else {
144
+ continue;
145
+ };
110
146
  found = Some(quoted[..close_quote].to_string());
111
147
  }
112
148
 
@@ -121,14 +157,18 @@ fn world_opener_name(line: &str) -> Option<String> {
121
157
 
122
158
  // Where an adapter class can live: the driven adapters, and the era plugin that holds `PostgresEra`,
123
159
  // the one adapter that declares itself lineage-capable.
124
- const ADAPTER_DIRS: [&str; 2] = ["lib/hecks/adapters/driven", "lib/hecks/ports/persistence/plugins/era"];
160
+ const ADAPTER_DIRS: [&str; 2] = [
161
+ "lib/hecks/adapters/driven",
162
+ "lib/hecks/ports/persistence/plugins/era",
163
+ ];
125
164
 
126
165
  // Reads each adapter's own source file rather than invoking Ruby.
127
166
  fn lineage_capable_adapter_names(root: &Path) -> Result<Vec<String>, String> {
128
167
  let mut names = Vec::new();
129
168
  for relative in ADAPTER_DIRS {
130
169
  let dir = root.join(relative);
131
- let entries = std::fs::read_dir(&dir).map_err(|e| format!("reading {}: {e}", dir.display()))?;
170
+ let entries =
171
+ std::fs::read_dir(&dir).map_err(|e| format!("reading {}: {e}", dir.display()))?;
132
172
 
133
173
  for entry in entries {
134
174
  let entry = entry.map_err(|e| format!("reading {}: {e}", dir.display()))?;
@@ -137,7 +177,8 @@ fn lineage_capable_adapter_names(root: &Path) -> Result<Vec<String>, String> {
137
177
  continue;
138
178
  }
139
179
 
140
- let text = std::fs::read_to_string(&path).map_err(|e| format!("reading {}: {e}", path.display()))?;
180
+ let text = std::fs::read_to_string(&path)
181
+ .map_err(|e| format!("reading {}: {e}", path.display()))?;
141
182
  if !declares_lineage_capable_true(&text) {
142
183
  continue;
143
184
  }
@@ -171,7 +212,10 @@ fn top_level_class_name(text: &str) -> Option<String> {
171
212
  for line in text.lines() {
172
213
  let trimmed = line.trim_start();
173
214
  if let Some(rest) = trimmed.strip_prefix("class ") {
174
- let name: String = rest.chars().take_while(|c| c.is_alphanumeric() || *c == '_').collect();
215
+ let name: String = rest
216
+ .chars()
217
+ .take_while(|c| c.is_alphanumeric() || *c == '_')
218
+ .collect();
175
219
  if !name.is_empty() {
176
220
  return Some(name);
177
221
  }
@@ -180,58 +224,6 @@ fn top_level_class_name(text: &str) -> Option<String> {
180
224
  None
181
225
  }
182
226
 
183
- // Hand-ported `Naming.snake` (Ruby): split acronym boundaries, then camelCase
184
- // boundaries, then downcase — no regex dependency.
185
- pub(crate) fn snake_case(name: &str) -> String {
186
- let after_pass1 = split_acronym_boundaries(name);
187
- let after_pass2 = split_camel_boundaries(&after_pass1);
188
- after_pass2.to_ascii_lowercase()
189
- }
190
-
191
- fn split_acronym_boundaries(text: &str) -> String {
192
- let chars: Vec<char> = text.chars().collect();
193
- let mut out = String::with_capacity(text.len() + 4);
194
- let mut i = 0;
195
- while i < chars.len() {
196
- // A maximal uppercase run starting at i.
197
- if chars[i].is_ascii_uppercase() {
198
- let mut run_end = i;
199
- while run_end < chars.len() && chars[run_end].is_ascii_uppercase() {
200
- run_end += 1;
201
- }
202
- let run_len = run_end - i;
203
- let followed_by_lowercase = run_end < chars.len() && chars[run_end].is_ascii_lowercase();
204
- if run_len >= 2 && followed_by_lowercase {
205
- // Emit the run minus its last char, an underscore, then
206
- // continue scanning from the run's own last char (which
207
- // pass 2 or a plain copy picks up next iteration).
208
- out.extend(&chars[i..run_end - 1]);
209
- out.push('_');
210
- i = run_end - 1;
211
- continue;
212
- }
213
- }
214
- out.push(chars[i]);
215
- i += 1;
216
- }
217
- out
218
- }
219
-
220
- fn split_camel_boundaries(text: &str) -> String {
221
- let chars: Vec<char> = text.chars().collect();
222
- let mut out = String::with_capacity(text.len() + 4);
223
- for (idx, &c) in chars.iter().enumerate() {
224
- if idx > 0 {
225
- let prev = chars[idx - 1];
226
- if (prev.is_ascii_lowercase() || prev.is_ascii_digit()) && c.is_ascii_uppercase() {
227
- out.push('_');
228
- }
229
- }
230
- out.push(c);
231
- }
232
- out
233
- }
234
-
235
227
  #[cfg(test)]
236
228
  mod tests {
237
229
  use super::*;
@@ -257,7 +249,11 @@ mod tests {
257
249
  ("Statement", "statement"),
258
250
  ];
259
251
  for (input, expected) in cases {
260
- assert_eq!(snake_case(input), expected, "snake_case({input:?})");
252
+ assert_eq!(
253
+ crate::hecks_naming::snake(input),
254
+ expected,
255
+ "snake({input:?})"
256
+ );
261
257
  }
262
258
  }
263
259
 
@@ -272,15 +268,25 @@ mod tests {
272
268
  #[test]
273
269
  fn reads_the_default_adapter_of_the_target_chapters_own_world_only() {
274
270
  let text = "Hecks.world \"Alpha\" do\n # default_adapter \"Heki\"\n default_adapter \"PostgresEra\"\nend\n\nHecks.world(\"Beta\") do\n default_adapter \"Memory\"\nend\n";
275
- assert_eq!(default_adapter_name(text, "Alpha"), Some("PostgresEra".to_string()));
276
- assert_eq!(default_adapter_name(text, "Beta"), Some("Memory".to_string()));
271
+ assert_eq!(
272
+ default_adapter_name(text, "Alpha"),
273
+ Some("PostgresEra".to_string())
274
+ );
275
+ assert_eq!(
276
+ default_adapter_name(text, "Beta"),
277
+ Some("Memory".to_string())
278
+ );
277
279
  assert_eq!(default_adapter_name(text, "Gamma"), None);
278
280
  }
279
281
 
280
282
  #[test]
281
283
  fn declares_lineage_capable_true_matches_the_endless_method_form() {
282
- assert!(declares_lineage_capable_true(" def self.lineage_capable? = true\n"));
283
- assert!(!declares_lineage_capable_true(" def self.lineage_capable? = false\n"));
284
+ assert!(declares_lineage_capable_true(
285
+ " def self.lineage_capable? = true\n"
286
+ ));
287
+ assert!(!declares_lineage_capable_true(
288
+ " def self.lineage_capable? = false\n"
289
+ ));
284
290
  assert!(!declares_lineage_capable_true("no such method here\n"));
285
291
  }
286
292
 
@@ -290,11 +296,19 @@ mod tests {
290
296
  fn finds_postgres_era_in_the_real_source_tree() {
291
297
  let root = Path::new(env!("CARGO_MANIFEST_DIR")).join("../..");
292
298
  let names = lineage_capable_adapter_names(&root).expect("scans the adapter directories");
293
- assert!(names.iter().any(|name| name == "PostgresEra"), "lineage-capable adapters found: {names:?}");
299
+ assert!(
300
+ names.iter().any(|name| name == "PostgresEra"),
301
+ "lineage-capable adapters found: {names:?}"
302
+ );
294
303
  }
295
304
 
296
305
  #[test]
297
306
  fn finds_the_top_level_class_name() {
298
- assert_eq!(top_level_class_name("module Hecks\n module Adapters\n class Postgres\n def x; end\n"), Some("Postgres".to_string()));
307
+ assert_eq!(
308
+ top_level_class_name(
309
+ "module Hecks\n module Adapters\n class Postgres\n def x; end\n"
310
+ ),
311
+ Some("Postgres".to_string())
312
+ );
299
313
  }
300
314
  }
@@ -2,23 +2,30 @@
2
2
  //! `<domain>` is a domain directory such as `examples/pizzas`; `--wasm` also cross-compiles to
3
3
  //! `wasm32-wasip1`, and `--no-build` generates source only.
4
4
 
5
+ mod adapters_pass;
5
6
  mod build_artifact;
6
7
  mod cargo_sync;
7
8
  // The one JSON implementation, compiled from `hecks-codegen`'s own source: neither crate depends on
8
9
  // the other, and a packaged workspace ships both side by side. This crate reads only part of it.
10
+ mod binding_pass;
9
11
  #[allow(dead_code)]
10
12
  #[path = "../../codegen/src/json.rs"]
11
13
  mod json;
12
- mod binding_pass;
14
+ // The one `Hecks::Naming` port, compiled from `hecks-codegen`'s source for the same reason.
15
+ #[allow(dead_code)]
16
+ #[path = "../../codegen/src/hecks_naming.rs"]
17
+ mod hecks_naming;
13
18
  mod lineage_pass;
14
19
  mod pipeline;
15
20
  mod reserved_names;
16
21
  mod resolve;
17
22
  mod root;
23
+ mod rustfmt;
18
24
  mod seams_pass;
19
25
  mod subprocess;
20
26
  mod tmp;
21
27
  mod translation_pass;
28
+ mod world_pass;
22
29
 
23
30
  use std::process::ExitCode;
24
31
 
@@ -50,7 +57,9 @@ fn run(args: &[String]) -> Result<(), String> {
50
57
  }
51
58
  }
52
59
 
53
- let domain = domain.ok_or_else(|| "usage: hecks-build <domain> [--wasm] [--no-build] [--out <dir>]".to_string())?;
60
+ let domain = domain.ok_or_else(|| {
61
+ "usage: hecks-build <domain> [--wasm] [--no-build] [--out <dir>]".to_string()
62
+ })?;
54
63
  let root = root::find()?;
55
64
 
56
65
  // `--out` generates the module tree into that directory only: no `Cargo.toml` rewrite, no
@@ -3,15 +3,18 @@
3
3
 
4
4
  use std::path::{Path, PathBuf};
5
5
 
6
+ use crate::adapters_pass;
6
7
  use crate::binding_pass;
7
8
  use crate::build_artifact;
8
9
  use crate::cargo_sync;
9
10
  use crate::json::Json;
10
11
  use crate::lineage_pass;
11
12
  use crate::resolve;
13
+ use crate::rustfmt;
12
14
  use crate::seams_pass;
13
15
  use crate::subprocess;
14
16
  use crate::tmp::TempDir;
17
+ use crate::world_pass;
15
18
 
16
19
  pub struct Options {
17
20
  pub build_native: bool,
@@ -53,7 +56,9 @@ pub fn run(root: &Path, domain: &str, opts: &Options) -> Result<(), String> {
53
56
 
54
57
  let bluebook_directory = domain_path.join("bluebook");
55
58
  let bluebook_paths = resolve::bluebook_files(&bluebook_directory)?;
56
- let first_bluebook = bluebook_paths.first().ok_or_else(|| format!("{} has no .bluebook files", domain_path.display()))?;
59
+ let first_bluebook = bluebook_paths
60
+ .first()
61
+ .ok_or_else(|| format!("{} has no .bluebook files", domain_path.display()))?;
57
62
  let hecksagon_candidate = bluebook_directory.join(format!("{target_mod_name}.hecksagon"));
58
63
  let hecksagon_path = hecksagon_candidate.is_file().then_some(hecksagon_candidate);
59
64
  let world_candidate = bluebook_directory.join(format!("{target_mod_name}.world"));
@@ -92,9 +97,26 @@ pub fn run(root: &Path, domain: &str, opts: &Options) -> Result<(), String> {
92
97
  // Only the target chapter gets a `lineage` key — a framework chapter or
93
98
  // `meta` never does, matching `derive_lineage` (Ruby) re-`JSON.parse`ing
94
99
  // rather than chaining a live object through both passes.
95
- let mut target_ir = Json::parse(&target_ir_text).map_err(|e| format!("re-parsing target ir.json for lineage: {e}"))?;
96
- lineage_pass::run(&mut target_ir, hecksagon_path.as_deref(), world_path.as_deref(), root)?;
97
- binding_pass::run(&mut target_ir, hecksagon_path.as_deref(), world_path.as_deref())?;
100
+ let mut target_ir = Json::parse(&target_ir_text)
101
+ .map_err(|e| format!("re-parsing target ir.json for lineage: {e}"))?;
102
+ lineage_pass::run(
103
+ &mut target_ir,
104
+ hecksagon_path.as_deref(),
105
+ world_path.as_deref(),
106
+ root,
107
+ )?;
108
+ binding_pass::run(
109
+ &mut target_ir,
110
+ hecksagon_path.as_deref(),
111
+ world_path.as_deref(),
112
+ |ir| {
113
+ // The same two places `TargetIr#adapters` scans: the source root's `lib/` and the
114
+ // domain's own directory.
115
+ let directories = [root.join("lib"), domain_path.clone()];
116
+ adapters_pass::declarations(&parser_bin, &directories, ir)
117
+ },
118
+ )?;
119
+ world_pass::run(&mut target_ir, &parser_bin, world_path.as_deref())?;
98
120
 
99
121
  // Every other chapter the gem supplies by `attaches "Name"`, resolved through the
100
122
  // same directory listing (`resolve::framework_members`) the Ruby
@@ -104,7 +126,9 @@ pub fn run(root: &Path, domain: &str, opts: &Options) -> Result<(), String> {
104
126
  let other_gem_chapters = resolve::other_gem_chapter_names(root)?;
105
127
  let mut chapters: Vec<Chapter> = Vec::new();
106
128
  for fw_name in &gem_chapter_names {
107
- if other_gem_chapters.contains(fw_name) && !framework_members.iter().any(|(name, _)| name == fw_name) {
129
+ if other_gem_chapters.contains(fw_name)
130
+ && !framework_members.iter().any(|(name, _)| name == fw_name)
131
+ {
108
132
  continue;
109
133
  }
110
134
  let fw_path = framework_members
@@ -114,7 +138,7 @@ pub fn run(root: &Path, domain: &str, opts: &Options) -> Result<(), String> {
114
138
  .ok_or_else(|| {
115
139
  let mut known: Vec<&str> = framework_members.iter().map(|(n, _)| n.as_str()).collect();
116
140
  known.extend(other_gem_chapters.iter().map(String::as_str));
117
- known.sort();
141
+ known.sort_unstable();
118
142
  format!("hecks-build: attaches {fw_name:?} names no chapter the gem carries — known: {}", known.join(", "))
119
143
  })?;
120
144
  let fw_chapter_name = resolve::header_chapter_name(&fw_path)?;
@@ -124,7 +148,11 @@ pub fn run(root: &Path, domain: &str, opts: &Options) -> Result<(), String> {
124
148
  fw_path.display()
125
149
  ));
126
150
  }
127
- let fw_ir_text = parse_chapter(&parser_bin, &fw_chapter_name, std::slice::from_ref(&fw_path))?;
151
+ let fw_ir_text = parse_chapter(
152
+ &parser_bin,
153
+ &fw_chapter_name,
154
+ std::slice::from_ref(&fw_path),
155
+ )?;
128
156
  chapters.push(Chapter {
129
157
  mod_name: fw_name.to_lowercase(),
130
158
  source_label: format!("{domain} (attaches {fw_name:?})"),
@@ -138,7 +166,7 @@ pub fn run(root: &Path, domain: &str, opts: &Options) -> Result<(), String> {
138
166
  for pkg_name in &vendored_package_names {
139
167
  let pkg_files = resolve::vendored_bluebook_files(&domain_path, pkg_name)?;
140
168
  let pkg_chapter_name = resolve::header_chapter_name(&pkg_files[0])?;
141
- let expected_chapter_name = resolve::pascal(pkg_name);
169
+ let expected_chapter_name = crate::hecks_naming::pascal(pkg_name);
142
170
  if pkg_chapter_name != expected_chapter_name {
143
171
  return Err(format!(
144
172
  "hecks-build: {} declares chapter {pkg_chapter_name:?}, but attaches {pkg_name:?}, from: :vendor \
@@ -200,7 +228,10 @@ pub fn run(root: &Path, domain: &str, opts: &Options) -> Result<(), String> {
200
228
  // once every chapter is loaded; `translations` and `source_text` close the binding facts.
201
229
  let mut member_irs = Vec::new();
202
230
  for c in &chapters {
203
- member_irs.push(Json::parse(&c.ir_text).map_err(|e| format!("re-parsing {} ir.json for its seams: {e}", c.mod_name))?);
231
+ member_irs.push(
232
+ Json::parse(&c.ir_text)
233
+ .map_err(|e| format!("re-parsing {} ir.json for its seams: {e}", c.mod_name))?,
234
+ );
204
235
  }
205
236
  seams_pass::run(&mut target_ir, &member_irs)?;
206
237
  binding_pass::finish(&mut target_ir, &source_files, &bluebook_directory)?;
@@ -211,8 +242,12 @@ pub fn run(root: &Path, domain: &str, opts: &Options) -> Result<(), String> {
211
242
  let grammar_files = resolve::grammar_files(root)?;
212
243
  let meta_ir_text = parse_chapter(&parser_bin, "Bluebook", &grammar_files)?;
213
244
 
214
- let out_root = opts.out_dir.clone().unwrap_or_else(|| root.join("rust/src/generated"));
215
- std::fs::create_dir_all(&out_root).map_err(|e| format!("creating {}: {e}", out_root.display()))?;
245
+ let out_root = opts
246
+ .out_dir
247
+ .clone()
248
+ .unwrap_or_else(|| root.join("rust/src/generated"));
249
+ std::fs::create_dir_all(&out_root)
250
+ .map_err(|e| format!("creating {}: {e}", out_root.display()))?;
216
251
 
217
252
  // Scoped clearing — identical reasoning to both Ruby pipelines'
218
253
  // own: multiple domains coexist on disk, so only this run's own
@@ -227,11 +262,21 @@ pub fn run(root: &Path, domain: &str, opts: &Options) -> Result<(), String> {
227
262
  let tmp = TempDir::new("hecks-build-codegen")?;
228
263
 
229
264
  let meta_ir_path = tmp.path().join("meta_ir.json");
230
- std::fs::write(&meta_ir_path, &meta_ir_text).map_err(|e| format!("writing {}: {e}", meta_ir_path.display()))?;
231
- run_codegen_full(&codegen_bin, &[out_root.to_string_lossy().to_string(), "meta".to_string(), META_SOURCE_LABEL.to_string(), meta_ir_path.to_string_lossy().to_string()])?;
265
+ std::fs::write(&meta_ir_path, &meta_ir_text)
266
+ .map_err(|e| format!("writing {}: {e}", meta_ir_path.display()))?;
267
+ run_codegen_full(
268
+ &codegen_bin,
269
+ &[
270
+ out_root.to_string_lossy().to_string(),
271
+ "meta".to_string(),
272
+ META_SOURCE_LABEL.to_string(),
273
+ meta_ir_path.to_string_lossy().to_string(),
274
+ ],
275
+ )?;
232
276
 
233
277
  let target_ir_path = tmp.path().join("target_ir.json");
234
- std::fs::write(&target_ir_path, &target_ir_text).map_err(|e| format!("writing {}: {e}", target_ir_path.display()))?;
278
+ std::fs::write(&target_ir_path, &target_ir_text)
279
+ .map_err(|e| format!("writing {}: {e}", target_ir_path.display()))?;
235
280
 
236
281
  let mut codegen_args = vec![
237
282
  out_root.to_string_lossy().to_string(),
@@ -239,14 +284,13 @@ pub fn run(root: &Path, domain: &str, opts: &Options) -> Result<(), String> {
239
284
  domain.to_string(),
240
285
  target_ir_path.to_string_lossy().to_string(),
241
286
  ];
242
- let mut chapter_ir_paths = Vec::new();
243
287
  for (idx, c) in chapters.iter().enumerate() {
244
288
  let ir_path = tmp.path().join(format!("chapter_{idx}_ir.json"));
245
- std::fs::write(&ir_path, &c.ir_text).map_err(|e| format!("writing {}: {e}", ir_path.display()))?;
289
+ std::fs::write(&ir_path, &c.ir_text)
290
+ .map_err(|e| format!("writing {}: {e}", ir_path.display()))?;
246
291
  codegen_args.push(c.mod_name.clone());
247
292
  codegen_args.push(c.source_label.clone());
248
293
  codegen_args.push(ir_path.to_string_lossy().to_string());
249
- chapter_ir_paths.push(ir_path);
250
294
  }
251
295
  run_codegen_full(&codegen_bin, &codegen_args)?;
252
296
 
@@ -257,6 +301,15 @@ pub fn run(root: &Path, domain: &str, opts: &Options) -> Result<(), String> {
257
301
  cargo_sync::run(&out_root, &cargo_toml_path, &target_mod_name)?;
258
302
  }
259
303
 
304
+ // `hecks project_rust` lays its Rust out with rustfmt; the tree must match it byte for byte.
305
+ let mut generated_dirs = vec![
306
+ "meta".to_string(),
307
+ "active".to_string(),
308
+ target_mod_name.clone(),
309
+ ];
310
+ generated_dirs.extend(chapters.iter().map(|c| c.mod_name.clone()));
311
+ rustfmt::format_generated(&out_root, &generated_dirs)?;
312
+
260
313
  let rust_dir = root.join("rust");
261
314
  if opts.build_native {
262
315
  build_artifact::build_native(&rust_dir)?;
@@ -270,9 +323,17 @@ pub fn run(root: &Path, domain: &str, opts: &Options) -> Result<(), String> {
270
323
 
271
324
  // Parses via `hecks-parse chapter`. `hecks-codegen` marks the IR and writes its `ir.json` and
272
325
  // `metadata.rs` itself, so the text goes through as `hecks-parse` printed it.
273
- fn parse_chapter(parser_bin: &Path, chapter_name: &str, files: &[PathBuf]) -> Result<String, String> {
326
+ fn parse_chapter(
327
+ parser_bin: &Path,
328
+ chapter_name: &str,
329
+ files: &[PathBuf],
330
+ ) -> Result<String, String> {
274
331
  let parser_bin_str = parser_bin.to_string_lossy().to_string();
275
- let mut args: Vec<String> = vec!["chapter".to_string(), "--chapter".to_string(), chapter_name.to_string()];
332
+ let mut args: Vec<String> = vec![
333
+ "chapter".to_string(),
334
+ "--chapter".to_string(),
335
+ chapter_name.to_string(),
336
+ ];
276
337
  args.extend(files.iter().map(|p| p.to_string_lossy().to_string()));
277
338
  let arg_refs: Vec<&str> = args.iter().map(String::as_str).collect();
278
339
 
@@ -2,57 +2,13 @@
2
2
  // CargoReservedName vocabularies (lib/hecks/language/bluebook/
3
3
  // vocabulary.bluebook). Do not hand-edit — re-run hecks project_reserved_names.
4
4
 
5
- /// The `RustReservedWord` vocabulary, 49 entries.
5
+ /// The `RustReservedWord` vocabulary, 51 entries.
6
6
  pub const RUST_KEYWORDS: &[&str] = &[
7
- "as",
8
- "break",
9
- "const",
10
- "continue",
11
- "crate",
12
- "dyn",
13
- "else",
14
- "enum",
15
- "extern",
16
- "false",
17
- "fn",
18
- "for",
19
- "if",
20
- "impl",
21
- "in",
22
- "let",
23
- "loop",
24
- "match",
25
- "mod",
26
- "move",
27
- "mut",
28
- "pub",
29
- "ref",
30
- "return",
31
- "self",
32
- "Self",
33
- "static",
34
- "struct",
35
- "super",
36
- "trait",
37
- "true",
38
- "type",
39
- "unsafe",
40
- "use",
41
- "where",
42
- "while",
43
- "abstract",
44
- "become",
45
- "box",
46
- "do",
47
- "final",
48
- "macro",
49
- "override",
50
- "priv",
51
- "typeof",
52
- "unsized",
53
- "virtual",
54
- "yield",
55
- "try",
7
+ "as", "async", "await", "break", "const", "continue", "crate", "dyn", "else", "enum", "extern",
8
+ "false", "fn", "for", "if", "impl", "in", "let", "loop", "match", "mod", "move", "mut", "pub",
9
+ "ref", "return", "self", "Self", "static", "struct", "super", "trait", "true", "type",
10
+ "unsafe", "use", "where", "while", "abstract", "become", "box", "do", "final", "macro",
11
+ "override", "priv", "typeof", "unsized", "virtual", "yield", "try",
56
12
  ];
57
13
 
58
14
  /// The `CargoReservedName` vocabulary, 16 entries.