hecks 3.13.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 (440) 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.bluebook +4 -4
  7. data/examples/pizzas/bluebook/pizzas.hecksagon +5 -3
  8. data/examples/pizzas/config.ru +22 -0
  9. data/lib/hecks/adapters/driven/prism.rb +0 -2
  10. data/lib/hecks/adapters/driven.rb +0 -2
  11. data/lib/hecks/adapters/driving/json.rb +1 -7
  12. data/lib/hecks/bluebook/dsl/attribute_collector.rb +15 -5
  13. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +2 -2
  14. data/lib/hecks/bluebook/dsl/derivation_words.rb +58 -0
  15. data/lib/hecks/bluebook/dsl/translation_builder.rb +6 -37
  16. data/lib/hecks/bluebook/expression/ast_json.rb +0 -9
  17. data/lib/hecks/bluebook/hecksagon.rb +1 -1
  18. data/lib/hecks/bluebook/meta_validator/translation_judge/rules.rb +7 -10
  19. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +4 -8
  20. data/lib/hecks/bluebook/meta_validator.rb +0 -11
  21. data/lib/hecks/bluebook/named_pattern.rb +33 -0
  22. data/lib/hecks/bluebook/translation.rb +18 -10
  23. data/lib/hecks/bluebook/translation_expression/evaluator.rb +63 -0
  24. data/lib/hecks/bluebook/translation_expression/parser.rb +127 -0
  25. data/lib/hecks/bluebook/translation_expression.rb +39 -0
  26. data/lib/hecks/bluebook/translation_plan.rb +70 -0
  27. data/lib/hecks/bluebook.rb +3 -0
  28. data/lib/hecks/chapters.rb +3 -3
  29. data/lib/hecks/cli/domain_stub.rb +1 -1
  30. data/lib/hecks/cli/interview_aggregate.rb +4 -4
  31. data/lib/hecks/composer/adapters/claude_composer.adapter +3 -0
  32. data/lib/hecks/composer/adapters/claude_composer.rb +83 -0
  33. data/lib/hecks/composer/adapters/fake_agent.adapter +3 -0
  34. data/lib/hecks/composer/adapters/fake_agent.rb +55 -0
  35. data/lib/hecks/composer/adapters/jev.adapter +3 -0
  36. data/lib/hecks/composer/adapters/jev.rb +66 -0
  37. data/lib/hecks/composer/agent.rb +113 -0
  38. data/lib/hecks/composer/assistant.rb +110 -0
  39. data/lib/hecks/composer/behavior_run.rb +53 -0
  40. data/lib/hecks/composer/bluebook/composer.bluebook +178 -0
  41. data/lib/hecks/composer/bluebook/composer.hecksagon +16 -0
  42. data/lib/hecks/composer/board.rb +65 -0
  43. data/lib/hecks/composer/board_edit/command_text.rb +39 -0
  44. data/lib/hecks/composer/board_edit/source.rb +84 -0
  45. data/lib/hecks/composer/board_edit.rb +144 -0
  46. data/lib/hecks/composer/check.rb +89 -0
  47. data/lib/hecks/composer/domains.rb +81 -0
  48. data/lib/hecks/composer/jobs.rb +134 -0
  49. data/lib/hecks/composer/layout.rb +37 -0
  50. data/lib/hecks/composer/live.rb +150 -0
  51. data/lib/hecks/composer/outline.rb +48 -0
  52. data/lib/hecks/composer/page.html +989 -0
  53. data/lib/hecks/composer/panels.rb +142 -0
  54. data/lib/hecks/composer/parse.rb +83 -0
  55. data/lib/hecks/composer/plain.rb +28 -0
  56. data/lib/hecks/composer/ports/composer_agent.port +7 -0
  57. data/lib/hecks/composer/session.rb +141 -0
  58. data/lib/hecks/composer/toolbox.rb +166 -0
  59. data/lib/hecks/composer/web.rb +156 -0
  60. data/lib/hecks/composer/workbench.rb +51 -0
  61. data/lib/hecks/composer/workspace.rb +81 -0
  62. data/lib/hecks/composer.rb +17 -0
  63. data/lib/hecks/corpus/adapters.rb +22 -0
  64. data/lib/hecks/corpus/rust.rb +1 -1
  65. data/lib/hecks/corpus/worlds.rb +19 -0
  66. data/lib/hecks/corpus.rb +5 -0
  67. data/lib/hecks/deploy/bluebook/deploy.bluebook +34 -34
  68. data/lib/hecks/forms/app/commands.rb +2 -3
  69. data/lib/hecks/forms/app/queries.rb +2 -3
  70. data/lib/hecks/forms/app/records.rb +17 -5
  71. data/lib/hecks/forms/command_form_renderer.rb +42 -16
  72. data/lib/hecks/forms/field_controls.rb +2 -2
  73. data/lib/hecks/forms/field_renderer.rb +1 -1
  74. data/lib/hecks/forms/field_shape.rb +36 -17
  75. data/lib/hecks/forms/index_renderer.rb +2 -1
  76. data/lib/hecks/forms/page.rb +1 -1
  77. data/lib/hecks/forms/params.rb +0 -13
  78. data/lib/hecks/forms/query_form_renderer.rb +7 -6
  79. data/lib/hecks/forms/record_renderer.rb +11 -7
  80. data/lib/hecks/forms/rule_words.rb +100 -0
  81. data/lib/hecks/forms/value_object_shape.rb +2 -1
  82. data/lib/hecks/framework/bluebook/change.bluebook +2 -2
  83. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +0 -3
  84. data/lib/hecks/fuzzing/domain_generator/block_rendering.rb +1 -1
  85. data/lib/hecks/fuzzing/isolated_boot/worlds.rb +3 -6
  86. data/lib/hecks/fuzzing/replay/filters.rb +2 -6
  87. data/lib/hecks/gate/stages.yml +25 -0
  88. data/lib/hecks/grammar/evolve/seeds.rb +0 -6
  89. data/lib/hecks/grammar/evolve.rb +0 -3
  90. data/lib/hecks/grammar/translation.bluebook +1 -1
  91. data/lib/hecks/hecks/adapters/codebase/composing.rb +56 -0
  92. data/lib/hecks/hecks/adapters/codebase/source_tree.rb +3 -1
  93. data/lib/hecks/hecks/adapters/journal_store/changes.rb +0 -26
  94. data/lib/hecks/hecks/adapters/journal_store/examination.rb +2 -23
  95. data/lib/hecks/hecks/adapters/journal_store/readings.rb +3 -11
  96. data/lib/hecks/hecks/adapters/journal_store.rb +0 -11
  97. data/lib/hecks/hecks/adapters/rehearsal/inquiry.rb +4 -11
  98. data/lib/hecks/hecks/adapters/rehearsal/memory_store.rb +0 -9
  99. data/lib/hecks/hecks/adapters/rehearsal/postgres_reading.rb +0 -4
  100. data/lib/hecks/hecks/codebase.behaviors +6 -0
  101. data/lib/hecks/hecks/codebase.bluebook +58 -37
  102. data/lib/hecks/hecks/composer.behaviors +68 -0
  103. data/lib/hecks/hecks/context_map.hecksagon +1 -1
  104. data/lib/hecks/hecks/custodian.behaviors +0 -6
  105. data/lib/hecks/hecks/custodian.bluebook +15 -92
  106. data/lib/hecks/hecks/hecks.bluebook +2 -2
  107. data/lib/hecks/hecks/hecks.hecksagon +2 -2
  108. data/lib/hecks/hecks/hecks.world +1 -1
  109. data/lib/hecks/hecks/rehearsal.bluebook +10 -16
  110. data/lib/hecks/hecks/tooling.bluebook +22 -22
  111. data/lib/hecks/kernel_capabilities/templates.rb +4 -4
  112. data/lib/hecks/kernel_capabilities.rb +1 -1
  113. data/lib/hecks/language/bluebook/aggregate.bluebook +1 -0
  114. data/lib/hecks/language/bluebook/command.bluebook +1 -0
  115. data/lib/hecks/language/bluebook/entity.bluebook +1 -0
  116. data/lib/hecks/language/bluebook/query.bluebook +1 -0
  117. data/lib/hecks/language/bluebook/shape.bluebook +8 -7
  118. data/lib/hecks/language/bluebook/vocabulary.bluebook +2 -3
  119. data/lib/hecks/language/hecksagon/port_operation.bluebook +1 -0
  120. data/lib/hecks/language/translation/translation_aggregate.bluebook +21 -24
  121. data/lib/hecks/ports/access_control.rb +2 -2
  122. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +3 -41
  123. data/lib/hecks/ports/persistence/plugins/era/lineage/coverage.rb +7 -7
  124. data/lib/hecks/ports/persistence/plugins/era/lineage/rewrite.rb +25 -2
  125. data/lib/hecks/ports/persistence/plugins/era/lineage/rules.rb +4 -4
  126. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +17 -25
  127. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/base_sql.rb +0 -12
  128. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/domain_rename.rb +1 -1
  129. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +2 -22
  130. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +0 -1
  131. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +9 -3
  132. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +3 -29
  133. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/refusals.rb +2 -26
  134. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +6 -33
  135. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +3 -3
  136. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +2 -4
  137. data/lib/hecks/ports/persistence/plugins/era/translation/expression_sql.rb +52 -0
  138. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +23 -29
  139. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +1 -1
  140. data/lib/hecks/ports/persistence/plugins/era/translation.rb +0 -1
  141. data/lib/hecks/ports/persistence/plugins/era.rb +3 -6
  142. data/lib/hecks/ports.rb +0 -1
  143. data/lib/hecks/projection_files.rb +3 -1
  144. data/lib/hecks/projections/deploy/templates/lambda/nat_comment_oauth.tmpl +1 -1
  145. data/lib/hecks/projections/glossary/entries.rb +10 -4
  146. data/lib/hecks/projections/glossary/groups.rb +42 -0
  147. data/lib/hecks/projections/glossary/html/page.rb +7 -2
  148. data/lib/hecks/projections/glossary/html/parser.rb +1 -1
  149. data/lib/hecks/projections/glossary/html/sections.rb +31 -11
  150. data/lib/hecks/projections/glossary/html.rb +1 -2
  151. data/lib/hecks/projections/glossary/markdown.rb +31 -28
  152. data/lib/hecks/projections/glossary/mermaid.rb +3 -0
  153. data/lib/hecks/projections/glossary/page.css +143 -153
  154. data/lib/hecks/projections/glossary/page.js +63 -3
  155. data/lib/hecks/projections/glossary/sentences/actions.rb +88 -0
  156. data/lib/hecks/projections/glossary/sentences/paths.rb +77 -0
  157. data/lib/hecks/projections/glossary/sentences/queries.rb +90 -0
  158. data/lib/hecks/projections/glossary/sentences/value_objects.rb +32 -5
  159. data/lib/hecks/projections/glossary/sentences.rb +13 -3
  160. data/lib/hecks/projections/glossary/slugs.rb +4 -1
  161. data/lib/hecks/projections/glossary.rb +2 -1
  162. data/lib/hecks/projections/plain_english.rb +97 -0
  163. data/lib/hecks/projections/rust_vocabulary/arguments.rb +2 -2
  164. data/lib/hecks/projections/rust_vocabulary/enum_body.rb +20 -25
  165. data/lib/hecks/projections/rust_vocabulary/tests.rb +2 -2
  166. data/lib/hecks/projections/rust_vocabulary.rb +14 -6
  167. data/lib/hecks/projections/site/cms_editor/color.rb +0 -6
  168. data/lib/hecks/projections/statements/actions.rb +99 -0
  169. data/lib/hecks/projections/statements/relationships.rb +86 -0
  170. data/lib/hecks/projections/statements.rb +15 -24
  171. data/lib/hecks/projector/exporter/adapters.rb +112 -0
  172. data/lib/hecks/projector/exporter/translations.rb +18 -35
  173. data/lib/hecks/projector/exporter/worlds.rb +103 -0
  174. data/lib/hecks/projector/exporter.rb +5 -1
  175. data/lib/hecks/quality_control/adapters/agent.rb +6 -7
  176. data/lib/hecks/quality_control/adapters/agent_profile.rb +16 -114
  177. data/lib/hecks/quality_control/cli/qa_mine_combinations.rb +1 -1
  178. data/lib/hecks/quality_control/quality_control.bluebook +51 -51
  179. data/lib/hecks/runtime/loader/boot_steps.rb +0 -20
  180. data/lib/hecks/runtime/outbox.rb +2 -9
  181. data/lib/hecks/runtime/port_operation_interpreter.rb +1 -9
  182. data/lib/hecks/runtime/remote_dispatcher.rb +2 -2
  183. data/lib/hecks/runtime/value.rb +13 -0
  184. data/lib/hecks/rust_build/project_rust/target_ir.rb +23 -4
  185. data/lib/hecks/rust_build/wasm.rb +0 -12
  186. data/lib/hecks/rust_build/write_if_changed.rb +12 -12
  187. data/lib/hecks/rust_format.rb +77 -0
  188. data/lib/hecks/site/bluebook/site.bluebook +5 -5
  189. data/lib/hecks/sme/bluebook/sme.bluebook +12 -12
  190. data/lib/hecks/tickets/bluebook/tickets.bluebook +3 -3
  191. data/lib/hecks/tools/fuzz_sweep/findings.rb +3 -9
  192. data/lib/hecks/tools/fuzz_sweep.rb +1 -3
  193. data/lib/hecks/tools/translation_audit/verdicts.rb +3 -28
  194. data/lib/hecks/tools/translation_audit.rb +8 -10
  195. data/lib/hecks/tools/translation_scaffold/reporting.rb +1 -1
  196. data/lib/hecks/version.rb +1 -1
  197. data/lib/hecks/vocabulary.rb +2 -1
  198. data/lib/hecks.rb +3 -0
  199. data/prebuilt/{58208399bada10b0db99928c48e38789288fc58f7b71a72b21bfa386680e32ed.marshal → 02c09c73b272c1fb5a9ae6d87bb5e5e529208e00f6b4b597dc2c0fd70a9c0964.marshal} +0 -0
  200. data/prebuilt/{2b59737fbdbd5f44edda0fddce2611f45d76087b3a7c782d1f1a17ea58c77948.marshal → bb84a835cf6a15136bad5ca03aa5bf93a5b92f5f72444618d3ef2412b987fb32.marshal} +0 -0
  201. data/prebuilt/verdicts-f1f10da14c8e8f01b4c9b7e3e2d74be8cd2bf4676364ed17fa16975d2a076d75.json +1 -0
  202. data/rust/Cargo.toml +40 -0
  203. data/rust/build/Cargo.toml +36 -0
  204. data/rust/build/clippy.toml +4 -0
  205. data/rust/build/src/adapters_pass.rs +196 -0
  206. data/rust/build/src/binding_pass.rs +54 -13
  207. data/rust/build/src/build_artifact.rs +23 -5
  208. data/rust/build/src/cargo_sync.rs +69 -26
  209. data/rust/build/src/lineage_pass.rs +101 -87
  210. data/rust/build/src/main.rs +11 -2
  211. data/rust/build/src/pipeline.rs +80 -19
  212. data/rust/build/src/reserved_names.rs +6 -50
  213. data/rust/build/src/resolve.rs +82 -35
  214. data/rust/build/src/rustfmt.rs +100 -0
  215. data/rust/build/src/seams_pass.rs +85 -16
  216. data/rust/build/src/subprocess.rs +30 -6
  217. data/rust/build/src/tmp.rs +5 -2
  218. data/rust/build/src/translation_pass.rs +72 -22
  219. data/rust/build/src/world_pass.rs +109 -0
  220. data/rust/clippy.toml +4 -0
  221. data/rust/codegen/Cargo.toml +36 -0
  222. data/rust/codegen/clippy.toml +4 -0
  223. data/rust/codegen/src/asks.rs +40 -14
  224. data/rust/codegen/src/attr.rs +2 -2
  225. data/rust/codegen/src/bridging.rs +676 -117
  226. data/rust/codegen/src/commands.rs +979 -373
  227. data/rust/codegen/src/constraints.rs +88 -25
  228. data/rust/codegen/src/dependency_planning.rs +128 -34
  229. data/rust/codegen/src/domain_generator.rs +300 -170
  230. data/rust/codegen/src/exemplar.rs +87 -32
  231. data/rust/codegen/src/expr_emitter.rs +54 -6
  232. data/rust/codegen/src/fielded.rs +193 -41
  233. data/rust/codegen/src/hecks_naming.rs +124 -16
  234. data/rust/codegen/src/hecks_naming_tests.rs +81 -0
  235. data/rust/codegen/src/json.rs +80 -44
  236. data/rust/codegen/src/json_codec.rs +585 -149
  237. data/rust/codegen/src/literal.rs +104 -138
  238. data/rust/codegen/src/main.rs +145 -52
  239. data/rust/codegen/src/manifest.rs +76 -14
  240. data/rust/codegen/src/mutations.rs +1212 -239
  241. data/rust/codegen/src/naming.rs +111 -60
  242. data/rust/codegen/src/optional_pass.rs +90 -28
  243. data/rust/codegen/src/ports.rs +5 -4
  244. data/rust/codegen/src/prelude.rs +139 -49
  245. data/rust/codegen/src/queries.rs +601 -226
  246. data/rust/codegen/src/reactions.rs +268 -74
  247. data/rust/codegen/src/read_models.rs +606 -205
  248. data/rust/codegen/src/reference_specs.rs +16 -3
  249. data/rust/codegen/src/registry.rs +128 -79
  250. data/rust/codegen/src/reserved_names.rs +6 -50
  251. data/rust/codegen/src/shared.rs +71 -22
  252. data/rust/codegen/src/sidecars.rs +15 -5
  253. data/rust/codegen/src/skip_reason.rs +8 -2
  254. data/rust/codegen/src/types.rs +306 -108
  255. data/rust/host/Cargo.lock +1 -0
  256. data/rust/host/Cargo.toml +40 -4
  257. data/rust/host/HECKS_RELEASE +1 -1
  258. data/rust/host/clippy.toml +4 -0
  259. data/rust/host/src/api.rs +698 -218
  260. data/rust/host/src/auth.rs +491 -214
  261. data/rust/host/src/b64.rs +91 -0
  262. data/rust/host/src/bin/expr_harness.rs +6 -2
  263. data/rust/host/src/bin/lineage_harness.rs +47 -11
  264. data/rust/host/src/bin/mint_harness.rs +70 -37
  265. data/rust/host/src/bin/validate_harness.rs +16 -8
  266. data/rust/host/src/boot.rs +144 -68
  267. data/rust/host/src/boundary_fuzz/api.rs +130 -32
  268. data/rust/host/src/boundary_fuzz/auth.rs +108 -23
  269. data/rust/host/src/boundary_fuzz/expr_json.rs +85 -11
  270. data/rust/host/src/boundary_fuzz/ir.rs +164 -12
  271. data/rust/host/src/boundary_fuzz/server.rs +283 -36
  272. data/rust/host/src/boundary_fuzz/web.rs +56 -8
  273. data/rust/host/src/checkout.rs +78 -32
  274. data/rust/host/src/clock.rs +8 -0
  275. data/rust/host/src/commerce_ir.rs +333 -95
  276. data/rust/host/src/crypto.rs +33 -0
  277. data/rust/host/src/dispatch.rs +990 -331
  278. data/rust/host/src/expr_json.rs +1021 -273
  279. data/rust/host/src/extension.rs +24 -4
  280. data/rust/host/src/field_hints.rs +11 -7
  281. data/rust/host/src/fuzz_support.rs +85 -13
  282. data/rust/host/src/host.rs +100 -0
  283. data/rust/host/src/http.rs +22 -0
  284. data/rust/host/src/ir.rs +326 -16
  285. data/rust/host/src/journal.rs +277 -176
  286. data/rust/host/src/lambda_client.rs +219 -53
  287. data/rust/host/src/lib.rs +16 -4
  288. data/rust/host/src/log.rs +62 -11
  289. data/rust/host/src/mint.rs +1153 -326
  290. data/rust/host/src/needs.rs +185 -44
  291. data/rust/host/src/payments/keystore.rs +197 -45
  292. data/rust/host/src/payments/roles.rs +191 -0
  293. data/rust/host/src/payments/tests.rs +1445 -275
  294. data/rust/host/src/payments.rs +409 -121
  295. data/rust/host/src/platform_signin/attack_tests.rs +5 -5
  296. data/rust/host/src/platform_signin/config.rs +11 -11
  297. data/rust/host/src/platform_signin/cookie.rs +2 -2
  298. data/rust/host/src/platform_signin/extension.rs +2 -1
  299. data/rust/host/src/platform_signin/handler.rs +9 -8
  300. data/rust/host/src/platform_signin/jti.rs +4 -2
  301. data/rust/host/src/platform_signin/jwks.rs +2 -1
  302. data/rust/host/src/platform_signin/jws.rs +2 -37
  303. data/rust/host/src/platform_signin/known_answer_tests.rs +5 -1
  304. data/rust/host/src/platform_signin/mode_tests.rs +5 -3
  305. data/rust/host/src/platform_signin/pages.rs +20 -20
  306. data/rust/host/src/platform_signin/session.rs +8 -1
  307. data/rust/host/src/platform_signin/settings.rs +6 -6
  308. data/rust/host/src/platform_signin/strict_json.rs +12 -9
  309. data/rust/host/src/platform_signin/verify.rs +41 -41
  310. data/rust/host/src/platform_signin/wiring_support.rs +13 -18
  311. data/rust/host/src/presentation.rs +207 -63
  312. data/rust/host/src/presentation_write.rs +502 -187
  313. data/rust/host/src/query_step.rs +69 -18
  314. data/rust/host/src/rate_limit.rs +541 -142
  315. data/rust/host/src/reference_transform.rs +77 -25
  316. data/rust/host/src/reference_validate.rs +443 -104
  317. data/rust/host/src/resend.rs +190 -55
  318. data/rust/host/src/secrets.rs +116 -21
  319. data/rust/host/src/server.rs +483 -104
  320. data/rust/host/src/storage_shape.rs +88 -21
  321. data/rust/host/src/test_pg.rs +17 -7
  322. data/rust/host/src/ui_schema.rs +495 -207
  323. data/rust/host/src/wasm_runner.rs +21 -6
  324. data/rust/host/src/web/commerce.rs +101 -23
  325. data/rust/host/src/web/newsletter/cooldown.rs +10 -3
  326. data/rust/host/src/web/newsletter/template.rs +26 -7
  327. data/rust/host/src/web/newsletter/tests.rs +112 -23
  328. data/rust/host/src/web/newsletter.rs +209 -59
  329. data/rust/host/src/web/newsletter_send.rs +251 -79
  330. data/rust/host/src/web/registration_receipt/tests.rs +16 -4
  331. data/rust/host/src/web/registration_receipt.rs +100 -24
  332. data/rust/host/src/web/registrations.rs +1384 -332
  333. data/rust/host/src/web/resend_webhook.rs +138 -80
  334. data/rust/host/src/web.rs +2416 -508
  335. data/rust/lsp/Cargo.toml +36 -0
  336. data/rust/lsp/clippy.toml +4 -0
  337. data/rust/lsp/src/json.rs +29 -22
  338. data/rust/lsp/src/main.rs +81 -31
  339. data/rust/lsp/src/outline.rs +23 -7
  340. data/rust/lsp/src/rpc.rs +17 -4
  341. data/rust/parser/Cargo.toml +36 -0
  342. data/rust/parser/clippy.toml +4 -0
  343. data/rust/parser/src/build/closed_sets.rs +6 -1
  344. data/rust/parser/src/build/identity.rs +4 -3
  345. data/rust/parser/src/build/mod.rs +1 -0
  346. data/rust/parser/src/build/named_pattern.rs +26 -0
  347. data/rust/parser/src/build/naming.rs +2 -80
  348. data/rust/parser/src/build/pattern_subset.rs +6 -8
  349. data/rust/parser/src/build/query_derive.rs +3 -1
  350. data/rust/parser/src/build/query_inference.rs +14 -5
  351. data/rust/parser/src/build/read_model.rs +4 -1
  352. data/rust/parser/src/build/references.rs +4 -3
  353. data/rust/parser/src/canonical.rs +34 -15
  354. data/rust/parser/src/emit.rs +187 -105
  355. data/rust/parser/src/expr/ast_json.rs +134 -42
  356. data/rust/parser/src/expr/evaluator.rs +27 -9
  357. data/rust/parser/src/expr/mod.rs +54 -13
  358. data/rust/parser/src/expr/resolver.rs +299 -92
  359. data/rust/parser/src/ir.rs +35 -3
  360. data/rust/parser/src/keywords.rs +7168 -510
  361. data/rust/parser/src/lex.rs +11 -25
  362. data/rust/parser/src/lib.rs +1 -0
  363. data/rust/parser/src/main.rs +65 -6
  364. data/rust/parser/src/parse/adapter.rs +157 -0
  365. data/rust/parser/src/parse/aggregate.rs +19 -10
  366. data/rust/parser/src/parse/chapter.rs +16 -17
  367. data/rust/parser/src/parse/command.rs +35 -16
  368. data/rust/parser/src/parse/domain_port.rs +4 -7
  369. data/rust/parser/src/parse/entity.rs +21 -11
  370. data/rust/parser/src/parse/hecksagon.rs +10 -4
  371. data/rust/parser/src/parse/lifecycle.rs +9 -5
  372. data/rust/parser/src/parse/mod.rs +43 -91
  373. data/rust/parser/src/parse/needs.rs +21 -12
  374. data/rust/parser/src/parse/policy.rs +2 -2
  375. data/rust/parser/src/parse/process_manager.rs +9 -11
  376. data/rust/parser/src/parse/query.rs +8 -4
  377. data/rust/parser/src/parse/read_model.rs +57 -16
  378. data/rust/parser/src/parse/value_object.rs +7 -2
  379. data/rust/parser/src/parse/world.rs +322 -0
  380. data/rust/parser/src/ruby_value.rs +7 -6
  381. data/rust/src/exemplar/json.rs +4 -4
  382. data/rust/src/exemplar/mutations.rs +18 -0
  383. data/rust/src/exemplar/read_models.rs +1 -10
  384. data/rust/src/kernel/attribute_shapes/composite.rs +54 -14
  385. data/rust/src/kernel/attribute_shapes/list.rs +2 -2
  386. data/rust/src/kernel/attribute_shapes/mod.rs +8 -8
  387. data/rust/src/kernel/attribute_shapes/scalar.rs +2 -2
  388. data/rust/src/kernel/cli.rs +229 -112
  389. data/rust/src/kernel/dispatch.rs +323 -115
  390. data/rust/src/kernel/dispatch_guard_tests.rs +327 -65
  391. data/rust/src/kernel/expr.rs +203 -86
  392. data/rust/src/kernel/expression_operators/arithmetic.rs +138 -25
  393. data/rust/src/kernel/expression_operators/comparison.rs +87 -2
  394. data/rust/src/kernel/expression_operators/enumeration.rs +106 -22
  395. data/rust/src/kernel/expression_operators/logical.rs +9 -3
  396. data/rust/src/kernel/expression_operators/membership.rs +3 -1
  397. data/rust/src/kernel/expression_operators/mod.rs +34 -21
  398. data/rust/src/kernel/expression_operators/pattern_match.rs +46 -11
  399. data/rust/src/kernel/expression_operators/positional.rs +84 -30
  400. data/rust/src/kernel/expression_operators/presence.rs +52 -13
  401. data/rust/src/kernel/expression_operators/sign_test.rs +3 -1
  402. data/rust/src/kernel/expression_operators/sized.rs +24 -8
  403. data/rust/src/kernel/expression_operators/text.rs +120 -27
  404. data/rust/src/kernel/expression_operators/to_string.rs +10 -4
  405. data/rust/src/kernel/json.rs +230 -148
  406. data/rust/src/kernel/mod.rs +46 -33
  407. data/rust/src/kernel/named_query.rs +63 -15
  408. data/rust/src/kernel/needs.rs +112 -25
  409. data/rust/src/kernel/orchestrate.rs +980 -581
  410. data/rust/src/kernel/orchestrate_guard_tests.rs +538 -140
  411. data/rust/src/kernel/pattern.rs +107 -34
  412. data/rust/src/kernel/query_comparators.rs +118 -41
  413. data/rust/src/kernel/query_ordering.rs +13 -5
  414. data/rust/src/kernel/read_model.rs +479 -181
  415. data/rust/src/kernel/reference_lookup.rs +122 -26
  416. data/rust/src/kernel/repository.rs +293 -66
  417. data/rust/src/kernel/routing.rs +58 -42
  418. data/rust/src/kernel/ruby_float.rs +139 -0
  419. data/rust/src/kernel/vocab/aggregate_dispatch_order.rs +57 -57
  420. data/rust/src/kernel/vocab/entity_dispatch_order.rs +54 -54
  421. data/rust/src/kernel/vocab/field_hint.rs +19 -25
  422. data/rust/src/kernel/vocab/mod.rs +6 -6
  423. data/rust/src/kernel/vocab/query_comparator.rs +23 -24
  424. data/rust/src/kernel/vocab/refusal_template.rs +501 -297
  425. data/rust/src/lib.rs +50 -0
  426. data/rust/src/main.rs +4 -1
  427. data/rust/web/Cargo.toml +36 -0
  428. data/rust/web/clippy.toml +4 -0
  429. metadata +85 -15
  430. data/examples/pizzas/bluebook/hecksagon/mock_stripe_payment_adapter.rb +0 -36
  431. data/lib/hecks/adapters/driven/google_authentication.adapter +0 -3
  432. data/lib/hecks/adapters/driven/google_authentication.rb +0 -99
  433. data/lib/hecks/adapters/driven/mock_stripe_adapter.adapter +0 -3
  434. data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +0 -32
  435. data/lib/hecks/ports/authentication.port +0 -6
  436. data/lib/hecks/ports/authentication.rb +0 -67
  437. data/lib/hecks/ports/persistence/plugins/era/translation/approval_file.rb +0 -255
  438. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +0 -22
  439. data/prebuilt/verdicts-48318ca172edd21443b1ad02801c697fff100b746e828f09db987647641a5df2.json +0 -1
  440. data/rust/host/src/approval.rs +0 -652
