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
@@ -8,8 +8,12 @@ use crate::subprocess;
8
8
 
9
9
  pub fn bluebook_files(directory: &Path) -> Result<Vec<PathBuf>, String> {
10
10
  let mut files = Vec::new();
11
- for entry in std::fs::read_dir(directory).map_err(|e| format!("reading {}: {e}", directory.display()))? {
12
- let path = entry.map_err(|e| format!("reading {}: {e}", directory.display()))?.path();
11
+ for entry in
12
+ std::fs::read_dir(directory).map_err(|e| format!("reading {}: {e}", directory.display()))?
13
+ {
14
+ let path = entry
15
+ .map_err(|e| format!("reading {}: {e}", directory.display()))?
16
+ .path();
13
17
  if path.extension().and_then(|extension| extension.to_str()) == Some("bluebook") {
14
18
  files.push(path);
15
19
  }
@@ -20,7 +24,8 @@ pub fn bluebook_files(directory: &Path) -> Result<Vec<PathBuf>, String> {
20
24
 
21
25
  // Plain text scan of the `Hecks.bluebook "Name"` line; the file is never executed.
22
26
  pub fn header_chapter_name(path: &Path) -> Result<String, String> {
23
- let text = std::fs::read_to_string(path).map_err(|e| format!("reading {}: {e}", path.display()))?;
27
+ let text =
28
+ std::fs::read_to_string(path).map_err(|e| format!("reading {}: {e}", path.display()))?;
24
29
  for line in text.lines() {
25
30
  let trimmed = line.trim_start();
26
31
  if let Some(rest) = trimmed.strip_prefix("Hecks.bluebook") {
@@ -29,7 +34,10 @@ pub fn header_chapter_name(path: &Path) -> Result<String, String> {
29
34
  }
30
35
  }
31
36
  }
32
- Err(format!("{} has no 'Hecks.bluebook \"Name\"' header", path.display()))
37
+ Err(format!(
38
+ "{} has no 'Hecks.bluebook \"Name\"' header",
39
+ path.display()
40
+ ))
33
41
  }
34
42
 
35
43
  fn extract_quoted(rest: &str) -> Option<String> {
@@ -52,25 +60,17 @@ pub fn framework_members(root: &Path) -> Result<Vec<(String, PathBuf)>, String>
52
60
  if path.extension().and_then(|e| e.to_str()) != Some("bluebook") {
53
61
  continue;
54
62
  }
55
- let stem = path.file_stem().and_then(|s| s.to_str()).unwrap_or("").to_string();
56
- members.push((pascal(&stem), path));
63
+ let stem = path
64
+ .file_stem()
65
+ .and_then(|s| s.to_str())
66
+ .unwrap_or("")
67
+ .to_string();
68
+ members.push((crate::hecks_naming::pascal(&stem), path));
57
69
  }
58
70
  members.sort();
59
71
  Ok(members)
60
72
  }
61
73
 
62
- pub fn pascal(text: &str) -> String {
63
- text.split('_')
64
- .map(|part| {
65
- let mut chars = part.chars();
66
- match chars.next() {
67
- Some(first) => first.to_uppercase().collect::<String>() + chars.as_str(),
68
- None => String::new(),
69
- }
70
- })
71
- .collect()
72
- }
73
-
74
74
  // Discovery, not a mirrored filename list, so a new concept cannot drift from this pipeline.
75
75
  pub fn grammar_files(root: &Path) -> Result<Vec<PathBuf>, String> {
76
76
  let dir = root.join("lib/hecks/language/bluebook");
@@ -78,27 +78,50 @@ pub fn grammar_files(root: &Path) -> Result<Vec<PathBuf>, String> {
78
78
  }
79
79
 
80
80
  // The names `attaches "Name"` gave, for a chapter the gem carries.
81
- pub fn resolve_gem_chapters(parser_bin: &Path, chapter_name: &str, hecksagon_path: &Path) -> Result<Vec<String>, String> {
81
+ pub fn resolve_gem_chapters(
82
+ parser_bin: &Path,
83
+ chapter_name: &str,
84
+ hecksagon_path: &Path,
85
+ ) -> Result<Vec<String>, String> {
82
86
  resolve_json(parser_bin, chapter_name, hecksagon_path, "gem_chapters")
83
87
  }
84
88
 
85
89
  // The names `attaches "name", from: :vendor` gave, for a vendored package.
86
- pub fn resolve_vendored_packages(parser_bin: &Path, chapter_name: &str, hecksagon_path: &Path) -> Result<Vec<String>, String> {
87
- resolve_json(parser_bin, chapter_name, hecksagon_path, "vendored_packages")
90
+ pub fn resolve_vendored_packages(
91
+ parser_bin: &Path,
92
+ chapter_name: &str,
93
+ hecksagon_path: &Path,
94
+ ) -> Result<Vec<String>, String> {
95
+ resolve_json(
96
+ parser_bin,
97
+ chapter_name,
98
+ hecksagon_path,
99
+ "vendored_packages",
100
+ )
88
101
  }
89
102
 
90
103
  // Where the gem keeps the chapters other than framework members, relative to `lib/hecks`; the
91
104
  // same places `Hecks::Chapters::GLOBS` lists.
92
- const OTHER_GEM_CHAPTER_DIRS: &[&str] = &["language", "grammar", "tenancy/bluebook", "deploy/bluebook", "quality_control"];
105
+ const OTHER_GEM_CHAPTER_DIRS: &[&str] = &[
106
+ "language",
107
+ "grammar",
108
+ "tenancy/bluebook",
109
+ "deploy/bluebook",
110
+ "quality_control",
111
+ ];
93
112
 
94
113
  // The names of the chapters the gem carries beside the framework members, found by reading each
95
114
  // file's header. Only the Ruby runtime loads them, so the build uses this list to tell a real
96
115
  // gem chapter from a misspelt name.
97
116
  pub fn other_gem_chapter_names(root: &Path) -> Result<Vec<String>, String> {
98
117
  fn walk(directory: &Path, names: &mut Vec<String>) -> Result<(), String> {
99
- let Ok(entries) = std::fs::read_dir(directory) else { return Ok(()) };
118
+ let Ok(entries) = std::fs::read_dir(directory) else {
119
+ return Ok(());
120
+ };
100
121
  for entry in entries {
101
- let path = entry.map_err(|e| format!("reading {}: {e}", directory.display()))?.path();
122
+ let path = entry
123
+ .map_err(|e| format!("reading {}: {e}", directory.display()))?
124
+ .path();
102
125
  if path.is_dir() {
103
126
  walk(&path, names)?;
104
127
  } else if path.extension().and_then(|e| e.to_str()) == Some("bluebook") {
@@ -118,13 +141,26 @@ pub fn other_gem_chapter_names(root: &Path) -> Result<Vec<String>, String> {
118
141
  Ok(names)
119
142
  }
120
143
 
121
- fn resolve_json(parser_bin: &Path, chapter_name: &str, hecksagon_path: &Path, key: &str) -> Result<Vec<String>, String> {
144
+ fn resolve_json(
145
+ parser_bin: &Path,
146
+ chapter_name: &str,
147
+ hecksagon_path: &Path,
148
+ key: &str,
149
+ ) -> Result<Vec<String>, String> {
122
150
  let parser_bin_str = parser_bin.to_string_lossy().to_string();
123
151
  let hecksagon_str = hecksagon_path.to_string_lossy().to_string();
124
- let out = subprocess::run_capture(&parser_bin_str, &["resolve", "--chapter", chapter_name, &hecksagon_str])?;
125
- let json = Json::parse(&out).map_err(|e| format!("parsing 'hecks-parse resolve' output: {e}\n{out}"))?;
126
- let names = json.get(key).map(Json::each).unwrap_or(&[]);
127
- Ok(names.iter().filter_map(Json::as_str).map(str::to_string).collect())
152
+ let out = subprocess::run_capture(
153
+ &parser_bin_str,
154
+ &["resolve", "--chapter", chapter_name, &hecksagon_str],
155
+ )?;
156
+ let json = Json::parse(&out)
157
+ .map_err(|e| format!("parsing 'hecks-parse resolve' output: {e}\n{out}"))?;
158
+ let names = json.get(key).map_or(&[][..], Json::each);
159
+ Ok(names
160
+ .iter()
161
+ .filter_map(Json::as_str)
162
+ .map(str::to_string)
163
+ .collect())
128
164
  }
129
165
 
130
166
  /// `/\A[a-z][a-z0-9_]*\z/` — the shape `hecks vendor` accepts for a package directory name.
@@ -132,7 +168,8 @@ fn resolve_json(parser_bin: &Path, chapter_name: &str, hecksagon_path: &Path, ke
132
168
  /// so `../x` or an absolute path must never reach either.
133
169
  pub fn valid_package_name(name: &str) -> bool {
134
170
  let mut chars = name.chars();
135
- matches!(chars.next(), Some(c) if c.is_ascii_lowercase()) && chars.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_')
171
+ matches!(chars.next(), Some(c) if c.is_ascii_lowercase())
172
+ && chars.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_')
136
173
  }
137
174
 
138
175
  pub fn vendored_bluebook_files(domain: &Path, pkg_name: &str) -> Result<Vec<PathBuf>, String> {
@@ -141,7 +178,11 @@ pub fn vendored_bluebook_files(domain: &Path, pkg_name: &str) -> Result<Vec<Path
141
178
  "attaches {pkg_name:?}, from: :vendor is not a package name — it must match [a-z][a-z0-9_]* (the name `hecks vendor` accepts)"
142
179
  ));
143
180
  }
144
- let dir = domain.join("vendor").join("embryonaut_bluebooks").join(pkg_name).join("bluebook");
181
+ let dir = domain
182
+ .join("vendor")
183
+ .join("embryonaut_bluebooks")
184
+ .join(pkg_name)
185
+ .join("bluebook");
145
186
  if !dir.is_dir() {
146
187
  return Err(format!(
147
188
  "attaches {pkg_name:?}, from: :vendor names no vendored bluebook at {} — run hecks vendor {pkg_name}",
@@ -169,14 +210,20 @@ mod tests {
169
210
 
170
211
  #[test]
171
212
  fn pascal_cases_snake_case_stems() {
172
- assert_eq!(pascal("governance"), "Governance");
173
- assert_eq!(pascal("console_settings"), "ConsoleSettings");
174
- assert_eq!(pascal("identity"), "Identity");
213
+ assert_eq!(crate::hecks_naming::pascal("governance"), "Governance");
214
+ assert_eq!(
215
+ crate::hecks_naming::pascal("console_settings"),
216
+ "ConsoleSettings"
217
+ );
218
+ assert_eq!(crate::hecks_naming::pascal("identity"), "Identity");
175
219
  }
176
220
 
177
221
  #[test]
178
222
  fn extracts_the_quoted_chapter_name() {
179
223
  assert_eq!(extract_quoted(r#" "Pizzas""#), Some("Pizzas".to_string()));
180
- assert_eq!(extract_quoted(r#" "Console Settings" do"#), Some("Console Settings".to_string()));
224
+ assert_eq!(
225
+ extract_quoted(r#" "Console Settings" do"#),
226
+ Some("Console Settings".to_string())
227
+ );
181
228
  }
182
229
  }
@@ -0,0 +1,100 @@
1
+ //! Lays generated Rust out with rustfmt, as `Hecks::RustFormat` does for `hecks project_rust`, so
2
+ //! the tree `hecks-build` writes matches the one `hecks project_rust` writes, byte for byte.
3
+
4
+ use std::io::Write as _;
5
+ use std::path::{Path, PathBuf};
6
+ use std::process::{Command, Stdio};
7
+
8
+ const EDITION: &str = "2021";
9
+
10
+ // The most passes made; a text still changing after them keeps the last result, as in Ruby.
11
+ const PASSES: usize = 3;
12
+
13
+ /// Formats every `.rs` file of this run's own output: the `mod.rs` at `out_root` and each of
14
+ /// `dirs` beneath it, skipping a directory that is not there. A file is rewritten only when its
15
+ /// layout changes, so mtimes stay put and Cargo does not rebuild what did not change.
16
+ pub fn format_generated(out_root: &Path, dirs: &[String]) -> Result<(), String> {
17
+ let mut files = vec![out_root.join("mod.rs")];
18
+ for dir in dirs {
19
+ collect_rust_files(&out_root.join(dir), &mut files)?;
20
+ }
21
+ for file in files.iter().filter(|f| f.is_file()) {
22
+ format_file(file)?;
23
+ }
24
+ Ok(())
25
+ }
26
+
27
+ fn collect_rust_files(dir: &Path, into: &mut Vec<PathBuf>) -> Result<(), String> {
28
+ let Ok(entries) = std::fs::read_dir(dir) else {
29
+ return Ok(());
30
+ };
31
+ let mut paths: Vec<PathBuf> = entries.filter_map(|e| e.ok().map(|e| e.path())).collect();
32
+ paths.sort();
33
+ for path in paths {
34
+ if path.is_dir() {
35
+ collect_rust_files(&path, into)?;
36
+ } else if path.extension().is_some_and(|ext| ext == "rs") {
37
+ into.push(path);
38
+ }
39
+ }
40
+ Ok(())
41
+ }
42
+
43
+ fn format_file(path: &Path) -> Result<(), String> {
44
+ let text =
45
+ std::fs::read_to_string(path).map_err(|e| format!("reading {}: {e}", path.display()))?;
46
+ let formatted = format_source(&text)?;
47
+ if formatted != text {
48
+ std::fs::write(path, formatted).map_err(|e| format!("writing {}: {e}", path.display()))?;
49
+ }
50
+ Ok(())
51
+ }
52
+
53
+ fn format_source(text: &str) -> Result<String, String> {
54
+ let mut current = text.to_string();
55
+ for _ in 0..PASSES {
56
+ let formatted = pass(&current)?;
57
+ if formatted == current {
58
+ break;
59
+ }
60
+ current = formatted;
61
+ }
62
+ Ok(current)
63
+ }
64
+
65
+ // One rustfmt run over stdin. Text rustfmt cannot parse is returned as it came, with a warning:
66
+ // the compiler will name the real fault. Any other failure of rustfmt is an error.
67
+ fn pass(text: &str) -> Result<String, String> {
68
+ let mut child = Command::new("rustfmt")
69
+ .args(["--edition", EDITION, "--emit", "stdout"])
70
+ .stdin(Stdio::piped())
71
+ .stdout(Stdio::piped())
72
+ .stderr(Stdio::piped())
73
+ .spawn()
74
+ .map_err(|e| {
75
+ format!("running rustfmt: {e} (generated Rust is formatted with it; run `rustup component add rustfmt`)")
76
+ })?;
77
+ let mut stdin = child.stdin.take().ok_or("rustfmt gave no stdin")?;
78
+ let input = text.to_string();
79
+ // Written from a thread: a large file fills the pipe before rustfmt starts reading its output.
80
+ let writer = std::thread::spawn(move || stdin.write_all(input.as_bytes()));
81
+ let output = child
82
+ .wait_with_output()
83
+ .map_err(|e| format!("waiting for rustfmt: {e}"))?;
84
+ let _ = writer.join();
85
+ if output.status.success() {
86
+ return String::from_utf8(output.stdout)
87
+ .map_err(|e| format!("rustfmt output was not UTF-8: {e}"));
88
+ }
89
+ let stderr = String::from_utf8_lossy(&output.stderr);
90
+ let first = stderr.lines().next().unwrap_or_default().to_string();
91
+ // A parse error names `<stdin>`; anything else is rustfmt itself failing, and falling back to
92
+ // the unformatted text would write the wrong bytes without a word.
93
+ if !stderr.contains("<stdin>") {
94
+ return Err(format!("rustfmt failed: {first}"));
95
+ }
96
+ eprintln!(
97
+ "hecks-build: rustfmt could not format generated Rust, leaving it as generated: {first}"
98
+ );
99
+ Ok(text.to_string())
100
+ }
@@ -16,9 +16,27 @@ struct Seam {
16
16
 
17
17
  // In `TargetIr::SEAMS` order, which is the order the keys land in `ir.json`.
18
18
  const SEAMS: &[Seam] = &[
19
- Seam { capability: "authorization", export_as: "authorization", verbs: &["grant", "assignments"], aggregates: &[("assignment_aggregate", "assignments")], marks: &[] },
20
- Seam { capability: "membership", export_as: "membership", verbs: &["admit", "grant", "people"], aggregates: &[("aggregate", "admit")], marks: &[] },
21
- Seam { capability: "identity", export_as: "identity", verbs: &["register", "link", "resolve"], aggregates: &[], marks: &[] },
19
+ Seam {
20
+ capability: "authorization",
21
+ export_as: "authorization",
22
+ verbs: &["grant", "assignments"],
23
+ aggregates: &[("assignment_aggregate", "assignments")],
24
+ marks: &[],
25
+ },
26
+ Seam {
27
+ capability: "membership",
28
+ export_as: "membership",
29
+ verbs: &["admit", "grant", "people"],
30
+ aggregates: &[("aggregate", "admit")],
31
+ marks: &[],
32
+ },
33
+ Seam {
34
+ capability: "identity",
35
+ export_as: "identity",
36
+ verbs: &["register", "link", "resolve"],
37
+ aggregates: &[],
38
+ marks: &[],
39
+ },
22
40
  Seam {
23
41
  capability: "newsletter",
24
42
  export_as: "newsletter",
@@ -30,21 +48,41 @@ const SEAMS: &[Seam] = &[
30
48
  capability: "newsletter_issues",
31
49
  export_as: "newsletter_issues",
32
50
  verbs: &["send_issue", "record_delivery"],
33
- aggregates: &[("issue_aggregate", "send_issue"), ("delivery_aggregate", "record_delivery")],
51
+ aggregates: &[
52
+ ("issue_aggregate", "send_issue"),
53
+ ("delivery_aggregate", "record_delivery"),
54
+ ],
34
55
  marks: &[],
35
56
  },
36
- Seam { capability: "payments", export_as: "payments", verbs: &["initiate", "succeeded", "failed"], aggregates: &[("aggregate", "initiate")], marks: &["holds_seat"] },
57
+ Seam {
58
+ capability: "payments",
59
+ export_as: "payments",
60
+ verbs: &["initiate", "succeeded", "failed"],
61
+ aggregates: &[("aggregate", "initiate")],
62
+ marks: &["holds_seat"],
63
+ },
37
64
  Seam {
38
65
  capability: "registrations",
39
66
  export_as: "registrations",
40
67
  verbs: &["schedule", "request"],
41
- aggregates: &[("event_aggregate", "schedule"), ("registration_aggregate", "request")],
68
+ aggregates: &[
69
+ ("event_aggregate", "schedule"),
70
+ ("registration_aggregate", "request"),
71
+ ],
42
72
  marks: &["frees_seat"],
43
73
  },
44
74
  Seam {
45
75
  capability: "payment_connection",
46
76
  export_as: "payment_connection",
47
- verbs: &["connect", "reconnect", "disconnect", "suspend", "resume", "enable", "disable"],
77
+ verbs: &[
78
+ "connect",
79
+ "reconnect",
80
+ "disconnect",
81
+ "suspend",
82
+ "resume",
83
+ "enable",
84
+ "disable",
85
+ ],
48
86
  aggregates: &[("aggregate", "connect")],
49
87
  marks: &[],
50
88
  },
@@ -60,7 +98,12 @@ pub fn run(target: &mut Json, members: &[Json]) -> Result<(), String> {
60
98
  let mut membership = false;
61
99
  let mut identity = false;
62
100
  for seam in SEAMS {
63
- let Some(provider) = chapters.iter().find(|chapter| provision(chapter, seam.capability).is_some()) else { continue };
101
+ let Some(provider) = chapters
102
+ .iter()
103
+ .find(|chapter| provision(chapter, seam.capability).is_some())
104
+ else {
105
+ continue;
106
+ };
64
107
  let exported = export(seam, provider);
65
108
  membership |= seam.capability == "membership";
66
109
  identity |= seam.capability == "identity";
@@ -79,7 +122,12 @@ pub fn run(target: &mut Json, members: &[Json]) -> Result<(), String> {
79
122
  fn export(seam: &Seam, provider: &Json) -> Json {
80
123
  let name = name_of(provider);
81
124
  let entries = provision(provider, seam.capability).unwrap_or_default();
82
- let verb = |key: &str| entries.iter().find(|(k, _)| k == key).map(|(_, v)| format!("{name}::{v}"));
125
+ let verb = |key: &str| {
126
+ entries
127
+ .iter()
128
+ .find(|(k, _)| k == key)
129
+ .map(|(_, v)| format!("{name}::{v}"))
130
+ };
83
131
 
84
132
  let mut out = vec![("provider".to_string(), Json::String(name.clone()))];
85
133
  for key in seam.verbs {
@@ -87,10 +135,19 @@ fn export(seam: &Seam, provider: &Json) -> Json {
87
135
  }
88
136
  for (exported, key) in seam.aggregates {
89
137
  let aggregate = verb(key).map(|v| v.split('.').next().unwrap_or_default().to_string());
90
- out.push((exported.to_string(), aggregate.map_or(Json::Null, Json::String)));
138
+ out.push((
139
+ exported.to_string(),
140
+ aggregate.map_or(Json::Null, Json::String),
141
+ ));
91
142
  }
92
143
  for key in seam.marks {
93
- if let Some(states) = marked_states(provider, entries.iter().find(|(k, _)| k == key).map(|(_, v)| v.as_str())) {
144
+ if let Some(states) = marked_states(
145
+ provider,
146
+ entries
147
+ .iter()
148
+ .find(|(k, _)| k == key)
149
+ .map(|(_, v)| v.as_str()),
150
+ ) {
94
151
  out.push((key.to_string(), states));
95
152
  }
96
153
  }
@@ -101,11 +158,15 @@ fn export(seam: &Seam, provider: &Json) -> Json {
101
158
  fn provision(chapter: &Json, capability: &str) -> Option<Vec<(String, String)>> {
102
159
  let entries: Vec<(String, String)> = chapter
103
160
  .get("provides")
104
- .map(Json::each)
105
- .unwrap_or(&[])
161
+ .map_or(&[][..], Json::each)
106
162
  .iter()
107
163
  .filter(|entry| entry.get("capability").and_then(Json::as_str) == Some(capability))
108
- .filter_map(|entry| Some((entry.get("key")?.as_str()?.to_string(), entry.get("verb")?.as_str()?.to_string())))
164
+ .filter_map(|entry| {
165
+ Some((
166
+ entry.get("key")?.as_str()?.to_string(),
167
+ entry.get("verb")?.as_str()?.to_string(),
168
+ ))
169
+ })
109
170
  .collect();
110
171
  (!entries.is_empty()).then_some(entries)
111
172
  }
@@ -114,10 +175,18 @@ fn provision(chapter: &Json, capability: &str) -> Option<Vec<(String, String)>>
114
175
  // entry is not declared or the mark is.
115
176
  fn marked_states(provider: &Json, entry: Option<&str>) -> Option<Json> {
116
177
  let (aggregate_name, mark) = entry?.split_once('.')?;
117
- let aggregate = provider.get("aggregates").map(Json::each)?.iter().find(|a| a.get("name").and_then(Json::as_str) == Some(aggregate_name))?;
178
+ let aggregate = provider
179
+ .get("aggregates")
180
+ .map(Json::each)?
181
+ .iter()
182
+ .find(|a| a.get("name").and_then(Json::as_str) == Some(aggregate_name))?;
118
183
  aggregate.get("lifecycle")?.get("marks")?.get(mark).cloned()
119
184
  }
120
185
 
121
186
  fn name_of(chapter: &Json) -> String {
122
- chapter.get("name").and_then(Json::as_str).unwrap_or_default().to_string()
187
+ chapter
188
+ .get("name")
189
+ .and_then(Json::as_str)
190
+ .unwrap_or_default()
191
+ .to_string()
123
192
  }
@@ -5,7 +5,10 @@ use std::path::Path;
5
5
  use std::process::{Command, Stdio};
6
6
 
7
7
  pub fn run_capture(program: &str, args: &[&str]) -> Result<String, String> {
8
- let output = Command::new(program).args(args).output().map_err(|e| format!("running {program} {}: {e}", args.join(" ")))?;
8
+ let output = Command::new(program)
9
+ .args(args)
10
+ .output()
11
+ .map_err(|e| format!("running {program} {}: {e}", args.join(" ")))?;
9
12
  if !output.status.success() {
10
13
  return Err(format!(
11
14
  "{program} {} failed:\n{}",
@@ -13,11 +16,19 @@ pub fn run_capture(program: &str, args: &[&str]) -> Result<String, String> {
13
16
  String::from_utf8_lossy(&output.stderr)
14
17
  ));
15
18
  }
16
- String::from_utf8(output.stdout).map_err(|e| format!("{program} {}: stdout was not valid UTF-8: {e}", args.join(" ")))
19
+ String::from_utf8(output.stdout).map_err(|e| {
20
+ format!(
21
+ "{program} {}: stdout was not valid UTF-8: {e}",
22
+ args.join(" ")
23
+ )
24
+ })
17
25
  }
18
26
 
19
27
  pub fn run(program: &str, args: &[&str]) -> Result<(), String> {
20
- let status = Command::new(program).args(args).status().map_err(|e| format!("running {program} {}: {e}", args.join(" ")))?;
28
+ let status = Command::new(program)
29
+ .args(args)
30
+ .status()
31
+ .map_err(|e| format!("running {program} {}: {e}", args.join(" ")))?;
21
32
  if !status.success() {
22
33
  return Err(format!("{program} {} failed", args.join(" ")));
23
34
  }
@@ -29,9 +40,19 @@ pub fn run_in(dir: &Path, program: &str, args: &[&str]) -> Result<(), String> {
29
40
  .args(args)
30
41
  .current_dir(dir)
31
42
  .status()
32
- .map_err(|e| format!("running {program} {} in {}: {e}", args.join(" "), dir.display()))?;
43
+ .map_err(|e| {
44
+ format!(
45
+ "running {program} {} in {}: {e}",
46
+ args.join(" "),
47
+ dir.display()
48
+ )
49
+ })?;
33
50
  if !status.success() {
34
- return Err(format!("{program} {} failed in {}", args.join(" "), dir.display()));
51
+ return Err(format!(
52
+ "{program} {} failed in {}",
53
+ args.join(" "),
54
+ dir.display()
55
+ ));
35
56
  }
36
57
  Ok(())
37
58
  }
@@ -45,7 +66,10 @@ pub fn cargo_build_quiet(dir: &Path) -> Result<(), String> {
45
66
  .status()
46
67
  .map_err(|e| format!("running cargo build in {}: {e}", dir.display()))?;
47
68
  if !status.success() {
48
- return Err(format!("cargo build failed in {} — run it there directly to see why", dir.display()));
69
+ return Err(format!(
70
+ "cargo build failed in {} — run it there directly to see why",
71
+ dir.display()
72
+ ));
49
73
  }
50
74
  Ok(())
51
75
  }
@@ -13,9 +13,12 @@ impl TempDir {
13
13
  pub fn new(prefix: &str) -> Result<Self, String> {
14
14
  let base = std::env::temp_dir();
15
15
  let pid = std::process::id();
16
- let nanos = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).map(|d| d.as_nanos()).unwrap_or(0);
16
+ let nanos = std::time::SystemTime::now()
17
+ .duration_since(std::time::UNIX_EPOCH)
18
+ .map_or(0, |d| d.as_nanos());
17
19
  let path = base.join(format!("{prefix}-{pid}-{nanos}"));
18
- std::fs::create_dir_all(&path).map_err(|e| format!("creating temp dir {}: {e}", path.display()))?;
20
+ std::fs::create_dir_all(&path)
21
+ .map_err(|e| format!("creating temp dir {}: {e}", path.display()))?;
19
22
  Ok(Self { path })
20
23
  }
21
24