hecks 3.14.0 → 5.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 (427) 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/exe/hecks +1 -1
  9. data/lib/hecks/adapters/driven/prism.rb +0 -2
  10. data/lib/hecks/adapters/driven.rb +0 -2
  11. data/lib/hecks/adapters/driving/json.rb +9 -16
  12. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +2 -2
  13. data/lib/hecks/bluebook/dsl/derivation_words.rb +58 -0
  14. data/lib/hecks/bluebook/dsl/translation_builder.rb +6 -37
  15. data/lib/hecks/bluebook/expression/ast_json.rb +0 -9
  16. data/lib/hecks/bluebook/hecksagon.rb +1 -1
  17. data/lib/hecks/bluebook/meta_validator/translation_judge/rules.rb +7 -10
  18. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +4 -8
  19. data/lib/hecks/bluebook/meta_validator.rb +0 -11
  20. data/lib/hecks/bluebook/translation.rb +18 -10
  21. data/lib/hecks/bluebook/translation_expression/evaluator.rb +63 -0
  22. data/lib/hecks/bluebook/translation_expression/parser.rb +127 -0
  23. data/lib/hecks/bluebook/translation_expression.rb +39 -0
  24. data/lib/hecks/bluebook/translation_plan.rb +70 -0
  25. data/lib/hecks/bluebook.rb +2 -0
  26. data/lib/hecks/chapters.rb +3 -3
  27. data/lib/hecks/cli/project_cli/launcher_source.rb +1 -1
  28. data/lib/hecks/composer/adapters/claude_composer.adapter +3 -0
  29. data/lib/hecks/composer/adapters/claude_composer.rb +83 -0
  30. data/lib/hecks/composer/adapters/fake_agent.adapter +3 -0
  31. data/lib/hecks/composer/adapters/fake_agent.rb +55 -0
  32. data/lib/hecks/composer/adapters/jev.adapter +3 -0
  33. data/lib/hecks/composer/adapters/jev.rb +66 -0
  34. data/lib/hecks/composer/agent.rb +113 -0
  35. data/lib/hecks/composer/assistant.rb +110 -0
  36. data/lib/hecks/composer/behavior_run.rb +53 -0
  37. data/lib/hecks/composer/bluebook/composer.bluebook +178 -0
  38. data/lib/hecks/composer/bluebook/composer.hecksagon +16 -0
  39. data/lib/hecks/composer/board.rb +65 -0
  40. data/lib/hecks/composer/board_edit/command_text.rb +39 -0
  41. data/lib/hecks/composer/board_edit/source.rb +84 -0
  42. data/lib/hecks/composer/board_edit.rb +144 -0
  43. data/lib/hecks/composer/check.rb +89 -0
  44. data/lib/hecks/composer/domains.rb +81 -0
  45. data/lib/hecks/composer/jobs.rb +134 -0
  46. data/lib/hecks/composer/layout.rb +37 -0
  47. data/lib/hecks/composer/live.rb +150 -0
  48. data/lib/hecks/composer/outline.rb +48 -0
  49. data/lib/hecks/composer/page.html +989 -0
  50. data/lib/hecks/composer/panels.rb +142 -0
  51. data/lib/hecks/composer/parse.rb +83 -0
  52. data/lib/hecks/composer/plain.rb +28 -0
  53. data/lib/hecks/composer/ports/composer_agent.port +7 -0
  54. data/lib/hecks/composer/session.rb +141 -0
  55. data/lib/hecks/composer/toolbox.rb +166 -0
  56. data/lib/hecks/composer/web.rb +156 -0
  57. data/lib/hecks/composer/workbench.rb +51 -0
  58. data/lib/hecks/composer/workspace.rb +81 -0
  59. data/lib/hecks/composer.rb +17 -0
  60. data/lib/hecks/corpus/adapters.rb +22 -0
  61. data/lib/hecks/corpus/rust.rb +1 -1
  62. data/lib/hecks/corpus/worlds.rb +19 -0
  63. data/lib/hecks/corpus.rb +5 -0
  64. data/lib/hecks/forms/app/commands.rb +2 -3
  65. data/lib/hecks/forms/app/queries.rb +2 -3
  66. data/lib/hecks/forms/app/records.rb +17 -5
  67. data/lib/hecks/forms/app/responses.rb +3 -1
  68. data/lib/hecks/forms/command_form_renderer.rb +42 -16
  69. data/lib/hecks/forms/field_controls.rb +2 -2
  70. data/lib/hecks/forms/field_renderer.rb +1 -1
  71. data/lib/hecks/forms/field_shape.rb +36 -17
  72. data/lib/hecks/forms/index_renderer.rb +2 -1
  73. data/lib/hecks/forms/page.rb +1 -1
  74. data/lib/hecks/forms/params.rb +0 -13
  75. data/lib/hecks/forms/query_form_renderer.rb +7 -6
  76. data/lib/hecks/forms/record_renderer.rb +11 -7
  77. data/lib/hecks/forms/rule_words.rb +100 -0
  78. data/lib/hecks/forms/value_object_shape.rb +2 -1
  79. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +0 -3
  80. data/lib/hecks/fuzzing/isolated_boot/worlds.rb +3 -6
  81. data/lib/hecks/fuzzing/replay/filters.rb +2 -6
  82. data/lib/hecks/gate/stages.yml +25 -0
  83. data/lib/hecks/grammar/evolve/seeds.rb +0 -6
  84. data/lib/hecks/grammar/evolve.rb +0 -3
  85. data/lib/hecks/grammar/translation.bluebook +1 -1
  86. data/lib/hecks/hecks/adapters/codebase/composing.rb +56 -0
  87. data/lib/hecks/hecks/adapters/codebase/source_tree.rb +3 -1
  88. data/lib/hecks/hecks/adapters/first_admin.rb +3 -3
  89. data/lib/hecks/hecks/adapters/journal_store/changes.rb +0 -26
  90. data/lib/hecks/hecks/adapters/journal_store/examination.rb +2 -23
  91. data/lib/hecks/hecks/adapters/journal_store/readings.rb +3 -11
  92. data/lib/hecks/hecks/adapters/journal_store.rb +0 -11
  93. data/lib/hecks/hecks/adapters/rehearsal/inquiry.rb +4 -11
  94. data/lib/hecks/hecks/adapters/rehearsal/memory_store.rb +0 -9
  95. data/lib/hecks/hecks/adapters/rehearsal/postgres_reading.rb +0 -4
  96. data/lib/hecks/hecks/codebase.behaviors +6 -0
  97. data/lib/hecks/hecks/codebase.bluebook +21 -0
  98. data/lib/hecks/hecks/composer.behaviors +68 -0
  99. data/lib/hecks/hecks/context_map.hecksagon +1 -1
  100. data/lib/hecks/hecks/custodian.behaviors +0 -6
  101. data/lib/hecks/hecks/custodian.bluebook +3 -80
  102. data/lib/hecks/hecks/hecks.hecksagon +2 -2
  103. data/lib/hecks/hecks/hecks.world +1 -1
  104. data/lib/hecks/hecks/rehearsal.bluebook +7 -13
  105. data/lib/hecks/kernel_capabilities/templates.rb +4 -4
  106. data/lib/hecks/kernel_capabilities.rb +1 -1
  107. data/lib/hecks/language/bluebook/vocabulary.bluebook +2 -3
  108. data/lib/hecks/language/translation/translation_aggregate.bluebook +21 -24
  109. data/lib/hecks/ports/access_control.rb +2 -2
  110. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +3 -41
  111. data/lib/hecks/ports/persistence/plugins/era/lineage/coverage.rb +7 -7
  112. data/lib/hecks/ports/persistence/plugins/era/lineage/rewrite.rb +25 -2
  113. data/lib/hecks/ports/persistence/plugins/era/lineage/rules.rb +4 -4
  114. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +17 -25
  115. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/base_sql.rb +0 -12
  116. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/domain_rename.rb +1 -1
  117. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +2 -22
  118. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +0 -1
  119. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +9 -3
  120. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +3 -29
  121. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/refusals.rb +2 -26
  122. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +6 -33
  123. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +3 -3
  124. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +2 -4
  125. data/lib/hecks/ports/persistence/plugins/era/translation/expression_sql.rb +52 -0
  126. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +23 -29
  127. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +1 -1
  128. data/lib/hecks/ports/persistence/plugins/era/translation.rb +0 -1
  129. data/lib/hecks/ports/persistence/plugins/era.rb +3 -6
  130. data/lib/hecks/ports.rb +0 -1
  131. data/lib/hecks/projection_files.rb +3 -1
  132. data/lib/hecks/projections/deploy/templates/lambda/nat_comment_oauth.tmpl +1 -1
  133. data/lib/hecks/projections/glossary/entries.rb +10 -4
  134. data/lib/hecks/projections/glossary/groups.rb +42 -0
  135. data/lib/hecks/projections/glossary/html/page.rb +7 -2
  136. data/lib/hecks/projections/glossary/html/parser.rb +1 -1
  137. data/lib/hecks/projections/glossary/html/sections.rb +31 -11
  138. data/lib/hecks/projections/glossary/html.rb +1 -2
  139. data/lib/hecks/projections/glossary/markdown.rb +31 -28
  140. data/lib/hecks/projections/glossary/mermaid.rb +3 -0
  141. data/lib/hecks/projections/glossary/page.css +143 -153
  142. data/lib/hecks/projections/glossary/page.js +63 -3
  143. data/lib/hecks/projections/glossary/sentences/actions.rb +88 -0
  144. data/lib/hecks/projections/glossary/sentences/paths.rb +77 -0
  145. data/lib/hecks/projections/glossary/sentences/queries.rb +90 -0
  146. data/lib/hecks/projections/glossary/sentences/value_objects.rb +32 -5
  147. data/lib/hecks/projections/glossary/sentences.rb +13 -3
  148. data/lib/hecks/projections/glossary/slugs.rb +4 -1
  149. data/lib/hecks/projections/glossary.rb +2 -1
  150. data/lib/hecks/projections/plain_english.rb +97 -0
  151. data/lib/hecks/projections/rust_vocabulary/arguments.rb +2 -2
  152. data/lib/hecks/projections/rust_vocabulary/enum_body.rb +20 -25
  153. data/lib/hecks/projections/rust_vocabulary/tests.rb +2 -2
  154. data/lib/hecks/projections/rust_vocabulary.rb +14 -6
  155. data/lib/hecks/projections/site/cms_editor/color.rb +0 -6
  156. data/lib/hecks/projections/site/cms_editor/templates/src/host.ts.tmpl +7 -2
  157. data/lib/hecks/projections/statements/actions.rb +99 -0
  158. data/lib/hecks/projections/statements/relationships.rb +86 -0
  159. data/lib/hecks/projections/statements.rb +15 -24
  160. data/lib/hecks/projector/exporter/adapters.rb +112 -0
  161. data/lib/hecks/projector/exporter/translations.rb +18 -35
  162. data/lib/hecks/projector/exporter/worlds.rb +103 -0
  163. data/lib/hecks/projector/exporter.rb +5 -1
  164. data/lib/hecks/quality_control/adapters/agent.rb +6 -7
  165. data/lib/hecks/quality_control/adapters/agent_profile.rb +16 -114
  166. data/lib/hecks/quality_control/cli/qa_mine_combinations.rb +1 -1
  167. data/lib/hecks/runtime/loader/boot_steps.rb +0 -20
  168. data/lib/hecks/runtime/outbox.rb +2 -9
  169. data/lib/hecks/runtime/port_operation_interpreter.rb +1 -9
  170. data/lib/hecks/runtime/remote_dispatcher.rb +2 -2
  171. data/lib/hecks/runtime/value/unwrapping.rb +60 -0
  172. data/lib/hecks/runtime/value.rb +10 -21
  173. data/lib/hecks/rust_build/project_rust/target_ir.rb +23 -4
  174. data/lib/hecks/rust_build/wasm.rb +0 -12
  175. data/lib/hecks/rust_build/write_if_changed.rb +12 -12
  176. data/lib/hecks/rust_format.rb +77 -0
  177. data/lib/hecks/tools/fuzz_sweep/findings.rb +3 -9
  178. data/lib/hecks/tools/fuzz_sweep.rb +1 -3
  179. data/lib/hecks/tools/translation_audit/verdicts.rb +3 -28
  180. data/lib/hecks/tools/translation_audit.rb +8 -10
  181. data/lib/hecks/tools/translation_scaffold/reporting.rb +1 -1
  182. data/lib/hecks/version.rb +1 -1
  183. data/lib/hecks/vocabulary.rb +2 -1
  184. data/lib/hecks.rb +3 -0
  185. data/prebuilt/{1a6f596be124228be93eba1d86dbc9d2933190fa389b284331241cb9f27115a0.marshal → ad68f9e06c8068c2a4176b57abf3083d591d5d8980c2b3e897b97331f965b643.marshal} +0 -0
  186. data/prebuilt/{a883a7e568667aa5ac904c82daff356bdf9e996252a36af6def4a4a43efb967e.marshal → ee22ec8642c4006717ba2b80f46e5efb81de459eef9dfec043e838c0ad4fd720.marshal} +0 -0
  187. data/prebuilt/{verdicts-872993b1aa26d73fe48f28251374b8585fed145d02aab42be0f00de386b6ff80.json → verdicts-b69f5dcbba79c20daa0493476fa7c317004a30aa2bc3d3dc6a722ea70ae2b43b.json} +1 -1
  188. data/rust/Cargo.toml +40 -0
  189. data/rust/build/Cargo.toml +36 -0
  190. data/rust/build/clippy.toml +4 -0
  191. data/rust/build/src/adapters_pass.rs +196 -0
  192. data/rust/build/src/binding_pass.rs +54 -13
  193. data/rust/build/src/build_artifact.rs +23 -5
  194. data/rust/build/src/cargo_sync.rs +69 -26
  195. data/rust/build/src/lineage_pass.rs +101 -87
  196. data/rust/build/src/main.rs +11 -2
  197. data/rust/build/src/pipeline.rs +80 -19
  198. data/rust/build/src/reserved_names.rs +6 -50
  199. data/rust/build/src/resolve.rs +82 -35
  200. data/rust/build/src/rustfmt.rs +100 -0
  201. data/rust/build/src/seams_pass.rs +85 -16
  202. data/rust/build/src/subprocess.rs +30 -6
  203. data/rust/build/src/tmp.rs +5 -2
  204. data/rust/build/src/translation_pass.rs +72 -22
  205. data/rust/build/src/world_pass.rs +109 -0
  206. data/rust/clippy.toml +4 -0
  207. data/rust/codegen/Cargo.toml +36 -0
  208. data/rust/codegen/clippy.toml +4 -0
  209. data/rust/codegen/src/asks.rs +40 -14
  210. data/rust/codegen/src/attr.rs +2 -2
  211. data/rust/codegen/src/bridging.rs +676 -117
  212. data/rust/codegen/src/commands.rs +979 -373
  213. data/rust/codegen/src/constraints.rs +88 -25
  214. data/rust/codegen/src/dependency_planning.rs +128 -34
  215. data/rust/codegen/src/domain_generator.rs +306 -170
  216. data/rust/codegen/src/exemplar.rs +87 -32
  217. data/rust/codegen/src/expr_emitter.rs +54 -6
  218. data/rust/codegen/src/fielded.rs +193 -41
  219. data/rust/codegen/src/hecks_naming.rs +124 -16
  220. data/rust/codegen/src/hecks_naming_tests.rs +81 -0
  221. data/rust/codegen/src/json.rs +80 -44
  222. data/rust/codegen/src/json_codec.rs +585 -149
  223. data/rust/codegen/src/literal.rs +104 -138
  224. data/rust/codegen/src/main.rs +151 -52
  225. data/rust/codegen/src/manifest.rs +76 -14
  226. data/rust/codegen/src/mutations.rs +1212 -239
  227. data/rust/codegen/src/naming.rs +111 -60
  228. data/rust/codegen/src/optional_pass.rs +90 -28
  229. data/rust/codegen/src/ports.rs +5 -4
  230. data/rust/codegen/src/prelude.rs +139 -49
  231. data/rust/codegen/src/presenters.rs +335 -0
  232. data/rust/codegen/src/queries.rs +601 -226
  233. data/rust/codegen/src/reactions.rs +268 -74
  234. data/rust/codegen/src/read_models.rs +574 -229
  235. data/rust/codegen/src/reference_specs.rs +16 -3
  236. data/rust/codegen/src/registry.rs +128 -79
  237. data/rust/codegen/src/reserved_names.rs +6 -50
  238. data/rust/codegen/src/shared.rs +71 -22
  239. data/rust/codegen/src/sidecars.rs +15 -5
  240. data/rust/codegen/src/skip_reason.rs +8 -2
  241. data/rust/codegen/src/types.rs +306 -108
  242. data/rust/host/Cargo.lock +1 -0
  243. data/rust/host/Cargo.toml +40 -4
  244. data/rust/host/HECKS_RELEASE +1 -1
  245. data/rust/host/clippy.toml +4 -0
  246. data/rust/host/src/api.rs +698 -218
  247. data/rust/host/src/auth.rs +491 -214
  248. data/rust/host/src/b64.rs +91 -0
  249. data/rust/host/src/bin/expr_harness.rs +6 -2
  250. data/rust/host/src/bin/lineage_harness.rs +47 -11
  251. data/rust/host/src/bin/mint_harness.rs +70 -37
  252. data/rust/host/src/bin/validate_harness.rs +16 -8
  253. data/rust/host/src/boot.rs +144 -68
  254. data/rust/host/src/boundary_fuzz/api.rs +130 -32
  255. data/rust/host/src/boundary_fuzz/auth.rs +108 -23
  256. data/rust/host/src/boundary_fuzz/expr_json.rs +85 -11
  257. data/rust/host/src/boundary_fuzz/ir.rs +164 -12
  258. data/rust/host/src/boundary_fuzz/server.rs +283 -36
  259. data/rust/host/src/boundary_fuzz/web.rs +56 -8
  260. data/rust/host/src/checkout.rs +78 -32
  261. data/rust/host/src/clock.rs +8 -0
  262. data/rust/host/src/commerce_ir.rs +333 -95
  263. data/rust/host/src/crypto.rs +33 -0
  264. data/rust/host/src/dispatch.rs +1037 -331
  265. data/rust/host/src/expr_json.rs +1021 -273
  266. data/rust/host/src/extension.rs +24 -4
  267. data/rust/host/src/field_hints.rs +11 -7
  268. data/rust/host/src/fuzz_support.rs +85 -13
  269. data/rust/host/src/host.rs +100 -0
  270. data/rust/host/src/http.rs +22 -0
  271. data/rust/host/src/ir.rs +326 -16
  272. data/rust/host/src/journal.rs +277 -176
  273. data/rust/host/src/lambda_client.rs +219 -53
  274. data/rust/host/src/lib.rs +16 -4
  275. data/rust/host/src/log.rs +62 -11
  276. data/rust/host/src/mint.rs +1153 -326
  277. data/rust/host/src/needs.rs +185 -44
  278. data/rust/host/src/payments/keystore.rs +197 -45
  279. data/rust/host/src/payments/roles.rs +39 -7
  280. data/rust/host/src/payments/tests.rs +1427 -282
  281. data/rust/host/src/payments.rs +391 -112
  282. data/rust/host/src/platform_signin/attack_tests.rs +5 -5
  283. data/rust/host/src/platform_signin/config.rs +11 -11
  284. data/rust/host/src/platform_signin/cookie.rs +2 -2
  285. data/rust/host/src/platform_signin/extension.rs +2 -1
  286. data/rust/host/src/platform_signin/handler.rs +9 -8
  287. data/rust/host/src/platform_signin/jti.rs +4 -2
  288. data/rust/host/src/platform_signin/jwks.rs +2 -1
  289. data/rust/host/src/platform_signin/jws.rs +2 -37
  290. data/rust/host/src/platform_signin/known_answer_tests.rs +5 -1
  291. data/rust/host/src/platform_signin/mode_tests.rs +5 -3
  292. data/rust/host/src/platform_signin/pages.rs +20 -20
  293. data/rust/host/src/platform_signin/session.rs +8 -1
  294. data/rust/host/src/platform_signin/settings.rs +6 -6
  295. data/rust/host/src/platform_signin/strict_json.rs +12 -9
  296. data/rust/host/src/platform_signin/verify.rs +41 -41
  297. data/rust/host/src/platform_signin/wiring_support.rs +13 -18
  298. data/rust/host/src/presentation.rs +207 -63
  299. data/rust/host/src/presentation_write.rs +502 -187
  300. data/rust/host/src/query_step.rs +69 -18
  301. data/rust/host/src/rate_limit.rs +541 -142
  302. data/rust/host/src/reference_transform.rs +77 -25
  303. data/rust/host/src/reference_validate.rs +443 -104
  304. data/rust/host/src/resend.rs +190 -55
  305. data/rust/host/src/secrets.rs +116 -21
  306. data/rust/host/src/server.rs +555 -104
  307. data/rust/host/src/storage_shape.rs +88 -21
  308. data/rust/host/src/test_pg.rs +17 -7
  309. data/rust/host/src/ui_schema.rs +495 -207
  310. data/rust/host/src/wasm_runner.rs +21 -6
  311. data/rust/host/src/web/commerce.rs +101 -23
  312. data/rust/host/src/web/newsletter/cooldown.rs +10 -3
  313. data/rust/host/src/web/newsletter/template.rs +26 -7
  314. data/rust/host/src/web/newsletter/tests.rs +112 -23
  315. data/rust/host/src/web/newsletter.rs +209 -59
  316. data/rust/host/src/web/newsletter_send.rs +251 -79
  317. data/rust/host/src/web/registration_receipt/tests.rs +16 -4
  318. data/rust/host/src/web/registration_receipt.rs +100 -24
  319. data/rust/host/src/web/registrations.rs +1384 -332
  320. data/rust/host/src/web/resend_webhook.rs +138 -80
  321. data/rust/host/src/web.rs +2416 -508
  322. data/rust/lsp/Cargo.toml +36 -0
  323. data/rust/lsp/clippy.toml +4 -0
  324. data/rust/lsp/src/json.rs +29 -22
  325. data/rust/lsp/src/main.rs +81 -31
  326. data/rust/lsp/src/outline.rs +23 -7
  327. data/rust/lsp/src/rpc.rs +17 -4
  328. data/rust/parser/Cargo.toml +36 -0
  329. data/rust/parser/clippy.toml +4 -0
  330. data/rust/parser/src/build/closed_sets.rs +6 -1
  331. data/rust/parser/src/build/identity.rs +4 -3
  332. data/rust/parser/src/build/mod.rs +1 -1
  333. data/rust/parser/src/build/named_pattern.rs +4 -1
  334. data/rust/parser/src/build/naming.rs +2 -80
  335. data/rust/parser/src/build/pattern_subset.rs +6 -8
  336. data/rust/parser/src/build/query_derive.rs +3 -1
  337. data/rust/parser/src/build/query_inference.rs +14 -5
  338. data/rust/parser/src/build/read_model.rs +4 -1
  339. data/rust/parser/src/build/references.rs +4 -3
  340. data/rust/parser/src/canonical.rs +34 -15
  341. data/rust/parser/src/emit.rs +187 -105
  342. data/rust/parser/src/expr/ast_json.rs +134 -42
  343. data/rust/parser/src/expr/evaluator.rs +27 -9
  344. data/rust/parser/src/expr/mod.rs +54 -13
  345. data/rust/parser/src/expr/resolver.rs +299 -92
  346. data/rust/parser/src/ir.rs +35 -3
  347. data/rust/parser/src/keywords.rs +7168 -516
  348. data/rust/parser/src/lex.rs +11 -25
  349. data/rust/parser/src/lib.rs +1 -0
  350. data/rust/parser/src/main.rs +65 -6
  351. data/rust/parser/src/parse/adapter.rs +157 -0
  352. data/rust/parser/src/parse/aggregate.rs +19 -10
  353. data/rust/parser/src/parse/chapter.rs +16 -17
  354. data/rust/parser/src/parse/command.rs +35 -16
  355. data/rust/parser/src/parse/domain_port.rs +4 -7
  356. data/rust/parser/src/parse/entity.rs +21 -11
  357. data/rust/parser/src/parse/hecksagon.rs +10 -4
  358. data/rust/parser/src/parse/lifecycle.rs +9 -5
  359. data/rust/parser/src/parse/mod.rs +22 -91
  360. data/rust/parser/src/parse/needs.rs +21 -12
  361. data/rust/parser/src/parse/policy.rs +2 -2
  362. data/rust/parser/src/parse/process_manager.rs +9 -11
  363. data/rust/parser/src/parse/query.rs +8 -4
  364. data/rust/parser/src/parse/read_model.rs +57 -16
  365. data/rust/parser/src/parse/value_object.rs +7 -2
  366. data/rust/parser/src/parse/world.rs +322 -0
  367. data/rust/parser/src/ruby_value.rs +7 -6
  368. data/rust/src/exemplar/json.rs +4 -4
  369. data/rust/src/exemplar/mutations.rs +18 -0
  370. data/rust/src/exemplar/read_models.rs +1 -10
  371. data/rust/src/kernel/attribute_shapes/composite.rs +54 -14
  372. data/rust/src/kernel/attribute_shapes/list.rs +2 -2
  373. data/rust/src/kernel/attribute_shapes/mod.rs +8 -8
  374. data/rust/src/kernel/attribute_shapes/scalar.rs +2 -2
  375. data/rust/src/kernel/cli.rs +244 -114
  376. data/rust/src/kernel/dispatch.rs +323 -115
  377. data/rust/src/kernel/dispatch_guard_tests.rs +327 -65
  378. data/rust/src/kernel/expr.rs +203 -86
  379. data/rust/src/kernel/expression_operators/arithmetic.rs +138 -25
  380. data/rust/src/kernel/expression_operators/comparison.rs +87 -2
  381. data/rust/src/kernel/expression_operators/enumeration.rs +106 -22
  382. data/rust/src/kernel/expression_operators/logical.rs +9 -3
  383. data/rust/src/kernel/expression_operators/membership.rs +3 -1
  384. data/rust/src/kernel/expression_operators/mod.rs +34 -21
  385. data/rust/src/kernel/expression_operators/pattern_match.rs +46 -11
  386. data/rust/src/kernel/expression_operators/positional.rs +84 -30
  387. data/rust/src/kernel/expression_operators/presence.rs +52 -13
  388. data/rust/src/kernel/expression_operators/sign_test.rs +3 -1
  389. data/rust/src/kernel/expression_operators/sized.rs +24 -8
  390. data/rust/src/kernel/expression_operators/text.rs +120 -27
  391. data/rust/src/kernel/expression_operators/to_string.rs +10 -4
  392. data/rust/src/kernel/json.rs +244 -148
  393. data/rust/src/kernel/mod.rs +47 -33
  394. data/rust/src/kernel/named_query.rs +63 -15
  395. data/rust/src/kernel/needs.rs +112 -25
  396. data/rust/src/kernel/orchestrate.rs +980 -581
  397. data/rust/src/kernel/orchestrate_guard_tests.rs +538 -140
  398. data/rust/src/kernel/pattern.rs +107 -34
  399. data/rust/src/kernel/present.rs +261 -0
  400. data/rust/src/kernel/query_comparators.rs +118 -41
  401. data/rust/src/kernel/query_ordering.rs +13 -5
  402. data/rust/src/kernel/read_model.rs +479 -181
  403. data/rust/src/kernel/reference_lookup.rs +122 -26
  404. data/rust/src/kernel/repository.rs +293 -66
  405. data/rust/src/kernel/routing.rs +58 -42
  406. data/rust/src/kernel/ruby_float.rs +139 -0
  407. data/rust/src/kernel/vocab/aggregate_dispatch_order.rs +57 -57
  408. data/rust/src/kernel/vocab/entity_dispatch_order.rs +54 -54
  409. data/rust/src/kernel/vocab/field_hint.rs +19 -25
  410. data/rust/src/kernel/vocab/mod.rs +6 -6
  411. data/rust/src/kernel/vocab/query_comparator.rs +23 -24
  412. data/rust/src/kernel/vocab/refusal_template.rs +501 -297
  413. data/rust/src/lib.rs +50 -0
  414. data/rust/src/main.rs +4 -1
  415. data/rust/web/Cargo.toml +36 -0
  416. data/rust/web/clippy.toml +4 -0
  417. metadata +85 -15
  418. data/examples/pizzas/bluebook/hecksagon/mock_stripe_payment_adapter.rb +0 -36
  419. data/lib/hecks/adapters/driven/google_authentication.adapter +0 -3
  420. data/lib/hecks/adapters/driven/google_authentication.rb +0 -99
  421. data/lib/hecks/adapters/driven/mock_stripe_adapter.adapter +0 -3
  422. data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +0 -32
  423. data/lib/hecks/ports/authentication.port +0 -6
  424. data/lib/hecks/ports/authentication.rb +0 -67
  425. data/lib/hecks/ports/persistence/plugins/era/translation/approval_file.rb +0 -255
  426. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +0 -22
  427. data/rust/host/src/approval.rs +0 -652