@@ -52,25 +52,34 @@ fn scan_leftover_placeholders(text: &str) -> Vec<String> {
52
52
 
53
53
  impl Exemplar {
54
54
  pub fn load() -> Self {
55
- Self::load_from(default_dir())
55
+ Self::load_from(&default_dir())
56
56
  }
57
57
 
58
- pub fn load_from(dir: PathBuf) -> Self {
58
+ #[allow(
59
+ clippy::panic,
60
+ reason = "a drifted exemplar or invalid IR aborts generation on purpose"
61
+ )]
62
+ pub fn load_from(dir: &std::path::Path) -> Self {
59
63
  let mut shapes = HashMap::new();
60
- let mut paths: Vec<_> = std::fs::read_dir(&dir)
64
+ let mut paths: Vec<_> = std::fs::read_dir(dir)
61
65
  .unwrap_or_else(|e| panic!("Exemplar: cannot read {}: {e}", dir.display()))
62
- .filter_map(|entry| entry.ok())
66
+ .filter_map(std::result::Result::ok)
63
67
  .map(|entry| entry.path())
64
- .filter(|p| p.extension().map(|ext| ext == "rs").unwrap_or(false))
68
+ .filter(|p| p.extension().is_some_and(|ext| ext == "rs"))
65
69
  .collect();
66
70
  paths.sort();
67
71
  for path in paths {
68
- let text = std::fs::read_to_string(&path).unwrap_or_else(|e| panic!("Exemplar: cannot read {}: {e}", path.display()));
72
+ let text = std::fs::read_to_string(&path)
73
+ .unwrap_or_else(|e| panic!("Exemplar: cannot read {}: {e}", path.display()));
69
74
  parse_file(&path, &text, &mut shapes);
70
75
  }
71
76
  Self { shapes }
72
77
  }
73
78
 
79
+ #[allow(
80
+ clippy::panic,
81
+ reason = "a drifted exemplar or invalid IR aborts generation on purpose"
82
+ )]
74
83
  pub fn raw(&self, shape_id: &str) -> &str {
75
84
  self.shapes
76
85
  .get(shape_id)
@@ -85,13 +94,31 @@ impl Exemplar {
85
94
  text
86
95
  }
87
96
 
88
- pub fn render_each(&self, shape_id: &str, subs_list: &[Vec<(&str, String)>], join_with: &str) -> String {
89
- subs_list.iter().map(|subs| self.render(shape_id, subs)).collect::<Vec<_>>().join(join_with)
97
+ pub fn render_each(
98
+ &self,
99
+ shape_id: &str,
100
+ subs_list: &[Vec<(&str, String)>],
101
+ join_with: &str,
102
+ ) -> String {
103
+ subs_list
104
+ .iter()
105
+ .map(|subs| self.render(shape_id, subs))
106
+ .collect::<Vec<_>>()
107
+ .join(join_with)
90
108
  }
91
109
 
92
110
  /// Fills nested slots (reindented to the slot's position) before the outer substitutions,
93
111
  /// so an outer marker cannot match text from a rendered slot.
94
- pub fn assemble(&self, outer_id: &str, outer_subs: &[(&str, String)], slots: &[(&str, String)]) -> String {
112
+ #[allow(
113
+ clippy::panic,
114
+ reason = "a drifted exemplar or invalid IR aborts generation on purpose"
115
+ )]
116
+ pub fn assemble(
117
+ &self,
118
+ outer_id: &str,
119
+ outer_subs: &[(&str, String)],
120
+ slots: &[(&str, String)],
121
+ ) -> String {
95
122
  let mut outer_text = self.raw(outer_id).to_string();
96
123
 
97
124
  for (field_id, content) in slots {
@@ -103,10 +130,21 @@ impl Exemplar {
103
130
  });
104
131
  let reindented: String = content
105
132
  .lines()
106
- .map(|l| if l.trim().is_empty() { l.to_string() } else { format!("{indent}{l}") })
133
+ .map(|l| {
134
+ if l.trim().is_empty() {
135
+ l.to_string()
136
+ } else {
137
+ format!("{indent}{l}")
138
+ }
139
+ })
107
140
  .collect::<Vec<_>>()
108
141
  .join("\n");
109
- outer_text = format!("{}{}{}", &outer_text[..line_start], reindented, &outer_text[line_end..]);
142
+ outer_text = format!(
143
+ "{}{}{}",
144
+ &outer_text[..line_start],
145
+ reindented,
146
+ &outer_text[line_end..]
147
+ );
110
148
  }
111
149
 
112
150
  render_subs(outer_id, "assemble", &mut outer_text, outer_subs);
@@ -114,7 +152,14 @@ impl Exemplar {
114
152
  }
115
153
 
116
154
  /// Renders `field_id` once per entry of `field_subs_list` and assembles it into `outer_id`.
117
- pub fn compose(&self, outer_id: &str, outer_subs: &[(&str, String)], field_id: &str, field_subs_list: &[Vec<(&str, String)>], join_with: &str) -> String {
155
+ pub fn compose(
156
+ &self,
157
+ outer_id: &str,
158
+ outer_subs: &[(&str, String)],
159
+ field_id: &str,
160
+ field_subs_list: &[Vec<(&str, String)>],
161
+ join_with: &str,
162
+ ) -> String {
118
163
  let content = self.render_each(field_id, field_subs_list, join_with);
119
164
  self.assemble(outer_id, outer_subs, &[(field_id, content)])
120
165
  }
@@ -127,16 +172,12 @@ fn render_subs(shape_id: &str, caller: &str, text: &mut String, subs: &[(&str, S
127
172
  ordered.sort_by_key(|(marker, _)| std::cmp::Reverse(marker.len()));
128
173
 
129
174
  for (marker, value) in ordered {
130
- if !text.contains(marker) {
131
- panic!("Exemplar.{caller}({shape_id:?}): marker {marker:?} not found in shape text — exemplar and caller have drifted");
132
- }
175
+ assert!(text.contains(marker), "Exemplar.{caller}({shape_id:?}): marker {marker:?} not found in shape text — exemplar and caller have drifted");
133
176
  *text = text.replace(marker, value);
134
177
  }
135
178
 
136
179
  let leftover = scan_leftover_placeholders(text);
137
- if !leftover.is_empty() {
138
- panic!("Exemplar.{caller}({shape_id:?}): unrendered placeholder(s) {leftover:?} — subs is missing a marker this shape actually contains");
139
- }
180
+ assert!(leftover.is_empty(), "Exemplar.{caller}({shape_id:?}): unrendered placeholder(s) {leftover:?} — subs is missing a marker this shape actually contains");
140
181
  }
141
182
 
142
183
  // Finds the line holding only `slot` after indentation; returns `(byte_start, byte_end, indent)`.
@@ -191,29 +232,40 @@ fn dedent(lines: &[String]) -> String {
191
232
  .unwrap_or(0);
192
233
  let joined: String = lines
193
234
  .iter()
194
- .map(|l| if l.is_empty() { l.clone() } else { l.chars().skip(margin).collect::<String>() })
195
- .collect::<Vec<_>>()
196
- .join("");
235
+ .map(|l| {
236
+ if l.is_empty() {
237
+ l.clone()
238
+ } else {
239
+ l.chars().skip(margin).collect::<String>()
240
+ }
241
+ })
242
+ .collect::<String>();
197
243
  joined.trim_end().to_string()
198
244
  }
199
245
 
246
+ #[allow(
247
+ clippy::unwrap_used,
248
+ reason = "the value was checked just before; None would mean invalid IR"
249
+ )]
200
250
  fn parse_file(path: &std::path::Path, text: &str, result: &mut HashMap<String, String>) {
201
251
  // Each frame is (id, lines, begin_indent).
202
252
  let mut stack: Vec<(String, Vec<String>, String)> = Vec::new();
203
253
  for raw_line in text.split_inclusive('\n') {
204
254
  let line = raw_line;
205
255
  if let Some(id) = begin_marker(line) {
206
- let indent: String = line.chars().take_while(|c| *c == ' ' || *c == '\t').collect();
256
+ let indent: String = line
257
+ .chars()
258
+ .take_while(|c| *c == ' ' || *c == '\t')
259
+ .collect();
207
260
  stack.push((id.to_string(), Vec::new(), indent));
208
261
  } else if let Some(id) = end_marker(line) {
209
262
  let top = stack.last().map(|(top_id, _, _)| top_id.as_str());
210
- if stack.is_empty() || top != Some(id) {
211
- panic!(
212
- "{}: TMPL:{id} END with no matching BEGIN (stack: {:?})",
213
- path.display(),
214
- stack.iter().map(|(i, _, _)| i.clone()).collect::<Vec<_>>()
215
- );
216
- }
263
+ assert!(
264
+ !(stack.is_empty() || top != Some(id)),
265
+ "{}: TMPL:{id} END with no matching BEGIN (stack: {:?})",
266
+ path.display(),
267
+ stack.iter().map(|(i, _, _)| i.clone()).collect::<Vec<_>>()
268
+ );
217
269
  let (id, lines, begin_indent) = stack.pop().unwrap();
218
270
  let dedented = dedent(&lines);
219
271
  result.insert(id.clone(), dedented);
@@ -224,7 +276,10 @@ fn parse_file(path: &std::path::Path, text: &str, result: &mut HashMap<String, S
224
276
  lines.push(line.to_string());
225
277
  }
226
278
  }
227
- if !stack.is_empty() {
228
- panic!("{}: unclosed TMPL region(s): {:?}", path.display(), stack.iter().map(|(i, _, _)| i.clone()).collect::<Vec<_>>());
229
- }
279
+ assert!(
280
+ stack.is_empty(),
281
+ "{}: unclosed TMPL region(s): {:?}",
282
+ path.display(),
283
+ stack.iter().map(|(i, _, _)| i.clone()).collect::<Vec<_>>()
284
+ );
230
285
  }
@@ -4,11 +4,24 @@
4
4
  use crate::json::Json;
5
5
  use crate::naming::ruby_inspect_string;
6
6
 
7
+ #[allow(
8
+ clippy::panic,
9
+ reason = "a drifted exemplar or invalid IR aborts generation on purpose"
10
+ )]
7
11
  pub fn emit_ast(node: &Json) -> String {
8
- let op = node.get("op").and_then(Json::as_str).unwrap_or_else(|| panic!("ast node has no string \"op\": {node:?}"));
12
+ let op = node
13
+ .get("op")
14
+ .and_then(Json::as_str)
15
+ .unwrap_or_else(|| panic!("ast node has no string \"op\": {node:?}"));
9
16
  let sub = |key: &str| emit_ast(field(node, op, key));
10
17
  let boxed = |key: &str| format!("Box::new({})", sub(key));
11
- let text = |key: &str| ruby_inspect_string(field(node, op, key).as_str().unwrap_or_else(|| panic!("{op}'s {key:?} isn't a string: {node:?}")));
18
+ let text = |key: &str| {
19
+ ruby_inspect_string(
20
+ field(node, op, key)
21
+ .as_str()
22
+ .unwrap_or_else(|| panic!("{op}'s {key:?} isn't a string: {node:?}")),
23
+ )
24
+ };
12
25
 
13
26
  match op {
14
27
  "or" => format!("Expr::Or({}, {})", boxed("left"), boxed("right")),
@@ -64,23 +77,54 @@ pub fn emit_ast(node: &Json) -> String {
64
77
  }
65
78
 
66
79
  // Fully qualified because the self-hosted grammar declares its own `Comparison` type.
80
+ #[allow(
81
+ clippy::panic,
82
+ reason = "a drifted exemplar or invalid IR aborts generation on purpose"
83
+ )]
67
84
  pub fn emit_comparison(cmp: &Json) -> String {
68
- let flag = |key: &str| cmp.get(key).unwrap_or_else(|| panic!("cmp has no {key:?}: {cmp:?}")).to_s();
69
- format!("crate::kernel::Comparison {{ less_than: {}, equal: {}, negated: {} }}", flag("less_than"), flag("equal"), flag("negated"))
85
+ let flag = |key: &str| {
86
+ cmp.get(key)
87
+ .unwrap_or_else(|| panic!("cmp has no {key:?}: {cmp:?}"))
88
+ .to_s()
89
+ };
90
+ format!(
91
+ "crate::kernel::Comparison {{ less_than: {}, equal: {}, negated: {} }}",
92
+ flag("less_than"),
93
+ flag("equal"),
94
+ flag("negated")
95
+ )
70
96
  }
71
97
 
98
+ #[allow(
99
+ clippy::panic,
100
+ reason = "a drifted exemplar or invalid IR aborts generation on purpose"
101
+ )]
72
102
  fn field<'a>(node: &'a Json, op: &str, key: &str) -> &'a Json {
