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
@@ -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;
@@ -29,12 +32,19 @@ mod read_models;
29
32
  mod reference_specs;
30
33
  mod registry;
31
34
  mod reserved_names;
35
+ #[path = "../../parser/src/ruby_value.rs"]
36
+ #[allow(
37
+ dead_code,
38
+ reason = "shared with the parser, which uses parts codegen does not"
39
+ )]
40
+ mod ruby_value;
32
41
  mod shared;
33
42
  mod sidecars;
34
43
  mod skip_reason;
35
44
  mod types;
36
45
 
37
46
  use json::Json;
47
+ use shared::{puts_blank, puts_str};
38
48
  use std::process::ExitCode;
39
49
 
40
50
  fn main() -> ExitCode {
@@ -69,7 +79,7 @@ fn run_prelude(args: &[String]) -> Result<(), String> {
69
79
 
70
80
  let ex = exemplar::Exemplar::load();
71
81
 
72
- let aggregates = ir.get("aggregates").map(Json::each).unwrap_or(&[]);
82
+ let aggregates = ir.get("aggregates").map_or_default(Json::each);
73
83
  for aggregate in aggregates {
74
84
  let name = aggregate.get("name").and_then(Json::as_str).unwrap_or("");
75
85
  match prelude::aggregate_prelude(&ex, &ir, aggregate, source_label) {
@@ -92,7 +102,9 @@ fn run_prelude(args: &[String]) -> Result<(), String> {
92
102
  /// sidecars into `out_dir`.
93
103
  fn run_domain(args: &[String]) -> Result<(), String> {
94
104
  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());
105
+ return Err(
106
+ "usage: hecks-codegen domain <ir.json> <source_label> <mod_name> <out_dir>".to_string(),
107
+ );
96
108
  };
97
109
 
98
110
  let ir = read_ir(ir_path)?;
@@ -123,8 +135,7 @@ fn write_domain(
123
135
  // Refuse before writing anything, so a name collision is reported before any file exists.
124
136
  let aggregate_names: Vec<&str> = ir
125
137
  .get("aggregates")
126
- .map(Json::each)
127
- .unwrap_or(&[])
138
+ .map_or_default(Json::each)
128
139
  .iter()
129
140
  .map(|a| a.get("name").and_then(Json::as_str).unwrap_or(""))
130
141
  .collect();
@@ -146,7 +157,8 @@ fn write_domain(
146
157
  }
147
158
 
148
159
  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}"))?;
160
+ std::fs::write(&registry_path, &generated.registry_rs)
161
+ .map_err(|e| format!("writing {registry_path}: {e}"))?;
150
162
  println!("wrote {registry_path}");
151
163
 
152
164
  let mod_path = format!("{out_dir}/mod.rs");
@@ -154,34 +166,25 @@ fn write_domain(
154
166
  println!("wrote {mod_path}");
155
167
 
156
168
  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}"))?;
169
+ std::fs::write(&manifest_path, &generated.manifest_json)
170
+ .map_err(|e| format!("writing {manifest_path}: {e}"))?;
158
171
  println!("wrote {manifest_path}");
159
172
 
160
- let (ir_json_path, metadata_path) = sidecars::write(std::path::Path::new(out_dir), ir, source_label)?;
173
+ let (ir_json_path, metadata_path) =
174
+ sidecars::write(std::path::Path::new(out_dir), ir, source_label)?;
161
175
  println!("wrote {}", ir_json_path.display());
162
176
  println!("wrote {}", metadata_path.display());
163
177
 
164
178
  Ok(generated)
165
179
  }
166
180
 
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
181
  /// `hecks-codegen full <out_root> <target_mod_name> <target_source_label> <target_ir.json>
179
182
  /// [<chapter_mod_name> <chapter_source_label> <chapter_ir.json>]...`
180
183
  ///
181
184
  /// Writes the target chapter's directory, each attached chapter's own directory, and the
182
185
  /// target's `merged.rs`: one `Store`/`dispatch_by_name` table spanning every chapter given.
183
186
  fn run_full(args: &[String]) -> Result<(), String> {
184
- if args.len() < 4 || (args.len() - 4) % 3 != 0 {
187
+ if args.len() < 4 || !(args.len() - 4).is_multiple_of(3) {
185
188
  return Err(
186
189
  "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
190
  .to_string(),
@@ -196,19 +199,38 @@ fn run_full(args: &[String]) -> Result<(), String> {
196
199
  let ex = exemplar::Exemplar::load();
197
200
 
198
201
  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();
202
+ let target_domain_name = target_ir
203
+ .get("name")
204
+ .and_then(Json::as_str)
205
+ .unwrap_or("")
206
+ .to_string();
200
207
 
201
208
  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)?;
209
+ let target_gen = write_domain(
210
+ &ex,
211
+ &target_ir,
212
+ target_source_label,
213
+ target_mod_name,
214
+ &target_out_dir,
215
+ )?;
203
216
 
204
217
  // 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())];
218
+ let mut reference_key_pairs: Vec<(String, Vec<String>)> = vec![(
219
+ target_domain_name.clone(),
220
+ target_gen
221
+ .registry_aggregates
222
+ .iter()
223
+ .map(|a| a.name.clone())
224
+ .collect(),
225
+ )];
207
226
 
208
227
  let mut merged_aggregates = target_gen.registry_aggregates;
209
228
  let mut merged_queries = target_gen.query_defs;
210
229
  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();
230
+ let mut merged_process_managers: Vec<Json> = target_ir
231
+ .get("process_managers")
232
+ .map_or_default(Json::each)
233
+ .to_vec();
212
234
 
213
235
  // Policies and process managers merge across chapters; `chapter_irs` keeps each parsed
214
236
  // chapter alive so `policy_sources` can borrow from it.
@@ -223,60 +245,127 @@ fn run_full(args: &[String]) -> Result<(), String> {
223
245
  i += 3;
224
246
 
225
247
  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();
248
+ let chapter_domain_name = chapter_ir
249
+ .get("name")
250
+ .and_then(Json::as_str)
251
+ .unwrap_or("")
252
+ .to_string();
227
253
 
228
254
  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()));
255
+ let chapter_gen = write_domain(
256
+ &ex,
257
+ &chapter_ir,
258
+ chapter_source_label,
259
+ chapter_mod_name,
260
+ &chapter_out_dir,
261
+ )?;
262
+
263
+ reference_key_pairs.push((
264
+ chapter_domain_name.clone(),
265
+ chapter_gen
266
+ .registry_aggregates
267
+ .iter()
268
+ .map(|a| a.name.clone())
269
+ .collect(),
270
+ ));
232
271
  merged_aggregates.extend(chapter_gen.registry_aggregates);
233
272
  merged_queries.extend(chapter_gen.query_defs);
234
273
  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());
274
+ merged_process_managers.extend(
275
+ chapter_ir
276
+ .get("process_managers")
277
+ .map_or_default(Json::each)
278
+ .to_vec(),
279
+ );
236
280
 
237
281
  chapter_domain_names.push(chapter_domain_name);
238
282
  chapter_irs.push(chapter_ir);
239
283
  }
240
284
 
241
285
  // 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 }];
286
+ let policies: Vec<Json> = target_ir
287
+ .get("policies")
288
+ .map_or_default(Json::each)
289
+ .to_vec();
290
+ let policy_aggregates: Vec<Json> = target_ir
291
+ .get("aggregates")
292
+ .map_or_default(Json::each)
293
+ .to_vec();
294
+
295
+ let mut policy_sources: Vec<reactions::PolicySource> = vec![reactions::PolicySource {
296
+ domain_name: &target_domain_name,
297
+ policies: &policies,
298
+ aggregates: &policy_aggregates,
299
+ }];
247
300
  for (name, ir) in chapter_domain_names.iter().zip(chapter_irs.iter()) {
248
301
  policy_sources.push(reactions::PolicySource {
249
302
  domain_name: name,
250
- policies: ir.get("policies").map(Json::each).unwrap_or(&[]),
251
- aggregates: ir.get("aggregates").map(Json::each).unwrap_or(&[]),
303
+ policies: ir.get("policies").map_or_default(Json::each),
304
+ aggregates: ir.get("aggregates").map_or_default(Json::each),
252
305
  });
253
306
  }
254
307
 
255
308
  let mut merged_rs = String::new();
256
- puts_str(&mut merged_rs, &crate::registry::emit_registry(&ex, &merged_aggregates));
309
+ puts_str(
310
+ &mut merged_rs,
311
+ &crate::registry::emit_registry(&ex, &merged_aggregates),
312
+ );
257
313
  puts_blank(&mut merged_rs);
258
- puts_str(&mut merged_rs, &crate::registry::emit_reference_lookup(&merged_aggregates));
314
+ puts_str(
315
+ &mut merged_rs,
316
+ &crate::registry::emit_reference_lookup(&merged_aggregates),
317
+ );
259
318
  puts_blank(&mut merged_rs);
260
- puts_str(&mut merged_rs, &reactions::emit_merged_policy_table(&ex, &policy_sources));
319
+ puts_str(
320
+ &mut merged_rs,
321
+ &reactions::emit_merged_policy_table(&ex, &policy_sources),
322
+ );
261
323
  puts_blank(&mut merged_rs);
262
- puts_str(&mut merged_rs, &reactions::emit_merged_cross_domain_policy_table(&ex, &policy_sources));
324
+ puts_str(
325
+ &mut merged_rs,
326
+ &reactions::emit_merged_cross_domain_policy_table(&ex, &policy_sources),
327
+ );
263
328
  puts_blank(&mut merged_rs);
264
- puts_str(&mut merged_rs, &reactions::emit_process_manager_table(&ex, &merged_process_managers));
329
+ puts_str(
330
+ &mut merged_rs,
331
+ &reactions::emit_process_manager_table(&ex, &merged_process_managers),
332
+ );
265
333
  puts_blank(&mut merged_rs);
266
- puts_str(&mut merged_rs, &reactions::emit_reference_key_table(&ex, &reference_key_pairs));
334
+ puts_str(
335
+ &mut merged_rs,
336
+ &reactions::emit_reference_key_table(&ex, &reference_key_pairs),
337
+ );
267
338
  puts_blank(&mut merged_rs);
268
- puts_str(&mut merged_rs, &reactions::emit_creates_table(&ex, &merged_aggregates));
339
+ puts_str(
340
+ &mut merged_rs,
341
+ &reactions::emit_creates_table(&ex, &merged_aggregates),
342
+ );
269
343
  puts_blank(&mut merged_rs);
270
- puts_str(&mut merged_rs, &reactions::emit_identity_head_table(&ex, &merged_aggregates));
344
+ puts_str(
345
+ &mut merged_rs,
346
+ &reactions::emit_identity_head_table(&ex, &merged_aggregates),
347
+ );
271
348
  puts_blank(&mut merged_rs);
272
349
  // 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));
350
+ puts_str(
351
+ &mut merged_rs,
352
+ &reactions::emit_entity_identity_head_table(&ex, &merged_aggregates),
353
+ );
274
354
  puts_blank(&mut merged_rs);
275
- puts_str(&mut merged_rs, &reactions::emit_command_attributes_table(&ex, &merged_aggregates));
355
+ puts_str(
356
+ &mut merged_rs,
357
+ &reactions::emit_command_attributes_table(&ex, &merged_aggregates),
358
+ );
276
359
  puts_blank(&mut merged_rs);
277
- puts_str(&mut merged_rs, &queries::emit_query_table(&ex, &merged_queries));
360
+ puts_str(
361
+ &mut merged_rs,
362
+ &queries::emit_query_table(&ex, &merged_queries),
363
+ );
278
364
  puts_blank(&mut merged_rs);
279
- puts_str(&mut merged_rs, &queries::emit_query_arg_check_table(&merged_queries));
365
+ puts_str(
366
+ &mut merged_rs,
367
+ &queries::emit_query_arg_check_table(&merged_queries),
368
+ );
280
369
  puts_blank(&mut merged_rs);
281
370
  for rmd in &merged_read_models {
282
371
  if let Some(body) = &rmd.group_by_fn_body {
@@ -284,7 +373,10 @@ fn run_full(args: &[String]) -> Result<(), String> {
284
373
  puts_blank(&mut merged_rs);
285
374
  }
286
375
  }
287
- puts_str(&mut merged_rs, &read_models::emit_read_model_table(&ex, &merged_read_models));
376
+ puts_str(
377
+ &mut merged_rs,
378
+ &read_models::emit_read_model_table(&ex, &merged_read_models),
379
+ );
288
380
 
289
381
  let merged_path = format!("{target_out_dir}/merged.rs");
290
382
  std::fs::write(&merged_path, &merged_rs).map_err(|e| format!("writing {merged_path}: {e}"))?;
@@ -292,7 +384,8 @@ fn run_full(args: &[String]) -> Result<(), String> {
292
384
 
293
385
  // `write_domain` already wrote `mod.rs`; only the target directory gets a `merged` module.
294
386
  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}"))?;
387
+ let mut mod_rs =
388
+ std::fs::read_to_string(&mod_path).map_err(|e| format!("reading {mod_path}: {e}"))?;
296
389
  if !mod_rs.ends_with('\n') {
297
390
  mod_rs.push('\n');
298
391
  }