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
@@ -6,18 +6,18 @@
6
6
  // uses — grouped by the `category` field Evaluator::PROJECTION already
7
7
  // partitions comparison operators by (evaluator.rb).
8
8
 
9
- pub mod logical;
10
- pub mod membership;
11
- pub mod comparison;
12
9
  pub mod arithmetic;
13
- pub mod sign_test;
14
- pub mod sized;
15
- pub mod to_string;
10
+ pub mod comparison;
16
11
  pub mod enumeration;
12
+ pub mod logical;
13
+ pub mod membership;
17
14
  pub mod pattern_match;
15
+ pub mod positional;
18
16
  pub mod presence;
17
+ pub mod sign_test;
18
+ pub mod sized;
19
19
  pub mod text;
20
- pub mod positional;
20
+ pub mod to_string;
21
21
 
22
22
  /// One variant per real expression-operator category the Ruby
23
23
  /// grammar admits, in the order Grammar.admitted_operators's own `category` field declares them. Every match
@@ -47,22 +47,35 @@ impl OperatorCategory {
47
47
  /// variant names — `hecks measure_kernel_coverage`'s own existence
48
48
  /// check reads this, so the file-name spelling here is the ONE
49
49
  /// place that ever needs to change if a name is renamed.
50
- pub fn module_name(&self) -> &'static str {
50
+ pub const fn module_name(&self) -> &'static str {
51
51
  match self {
52
- OperatorCategory::Logical => "logical",
53
- OperatorCategory::Membership => "membership",
54
- OperatorCategory::Comparison => "comparison",
55
- OperatorCategory::Arithmetic => "arithmetic",
56
- OperatorCategory::SignTest => "sign_test",
57
- OperatorCategory::Sized => "sized",
58
- OperatorCategory::ToString => "to_string",
59
- OperatorCategory::Enumeration => "enumeration",
60
- OperatorCategory::PatternMatch => "pattern_match",
61
- OperatorCategory::Presence => "presence",
62
- OperatorCategory::Text => "text",
63
- OperatorCategory::Positional => "positional",
52
+ Self::Logical => "logical",
53
+ Self::Membership => "membership",
54
+ Self::Comparison => "comparison",
55
+ Self::Arithmetic => "arithmetic",
56
+ Self::SignTest => "sign_test",
57
+ Self::Sized => "sized",
58
+ Self::ToString => "to_string",
59
+ Self::Enumeration => "enumeration",
60
+ Self::PatternMatch => "pattern_match",
61
+ Self::Presence => "presence",
62
+ Self::Text => "text",
63
+ Self::Positional => "positional",
64
64
  }
65
65
  }
66
66
 
67
- pub const ALL: &'static [OperatorCategory] = &[OperatorCategory::Logical, OperatorCategory::Membership, OperatorCategory::Comparison, OperatorCategory::Arithmetic, OperatorCategory::SignTest, OperatorCategory::Sized, OperatorCategory::ToString, OperatorCategory::Enumeration, OperatorCategory::PatternMatch, OperatorCategory::Presence, OperatorCategory::Text, OperatorCategory::Positional];
67
+ pub const ALL: &'static [Self] = &[
68
+ Self::Logical,
69
+ Self::Membership,
70
+ Self::Comparison,
71
+ Self::Arithmetic,
72
+ Self::SignTest,
73
+ Self::Sized,
74
+ Self::ToString,
75
+ Self::Enumeration,
76
+ Self::PatternMatch,
77
+ Self::Presence,
78
+ Self::Text,
79
+ Self::Positional,
80
+ ];
68
81
  }
@@ -6,8 +6,15 @@ use crate::kernel::Refusal;
6
6
  use regex::RegexBuilder;
7
7
 
