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
@@ -5,12 +5,16 @@ use crate::json::Json;
5
5
  use crate::naming;
6
6
 
7
7
  /// The scalar `admitted_scalar`/`check_patterns` actually operate on.
8
- pub fn scalar_field_expr(value_expr: &str, attr_type: &str, value_objects_by_name: &std::collections::HashMap<String, &Json>) -> Option<String> {
8
+ pub fn scalar_field_expr(
9
+ value_expr: &str,
10
+ attr_type: &str,
11
+ value_objects_by_name: &std::collections::HashMap<String, &Json>,
12
+ ) -> Option<String> {
9
13
  if attr_type == "String" {
10
14
  return Some(value_expr.to_string());
11
15
  }
12
16
  let vo = value_objects_by_name.get(attr_type)?;
13
- let attrs = vo.get("attributes").map(Json::each).unwrap_or(&[]);
17
+ let attrs = vo.get("attributes").map_or_default(Json::each);
14
18
  if attrs.len() != 1 {
15
19
  return None;
16
20
  }
@@ -29,11 +33,19 @@ fn optional_scalar_expr(
29
33
  value_objects_by_name: &std::collections::HashMap<String, &Json>,
30
34
  ) -> Option<(String, Option<String>)> {
31
35
  if !crate::attr::optional(attr) {
32
- let scalar = scalar_field_expr(value_expr, crate::attr::type_name(attr), value_objects_by_name)?;
36
+ let scalar = scalar_field_expr(
37
+ value_expr,
38
+ crate::attr::type_name(attr),
39
+ value_objects_by_name,
40
+ )?;
33
41
  return Some((scalar, None));
34
42
  }
35
43
 
36
- let scalar = scalar_field_expr(OPTIONAL_BINDING, crate::attr::type_name(attr), value_objects_by_name)?;
44
+ let scalar = scalar_field_expr(
45
+ OPTIONAL_BINDING,
46
+ crate::attr::type_name(attr),
47
+ value_objects_by_name,
48
+ )?;
37
49
  Some((scalar, Some(value_expr.to_string())))
38
50
  }
39
51
 
@@ -45,23 +57,30 @@ fn wrap_if_optional(check: String, optional_source: Option<String>) -> String {
45
57
  }
46
58
 
47
59
  /// `"Aggregate::SetName"` resolved against the full domain.
48
- pub fn admitted_set_members(admits: &str, aggregates_by_name: &std::collections::HashMap<String, &Json>) -> Option<Vec<String>> {
49
- let mut parts = admits.splitn(2, "::");
50
- let aggregate_name = parts.next()?;
51
- let set_name = parts.next()?;
60
+ pub fn admitted_set_members(
61
+ admits: &str,
62
+ aggregates_by_name: &std::collections::HashMap<String, &Json>,
63
+ ) -> Option<Vec<String>> {
64
+ let (aggregate_name, set_name) = admits.split_once("::")?;
65
+
52
66
  let aggregate = aggregates_by_name.get(aggregate_name)?;
53
- let vos = aggregate.get("value_objects").map(Json::each).unwrap_or(&[]);
54
- let vo = vos.iter().find(|v| v.get("name").and_then(Json::as_str) == Some(set_name))?;
55
- if !vo.get("closed_set").map(Json::as_bool).unwrap_or(false) {
67
+ let vos = aggregate.get("value_objects").map_or_default(Json::each);
68
+ let vo = vos
69
+ .iter()
70
+ .find(|v| v.get("name").and_then(Json::as_str) == Some(set_name))?;
71
+ if !vo.get("closed_set").is_some_and(Json::as_bool) {
56
72
  return None;
57
73
  }
58
- let members = vo.get("members").map(Json::each).unwrap_or(&[]);
74
+ let members = vo.get("members").map_or_default(Json::each);
59
75
  Some(
60
76
  members
61
77
  .iter()
62
78
  .map(|row| {
63
79
  let pairs = row.as_array().unwrap_or(&[]);
64
- let first = pairs.first().and_then(|pair| pair.as_array()).unwrap_or(&[]);
80
+ let first = pairs
81
+ .first()
82
+ .and_then(|pair| pair.as_array())
83
+ .unwrap_or(&[]);
65
84
  first.get(1).map(Json::to_s).unwrap_or_default()
66
85
  })
67
86
  .collect(),
@@ -79,30 +98,58 @@ pub fn emit_admits_check(
79
98
  let members = admitted_set_members(admits, aggregates_by_name)?;
80
99
  let (scalar, optional_source) = optional_scalar_expr(value_expr, attr, value_objects_by_name)?;
81
100
 
82
- let members_array = format!("[{}]", members.iter().map(|m| naming::ruby_inspect_string(m)).collect::<Vec<_>>().join(", "));
101
+ let members_array = format!(
102
+ "[{}]",
103
+ members
104
+ .iter()
105
+ .map(|m| naming::ruby_inspect_string(m))
106
+ .collect::<Vec<_>>()
107
+ .join(", ")
108
+ );
83
109
  let check = exemplar.render(
84
110
  "admits_check",
85
111
  &[
86
112
  ("[\"tmpl_member_a\", \"tmpl_member_b\"]", members_array),
87
113
  ("tmpl_scalar", scalar),
88
- ("\"tmpl_admits_name\"", naming::ruby_inspect_string(crate::attr::name(attr))),
89
- ("\"tmpl_admits_target\"", naming::ruby_inspect_string(admits)),
114
+ (
115
+ "\"tmpl_admits_name\"",
116
+ naming::ruby_inspect_string(crate::attr::name(attr)),
117
+ ),
118
+ (
119
+ "\"tmpl_admits_target\"",
120
+ naming::ruby_inspect_string(admits),
121
+ ),
90
122
  ],
91
123
  );
92
124
  Some(wrap_if_optional(check, optional_source))
93
125
  }
94
126
 
95
- pub fn emit_pattern_check(exemplar: &Exemplar, value_expr: &str, attr: &Json, owner_type_name: &str, value_objects_by_name: &std::collections::HashMap<String, &Json>) -> Option<String> {
127
+ pub fn emit_pattern_check(
128
+ exemplar: &Exemplar,
129
+ value_expr: &str,
130
+ attr: &Json,
131
+ owner_type_name: &str,
132
+ value_objects_by_name: &std::collections::HashMap<String, &Json>,
133
+ ) -> Option<String> {
96
134
  let pattern = crate::attr::pattern(attr)?;
97
135
  let (scalar, optional_source) = optional_scalar_expr(value_expr, attr, value_objects_by_name)?;
98
136
 
99
137
  let check = exemplar.render(
100
138
  "pattern_check",
101
139
  &[
102
- ("\"tmpl_pattern_text\"", naming::ruby_inspect_string(pattern)),
140
+ (
141
+ "\"tmpl_pattern_text\"",
142
+ naming::ruby_inspect_string(pattern),
143
+ ),
103
144
  ("tmpl_scalar", scalar),
104
- ("\"tmpl_pattern_owner\"", naming::ruby_inspect_string(owner_type_name)),
105
- ("\"tmpl_pattern_field\"", naming::ruby_inspect_string(crate::attr::name(attr))),
145
+ (
146
+ "\"tmpl_pattern_owner\"",
147
+ naming::ruby_inspect_string(owner_type_name),
148
+ ),
149
+ (
150
+ "\"tmpl_pattern_field\"",
151
+ naming::ruby_inspect_string(crate::attr::name(attr)),
152
+ ),
106
153
  ],
107
154
  );
108
155
  Some(wrap_if_optional(check, optional_source))
@@ -111,19 +158,35 @@ pub fn emit_pattern_check(exemplar: &Exemplar, value_expr: &str, attr: &Json, ow
111
158
  /// A `pattern:` on a list of Strings, checked once per member: Ruby's `check_patterns` walks
112
159
  /// `offered_items`, so every element of the list must match, and an optional list left unset has
113
160
  /// none to check. Lists of anything but plain Strings get no check.
114
- pub fn emit_list_pattern_check(exemplar: &Exemplar, value_expr: &str, attr: &Json, owner_type_name: &str) -> Option<String> {
161
+ pub fn emit_list_pattern_check(
162
+ exemplar: &Exemplar,
163
+ value_expr: &str,
164
+ attr: &Json,
165
+ owner_type_name: &str,
166
+ ) -> Option<String> {
115
167
  let pattern = crate::attr::pattern(attr)?;
116
- if !crate::attr::list(attr) || naming::effective_scalar_type(crate::attr::type_name(attr)) != Some("String") {
168
+ if !crate::attr::list(attr)
169
+ || naming::effective_scalar_type(crate::attr::type_name(attr)) != Some("String")
170
+ {
117
171
  return None;
118
172
  }
119
173
 
120
174
  let check = exemplar.render(
121
175
  "pattern_check",
122
176
  &[
123
- ("\"tmpl_pattern_text\"", naming::ruby_inspect_string(pattern)),
177
+ (
178
+ "\"tmpl_pattern_text\"",
179
+ naming::ruby_inspect_string(pattern),
180
+ ),
124
181
  ("tmpl_scalar", "item".to_string()),
125
- ("\"tmpl_pattern_owner\"", naming::ruby_inspect_string(owner_type_name)),
126
- ("\"tmpl_pattern_field\"", naming::ruby_inspect_string(crate::attr::name(attr))),
182
+ (
183
+ "\"tmpl_pattern_owner\"",
184
+ naming::ruby_inspect_string(owner_type_name),
185
+ ),
186
+ (
187
+ "\"tmpl_pattern_field\"",
188
+ naming::ruby_inspect_string(crate::attr::name(attr)),
189
+ ),
127
190
  ],
128
191
  );
129
192
  Some(if crate::attr::optional(attr) {
@@ -8,32 +8,59 @@ use std::collections::{HashMap, HashSet};
8
8
  /// True when every owner field of a creating command is written from known values.
9
9
  ///
10
10
  /// The router uses it alone to decide whether a route is checked against derived identity.
11
- pub fn complete_state_creation(aggregate: &Json, command: &Json, value_objects_by_name: &HashMap<String, &Json>) -> bool {
11
+ pub fn complete_state_creation(
12
+ aggregate: &Json,
13
+ command: &Json,
14
+ value_objects_by_name: &HashMap<String, &Json>,
15
+ ) -> bool {
12
16
  let owner_fields = owner_fields(aggregate);
13
- let payload_fields: HashSet<String> = command.get("attributes").map(Json::each).unwrap_or(&[]).iter().map(|a| crate::attr::name(a).to_string()).collect();
17
+ let payload_fields: HashSet<String> = command
18
+ .get("attributes")
19
+ .map_or_default(Json::each)
20
+ .iter()
21
+ .map(|a| crate::attr::name(a).to_string())
22
+ .collect();
14
23
 
15
- let (known_writes, disqualified) = known_writes(aggregate, command, &owner_fields, &payload_fields, value_objects_by_name);
16
- !disqualified && owner_fields.iter().all(|field| known_writes.contains(field))
24
+ let (known_writes, disqualified) = known_writes(
25
+ aggregate,
26
+ command,
27
+ &owner_fields,
28
+ &payload_fields,
29
+ value_objects_by_name,
30
+ );
31
+ !disqualified
32
+ && owner_fields
33
+ .iter()
34
+ .all(|field| known_writes.contains(field))
17
35
  }
18
36
 
19
37
  /// True when a creating command's result does not depend on prior state.
20
38
  ///
21
39
  /// Only meaningful for a command `crate::shared::creates_owner` answered `true` for.
22
- pub fn state_independent_creation(aggregate: &Json, command: &Json, value_objects_by_name: &HashMap<String, &Json>) -> bool {
40
+ pub fn state_independent_creation(
41
+ aggregate: &Json,
42
+ command: &Json,
43
+ value_objects_by_name: &HashMap<String, &Json>,
44
+ ) -> bool {
23
45
  if !complete_state_creation(aggregate, command, value_objects_by_name) {
24
46
  return false;
25
47
  }
26
48
  let owner_fields = owner_fields(aggregate);
27
- let payload_fields: HashSet<String> = command.get("attributes").map(Json::each).unwrap_or(&[]).iter().map(|a| crate::attr::name(a).to_string()).collect();
49
+ let payload_fields: HashSet<String> = command
50
+ .get("attributes")
51
+ .map_or_default(Json::each)
52
+ .iter()
53
+ .map(|a| crate::attr::name(a).to_string())
54
+ .collect();
28
55
 
29
56
  let mut rules: Vec<(&Json, bool)> = Vec::new();
30
- for rule in command.get("givens").map(Json::each).unwrap_or(&[]) {
57
+ for rule in command.get("givens").map_or_default(Json::each) {
31
58
  rules.push((rule, true)); // `true` == "before" (a given)
32
59
  }
33
- for rule in command.get("ensures").map(Json::each).unwrap_or(&[]) {
60
+ for rule in command.get("ensures").map_or_default(Json::each) {
34
61
  rules.push((rule, false));
35
62
  }
36
- for rule in aggregate.get("invariants").map(Json::each).unwrap_or(&[]) {
63
+ for rule in aggregate.get("invariants").map_or_default(Json::each) {
37
64
  rules.push((rule, false));
38
65
  }
39
66
 
@@ -44,11 +71,16 @@ pub fn state_independent_creation(aggregate: &Json, command: &Json, value_object
44
71
  }
45
72
 
46
73
  fn owner_fields(aggregate: &Json) -> HashSet<String> {
47
- let mut fields: HashSet<String> = aggregate.get("attributes").map(Json::each).unwrap_or(&[]).iter().map(|a| crate::attr::name(a).to_string()).collect();
74
+ let mut fields: HashSet<String> = aggregate
75
+ .get("attributes")
76
+ .map_or_default(Json::each)
77
+ .iter()
78
+ .map(|a| crate::attr::name(a).to_string())
79
+ .collect();
48
80
  if let Some(lifecycle) = aggregate.get("lifecycle") {
49
81
  fields.insert(lifecycle.get("field").map(Json::to_s).unwrap_or_default());
50
82
  }
51
- for field in aggregate.get("projected_fields").map(Json::each).unwrap_or(&[]) {
83
+ for field in aggregate.get("projected_fields").map_or_default(Json::each) {
52
84
  fields.insert(field.get("name").map(Json::to_s).unwrap_or_default());
53
85
  }
54
86
  fields
@@ -60,7 +92,11 @@ enum Classification {
60
92
  Unresolved,
61
93
  }
62
94
 
63
- fn classify_symbol(name: &str, payload_fields: &HashSet<String>, owner_fields: &HashSet<String>) -> Classification {
95
+ fn classify_symbol(
96
+ name: &str,
97
+ payload_fields: &HashSet<String>,
98
+ owner_fields: &HashSet<String>,
99
+ ) -> Classification {
64
100
  if payload_fields.contains(name) {
65
101
  Classification::Known
66
102
  } else if owner_fields.contains(name) {
@@ -72,9 +108,17 @@ fn classify_symbol(name: &str, payload_fields: &HashSet<String>, owner_fields: &
72
108
 
73
109
  // A `StateRef` source falls through to `Known`, matching the Ruby Analyzer, which has no
74
110
  // `when StateRef` branch.
75
- fn classify_source(source: &Json, payload_fields: &HashSet<String>, owner_fields: &HashSet<String>) -> Classification {
111
+ fn classify_source(
112
+ source: &Json,
113
+ payload_fields: &HashSet<String>,
114
+ owner_fields: &HashSet<String>,
115
+ ) -> Classification {
76
116
  match source.get("kind").map(Json::to_s).as_deref() {
77
- Some("argument") => classify_symbol(&source.get("name").map(Json::to_s).unwrap_or_default(), payload_fields, owner_fields),
117
+ Some("argument") => classify_symbol(
118
+ &source.get("name").map(Json::to_s).unwrap_or_default(),
119
+ payload_fields,
120
+ owner_fields,
121
+ ),
78
122
  _ => Classification::Known,
79
123
  }
80
124
  }
@@ -87,12 +131,14 @@ fn known_writes(
87
131
  value_objects_by_name: &HashMap<String, &Json>,
88
132
  ) -> (HashSet<String>, bool) {
89
133
  let mut known = HashSet::new();
90
- for attr in aggregate.get("attributes").map(Json::each).unwrap_or(&[]) {
134
+ for attr in aggregate.get("attributes").map_or_default(Json::each) {
91
135
  if deterministic_initial_value(attr, value_objects_by_name) {
92
136
  known.insert(crate::attr::name(attr).to_string());
93
137
  }
94
138
  }
95
- let lifecycle_field = aggregate.get("lifecycle").map(|l| l.get("field").map(Json::to_s).unwrap_or_default());
139
+ let lifecycle_field = aggregate
140
+ .get("lifecycle")
141
+ .map(|l| l.get("field").map(Json::to_s).unwrap_or_default());
96
142
  if let Some(field) = &lifecycle_field {
97
143
  known.insert(field.clone());
98
144
  }
@@ -100,7 +146,7 @@ fn known_writes(
100
146
  // A creating command guarded by a lifecycle `from:` state has no prior state to check.
101
147
  let mut disqualified = command.get("from").is_some() && aggregate.get("lifecycle").is_some();
102
148
 
103
- for mutation in command.get("mutations").map(Json::each).unwrap_or(&[]) {
149
+ for mutation in command.get("mutations").map_or_default(Json::each) {
104
150
  let target = mutation.get("target").map(Json::to_s).unwrap_or_default();
105
151
  let op = mutation.get("op").map(Json::to_s).unwrap_or_default();
106
152
 
@@ -125,7 +171,10 @@ fn known_writes(
125
171
  for (_, wire_value) in pairs {
126
172
  let parsed = literal::read(wire_value.as_str().unwrap_or(""));
127
173
  if let Literal::Symbol(name) = parsed {
128
- if matches!(classify_symbol(&name, payload_fields, owner_fields), Classification::Unresolved) {
174
+ if matches!(
175
+ classify_symbol(&name, payload_fields, owner_fields),
176
+ Classification::Unresolved
177
+ ) {
129
178
  disqualified = true;
130
179
  }
131
180
  }
@@ -142,20 +191,37 @@ fn known_writes(
142
191
  (known, disqualified)
143
192
  }
144
193
 
145
- fn deterministic_initial_value(attr: &Json, value_objects_by_name: &HashMap<String, &Json>) -> bool {
146
- if crate::attr::list(attr) || crate::attr::optional(attr) || crate::attr::default(attr).is_some() {
194
+ fn deterministic_initial_value(
195
+ attr: &Json,
196
+ value_objects_by_name: &HashMap<String, &Json>,
197
+ ) -> bool {
198
+ if crate::attr::list(attr)
199
+ || crate::attr::optional(attr)
200
+ || crate::attr::default(attr).is_some()
201
+ {
147
202
  return true;
148
203
  }
149
204
 
150
- match value_objects_by_name.get(crate::attr::type_name(attr)) {
151
- Some(vo) => vo.get("attributes").map(Json::each).unwrap_or(&[]).iter().all(|field| crate::attr::default(field).is_some()),
152
- None => false,
153
- }
205
+ value_objects_by_name
206
+ .get(crate::attr::type_name(attr))
207
+ .is_some_and(|vo| {
208
+ vo.get("attributes")
209
+ .map_or_default(Json::each)
210
+ .iter()
211
+ .all(|field| crate::attr::default(field).is_some())
212
+ })
154
213
  }
155
214
 
156
215
  // `before` is true for a `given`, false for an `ensures` or invariant.
157
- fn rule_state_independent(ast: &Json, before: bool, payload_fields: &HashSet<String>, owner_fields: &HashSet<String>) -> bool {
158
- paths(ast, &HashSet::new()).iter().all(|path| path_state_independent(path, before, payload_fields, owner_fields))
216
+ fn rule_state_independent(
217
+ ast: &Json,
218
+ before: bool,
219
+ payload_fields: &HashSet<String>,
220
+ owner_fields: &HashSet<String>,
221
+ ) -> bool {
222
+ paths(ast, &HashSet::new())
223
+ .iter()
224
+ .all(|path| path_state_independent(path, before, payload_fields, owner_fields))
159
225
  }
160
226
 
161
227
  // Only `lookup` and `block_predicate` nodes are special-cased; the rest recurse.
@@ -165,33 +231,61 @@ fn paths(node: &Json, bound_names: &HashSet<String>) -> Vec<String> {
165
231
  let op = pairs.iter().find(|(k, _)| k == "op").map(|(_, v)| v.to_s());
166
232
  match op.as_deref() {
167
233
  Some("lookup") => {
168
- let segments = pairs.iter().find(|(k, _)| k == "path").map(|(_, v)| v.each()).unwrap_or(&[]);
234
+ let segments = pairs
235
+ .iter()
236
+ .find(|(k, _)| k == "path")
237
+ .map_or_default(|(_, v)| v.each());
169
238
  let root = segments.first().map(Json::to_s).unwrap_or_default();
170
239
  if bound_names.contains(&root) {
171
240
  vec![]
172
241
  } else {
173
- vec![segments.iter().map(Json::to_s).collect::<Vec<_>>().join(".")]
242
+ vec![segments
243
+ .iter()
244
+ .map(Json::to_s)
245
+ .collect::<Vec<_>>()
246
+ .join(".")]
174
247
  }
175
248
  }
176
249
  Some("block_predicate") => {
177
- let receiver = pairs.iter().find(|(k, _)| k == "receiver").map(|(_, v)| v).unwrap_or(&Json::Null);
178
- let predicate = pairs.iter().find(|(k, _)| k == "predicate").map(|(_, v)| v).unwrap_or(&Json::Null);
179
- let param = pairs.iter().find(|(k, _)| k == "param").map(|(_, v)| v.to_s()).unwrap_or_default();
250
+ let receiver = pairs
251
+ .iter()
252
+ .find(|(k, _)| k == "receiver")
253
+ .map_or(&Json::Null, |(_, v)| v);
254
+ let predicate = pairs
255
+ .iter()
256
+ .find(|(k, _)| k == "predicate")
257
+ .map_or(&Json::Null, |(_, v)| v);
258
+ let param = pairs
259
+ .iter()
260
+ .find(|(k, _)| k == "param")
261
+ .map(|(_, v)| v.to_s())
262
+ .unwrap_or_default();
180
263
  let mut bound_with_param = bound_names.clone();
181
264
  bound_with_param.insert(param);
182
265
  let mut found = paths(receiver, bound_names);
183
266
  found.extend(paths(predicate, &bound_with_param));
184
267
  found
185
268
  }
186
- _ => pairs.iter().flat_map(|(_, value)| paths(value, bound_names)).collect(),
269
+ _ => pairs
270
+ .iter()
271
+ .flat_map(|(_, value)| paths(value, bound_names))
272
+ .collect(),
187
273
  }
188
274
  }
189
- Json::Array(items) => items.iter().flat_map(|value| paths(value, bound_names)).collect(),
275
+ Json::Array(items) => items
276
+ .iter()
277
+ .flat_map(|value| paths(value, bound_names))
278
+ .collect(),
190
279
  _ => vec![],
191
280
  }
192
281
  }
193
282
 
194
- fn path_state_independent(path: &str, before: bool, payload_fields: &HashSet<String>, owner_fields: &HashSet<String>) -> bool {
283
+ fn path_state_independent(
284
+ path: &str,
285
+ before: bool,
286
+ payload_fields: &HashSet<String>,
287
+ owner_fields: &HashSet<String>,
288
+ ) -> bool {
195
289
  let head = path.split('.').next().unwrap_or("");
196
290
 
197
291
  match head {