@@ -2,6 +2,8 @@
2
2
  //! `Literal::from_json` covers values embedded raw in ir.json, which were never rendered.
3
3
 
4
4
  use crate::json::{Json, Number};
5
+ use crate::ruby_value::{self, Value};
6
+ use crate::shared::ruby_to_i;
5
7
 
6
8
  #[derive(Debug, Clone, PartialEq)]
7
9
  pub enum Literal {
@@ -12,73 +14,71 @@ pub enum Literal {
12
14
  /// A Symbol, which JSON cannot represent; only `read` produces one (leading-colon strings).
13
15
  Symbol(String),
14
16
  Str(String),
15
- Hash(Vec<(String, Literal)>),
16
- Array(Vec<Literal>),
17
+ Hash(Vec<(String, Self)>),
18
+ Array(Vec<Self>),
17
19
  }
18
20
 
19
21
  impl Literal {
20
22
  /// Converts a raw `Json` value structurally, with no text parsing and no Symbol case.
21
- pub fn from_json(value: &Json) -> Literal {
23
+ pub fn from_json(value: &Json) -> Self {
22
24
  match value {
23
- Json::Null => Literal::Nil,
24
- Json::Bool(b) => Literal::Bool(*b),
25
+ Json::Null => Self::Nil,
26
+ Json::Bool(b) => Self::Bool(*b),
25
27
  Json::Number(_) => match value.number() {
26
- Some(Number::Int(n)) => Literal::Int(n),
27
- Some(Number::Float(n)) => Literal::Float(n),
28
- None => Literal::Nil,
28
+ Some(Number::Int(n)) => Self::Int(n),
29
+ Some(Number::Float(n)) => Self::Float(n),
30
+ None => Self::Nil,
29
31
  },
30
- Json::String(s) => Literal::Str(s.clone()),
31
- Json::Array(items) => Literal::Array(items.iter().map(Literal::from_json).collect()),
32
- Json::Object(pairs) => Literal::Hash(pairs.iter().map(|(k, v)| (k.clone(), Literal::from_json(v))).collect()),
32
+ Json::String(s) => Self::Str(s.clone()),
33
+ Json::Array(items) => Self::Array(items.iter().map(Self::from_json).collect()),
34
+ Json::Object(pairs) => Self::Hash(
35
+ pairs
36
+ .iter()
37
+ .map(|(k, v)| (k.clone(), Self::from_json(v)))
38
+ .collect(),
39
+ ),
33
40
  }
34
41
  }
35
42
 
36
- pub fn as_hash(&self) -> Option<&[(String, Literal)]> {
43
+ pub const fn as_hash(&self) -> Option<&[(String, Self)]> {
37
44
  match self {
38
- Literal::Hash(pairs) => Some(pairs.as_slice()),
45
+ Self::Hash(pairs) => Some(pairs.as_slice()),
39
46
  _ => None,
40
47
  }
41
48
  }
42
49
 
43
- pub fn get(&self, key: &str) -> Option<&Literal> {
44
- self.as_hash()?.iter().find(|(k, _)| k == key).map(|(_, v)| v)
50
+ pub fn get(&self, key: &str) -> Option<&Self> {
51
+ self.as_hash()?
52
+ .iter()
53
+ .find(|(k, _)| k == key)
54
+ .map(|(_, v)| v)
45
55
  }
46
56
  }
47
57
 
48
58
  /// `Literal.read`: parses a rendered wire string back to a `Literal`.
49
59
  ///
50
- /// Anything not matching a more specific shape, including a bare word, becomes a `Str`.
60
+ /// Reads through the parser's shared `ruby_value::read`. A bare word becomes a `Str`. An integer
61
+ /// too large for `i64` (the shared reader returns it as `Bare`) becomes `Int(0)`, as `ruby_to_i`
62
+ /// always did: no corpus domain holds one, and a stable `0` keeps generation total.
51
63
  pub fn read(text: &str) -> Literal {
52
- let raw = text.trim();
53
- if raw.is_empty() || raw == "nil" {
54
- return Literal::Nil;
55
- }
56
- if raw == "true" {
57
- return Literal::Bool(true);
58
- }
59
- if raw == "false" {
60
- return Literal::Bool(false);
61
- }
62
- if is_integer(raw) {
63
- return Literal::Int(ruby_to_i(raw));
64
- }
65
- if is_float(raw) {
66
- return Literal::Float(raw.parse::<f64>().unwrap_or(0.0));
67
- }
68
- if let Some(rest) = raw.strip_prefix(':') {
69
- return Literal::Symbol(rest.to_string());
70
- }
71
- if is_quoted(raw) {
72
- return Literal::Str(unquote(raw));
73
- }
74
- if raw.starts_with('{') && raw.ends_with('}') {
75
- return read_hash(raw);
76
- }
77
- if raw.starts_with('[') && raw.ends_with(']') {
78
- return read_array(raw);
79
- }
64
+ from_value(ruby_value::read(text))
65
+ }
80
66
 
81
- Literal::Str(raw.to_string())
67
+ fn from_value(value: Value) -> Literal {
68
+ match value {
69
+ Value::Nil => Literal::Nil,
70
+ Value::Bool(b) => Literal::Bool(b),
71
+ Value::Int(n) => Literal::Int(n),
72
+ Value::Float(n) => Literal::Float(n),
73
+ Value::Str(s) => Literal::Str(s),
74
+ Value::Symbol(s) => Literal::Symbol(s),
75
+ Value::Hash(pairs) => {
76
+ Literal::Hash(pairs.into_iter().map(|(k, v)| (k, from_value(v))).collect())
77
+ }
78
+ Value::Array(items) => Literal::Array(items.into_iter().map(from_value).collect()),
79
+ Value::Bare(text) if is_integer(&text) => Literal::Int(ruby_to_i(&text)),
80
+ Value::Bare(text) => Literal::Str(text),
81
+ }
82
82
  }
83
83
 
84
84
  /// `/\A-?\d+\z/`
@@ -87,63 +87,11 @@ fn is_integer(raw: &str) -> bool {
87
87
  !s.is_empty() && s.bytes().all(|b| b.is_ascii_digit())
88
88
  }
89
89
 
90
- /// `/\A-?\d+\.\d+\z/`
91
- fn is_float(raw: &str) -> bool {
92
- let s = raw.strip_prefix('-').unwrap_or(raw);
93
- let Some((int_part, frac_part)) = s.split_once('.') else { return false };
94
- !int_part.is_empty() && !frac_part.is_empty() && int_part.bytes().all(|b| b.is_ascii_digit()) && frac_part.bytes().all(|b| b.is_ascii_digit())
95
- }
96
-
97
- fn ruby_to_i(raw: &str) -> i64 {
98
- raw.parse().unwrap_or(0)
99
- }
100
-
101
- fn is_quoted(raw: &str) -> bool {
102
- raw.len() >= 2 && raw.starts_with('"') && raw.ends_with('"')
103
- }
104
-
105
- /// Drops the surrounding quotes, then unescapes any backslash-escaped character.
106
- fn unquote(raw: &str) -> String {
107
- let inner = &raw[1..raw.len() - 1];
108
- let mut out = String::with_capacity(inner.len());
109
- let mut chars = inner.chars().peekable();
110
- while let Some(c) = chars.next() {
111
- if c == '\\' {
112
- if let Some(next) = chars.next() {
113
- out.push(next);
114
- continue;
115
- }
116
- }
117
- out.push(c);
118
- }
119
- out
120
- }
121
-
122
- fn read_hash(raw: &str) -> Literal {
123
- let inner = &raw[1..raw.len() - 1];
124
- let pairs = split_items(inner)
125
- .into_iter()
126
- .map(|item| {
127
- // Splits at the first ":" only, like Ruby's `partition`; field names have none.
128
- match item.find(':') {
129
- Some(idx) => {
130
- let key = item[..idx].trim().to_string();
131
- let held = &item[idx + 1..];
132
- (key, read(held))
133
- }
134
- None => (item.trim().to_string(), read("")),
135
- }
136
- })
137
- .collect();
138
- Literal::Hash(pairs)
139
- }
140
-
141
- fn read_array(raw: &str) -> Literal {
142
- let inner = &raw[1..raw.len() - 1];
143
- Literal::Array(split_items(inner).into_iter().map(|item| read(&item)).collect())
144
- }
145
-
146
90
  /// `naming.rb#literal_rhs` for a `Literal`: String/Integer/Float/Boolean, else panics.
91
+ #[allow(
92
+ clippy::panic,
93
+ reason = "a drifted exemplar or invalid IR aborts generation on purpose"
94
+ )]
147
95
  pub fn literal_rhs(literal: &Literal) -> String {
148
96
  match literal {
149
97
  Literal::Str(s) => format!("{}.to_string()", crate::naming::ruby_inspect_string(s)),
@@ -168,43 +116,61 @@ fn ruby_float_to_s(literal: &Literal) -> String {
168
116
  }
169
117
  }
170
118
 
171
- /// Splits on commas outside quoted strings and nested braces/brackets.
172
- fn split_items(body: &str) -> Vec<String> {
173
- let mut items: Vec<String> = Vec::new();
174
- let mut current = String::new();
175
- let mut depth: i32 = 0;
176
- let mut quoting = false;
177
- let mut escaping = false;
178
-
179
- for ch in body.chars() {
180
- current.push(ch);
181
- if escaping {
182
- escaping = false;
183
- continue;
184
- }
185
- if quoting && ch == '\\' {
186
- escaping = true;
187
- continue;
188
- }
189
- if ch == '"' {
190
- quoting = !quoting;
191
- continue;
192
- }
193
- if quoting {
194
- continue;
195
- }
119
+ #[cfg(test)]
120
+ mod tests {
121
+ use super::*;
196
122
 
197
- if ch == '{' || ch == '[' {
198
- depth += 1;
199
- }
200
- if ch == '}' || ch == ']' {
201
- depth -= 1;
202
- }
203
- if ch == ',' && depth == 0 {
204
- current.pop();
205
- items.push(std::mem::take(&mut current));
206
- }
123
+ fn s(text: &str) -> Literal {
124
+ Literal::Str(text.to_string())
125
+ }
126
+
127
+ #[test]
128
+ fn reads_scalars() {
129
+ assert_eq!(read(""), Literal::Nil);
130
+ assert_eq!(read("nil"), Literal::Nil);
131
+ assert_eq!(read("true"), Literal::Bool(true));
132
+ assert_eq!(read("-12"), Literal::Int(-12));
133
+ assert_eq!(read("1.5"), Literal::Float(1.5));
134
+ assert_eq!(read(":name"), Literal::Symbol("name".to_string()));
135
+ assert_eq!(read(r#""a\"b""#), s("a\"b"));
136
+ }
137
+
138
+ #[test]
139
+ fn reads_collections() {
140
+ assert_eq!(
141
+ read(r#"[1, "x"]"#),
142
+ Literal::Array(vec![Literal::Int(1), s("x")])
143
+ );
144
+ assert_eq!(
145
+ read("{a: 1, b: :c}"),
146
+ Literal::Hash(vec![
147
+ ("a".to_string(), Literal::Int(1)),
148
+ ("b".to_string(), Literal::Symbol("c".to_string()))
149
+ ])
150
+ );
151
+ }
152
+
153
+ #[test]
154
+ fn bare_word_is_a_string() {
155
+ assert_eq!(read("Widget"), s("Widget"));
156
+ }
157
+
158
+ #[test]
159
+ fn overflowing_integer_is_zero_like_ruby_to_i() {
160
+ assert_eq!(read("99999999999999999999"), Literal::Int(0));
161
+ assert_eq!(read("-99999999999999999999"), Literal::Int(0));
162
+ }
163
+
164
+ #[test]
165
+ fn adjacent_and_single_quoted_strings() {
166
+ assert_eq!(read(r#""a" "b""#), s("ab"));
167
+ assert_eq!(read(r"'it\'s'"), s("it's"));
168
+ }
169
+
170
+ #[test]
171
+ fn from_json_and_get() {
172
+ let hash = read("{a: 1}");
173
+ assert_eq!(hash.get("a"), Some(&Literal::Int(1)));
174
+ assert_eq!(Literal::from_json(&Json::Null), Literal::Nil);
207
175
  }
208
- items.push(current);
209
- items.into_iter().map(|s| s.trim().to_string()).filter(|s| !s.is_empty()).collect()
210
176
  }
@@ -1,7 +1,5 @@
1
1
  //! `hecks-codegen` — the IR-to-Rust-source generator, the only one (ADR 0086).
2
2
  //! Usage: `hecks-codegen <prelude|domain|full> ...`, one subcommand per function below.
3
- // Some ported `naming.rs` helpers are not called yet.
4
- #![allow(dead_code)]
5
3
 
6
4
  mod asks;
7
5
  mod attr;
@@ -13,7 +11,12 @@ mod domain_generator;
13
11
  mod exemplar;
14
12
  mod expr_emitter;
15
13
  mod fielded;
14
+ // The one `Hecks::Naming` port, compiled by path into the other Cargo projects too; codegen uses
15
+ // only part of it.
16
+ #[allow(dead_code)]
16
17
  mod hecks_naming;
18
+ #[cfg(test)]
19
+ mod hecks_naming_tests;
17
20
  mod json;
18
21
  mod json_codec;
19
22
  mod literal;
@@ -23,18 +26,26 @@ mod naming;
23
26
  mod optional_pass;
24
27
  mod ports;
25
28
  mod prelude;
29
+ mod presenters;
26
30
  mod queries;
27
31
  mod reactions;
28
32
  mod read_models;
29
33
  mod reference_specs;
30
34
  mod registry;
31
35
  mod reserved_names;
36
+ #[path = "../../parser/src/ruby_value.rs"]
37
+ #[allow(
38
+ dead_code,
39
+ reason = "shared with the parser, which uses parts codegen does not"
40
+ )]
41
+ mod ruby_value;
32
42
  mod shared;
33
43
  mod sidecars;
34
44
  mod skip_reason;
35
45
  mod types;
36
46
 
37
47
  use json::Json;
48
+ use shared::{puts_blank, puts_str};
38
49
  use std::process::ExitCode;
39
50
 
40
51
  fn main() -> ExitCode {
@@ -69,7 +80,7 @@ fn run_prelude(args: &[String]) -> Result<(), String> {
69
80
 
70
81
  let ex = exemplar::Exemplar::load();
71
82
 
72
- let aggregates = ir.get("aggregates").map(Json::each).unwrap_or(&[]);
83
+ let aggregates = ir.get("aggregates").map_or_default(Json::each);
73
84
  for aggregate in aggregates {
74
85
  let name = aggregate.get("name").and_then(Json::as_str).unwrap_or("");
75
86
  match prelude::aggregate_prelude(&ex, &ir, aggregate, source_label) {
@@ -92,7 +103,9 @@ fn run_prelude(args: &[String]) -> Result<(), String> {
92
103
  /// sidecars into `out_dir`.
93
104
  fn run_domain(args: &[String]) -> Result<(), String> {
94
105
  let [ir_path, source_label, mod_name, out_dir] = args else {
95
- return Err("usage: hecks-codegen domain <ir.json> <source_label> <mod_name> <out_dir>".to_string());
106
+ return Err(
107
+ "usage: hecks-codegen domain <ir.json> <source_label> <mod_name> <out_dir>".to_string(),
108
+ );
96
109
  };
97
110
 
98
111
  let ir = read_ir(ir_path)?;
@@ -123,8 +136,7 @@ fn write_domain(
123
136
  // Refuse before writing anything, so a name collision is reported before any file exists.
124
137
  let aggregate_names: Vec<&str> = ir
125
138
  .get("aggregates")
126
- .map(Json::each)
127
- .unwrap_or(&[])
139
+ .map_or_default(Json::each)
128
140
  .iter()
129
141
  .map(|a| a.get("name").and_then(Json::as_str).unwrap_or(""))
130
142
  .collect();
@@ -146,7 +158,8 @@ fn write_domain(
146
158
  }
147
159
 
148
160
  let registry_path = format!("{out_dir}/registry.rs");
149
- std::fs::write(&registry_path, &generated.registry_rs).map_err(|e| format!("writing {registry_path}: {e}"))?;
161
+ std::fs::write(&registry_path, &generated.registry_rs)
162
+ .map_err(|e| format!("writing {registry_path}: {e}"))?;
150
163
  println!("wrote {registry_path}");
151
164
 
152
165
  let mod_path = format!("{out_dir}/mod.rs");
@@ -154,34 +167,25 @@ fn write_domain(
154
167
  println!("wrote {mod_path}");
155
168
 
156
169
  let manifest_path = format!("{out_dir}/manifest.json");
157
- std::fs::write(&manifest_path, &generated.manifest_json).map_err(|e| format!("writing {manifest_path}: {e}"))?;
170
+ std::fs::write(&manifest_path, &generated.manifest_json)
171
+ .map_err(|e| format!("writing {manifest_path}: {e}"))?;
158
172
  println!("wrote {manifest_path}");
159
173
 
160
- let (ir_json_path, metadata_path) = sidecars::write(std::path::Path::new(out_dir), ir, source_label)?;
174
+ let (ir_json_path, metadata_path) =
175
+ sidecars::write(std::path::Path::new(out_dir), ir, source_label)?;
161
176
  println!("wrote {}", ir_json_path.display());
162
177
  println!("wrote {}", metadata_path.display());
163
178
 
164
179
  Ok(generated)
165
180
  }
166
181
 
167
- fn puts_str(out: &mut String, s: &str) {
168
- out.push_str(s);
169
- if !s.ends_with('\n') {
170
- out.push('\n');
171
- }
172
- }
173
-
174
- fn puts_blank(out: &mut String) {
175
- out.push('\n');
176
- }
177
-
178
182
  /// `hecks-codegen full <out_root> <target_mod_name> <target_source_label> <target_ir.json>
179
183
  /// [<chapter_mod_name> <chapter_source_label> <chapter_ir.json>]...`
180
184
  ///
181
185
  /// Writes the target chapter's directory, each attached chapter's own directory, and the
182
186
  /// target's `merged.rs`: one `Store`/`dispatch_by_name` table spanning every chapter given.
183
187
  fn run_full(args: &[String]) -> Result<(), String> {
184
- if args.len() < 4 || (args.len() - 4) % 3 != 0 {
188
+ if args.len() < 4 || !(args.len() - 4).is_multiple_of(3) {
185
189
  return Err(
186
190
  "usage: hecks-codegen full <out_root> <target_mod_name> <target_source_label> <target_ir.json> [<chapter_mod_name> <chapter_source_label> <chapter_ir.json>]..."
187
191
  .to_string(),
@@ -196,19 +200,38 @@ fn run_full(args: &[String]) -> Result<(), String> {
196
200
  let ex = exemplar::Exemplar::load();
197
201
 
198
202
  let target_ir = read_ir(target_ir_path)?;
199
- let target_domain_name = target_ir.get("name").and_then(Json::as_str).unwrap_or("").to_string();
203
+ let target_domain_name = target_ir
204
+ .get("name")
205
+ .and_then(Json::as_str)
206
+ .unwrap_or("")
207
+ .to_string();
200
208
 
201
209
  let target_out_dir = format!("{out_root}/{target_mod_name}");
202
- let target_gen = write_domain(&ex, &target_ir, target_source_label, target_mod_name, &target_out_dir)?;
210
+ let target_gen = write_domain(
211
+ &ex,
212
+ &target_ir,
213
+ target_source_label,
214
+ target_mod_name,
215
+ &target_out_dir,
216
+ )?;
203
217
 
204
218
  // Target first, then each attached chapter in command-line order.
205
- let mut reference_key_pairs: Vec<(String, Vec<String>)> =
206
- vec![(target_domain_name.clone(), target_gen.registry_aggregates.iter().map(|a| a.name.clone()).collect())];
219
+ let mut reference_key_pairs: Vec<(String, Vec<String>)> = vec![(
220
+ target_domain_name.clone(),
221
+ target_gen
222
+ .registry_aggregates
223
+ .iter()
224
+ .map(|a| a.name.clone())
225
+ .collect(),
226
+ )];
207
227
 
208
228
  let mut merged_aggregates = target_gen.registry_aggregates;
209
229
  let mut merged_queries = target_gen.query_defs;
210
230
  let mut merged_read_models = target_gen.read_model_defs;
211
- let mut merged_process_managers: Vec<Json> = target_ir.get("process_managers").map(Json::each).unwrap_or(&[]).to_vec();
231
+ let mut merged_process_managers: Vec<Json> = target_ir
232
+ .get("process_managers")
233
+ .map_or_default(Json::each)
234
+ .to_vec();
212
235
 
213
236
  // Policies and process managers merge across chapters; `chapter_irs` keeps each parsed
214
237
  // chapter alive so `policy_sources` can borrow from it.
@@ -223,60 +246,127 @@ fn run_full(args: &[String]) -> Result<(), String> {
223
246
  i += 3;
224
247
 
225
248
  let chapter_ir = read_ir(chapter_ir_path)?;
226
- let chapter_domain_name = chapter_ir.get("name").and_then(Json::as_str).unwrap_or("").to_string();
249
+ let chapter_domain_name = chapter_ir
250
+ .get("name")
251
+ .and_then(Json::as_str)
252
+ .unwrap_or("")
253
+ .to_string();
227
254
 
228
255
  let chapter_out_dir = format!("{out_root}/{chapter_mod_name}");
229
- let chapter_gen = write_domain(&ex, &chapter_ir, chapter_source_label, chapter_mod_name, &chapter_out_dir)?;
230
-
231
- reference_key_pairs.push((chapter_domain_name.clone(), chapter_gen.registry_aggregates.iter().map(|a| a.name.clone()).collect()));
256
+ let chapter_gen = write_domain(
257
+ &ex,
258
+ &chapter_ir,
259
+ chapter_source_label,
260
+ chapter_mod_name,
261
+ &chapter_out_dir,
262
+ )?;
263
+
264
+ reference_key_pairs.push((
265
+ chapter_domain_name.clone(),
266
+ chapter_gen
267
+ .registry_aggregates
268
+ .iter()
269
+ .map(|a| a.name.clone())
270
+ .collect(),
271
+ ));
232
272
  merged_aggregates.extend(chapter_gen.registry_aggregates);
233
273
  merged_queries.extend(chapter_gen.query_defs);
234
274
  merged_read_models.extend(chapter_gen.read_model_defs);
235
- merged_process_managers.extend(chapter_ir.get("process_managers").map(Json::each).unwrap_or(&[]).to_vec());
275
+ merged_process_managers.extend(
276
+ chapter_ir
277
+ .get("process_managers")
278
+ .map_or_default(Json::each)
279
+ .to_vec(),
280
+ );
236
281
 
237
282
  chapter_domain_names.push(chapter_domain_name);
238
283
  chapter_irs.push(chapter_ir);
239
284
  }
240
285
 
241
286
  // One entry per chapter, so its policies qualify against its own domain and aggregates.
242
- let policies: Vec<Json> = target_ir.get("policies").map(Json::each).unwrap_or(&[]).to_vec();
243
- let policy_aggregates: Vec<Json> = target_ir.get("aggregates").map(Json::each).unwrap_or(&[]).to_vec();
244
-
245
- let mut policy_sources: Vec<reactions::PolicySource> =
246
- vec![reactions::PolicySource { domain_name: &target_domain_name, policies: &policies, aggregates: &policy_aggregates }];
287
+ let policies: Vec<Json> = target_ir
288
+ .get("policies")
289
+ .map_or_default(Json::each)
290
+ .to_vec();
291
+ let policy_aggregates: Vec<Json> = target_ir
292
+ .get("aggregates")
293
+ .map_or_default(Json::each)
294
+ .to_vec();
295
+
296
+ let mut policy_sources: Vec<reactions::PolicySource> = vec![reactions::PolicySource {
297
+ domain_name: &target_domain_name,
298
+ policies: &policies,
299
+ aggregates: &policy_aggregates,
300
+ }];
247
301
  for (name, ir) in chapter_domain_names.iter().zip(chapter_irs.iter()) {
248
302
  policy_sources.push(reactions::PolicySource {
249
303
  domain_name: name,
250
- policies: ir.get("policies").map(Json::each).unwrap_or(&[]),
251
- aggregates: ir.get("aggregates").map(Json::each).unwrap_or(&[]),
304
+ policies: ir.get("policies").map_or_default(Json::each),
305
+ aggregates: ir.get("aggregates").map_or_default(Json::each),
252
306
  });
253
307
  }
254
308
 
255
309
  let mut merged_rs = String::new();
256
- puts_str(&mut merged_rs, &crate::registry::emit_registry(&ex, &merged_aggregates));
310
+ puts_str(
311
+ &mut merged_rs,
312
+ &crate::registry::emit_registry(&ex, &merged_aggregates),
313
+ );
257
314
  puts_blank(&mut merged_rs);
258
- puts_str(&mut merged_rs, &crate::registry::emit_reference_lookup(&merged_aggregates));
315
+ puts_str(
316
+ &mut merged_rs,
317
+ &crate::registry::emit_reference_lookup(&merged_aggregates),
318
+ );
259
319
  puts_blank(&mut merged_rs);
260
- puts_str(&mut merged_rs, &reactions::emit_merged_policy_table(&ex, &policy_sources));
320
+ puts_str(
321
+ &mut merged_rs,
322
+ &reactions::emit_merged_policy_table(&ex, &policy_sources),
323
+ );
261
324
  puts_blank(&mut merged_rs);
262
- puts_str(&mut merged_rs, &reactions::emit_merged_cross_domain_policy_table(&ex, &policy_sources));
325
+ puts_str(
326
+ &mut merged_rs,
327
+ &reactions::emit_merged_cross_domain_policy_table(&ex, &policy_sources),
328
+ );
263
329
  puts_blank(&mut merged_rs);
264
- puts_str(&mut merged_rs, &reactions::emit_process_manager_table(&ex, &merged_process_managers));
330
+ puts_str(
331
+ &mut merged_rs,
332
+ &reactions::emit_process_manager_table(&ex, &merged_process_managers),
333
+ );
265
334
  puts_blank(&mut merged_rs);
266
- puts_str(&mut merged_rs, &reactions::emit_reference_key_table(&ex, &reference_key_pairs));
335
+ puts_str(
336
+ &mut merged_rs,
337
+ &reactions::emit_reference_key_table(&ex, &reference_key_pairs),
338
+ );
267
339
  puts_blank(&mut merged_rs);
268
- puts_str(&mut merged_rs, &reactions::emit_creates_table(&ex, &merged_aggregates));
340
+ puts_str(
341
+ &mut merged_rs,
342
+ &reactions::emit_creates_table(&ex, &merged_aggregates),
343
+ );
269
344
  puts_blank(&mut merged_rs);
270
- puts_str(&mut merged_rs, &reactions::emit_identity_head_table(&ex, &merged_aggregates));
345
+ puts_str(
346
+ &mut merged_rs,
347
+ &reactions::emit_identity_head_table(&ex, &merged_aggregates),
348
+ );
271
349
  puts_blank(&mut merged_rs);
272
350
  // Section order is part of the output: `hecks regenerate_corpus --check` diffs this file byte for byte.
273
- puts_str(&mut merged_rs, &reactions::emit_entity_identity_head_table(&ex, &merged_aggregates));
351
+ puts_str(
352
+ &mut merged_rs,
353
+ &reactions::emit_entity_identity_head_table(&ex, &merged_aggregates),
354
+ );
274
355
  puts_blank(&mut merged_rs);
275
- puts_str(&mut merged_rs, &reactions::emit_command_attributes_table(&ex, &merged_aggregates));
356
+ puts_str(
357
+ &mut merged_rs,
358
+ &reactions::emit_command_attributes_table(&ex, &merged_aggregates),
359
+ );
276
360
  puts_blank(&mut merged_rs);
277
- puts_str(&mut merged_rs, &queries::emit_query_table(&ex, &merged_queries));
361
+ puts_str(
362
+ &mut merged_rs,
363
+ &queries::emit_query_table(&ex, &merged_queries),
364
+ );
278
365
  puts_blank(&mut merged_rs);
279
- puts_str(&mut merged_rs, &queries::emit_query_arg_check_table(&merged_queries));
366
+ puts_str(
367
+ &mut merged_rs,
368
+ &queries::emit_query_arg_check_table(&merged_queries),
369
+ );
280
370
  puts_blank(&mut merged_rs);
281
371
  for rmd in &merged_read_models {
282
372
  if let Some(body) = &rmd.group_by_fn_body {
@@ -284,7 +374,15 @@ fn run_full(args: &[String]) -> Result<(), String> {
284
374
  puts_blank(&mut merged_rs);
285
375
  }
286
376
  }
287
- puts_str(&mut merged_rs, &read_models::emit_read_model_table(&ex, &merged_read_models));
377
+ puts_str(
378
+ &mut merged_rs,
379
+ &read_models::emit_read_model_table(&ex, &merged_read_models),
380
+ );
381
+ puts_blank(&mut merged_rs);
382
+ puts_str(
383
+ &mut merged_rs,
384
+ &presenters::emit_present_router(&merged_aggregates),
385
+ );
288
386
 
289
387
  let merged_path = format!("{target_out_dir}/merged.rs");
290
388
  std::fs::write(&merged_path, &merged_rs).map_err(|e| format!("writing {merged_path}: {e}"))?;
@@ -292,7 +390,8 @@ fn run_full(args: &[String]) -> Result<(), String> {
292
390
 
293
391
  // `write_domain` already wrote `mod.rs`; only the target directory gets a `merged` module.
294
392
  let mod_path = format!("{target_out_dir}/mod.rs");
295
- let mut mod_rs = std::fs::read_to_string(&mod_path).map_err(|e| format!("reading {mod_path}: {e}"))?;
393
+ let mut mod_rs =
394
+ std::fs::read_to_string(&mod_path).map_err(|e| format!("reading {mod_path}: {e}"))?;
296
395
  if !mod_rs.ends_with('\n') {
297
396
  mod_rs.push('\n');
298
397
  }