73
- node.get(key).unwrap_or_else(|| panic!("{op} node has no {key:?} field: {node:?}"))
103
+ node.get(key)
104
+ .unwrap_or_else(|| panic!("{op} node has no {key:?} field: {node:?}"))
74
105
  }
75
106
 
107
+ #[allow(
108
+ clippy::panic,
109
+ reason = "a drifted exemplar or invalid IR aborts generation on purpose"
110
+ )]
76
111
  fn path_of(node: &Json, op: &str) -> Vec<String> {
77
112
  field(node, op, "path")
78
113
  .each()
79
114
  .iter()
80
- .map(|segment| segment.as_str().map(str::to_string).unwrap_or_else(|| panic!("{op}'s path has a non-string segment: {node:?}")))
115
+ .map(|segment| {
116
+ segment.as_str().map_or_else(
117
+ || panic!("{op}'s path has a non-string segment: {node:?}"),
118
+ str::to_string,
119
+ )
120
+ })
81
121
  .collect()
82
122
  }
83
123
 
124
+ #[allow(
125
+ clippy::panic,
126
+ reason = "a drifted exemplar or invalid IR aborts generation on purpose"
127
+ )]
84
128
  fn strip_side(node: &Json, op: &str) -> &'static str {
85
129
  match field(node, op, "side").as_str() {
86
130
  Some("both") => "Both",
@@ -90,6 +134,10 @@ fn strip_side(node: &Json, op: &str) -> &'static str {
90
134
  }