8
8
  pub fn interpret(expr: &Expr, ctx: &EvalContext) -> Result<Value, Refusal> {
9
- let Expr::MatchesRegex { receiver, pattern, flags } = expr else {
10
- return Err(Refusal::TypeMismatch(format!("pattern_match::interpret called with a non-match? node {expr:?} — a router bug")));
9
+ let Expr::MatchesRegex {
10
+ receiver,
11
+ pattern,
12
+ flags,
13
+ } = expr
14
+ else {
15
+ return Err(Refusal::TypeMismatch(format!(
16
+ "pattern_match::interpret called with a non-match? node {expr:?} — a router bug"
17
+ )));
11
18
  };
12
19
 
13
20
  let text = coerce_text(&eval(receiver, ctx)?)?;
@@ -32,7 +39,9 @@ fn coerce_text(v: &Value) -> Result<String, Refusal> {
32
39
  Value::Int(i) => Ok(i.to_string()),
33
40
  Value::Float(f) => Ok(f.to_string()),
34
41
  Value::Nil => Ok(String::new()),
35
- Value::Bool(_) | Value::List(_) | Value::Array(_) => Err(eval_error(format!("match? expects a scalar, got {v:?}"))),
42
+ Value::Bool(_) | Value::List(_) | Value::Array(_) => {
43
+ Err(eval_error(format!("match? expects a scalar, got {v:?}")))
44
+ }
36
45
  }
37
46
  }
38
47
 
@@ -41,21 +50,40 @@ mod tests {
41
50
  use super::*;
42
51
 
43
52
  fn matches(receiver: Expr, pattern: &str, flags: &str) -> Value {
44
- let expr = Expr::MatchesRegex { receiver: Box::new(receiver), pattern: pattern.to_string(), flags: flags.to_string() };
45
- let ctx = EvalContext { args: &crate::kernel::expr::NoFields, instance: &crate::kernel::expr::NoFields };
53
+ let expr = Expr::MatchesRegex {
54
+ receiver: Box::new(receiver),
55
+ pattern: pattern.to_string(),
56
+ flags: flags.to_string(),
57
+ };
58
+ let ctx = EvalContext {
59
+ args: &crate::kernel::expr::NoFields,
60
+ instance: &crate::kernel::expr::NoFields,
61
+ };
46
62
  eval(&expr, &ctx).expect("evaluates")
47
63
  }
48
64
 
49
65
  #[test]
50
66
  fn matches_a_simple_anchored_pattern() {
51
- assert_eq!(matches(Expr::Str("12345".to_string()), r"\A\d{5}\z", ""), Value::Bool(true));
52
- assert_eq!(matches(Expr::Str("1234".to_string()), r"\A\d{5}\z", ""), Value::Bool(false));
67
+ assert_eq!(
68
+ matches(Expr::Str("12345".to_string()), r"\A\d{5}\z", ""),
69
+ Value::Bool(true)
70
+ );
71
+ assert_eq!(
72
+ matches(Expr::Str("1234".to_string()), r"\A\d{5}\z", ""),
73
+ Value::Bool(false)
74
+ );
53
75
  }
54
76
 
55
77
  #[test]
56
78
  fn the_i_flag_ignores_case() {
57
- assert_eq!(matches(Expr::Str("HELLO".to_string()), "hello", ""), Value::Bool(false));
58
- assert_eq!(matches(Expr::Str("HELLO".to_string()), "hello", "i"), Value::Bool(true));
79
+ assert_eq!(
80
+ matches(Expr::Str("HELLO".to_string()), "hello", ""),
81
+ Value::Bool(false)
82
+ );
83
+ assert_eq!(
84
+ matches(Expr::Str("HELLO".to_string()), "hello", "i"),
85
+ Value::Bool(true)
86
+ );
59
87
  }
60
88
 
61
89
  #[test]
@@ -66,8 +94,15 @@ mod tests {
66
94
 
67
95
  #[test]
68
96
  fn an_invalid_pattern_refuses_cleanly() {
69
- let expr = Expr::MatchesRegex { receiver: Box::new(Expr::Str("x".to_string())), pattern: "(".to_string(), flags: String::new() };
70
- let ctx = EvalContext { args: &crate::kernel::expr::NoFields, instance: &crate::kernel::expr::NoFields };
97
+ let expr = Expr::MatchesRegex {
98
+ receiver: Box::new(Expr::Str("x".to_string())),
99
+ pattern: "(".to_string(),
100
+ flags: String::new(),
101
+ };
102
+ let ctx = EvalContext {
103
+ args: &crate::kernel::expr::NoFields,
104
+ instance: &crate::kernel::expr::NoFields,
105
+ };
71
106
  assert!(eval(&expr, &ctx).is_err());
72
107
  }
73
108
  }
@@ -1,30 +1,41 @@
1
1
  //! Interprets `.first` and `.last` over an Array value or a list-typed field lookup.
2
2
 
3
- use crate::kernel::expr::{eval_error, interpret as eval, lookup_items, EvalContext, Expr, Field, Value};
3
+ use crate::kernel::expr::{
4
+ eval_error, interpret as eval, lookup_items, EvalContext, Expr, Field, Value,
5
+ };
4
6
  use crate::kernel::Refusal;
5
7
 
6
8
  pub fn interpret(expr: &Expr, ctx: &EvalContext) -> Result<Value, Refusal> {
7
9
  let (op, receiver, take_first) = match expr {
8
10
  Expr::First(receiver) => ("first", receiver, true),
9
11
  Expr::Last(receiver) => ("last", receiver, false),
10
- _ => return Err(Refusal::TypeMismatch(format!("positional::interpret called with a non-positional node {expr:?} — a router bug"))),
12
+ _ => {
13
+ return Err(Refusal::TypeMismatch(format!(
14
+ "positional::interpret called with a non-positional node {expr:?} — a router bug"
15
+ )))
16
+ }
11
17
  };
12
18
 
13
19
  match receiver.as_ref() {
14
20
  Expr::Lookup(path) => {
15
21
  // `Value::List` carries only a length, so read the elements directly.
16
22
  let items = lookup_items(path, ctx, op)?;
17
- field_value(if take_first { items.into_iter().next() } else { items.into_iter().last() }, op)
23
+ field_value(
24
+ if take_first {
25
+ items.into_iter().next()
26
+ } else {
27
+ items.into_iter().last()
28
+ },
29
+ op,
30
+ )
18
31
  }
19
32
  other => match eval(other, ctx)? {
20
- Value::Array(mut elements) => {
21
- Ok(if take_first {
22
- elements.into_iter().next()
23
- } else {
24
- elements.pop()
25
- }
26
- .unwrap_or(Value::Nil))
33
+ Value::Array(mut elements) => Ok(if take_first {
34
+ elements.into_iter().next()
35
+ } else {
36
+ elements.pop()
27
37
  }
38
+ .unwrap_or(Value::Nil)),
28
39
  v => Err(eval_error(format!("{op} expects a list, got {v:?}"))),
29
40
  },
30
41
  }
@@ -36,7 +47,9 @@ fn field_value(item: Option<Field<'_>>, op: &str) -> Result<Value, Refusal> {
36
47
  match item {
37
48
  None => Ok(Value::Nil),
38
49
  Some(Field::Value(v)) => Ok(v),
39
- Some(Field::Nested(_)) => Err(eval_error(format!("{op} on a list of objects is not generated yet — only scalar-element lists are"))),
50
+ Some(Field::Nested(_)) => Err(eval_error(format!(
51
+ "{op} on a list of objects is not generated yet — only scalar-element lists are"
52
+ ))),
40
53
  }
41
54
  }
42
55
 
@@ -45,30 +58,48 @@ mod tests {
45
58
  use super::*;
46
59
  use crate::kernel::expr::{Fielded, NoFields};
47
60
 
48
- fn run(expr: Expr, instance: &dyn Fielded) -> Value {
49
- let ctx = EvalContext { args: &NoFields, instance };
50
- eval(&expr, &ctx).expect("evaluates")
61
+ fn run(expr: &Expr, instance: &dyn Fielded) -> Value {
62
+ let ctx = EvalContext {
63
+ args: &NoFields,
64
+ instance,
65
+ };
66
+ eval(expr, &ctx).expect("evaluates")
51
67
  }
52
68
 
53
69
  #[test]
54
70
  fn first_and_last_over_an_array_literal() {
55
71
  let arr = Expr::Array(vec![Expr::Int(1), Expr::Int(2), Expr::Int(3)]);
56
- assert_eq!(run(Expr::First(Box::new(arr.clone())), &NoFields), Value::Int(1));
57
- assert_eq!(run(Expr::Last(Box::new(arr)), &NoFields), Value::Int(3));
72
+ assert_eq!(
73
+ run(&Expr::First(Box::new(arr.clone())), &NoFields),
74
+ Value::Int(1)
75
+ );
76
+ assert_eq!(run(&Expr::Last(Box::new(arr)), &NoFields), Value::Int(3));
58
77
  }
59
78
 
60
79
  #[test]
61
80
  fn first_and_last_over_an_empty_array_literal_answer_nil() {
62
81
  let arr = Expr::Array(vec![]);
63
- assert_eq!(run(Expr::First(Box::new(arr.clone())), &NoFields), Value::Nil);
64
- assert_eq!(run(Expr::Last(Box::new(arr)), &NoFields), Value::Nil);
82
+ assert_eq!(
83
+ run(&Expr::First(Box::new(arr.clone())), &NoFields),
84
+ Value::Nil
85
+ );
86
+ assert_eq!(run(&Expr::Last(Box::new(arr)), &NoFields), Value::Nil);
65
87
  }
66
88
 
67
89
  #[test]
68
90
  fn first_and_last_compose_with_split() {
69
- let phrase = Expr::Split { receiver: Box::new(Expr::Str("a::b::c".to_string())), separator: "::".to_string() };
70
- assert_eq!(run(Expr::Last(Box::new(phrase.clone())), &NoFields), Value::Str("c".to_string()));
71
- assert_eq!(run(Expr::First(Box::new(phrase)), &NoFields), Value::Str("a".to_string()));
91
+ let phrase = Expr::Split {
92
+ receiver: Box::new(Expr::Str("a::b::c".to_string())),
93
+ separator: "::".to_string(),
94
+ };
95
+ assert_eq!(
96
+ run(&Expr::Last(Box::new(phrase.clone())), &NoFields),
97
+ Value::Str("c".to_string())
98
+ );
99
+ assert_eq!(
100
+ run(&Expr::First(Box::new(phrase)), &NoFields),
101
+ Value::Str("a".to_string())
102
+ );
72
103
  }
73
104
 
74
105
  struct Tags {
@@ -76,24 +107,40 @@ mod tests {
76
107
  }
77
108
  impl Fielded for Tags {
78
109
  fn field(&self, name: &str) -> Option<Field<'_>> {
79
- (name == "tags").then(|| Field::Value(Value::List(self.values.len())))
110
+ (name == "tags").then_some(Field::Value(Value::List(self.values.len())))
80
111
  }
81
112
  fn items(&self, name: &str) -> Option<Vec<Field<'_>>> {
82
- (name == "tags").then(|| self.values.iter().map(|t| Field::Value(Value::Str(t.clone()))).collect())
113
+ (name == "tags").then(|| {
114
+ self.values
115
+ .iter()
116
+ .map(|t| Field::Value(Value::Str(t.clone())))
117
+ .collect()
118
+ })
83
119
  }
84
120
  }
85
121
 
86
122
  #[test]
87
123
  fn first_over_a_real_scalar_element_field() {
88
- let tags = Tags { values: vec!["a".to_string(), "b".to_string()] };
89
- assert_eq!(run(Expr::First(Box::new(Expr::Lookup("tags"))), &tags), Value::Str("a".to_string()));
90
- assert_eq!(run(Expr::Last(Box::new(Expr::Lookup("tags"))), &tags), Value::Str("b".to_string()));
124
+ let tags = Tags {
125
+ values: vec!["a".to_string(), "b".to_string()],
126
+ };
127
+ assert_eq!(
128
+ run(&Expr::First(Box::new(Expr::Lookup("tags"))), &tags),
129
+ Value::Str("a".to_string())
130
+ );
131
+ assert_eq!(
132
+ run(&Expr::Last(Box::new(Expr::Lookup("tags"))), &tags),
133
+ Value::Str("b".to_string())
134
+ );
91
135
  }
92
136
 
93
137
  #[test]
94
138
  fn first_over_an_empty_field_answers_nil() {
95
139
  let tags = Tags { values: vec![] };
96
- assert_eq!(run(Expr::First(Box::new(Expr::Lookup("tags"))), &tags), Value::Nil);
140
+ assert_eq!(
141
+ run(&Expr::First(Box::new(Expr::Lookup("tags"))), &tags),
142
+ Value::Nil
143
+ );
97
144
  }
98
145
 
99
146
  struct Leg {
@@ -109,7 +156,7 @@ mod tests {
109
156
  }
110
157
  impl Fielded for Itinerary {
111
158
  fn field(&self, name: &str) -> Option<Field<'_>> {
112
- (name == "legs").then(|| Field::Value(Value::List(self.legs.len())))
159
+ (name == "legs").then_some(Field::Value(Value::List(self.legs.len())))
113
160
  }
114
161
  fn items(&self, name: &str) -> Option<Vec<Field<'_>>> {
115
162
  (name == "legs").then(|| self.legs.iter().map(|leg| Field::Nested(leg)).collect())
@@ -118,8 +165,15 @@ mod tests {
118
165
 
119
166
  #[test]
120
167
  fn first_over_a_list_of_objects_refuses_by_name_instead_of_misrepresenting_one() {
121
- let itinerary = Itinerary { legs: vec![Leg { origin: "SFO".to_string() }] };
122
- let ctx = EvalContext { args: &NoFields, instance: &itinerary };
168
+ let itinerary = Itinerary {
169
+ legs: vec![Leg {
170
+ origin: "SFO".to_string(),
171
+ }],
172
+ };
173
+ let ctx = EvalContext {
174
+ args: &NoFields,
175
+ instance: &itinerary,
176
+ };
123
177
  let err = eval(&Expr::First(Box::new(Expr::Lookup("legs"))), &ctx).unwrap_err();
124
178
  assert!(format!("{err:?}").contains("not generated yet"), "{err:?}");
125
179
  }
@@ -6,7 +6,9 @@ use crate::kernel::Refusal;
6
6
 
7
7
  pub fn interpret(expr: &Expr, ctx: &EvalContext) -> Result<Value, Refusal> {
8
8
  let Expr::Presence { receiver, negated } = expr else {
9
- return Err(Refusal::TypeMismatch(format!("presence::interpret called with a non-presence node {expr:?} — a router bug")));
9
+ return Err(Refusal::TypeMismatch(format!(
10
+ "presence::interpret called with a non-presence node {expr:?} — a router bug"
11
+ )));
10
12
  };
11
13
 
12
14
  let v = eval(receiver, ctx)?;
@@ -32,7 +34,7 @@ pub fn interpret_assignment(expr: &Expr, ctx: &EvalContext) -> Result<Value, Ref
32
34
  }
33
35
 
34
36
  /// Rails-style blankness: nil, false, and an empty Str or Array.
35
- fn blank(v: &Value) -> bool {
37
+ const fn blank(v: &Value) -> bool {
36
38
  match v {
37
39
  Value::Nil | Value::Bool(false) => true,
38
40
  Value::Str(s) => s.is_empty(),
@@ -47,8 +49,14 @@ mod tests {
47
49
  use crate::kernel::expr::NoFields;
48
50
 
49
51
  fn present(receiver: Expr, negated: bool) -> Value {
50
- let expr = Expr::Presence { receiver: Box::new(receiver), negated };
51
- let ctx = EvalContext { args: &NoFields, instance: &NoFields };
52
+ let expr = Expr::Presence {
53
+ receiver: Box::new(receiver),
54
+ negated,
55
+ };
56
+ let ctx = EvalContext {
57
+ args: &NoFields,
58
+ instance: &NoFields,
59
+ };
52
60
  eval(&expr, &ctx).expect("evaluates")
53
61
  }
54
62
 
@@ -61,7 +69,10 @@ mod tests {
61
69
  #[test]
62
70
  fn an_empty_string_is_blank_but_a_nonempty_one_is_present() {
63
71
  assert_eq!(present(Expr::Str(String::new()), false), Value::Bool(false));
64
- assert_eq!(present(Expr::Str("x".to_string()), false), Value::Bool(true));
72
+ assert_eq!(
73
+ present(Expr::Str("x".to_string()), false),
74
+ Value::Bool(true)
75
+ );
65
76
  }
66
77
 
67
78
  #[test]
@@ -72,18 +83,30 @@ mod tests {
72
83
  #[test]
73
84
  fn blank_negates_present() {
74
85
  assert_eq!(present(Expr::Nil, true), Value::Bool(true));
75
- assert_eq!(present(Expr::Str("x".to_string()), true), Value::Bool(false));
86
+ assert_eq!(
87
+ present(Expr::Str("x".to_string()), true),
88
+ Value::Bool(false)
89
+ );
76
90
  }
77
91
 
78
92
  #[test]
79
93
  fn an_empty_array_literal_is_blank() {
80
94
  assert_eq!(present(Expr::Array(vec![]), false), Value::Bool(false));
81
- assert_eq!(present(Expr::Array(vec![Expr::Int(1)]), false), Value::Bool(true));
95
+ assert_eq!(
96
+ present(Expr::Array(vec![Expr::Int(1)]), false),
97
+ Value::Bool(true)
98
+ );
82
99
  }
83
100
 
84
101
  fn set(receiver: Expr, negated: bool) -> Value {
85
- let expr = Expr::Assignment { receiver: Box::new(receiver), negated };
86
- let ctx = EvalContext { args: &NoFields, instance: &NoFields };
102
+ let expr = Expr::Assignment {
103
+ receiver: Box::new(receiver),
104
+ negated,
105
+ };
106
+ let ctx = EvalContext {
107
+ args: &NoFields,
108
+ instance: &NoFields,
109
+ };
87
110
  eval(&expr, &ctx).expect("evaluates")
88
111
  }
89
112
 
@@ -120,15 +143,31 @@ mod tests {
120
143
  }
121
144
 
122
145
  fn set_on(draft: &Draft, negated: bool) -> Value {
123
- let expr = Expr::Assignment { receiver: Box::new(Expr::Lookup("body")), negated };
124
- eval(&expr, &EvalContext { args: &NoFields, instance: draft }).expect("evaluates")
146
+ let expr = Expr::Assignment {
147
+ receiver: Box::new(Expr::Lookup("body")),
148
+ negated,
149
+ };
150
+ eval(
151
+ &expr,
152
+ &EvalContext {
153
+ args: &NoFields,
154
+ instance: draft,
155
+ },
156
+ )
157
+ .expect("evaluates")
125
158
  }
126
159
 
127
160
  #[test]
128
161
  fn a_nested_object_field_is_set_and_an_absent_one_is_not() {
129
- assert_eq!(set_on(&Draft { body: Some(Body) }, false), Value::Bool(true));
162
+ assert_eq!(
163
+ set_on(&Draft { body: Some(Body) }, false),
164
+ Value::Bool(true)
165
+ );
130
166
  assert_eq!(set_on(&Draft { body: None }, false), Value::Bool(false));
131
- assert_eq!(set_on(&Draft { body: Some(Body) }, true), Value::Bool(false));
167
+ assert_eq!(
168
+ set_on(&Draft { body: Some(Body) }, true),
169
+ Value::Bool(false)
170
+ );
132
171
  }
133
172
 
134
173
  #[test]
@@ -6,7 +6,9 @@ use crate::kernel::Refusal;
6
6
 
7
7
  pub fn interpret(expr: &Expr, ctx: &EvalContext) -> Result<Value, Refusal> {
8
8
  let Expr::SignTest { op, receiver } = expr else {
9
- return Err(Refusal::TypeMismatch(format!("sign_test::interpret called with a non-sign-test node {expr:?} — a router bug")));
9
+ return Err(Refusal::TypeMismatch(format!(
10
+ "sign_test::interpret called with a non-sign-test node {expr:?} — a router bug"
11
+ )));
10
12
  };
11
13
 
12
14
  // Comparing against 0 through `comparison::apply` treats int and float alike.
@@ -6,30 +6,46 @@ use crate::kernel::Refusal;
6
6
 
7
7
  pub fn empty(expr: &Expr, ctx: &EvalContext) -> Result<Value, Refusal> {
8
8
  let Expr::Empty(receiver) = expr else {
9
- return Err(Refusal::TypeMismatch(format!("sized::empty called with a non-empty? node {expr:?} — a router bug")));
9
+ return Err(Refusal::TypeMismatch(format!(
10
+ "sized::empty called with a non-empty? node {expr:?} — a router bug"
11
+ )));
10
12
  };
11
13
 
12
14
  let v = eval(receiver, ctx)?;
13
15
  // Every `Value` variant is named so a new one forces a decision here.
14
16
  match v {
15
- Value::Str(_) => Ok(Value::Bool(scalar::is_empty(&v).expect("Str always answers is_empty"))),
16
- Value::List(_) => Ok(Value::Bool(list::is_empty(&v).expect("List always answers is_empty"))),
17
+ Value::Str(_) => Ok(Value::Bool(scalar::is_empty(&v).ok_or_else(|| {
18
+ eval_error("router bug: Str always answers is_empty".to_string())
19
+ })?)),
20
+ Value::List(_) => Ok(Value::Bool(list::is_empty(&v).ok_or_else(|| {
21
+ eval_error("router bug: List always answers is_empty".to_string())
22
+ })?)),
17
23
  // A materialised Array answers from its own length.
18
24
  Value::Array(ref elements) => Ok(Value::Bool(elements.is_empty())),
19
- Value::Int(_) | Value::Float(_) | Value::Bool(_) | Value::Nil => Err(eval_error(format!("empty? expects a list or string, got {v:?}"))),
25
+ Value::Int(_) | Value::Float(_) | Value::Bool(_) | Value::Nil => Err(eval_error(format!(
26
+ "empty? expects a list or string, got {v:?}"
27
+ ))),
20
28
  }
21
29
  }
22
30
 
23
31
  pub fn size(expr: &Expr, ctx: &EvalContext) -> Result<Value, Refusal> {
24
32
  let Expr::Size(receiver) = expr else {
25
- return Err(Refusal::TypeMismatch(format!("sized::size called with a non-size node {expr:?} — a router bug")));
33
+ return Err(Refusal::TypeMismatch(format!(
34
+ "sized::size called with a non-size node {expr:?} — a router bug"
35
+ )));
26
36
  };
27
37
 
28
38
  let v = eval(receiver, ctx)?;
29
39
  match v {
30
- Value::Str(_) => Ok(Value::Int(scalar::size(&v).expect("Str always answers size"))),
31
- Value::List(_) => Ok(Value::Int(list::size(&v).expect("List always answers size"))),
40
+ Value::Str(_) => Ok(Value::Int(scalar::size(&v).ok_or_else(|| {
41
+ eval_error("router bug: Str always answers size".to_string())
42
+ })?)),
43
+ Value::List(_) => Ok(Value::Int(list::size(&v).ok_or_else(|| {
44
+ eval_error("router bug: List always answers size".to_string())
45
+ })?)),
32
46
  Value::Array(ref elements) => Ok(Value::Int(elements.len() as i64)),
33
- Value::Int(_) | Value::Float(_) | Value::Bool(_) | Value::Nil => Err(eval_error(format!("size expects a list or string, got {v:?}"))),
47
+ Value::Int(_) | Value::Float(_) | Value::Bool(_) | Value::Nil => Err(eval_error(format!(
48
+ "size expects a list or string, got {v:?}"
49
+ ))),
34
50
  }
35
51
  }