91
135
  }
92
136
 
137
+ #[allow(
138
+ clippy::panic,
139
+ reason = "a drifted exemplar or invalid IR aborts generation on purpose"
140
+ )]
93
141
  fn block_mode(node: &Json, op: &str) -> &'static str {
94
142
  match field(node, op, "mode").as_str() {
95
143
  Some("all") => "All",
@@ -6,13 +6,22 @@ use crate::naming;
6
6
  use std::collections::HashMap;
7
7
 
8
8
  fn is_fielded_nested(attr_type: &str, value_objects_by_name: &HashMap<String, &Json>) -> bool {
9
- match value_objects_by_name.get(attr_type) {
10
- Some(vo) => naming::fielded_capable_nested(vo),
11
- None => false,
12
- }
9
+ value_objects_by_name
10
+ .get(attr_type)
11
+ .is_some_and(|vo| naming::fielded_capable_nested(vo))
13
12
  }
14
13
 
15
- pub fn emit_fielded_flat(exemplar: &Exemplar, struct_name: &str, attributes: &[Json], value_objects_by_name: &HashMap<String, &Json>, extra_arms: &[String]) -> String {
14
+ #[allow(
15
+ clippy::unwrap_used,
16
+ reason = "the value was checked just before; None would mean invalid IR"
17
+ )]
18
+ pub fn emit_fielded_flat(
19
+ exemplar: &Exemplar,
20
+ struct_name: &str,
21
+ attributes: &[Json],
22
+ value_objects_by_name: &HashMap<String, &Json>,
23
+ extra_arms: &[String],
24
+ ) -> String {
16
25
  let mut arms: Vec<String> = Vec::new();
17
26
 
18
27
  for attr in attributes {
@@ -23,28 +32,67 @@ pub fn emit_fielded_flat(exemplar: &Exemplar, struct_name: &str, attributes: &[J
23
32
  let optional = crate::attr::optional(attr);
24
33
 
25
34
  let arm = if list && optional {
26
- Some(exemplar.render("fielded_arm_list_optional", &[("\"tmpl_field\"", naming::ruby_inspect_string(&key)), ("tmpl_ident", ident)]))
35
+ Some(exemplar.render(
36
+ "fielded_arm_list_optional",
37
+ &[
38
+ ("\"tmpl_field\"", naming::ruby_inspect_string(&key)),
39
+ ("tmpl_ident", ident),
40
+ ],
41
+ ))
27
42
  } else if list {
28
- Some(exemplar.render("fielded_arm_list", &[("\"tmpl_field\"", naming::ruby_inspect_string(&key)), ("tmpl_ident", ident)]))
29
- } else if optional && scalar.is_some() {
30
- let scalar = scalar.unwrap();
31
43
  Some(exemplar.render(
32
- "fielded_arm_optional_scalar",
33
- &[("\"tmpl_field\"", naming::ruby_inspect_string(&key)), ("tmpl_ident", ident.clone()), ("tmpl_value_expr_placeholder(v)", naming::scalar_to_value(scalar, &naming::deref_scalar(scalar, "v")).unwrap())],
44
+ "fielded_arm_list",
45
+ &[
46
+ ("\"tmpl_field\"", naming::ruby_inspect_string(&key)),
47
+ ("tmpl_ident", ident),
48
+ ],
34
49
  ))
50
+ } else if let (true, Some(scalar)) = (optional, scalar) {
51
+ Some(
52
+ exemplar.render(
53
+ "fielded_arm_optional_scalar",
54
+ &[
55
+ ("\"tmpl_field\"", naming::ruby_inspect_string(&key)),
56
+ ("tmpl_ident", ident.clone()),
57
+ (
58
+ "tmpl_value_expr_placeholder(v)",
59
+ naming::scalar_to_value(scalar, &naming::deref_scalar(scalar, "v"))
60
+ .unwrap(),
61
+ ),
62
+ ],
63
+ ),
64
+ )
35
65
  } else if optional {
36
66
  if is_fielded_nested(crate::attr::type_name(attr), value_objects_by_name) {
37
- Some(exemplar.render("fielded_arm_optional_nested", &[("\"tmpl_field\"", naming::ruby_inspect_string(&key)), ("tmpl_ident", ident)]))
67
+ Some(exemplar.render(
68
+ "fielded_arm_optional_nested",
69
+ &[
70
+ ("\"tmpl_field\"", naming::ruby_inspect_string(&key)),
71
+ ("tmpl_ident", ident),
72
+ ],
73
+ ))
38
74
  } else {
39
75
  None
40
76
  }
41
77
  } else if let Some(scalar) = scalar {
42
78
  Some(exemplar.render(
43
79
  "fielded_arm_scalar",
44
- &[("\"tmpl_field\"", naming::ruby_inspect_string(&key)), ("tmpl_value_expr_placeholder(&self.tmpl_ident)", naming::scalar_to_value(scalar, &format!("self.{ident}")).unwrap())],
80
+ &[
81
+ ("\"tmpl_field\"", naming::ruby_inspect_string(&key)),
82
+ (
83
+ "tmpl_value_expr_placeholder(&self.tmpl_ident)",
84
+ naming::scalar_to_value(scalar, &format!("self.{ident}")).unwrap(),
85
+ ),
86
+ ],
45
87
  ))
46
88
  } else if is_fielded_nested(crate::attr::type_name(attr), value_objects_by_name) {
47
- Some(exemplar.render("fielded_arm_nested", &[("\"tmpl_field\"", naming::ruby_inspect_string(&key)), ("tmpl_ident", ident)]))
89
+ Some(exemplar.render(
90
+ "fielded_arm_nested",
91
+ &[
92
+ ("\"tmpl_field\"", naming::ruby_inspect_string(&key)),
93
+ ("tmpl_ident", ident),
94
+ ],
95
+ ))
48
96
  } else {
49
97
  None
50
98
  };
@@ -69,19 +117,50 @@ pub fn emit_fielded_flat(exemplar: &Exemplar, struct_name: &str, attributes: &[J
69
117
  "fielded_flat",
70
118
  &[
71
119
  ("TmplFlatType", struct_name.to_string()),
72
- ("\"tmpl_arms_placeholder\" => tmpl_arms_block(),", arms.join("\n")),
73
- ("\"tmpl_items_placeholder\" => tmpl_items_block(),", items_arms(exemplar, attributes, value_objects_by_name, &[], |attr| crate::attr::optional(attr)).join("\n")),
120
+ (
121
+ "\"tmpl_arms_placeholder\" => tmpl_arms_block(),",
122
+ arms.join("\n")
123
+ ),
124
+ (
125
+ "\"tmpl_items_placeholder\" => tmpl_items_block(),",
126
+ items_arms(exemplar, attributes, value_objects_by_name, &[], |attr| {
127
+ crate::attr::optional(attr)
128
+ })
129
+ .join("\n")
130
+ ),
74
131
  ("tmpl_as_scalar_placeholder()", as_scalar_expr(attributes)),
75
- ("use crate::kernel::Field;", if uses_field { "use crate::kernel::Field;".to_string() } else { String::new() }),
76
- ("use crate::kernel::Value;", if uses_value { "use crate::kernel::Value;".to_string() } else { String::new() }),
132
+ (
133
+ "use crate::kernel::Field;",
134
+ if uses_field {
135
+ "use crate::kernel::Field;".to_string()
136
+ } else {
137
+ String::new()
138
+ }
139
+ ),
140
+ (
141
+ "use crate::kernel::Value;",
142
+ if uses_value {
143
+ "use crate::kernel::Value;".to_string()
144
+ } else {
145
+ String::new()
146
+ }
147
+ ),
77
148
  ],
78
149
  )
79
150
  )
80
151
  }
81
152
 
82
- pub fn emit_fielded_record(exemplar: &Exemplar, aggregate: &Json, value_objects_by_name: &HashMap<String, &Json>) -> String {
153
+ #[allow(
154
+ clippy::unwrap_used,
155
+ reason = "the value was checked just before; None would mean invalid IR"
156
+ )]
157
+ pub fn emit_fielded_record(
158
+ exemplar: &Exemplar,
159
+ aggregate: &Json,
160
+ value_objects_by_name: &HashMap<String, &Json>,
161
+ ) -> String {
83
162
  let name = naming::rust_ident(aggregate.get("name").and_then(Json::as_str).unwrap_or(""));
84
- let attributes = aggregate.get("attributes").map(Json::each).unwrap_or(&[]);
163
+ let attributes = aggregate.get("attributes").map_or_default(Json::each);
85
164
  let mut arms: Vec<String> = Vec::new();
86
165
 
87
166
  for attr in attributes {
@@ -90,17 +169,47 @@ pub fn emit_fielded_record(exemplar: &Exemplar, aggregate: &Json, value_objects_
90
169
  let scalar = naming::effective_scalar_type(crate::attr::type_name(attr));
91
170
  let list = crate::attr::list(attr);
92
171
 
93
- let arm = if list && crate::shared::list_attr_creation_optional(aggregate, crate::attr::name(attr), value_objects_by_name) {
94
- Some(exemplar.render("fielded_arm_list_optional", &[("\"tmpl_field\"", naming::ruby_inspect_string(&key)), ("tmpl_ident", ident)]))
172
+ let arm = if list
173
+ && crate::shared::list_attr_creation_optional(aggregate, crate::attr::name(attr))
174
+ {
175
+ Some(exemplar.render(
176
+ "fielded_arm_list_optional",
177
+ &[
178
+ ("\"tmpl_field\"", naming::ruby_inspect_string(&key)),
179
+ ("tmpl_ident", ident),
180
+ ],
181
+ ))
95
182
  } else if list {
96
- Some(exemplar.render("fielded_arm_list", &[("\"tmpl_field\"", naming::ruby_inspect_string(&key)), ("tmpl_ident", ident)]))
97
- } else if let Some(scalar) = scalar {
98
183
  Some(exemplar.render(
99
- "fielded_arm_optional_scalar",
100
- &[("\"tmpl_field\"", naming::ruby_inspect_string(&key)), ("tmpl_ident", ident.clone()), ("tmpl_value_expr_placeholder(v)", naming::scalar_to_value(scalar, &naming::deref_scalar(scalar, "v")).unwrap())],
184
+ "fielded_arm_list",
185
+ &[
186
+ ("\"tmpl_field\"", naming::ruby_inspect_string(&key)),
187
+ ("tmpl_ident", ident),
188
+ ],
101
189
  ))
190
+ } else if let Some(scalar) = scalar {
191
+ Some(
192
+ exemplar.render(
193
+ "fielded_arm_optional_scalar",
194
+ &[
195
+ ("\"tmpl_field\"", naming::ruby_inspect_string(&key)),
196
+ ("tmpl_ident", ident.clone()),
197
+ (
198
+ "tmpl_value_expr_placeholder(v)",
199
+ naming::scalar_to_value(scalar, &naming::deref_scalar(scalar, "v"))
200
+ .unwrap(),
201
+ ),
202
+ ],
203
+ ),
204
+ )
102
205
  } else if is_fielded_nested(crate::attr::type_name(attr), value_objects_by_name) {
103
- Some(exemplar.render("fielded_arm_optional_nested", &[("\"tmpl_field\"", naming::ruby_inspect_string(&key)), ("tmpl_ident", ident)]))
206
+ Some(exemplar.render(
207
+ "fielded_arm_optional_nested",
208
+ &[
209
+ ("\"tmpl_field\"", naming::ruby_inspect_string(&key)),
210
+ ("tmpl_ident", ident),
211
+ ],
212
+ ))
104
213
  } else {
105
214
  None
106
215
  };
@@ -114,25 +223,40 @@ pub fn emit_fielded_record(exemplar: &Exemplar, aggregate: &Json, value_objects_
114
223
  let field = lifecycle.get("field").and_then(Json::as_str).unwrap_or("");
115
224
  let key = naming::rust_field(field);
116
225
  let ident = naming::rust_ident_field(field);
117
- arms.push(exemplar.render("fielded_lifecycle_arm", &[("\"tmpl_field\"", naming::ruby_inspect_string(&key)), ("tmpl_ident", ident)]));
226
+ arms.push(exemplar.render(
227
+ "fielded_lifecycle_arm",
228
+ &[
229
+ ("\"tmpl_field\"", naming::ruby_inspect_string(&key)),
230
+ ("tmpl_ident", ident),
231
+ ],
232
+ ));
118
233
  }
119
234
  for ev in crate::bridging::correctable_event_names(aggregate) {
120
235
  let ident = crate::bridging::corrects_flag_field(&ev);
121
- arms.push(exemplar.render("fielded_corrects_flag_arm", &[("\"tmpl_field\"", naming::ruby_inspect_string(&ident)), ("tmpl_ident", ident)]));
236
+ arms.push(exemplar.render(
237
+ "fielded_corrects_flag_arm",
238
+ &[
239
+ ("\"tmpl_field\"", naming::ruby_inspect_string(&ident)),
240
+ ("tmpl_ident", ident),
241
+ ],
242
+ ));
122
243
  }
123
244
 
124
245
  let arms: Vec<String> = arms.iter().map(|a| format!(" {a}")).collect();
125
246
 
126
247
  let entity_names: Vec<String> = aggregate
127
248
  .get("entities")
128
- .map(Json::each)
129
- .unwrap_or(&[])
249
+ .map_or_default(Json::each)
130
250
  .iter()
131
251
  .filter_map(|e| e.get("name").and_then(Json::as_str).map(str::to_string))
132
252
  .collect();
133
- let items = items_arms(exemplar, attributes, value_objects_by_name, &entity_names, |attr| {
134
- crate::shared::list_attr_creation_optional(aggregate, crate::attr::name(attr), value_objects_by_name)
135
- });
253
+ let items = items_arms(
254
+ exemplar,
255
+ attributes,
256
+ value_objects_by_name,
257
+ &entity_names,
258
+ |attr| crate::shared::list_attr_creation_optional(aggregate, crate::attr::name(attr)),
259
+ );
136
260
 
137
261
  format!(
138
262
  "{}\n",
@@ -140,8 +264,14 @@ pub fn emit_fielded_record(exemplar: &Exemplar, aggregate: &Json, value_objects_
140
264
  "fielded_record",
141
265
  &[
142
266
  ("TmplRecordType", name),
143
- ("\"tmpl_arms_placeholder\" => tmpl_arms_block(),", arms.join("\n")),
144
- ("\"tmpl_items_placeholder\" => tmpl_items_block(),", items.join("\n")),
267
+ (
268
+ "\"tmpl_arms_placeholder\" => tmpl_arms_block(),",
269
+ arms.join("\n")
270
+ ),
271
+ (
272
+ "\"tmpl_items_placeholder\" => tmpl_items_block(),",
273
+ items.join("\n")
274
+ ),
145
275
  ("tmpl_as_scalar_placeholder()", as_scalar_expr(attributes)),
146
276
  ],
147
277
  )
@@ -167,7 +297,17 @@ fn as_scalar_expr(attributes: &[Json]) -> String {
167
297
 
168
298
  // One `Fielded::items` arm per list attribute whose element is a scalar, value object or
169
299
  // entity; other lists stay length-only.
170
- fn items_arms(exemplar: &Exemplar, attributes: &[Json], value_objects_by_name: &HashMap<String, &Json>, entity_names: &[String], optional: impl Fn(&Json) -> bool) -> Vec<String> {
300
+ #[allow(
301
+ clippy::unwrap_used,
302
+ reason = "the value was checked just before; None would mean invalid IR"
303
+ )]
304
+ fn items_arms(
305
+ exemplar: &Exemplar,
306
+ attributes: &[Json],
307
+ value_objects_by_name: &HashMap<String, &Json>,
308
+ entity_names: &[String],
309
+ optional: impl Fn(&Json) -> bool,
310
+ ) -> Vec<String> {
171
311
  let mut arms = Vec::new();
172
312
  for attr in attributes {
173
313
  if !crate::attr::list(attr) {
@@ -177,14 +317,26 @@ fn items_arms(exemplar: &Exemplar, attributes: &[Json], value_objects_by_name: &
177
317
  let ident = naming::rust_ident_field(crate::attr::name(attr));
178
318
  let type_name = crate::attr::type_name(attr);
179
319
  let scalar = naming::effective_scalar_type(type_name);
180
- let fielded_element = scalar.is_some() || is_fielded_nested(type_name, value_objects_by_name) || entity_names.iter().any(|e| e == type_name);
320
+ let fielded_element = scalar.is_some()
321
+ || is_fielded_nested(type_name, value_objects_by_name)
322
+ || entity_names.iter().any(|e| e == type_name);
181
323
  if !fielded_element {
182
324
  continue;
183
325
  }
184
- let shape = format!("fielded_items_arm_list_{}{}", if optional(attr) { "optional_" } else { "" }, if scalar.is_some() { "scalar" } else { "nested" });
185
- let mut subs: Vec<(&str, String)> = vec![("\"tmpl_field\"", naming::ruby_inspect_string(&key)), ("tmpl_ident", ident)];
326
+ let shape = format!(
327
+ "fielded_items_arm_list_{}{}",
328
+ if optional(attr) { "optional_" } else { "" },
329
+ if scalar.is_some() { "scalar" } else { "nested" }
330
+ );
331
+ let mut subs: Vec<(&str, String)> = vec![
332
+ ("\"tmpl_field\"", naming::ruby_inspect_string(&key)),
333
+ ("tmpl_ident", ident),
334
+ ];
186
335
  if let Some(scalar) = scalar {
187
- subs.push(("tmpl_value_expr_placeholder(v)", naming::scalar_to_value(scalar, &naming::deref_scalar(scalar, "v")).unwrap()));
336
+ subs.push((
337
+ "tmpl_value_expr_placeholder(v)",
338
+ naming::scalar_to_value(scalar, &naming::deref_scalar(scalar, "v")).unwrap(),
339
+ ));
188
340
  }
189
341
  arms.push(format!(" {}", exemplar.render(&shape, &subs)));
190
342
  }