hecks 3.14.0 → 5.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.
- checksums.yaml +4 -4
- data/examples/pizzas/adapters/fake_processor.rb +34 -0
- data/examples/pizzas/adapters/payment_signature.rb +66 -0
- data/examples/pizzas/adapters/payment_webhook.rb +87 -0
- data/examples/pizzas/bluebook/pizzas.behaviors +33 -0
- data/examples/pizzas/bluebook/pizzas.hecksagon +5 -3
- data/examples/pizzas/config.ru +22 -0
- data/exe/hecks +1 -1
- data/lib/hecks/adapters/driven/prism.rb +0 -2
- data/lib/hecks/adapters/driven.rb +0 -2
- data/lib/hecks/adapters/driving/json.rb +9 -16
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +2 -2
- data/lib/hecks/bluebook/dsl/derivation_words.rb +58 -0
- data/lib/hecks/bluebook/dsl/translation_builder.rb +6 -37
- data/lib/hecks/bluebook/expression/ast_json.rb +0 -9
- data/lib/hecks/bluebook/hecksagon.rb +1 -1
- data/lib/hecks/bluebook/meta_validator/translation_judge/rules.rb +7 -10
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +4 -8
- data/lib/hecks/bluebook/meta_validator.rb +0 -11
- data/lib/hecks/bluebook/translation.rb +18 -10
- data/lib/hecks/bluebook/translation_expression/evaluator.rb +63 -0
- data/lib/hecks/bluebook/translation_expression/parser.rb +127 -0
- data/lib/hecks/bluebook/translation_expression.rb +39 -0
- data/lib/hecks/bluebook/translation_plan.rb +70 -0
- data/lib/hecks/bluebook.rb +2 -0
- data/lib/hecks/chapters.rb +3 -3
- data/lib/hecks/cli/project_cli/launcher_source.rb +1 -1
- data/lib/hecks/composer/adapters/claude_composer.adapter +3 -0
- data/lib/hecks/composer/adapters/claude_composer.rb +83 -0
- data/lib/hecks/composer/adapters/fake_agent.adapter +3 -0
- data/lib/hecks/composer/adapters/fake_agent.rb +55 -0
- data/lib/hecks/composer/adapters/jev.adapter +3 -0
- data/lib/hecks/composer/adapters/jev.rb +66 -0
- data/lib/hecks/composer/agent.rb +113 -0
- data/lib/hecks/composer/assistant.rb +110 -0
- data/lib/hecks/composer/behavior_run.rb +53 -0
- data/lib/hecks/composer/bluebook/composer.bluebook +178 -0
- data/lib/hecks/composer/bluebook/composer.hecksagon +16 -0
- data/lib/hecks/composer/board.rb +65 -0
- data/lib/hecks/composer/board_edit/command_text.rb +39 -0
- data/lib/hecks/composer/board_edit/source.rb +84 -0
- data/lib/hecks/composer/board_edit.rb +144 -0
- data/lib/hecks/composer/check.rb +89 -0
- data/lib/hecks/composer/domains.rb +81 -0
- data/lib/hecks/composer/jobs.rb +134 -0
- data/lib/hecks/composer/layout.rb +37 -0
- data/lib/hecks/composer/live.rb +150 -0
- data/lib/hecks/composer/outline.rb +48 -0
- data/lib/hecks/composer/page.html +989 -0
- data/lib/hecks/composer/panels.rb +142 -0
- data/lib/hecks/composer/parse.rb +83 -0
- data/lib/hecks/composer/plain.rb +28 -0
- data/lib/hecks/composer/ports/composer_agent.port +7 -0
- data/lib/hecks/composer/session.rb +141 -0
- data/lib/hecks/composer/toolbox.rb +166 -0
- data/lib/hecks/composer/web.rb +156 -0
- data/lib/hecks/composer/workbench.rb +51 -0
- data/lib/hecks/composer/workspace.rb +81 -0
- data/lib/hecks/composer.rb +17 -0
- data/lib/hecks/corpus/adapters.rb +22 -0
- data/lib/hecks/corpus/rust.rb +1 -1
- data/lib/hecks/corpus/worlds.rb +19 -0
- data/lib/hecks/corpus.rb +5 -0
- data/lib/hecks/forms/app/commands.rb +2 -3
- data/lib/hecks/forms/app/queries.rb +2 -3
- data/lib/hecks/forms/app/records.rb +17 -5
- data/lib/hecks/forms/app/responses.rb +3 -1
- data/lib/hecks/forms/command_form_renderer.rb +42 -16
- data/lib/hecks/forms/field_controls.rb +2 -2
- data/lib/hecks/forms/field_renderer.rb +1 -1
- data/lib/hecks/forms/field_shape.rb +36 -17
- data/lib/hecks/forms/index_renderer.rb +2 -1
- data/lib/hecks/forms/page.rb +1 -1
- data/lib/hecks/forms/params.rb +0 -13
- data/lib/hecks/forms/query_form_renderer.rb +7 -6
- data/lib/hecks/forms/record_renderer.rb +11 -7
- data/lib/hecks/forms/rule_words.rb +100 -0
- data/lib/hecks/forms/value_object_shape.rb +2 -1
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +0 -3
- data/lib/hecks/fuzzing/isolated_boot/worlds.rb +3 -6
- data/lib/hecks/fuzzing/replay/filters.rb +2 -6
- data/lib/hecks/gate/stages.yml +25 -0
- data/lib/hecks/grammar/evolve/seeds.rb +0 -6
- data/lib/hecks/grammar/evolve.rb +0 -3
- data/lib/hecks/grammar/translation.bluebook +1 -1
- data/lib/hecks/hecks/adapters/codebase/composing.rb +56 -0
- data/lib/hecks/hecks/adapters/codebase/source_tree.rb +3 -1
- data/lib/hecks/hecks/adapters/first_admin.rb +3 -3
- data/lib/hecks/hecks/adapters/journal_store/changes.rb +0 -26
- data/lib/hecks/hecks/adapters/journal_store/examination.rb +2 -23
- data/lib/hecks/hecks/adapters/journal_store/readings.rb +3 -11
- data/lib/hecks/hecks/adapters/journal_store.rb +0 -11
- data/lib/hecks/hecks/adapters/rehearsal/inquiry.rb +4 -11
- data/lib/hecks/hecks/adapters/rehearsal/memory_store.rb +0 -9
- data/lib/hecks/hecks/adapters/rehearsal/postgres_reading.rb +0 -4
- data/lib/hecks/hecks/codebase.behaviors +6 -0
- data/lib/hecks/hecks/codebase.bluebook +21 -0
- data/lib/hecks/hecks/composer.behaviors +68 -0
- data/lib/hecks/hecks/context_map.hecksagon +1 -1
- data/lib/hecks/hecks/custodian.behaviors +0 -6
- data/lib/hecks/hecks/custodian.bluebook +3 -80
- data/lib/hecks/hecks/hecks.hecksagon +2 -2
- data/lib/hecks/hecks/hecks.world +1 -1
- data/lib/hecks/hecks/rehearsal.bluebook +7 -13
- data/lib/hecks/kernel_capabilities/templates.rb +4 -4
- data/lib/hecks/kernel_capabilities.rb +1 -1
- data/lib/hecks/language/bluebook/vocabulary.bluebook +2 -3
- data/lib/hecks/language/translation/translation_aggregate.bluebook +21 -24
- data/lib/hecks/ports/access_control.rb +2 -2
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +3 -41
- data/lib/hecks/ports/persistence/plugins/era/lineage/coverage.rb +7 -7
- data/lib/hecks/ports/persistence/plugins/era/lineage/rewrite.rb +25 -2
- data/lib/hecks/ports/persistence/plugins/era/lineage/rules.rb +4 -4
- data/lib/hecks/ports/persistence/plugins/era/lineage.rb +17 -25
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/base_sql.rb +0 -12
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/domain_rename.rb +1 -1
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +2 -22
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +0 -1
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +9 -3
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +3 -29
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/refusals.rb +2 -26
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +6 -33
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +3 -3
- data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +2 -4
- data/lib/hecks/ports/persistence/plugins/era/translation/expression_sql.rb +52 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +23 -29
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +1 -1
- data/lib/hecks/ports/persistence/plugins/era/translation.rb +0 -1
- data/lib/hecks/ports/persistence/plugins/era.rb +3 -6
- data/lib/hecks/ports.rb +0 -1
- data/lib/hecks/projection_files.rb +3 -1
- data/lib/hecks/projections/deploy/templates/lambda/nat_comment_oauth.tmpl +1 -1
- data/lib/hecks/projections/glossary/entries.rb +10 -4
- data/lib/hecks/projections/glossary/groups.rb +42 -0
- data/lib/hecks/projections/glossary/html/page.rb +7 -2
- data/lib/hecks/projections/glossary/html/parser.rb +1 -1
- data/lib/hecks/projections/glossary/html/sections.rb +31 -11
- data/lib/hecks/projections/glossary/html.rb +1 -2
- data/lib/hecks/projections/glossary/markdown.rb +31 -28
- data/lib/hecks/projections/glossary/mermaid.rb +3 -0
- data/lib/hecks/projections/glossary/page.css +143 -153
- data/lib/hecks/projections/glossary/page.js +63 -3
- data/lib/hecks/projections/glossary/sentences/actions.rb +88 -0
- data/lib/hecks/projections/glossary/sentences/paths.rb +77 -0
- data/lib/hecks/projections/glossary/sentences/queries.rb +90 -0
- data/lib/hecks/projections/glossary/sentences/value_objects.rb +32 -5
- data/lib/hecks/projections/glossary/sentences.rb +13 -3
- data/lib/hecks/projections/glossary/slugs.rb +4 -1
- data/lib/hecks/projections/glossary.rb +2 -1
- data/lib/hecks/projections/plain_english.rb +97 -0
- data/lib/hecks/projections/rust_vocabulary/arguments.rb +2 -2
- data/lib/hecks/projections/rust_vocabulary/enum_body.rb +20 -25
- data/lib/hecks/projections/rust_vocabulary/tests.rb +2 -2
- data/lib/hecks/projections/rust_vocabulary.rb +14 -6
- data/lib/hecks/projections/site/cms_editor/color.rb +0 -6
- data/lib/hecks/projections/site/cms_editor/templates/src/host.ts.tmpl +7 -2
- data/lib/hecks/projections/statements/actions.rb +99 -0
- data/lib/hecks/projections/statements/relationships.rb +86 -0
- data/lib/hecks/projections/statements.rb +15 -24
- data/lib/hecks/projector/exporter/adapters.rb +112 -0
- data/lib/hecks/projector/exporter/translations.rb +18 -35
- data/lib/hecks/projector/exporter/worlds.rb +103 -0
- data/lib/hecks/projector/exporter.rb +5 -1
- data/lib/hecks/quality_control/adapters/agent.rb +6 -7
- data/lib/hecks/quality_control/adapters/agent_profile.rb +16 -114
- data/lib/hecks/quality_control/cli/qa_mine_combinations.rb +1 -1
- data/lib/hecks/runtime/loader/boot_steps.rb +0 -20
- data/lib/hecks/runtime/outbox.rb +2 -9
- data/lib/hecks/runtime/port_operation_interpreter.rb +1 -9
- data/lib/hecks/runtime/remote_dispatcher.rb +2 -2
- data/lib/hecks/runtime/value/unwrapping.rb +60 -0
- data/lib/hecks/runtime/value.rb +10 -21
- data/lib/hecks/rust_build/project_rust/target_ir.rb +23 -4
- data/lib/hecks/rust_build/wasm.rb +0 -12
- data/lib/hecks/rust_build/write_if_changed.rb +12 -12
- data/lib/hecks/rust_format.rb +77 -0
- data/lib/hecks/tools/fuzz_sweep/findings.rb +3 -9
- data/lib/hecks/tools/fuzz_sweep.rb +1 -3
- data/lib/hecks/tools/translation_audit/verdicts.rb +3 -28
- data/lib/hecks/tools/translation_audit.rb +8 -10
- data/lib/hecks/tools/translation_scaffold/reporting.rb +1 -1
- data/lib/hecks/version.rb +1 -1
- data/lib/hecks/vocabulary.rb +2 -1
- data/lib/hecks.rb +3 -0
- data/prebuilt/{1a6f596be124228be93eba1d86dbc9d2933190fa389b284331241cb9f27115a0.marshal → ad68f9e06c8068c2a4176b57abf3083d591d5d8980c2b3e897b97331f965b643.marshal} +0 -0
- data/prebuilt/{a883a7e568667aa5ac904c82daff356bdf9e996252a36af6def4a4a43efb967e.marshal → ee22ec8642c4006717ba2b80f46e5efb81de459eef9dfec043e838c0ad4fd720.marshal} +0 -0
- data/prebuilt/{verdicts-872993b1aa26d73fe48f28251374b8585fed145d02aab42be0f00de386b6ff80.json → verdicts-b69f5dcbba79c20daa0493476fa7c317004a30aa2bc3d3dc6a722ea70ae2b43b.json} +1 -1
- data/rust/Cargo.toml +40 -0
- data/rust/build/Cargo.toml +36 -0
- data/rust/build/clippy.toml +4 -0
- data/rust/build/src/adapters_pass.rs +196 -0
- data/rust/build/src/binding_pass.rs +54 -13
- data/rust/build/src/build_artifact.rs +23 -5
- data/rust/build/src/cargo_sync.rs +69 -26
- data/rust/build/src/lineage_pass.rs +101 -87
- data/rust/build/src/main.rs +11 -2
- data/rust/build/src/pipeline.rs +80 -19
- data/rust/build/src/reserved_names.rs +6 -50
- data/rust/build/src/resolve.rs +82 -35
- data/rust/build/src/rustfmt.rs +100 -0
- data/rust/build/src/seams_pass.rs +85 -16
- data/rust/build/src/subprocess.rs +30 -6
- data/rust/build/src/tmp.rs +5 -2
- data/rust/build/src/translation_pass.rs +72 -22
- data/rust/build/src/world_pass.rs +109 -0
- data/rust/clippy.toml +4 -0
- data/rust/codegen/Cargo.toml +36 -0
- data/rust/codegen/clippy.toml +4 -0
- data/rust/codegen/src/asks.rs +40 -14
- data/rust/codegen/src/attr.rs +2 -2
- data/rust/codegen/src/bridging.rs +676 -117
- data/rust/codegen/src/commands.rs +979 -373
- data/rust/codegen/src/constraints.rs +88 -25
- data/rust/codegen/src/dependency_planning.rs +128 -34
- data/rust/codegen/src/domain_generator.rs +306 -170
- data/rust/codegen/src/exemplar.rs +87 -32
- data/rust/codegen/src/expr_emitter.rs +54 -6
- data/rust/codegen/src/fielded.rs +193 -41
- data/rust/codegen/src/hecks_naming.rs +124 -16
- data/rust/codegen/src/hecks_naming_tests.rs +81 -0
- data/rust/codegen/src/json.rs +80 -44
- data/rust/codegen/src/json_codec.rs +585 -149
- data/rust/codegen/src/literal.rs +104 -138
- data/rust/codegen/src/main.rs +151 -52
- data/rust/codegen/src/manifest.rs +76 -14
- data/rust/codegen/src/mutations.rs +1212 -239
- data/rust/codegen/src/naming.rs +111 -60
- data/rust/codegen/src/optional_pass.rs +90 -28
- data/rust/codegen/src/ports.rs +5 -4
- data/rust/codegen/src/prelude.rs +139 -49
- data/rust/codegen/src/presenters.rs +335 -0
- data/rust/codegen/src/queries.rs +601 -226
- data/rust/codegen/src/reactions.rs +268 -74
- data/rust/codegen/src/read_models.rs +574 -229
- data/rust/codegen/src/reference_specs.rs +16 -3
- data/rust/codegen/src/registry.rs +128 -79
- data/rust/codegen/src/reserved_names.rs +6 -50
- data/rust/codegen/src/shared.rs +71 -22
- data/rust/codegen/src/sidecars.rs +15 -5
- data/rust/codegen/src/skip_reason.rs +8 -2
- data/rust/codegen/src/types.rs +306 -108
- data/rust/host/Cargo.lock +1 -0
- data/rust/host/Cargo.toml +40 -4
- data/rust/host/HECKS_RELEASE +1 -1
- data/rust/host/clippy.toml +4 -0
- data/rust/host/src/api.rs +698 -218
- data/rust/host/src/auth.rs +491 -214
- data/rust/host/src/b64.rs +91 -0
- data/rust/host/src/bin/expr_harness.rs +6 -2
- data/rust/host/src/bin/lineage_harness.rs +47 -11
- data/rust/host/src/bin/mint_harness.rs +70 -37
- data/rust/host/src/bin/validate_harness.rs +16 -8
- data/rust/host/src/boot.rs +144 -68
- data/rust/host/src/boundary_fuzz/api.rs +130 -32
- data/rust/host/src/boundary_fuzz/auth.rs +108 -23
- data/rust/host/src/boundary_fuzz/expr_json.rs +85 -11
- data/rust/host/src/boundary_fuzz/ir.rs +164 -12
- data/rust/host/src/boundary_fuzz/server.rs +283 -36
- data/rust/host/src/boundary_fuzz/web.rs +56 -8
- data/rust/host/src/checkout.rs +78 -32
- data/rust/host/src/clock.rs +8 -0
- data/rust/host/src/commerce_ir.rs +333 -95
- data/rust/host/src/crypto.rs +33 -0
- data/rust/host/src/dispatch.rs +1037 -331
- data/rust/host/src/expr_json.rs +1021 -273
- data/rust/host/src/extension.rs +24 -4
- data/rust/host/src/field_hints.rs +11 -7
- data/rust/host/src/fuzz_support.rs +85 -13
- data/rust/host/src/host.rs +100 -0
- data/rust/host/src/http.rs +22 -0
- data/rust/host/src/ir.rs +326 -16
- data/rust/host/src/journal.rs +277 -176
- data/rust/host/src/lambda_client.rs +219 -53
- data/rust/host/src/lib.rs +16 -4
- data/rust/host/src/log.rs +62 -11
- data/rust/host/src/mint.rs +1153 -326
- data/rust/host/src/needs.rs +185 -44
- data/rust/host/src/payments/keystore.rs +197 -45
- data/rust/host/src/payments/roles.rs +39 -7
- data/rust/host/src/payments/tests.rs +1427 -282
- data/rust/host/src/payments.rs +391 -112
- data/rust/host/src/platform_signin/attack_tests.rs +5 -5
- data/rust/host/src/platform_signin/config.rs +11 -11
- data/rust/host/src/platform_signin/cookie.rs +2 -2
- data/rust/host/src/platform_signin/extension.rs +2 -1
- data/rust/host/src/platform_signin/handler.rs +9 -8
- data/rust/host/src/platform_signin/jti.rs +4 -2
- data/rust/host/src/platform_signin/jwks.rs +2 -1
- data/rust/host/src/platform_signin/jws.rs +2 -37
- data/rust/host/src/platform_signin/known_answer_tests.rs +5 -1
- data/rust/host/src/platform_signin/mode_tests.rs +5 -3
- data/rust/host/src/platform_signin/pages.rs +20 -20
- data/rust/host/src/platform_signin/session.rs +8 -1
- data/rust/host/src/platform_signin/settings.rs +6 -6
- data/rust/host/src/platform_signin/strict_json.rs +12 -9
- data/rust/host/src/platform_signin/verify.rs +41 -41
- data/rust/host/src/platform_signin/wiring_support.rs +13 -18
- data/rust/host/src/presentation.rs +207 -63
- data/rust/host/src/presentation_write.rs +502 -187
- data/rust/host/src/query_step.rs +69 -18
- data/rust/host/src/rate_limit.rs +541 -142
- data/rust/host/src/reference_transform.rs +77 -25
- data/rust/host/src/reference_validate.rs +443 -104
- data/rust/host/src/resend.rs +190 -55
- data/rust/host/src/secrets.rs +116 -21
- data/rust/host/src/server.rs +555 -104
- data/rust/host/src/storage_shape.rs +88 -21
- data/rust/host/src/test_pg.rs +17 -7
- data/rust/host/src/ui_schema.rs +495 -207
- data/rust/host/src/wasm_runner.rs +21 -6
- data/rust/host/src/web/commerce.rs +101 -23
- data/rust/host/src/web/newsletter/cooldown.rs +10 -3
- data/rust/host/src/web/newsletter/template.rs +26 -7
- data/rust/host/src/web/newsletter/tests.rs +112 -23
- data/rust/host/src/web/newsletter.rs +209 -59
- data/rust/host/src/web/newsletter_send.rs +251 -79
- data/rust/host/src/web/registration_receipt/tests.rs +16 -4
- data/rust/host/src/web/registration_receipt.rs +100 -24
- data/rust/host/src/web/registrations.rs +1384 -332
- data/rust/host/src/web/resend_webhook.rs +138 -80
- data/rust/host/src/web.rs +2416 -508
- data/rust/lsp/Cargo.toml +36 -0
- data/rust/lsp/clippy.toml +4 -0
- data/rust/lsp/src/json.rs +29 -22
- data/rust/lsp/src/main.rs +81 -31
- data/rust/lsp/src/outline.rs +23 -7
- data/rust/lsp/src/rpc.rs +17 -4
- data/rust/parser/Cargo.toml +36 -0
- data/rust/parser/clippy.toml +4 -0
- data/rust/parser/src/build/closed_sets.rs +6 -1
- data/rust/parser/src/build/identity.rs +4 -3
- data/rust/parser/src/build/mod.rs +1 -1
- data/rust/parser/src/build/named_pattern.rs +4 -1
- data/rust/parser/src/build/naming.rs +2 -80
- data/rust/parser/src/build/pattern_subset.rs +6 -8
- data/rust/parser/src/build/query_derive.rs +3 -1
- data/rust/parser/src/build/query_inference.rs +14 -5
- data/rust/parser/src/build/read_model.rs +4 -1
- data/rust/parser/src/build/references.rs +4 -3
- data/rust/parser/src/canonical.rs +34 -15
- data/rust/parser/src/emit.rs +187 -105
- data/rust/parser/src/expr/ast_json.rs +134 -42
- data/rust/parser/src/expr/evaluator.rs +27 -9
- data/rust/parser/src/expr/mod.rs +54 -13
- data/rust/parser/src/expr/resolver.rs +299 -92
- data/rust/parser/src/ir.rs +35 -3
- data/rust/parser/src/keywords.rs +7168 -516
- data/rust/parser/src/lex.rs +11 -25
- data/rust/parser/src/lib.rs +1 -0
- data/rust/parser/src/main.rs +65 -6
- data/rust/parser/src/parse/adapter.rs +157 -0
- data/rust/parser/src/parse/aggregate.rs +19 -10
- data/rust/parser/src/parse/chapter.rs +16 -17
- data/rust/parser/src/parse/command.rs +35 -16
- data/rust/parser/src/parse/domain_port.rs +4 -7
- data/rust/parser/src/parse/entity.rs +21 -11
- data/rust/parser/src/parse/hecksagon.rs +10 -4
- data/rust/parser/src/parse/lifecycle.rs +9 -5
- data/rust/parser/src/parse/mod.rs +22 -91
- data/rust/parser/src/parse/needs.rs +21 -12
- data/rust/parser/src/parse/policy.rs +2 -2
- data/rust/parser/src/parse/process_manager.rs +9 -11
- data/rust/parser/src/parse/query.rs +8 -4
- data/rust/parser/src/parse/read_model.rs +57 -16
- data/rust/parser/src/parse/value_object.rs +7 -2
- data/rust/parser/src/parse/world.rs +322 -0
- data/rust/parser/src/ruby_value.rs +7 -6
- data/rust/src/exemplar/json.rs +4 -4
- data/rust/src/exemplar/mutations.rs +18 -0
- data/rust/src/exemplar/read_models.rs +1 -10
- data/rust/src/kernel/attribute_shapes/composite.rs +54 -14
- data/rust/src/kernel/attribute_shapes/list.rs +2 -2
- data/rust/src/kernel/attribute_shapes/mod.rs +8 -8
- data/rust/src/kernel/attribute_shapes/scalar.rs +2 -2
- data/rust/src/kernel/cli.rs +244 -114
- data/rust/src/kernel/dispatch.rs +323 -115
- data/rust/src/kernel/dispatch_guard_tests.rs +327 -65
- data/rust/src/kernel/expr.rs +203 -86
- data/rust/src/kernel/expression_operators/arithmetic.rs +138 -25
- data/rust/src/kernel/expression_operators/comparison.rs +87 -2
- data/rust/src/kernel/expression_operators/enumeration.rs +106 -22
- data/rust/src/kernel/expression_operators/logical.rs +9 -3
- data/rust/src/kernel/expression_operators/membership.rs +3 -1
- data/rust/src/kernel/expression_operators/mod.rs +34 -21
- data/rust/src/kernel/expression_operators/pattern_match.rs +46 -11
- data/rust/src/kernel/expression_operators/positional.rs +84 -30
- data/rust/src/kernel/expression_operators/presence.rs +52 -13
- data/rust/src/kernel/expression_operators/sign_test.rs +3 -1
- data/rust/src/kernel/expression_operators/sized.rs +24 -8
- data/rust/src/kernel/expression_operators/text.rs +120 -27
- data/rust/src/kernel/expression_operators/to_string.rs +10 -4
- data/rust/src/kernel/json.rs +244 -148
- data/rust/src/kernel/mod.rs +47 -33
- data/rust/src/kernel/named_query.rs +63 -15
- data/rust/src/kernel/needs.rs +112 -25
- data/rust/src/kernel/orchestrate.rs +980 -581
- data/rust/src/kernel/orchestrate_guard_tests.rs +538 -140
- data/rust/src/kernel/pattern.rs +107 -34
- data/rust/src/kernel/present.rs +261 -0
- data/rust/src/kernel/query_comparators.rs +118 -41
- data/rust/src/kernel/query_ordering.rs +13 -5
- data/rust/src/kernel/read_model.rs +479 -181
- data/rust/src/kernel/reference_lookup.rs +122 -26
- data/rust/src/kernel/repository.rs +293 -66
- data/rust/src/kernel/routing.rs +58 -42
- data/rust/src/kernel/ruby_float.rs +139 -0
- data/rust/src/kernel/vocab/aggregate_dispatch_order.rs +57 -57
- data/rust/src/kernel/vocab/entity_dispatch_order.rs +54 -54
- data/rust/src/kernel/vocab/field_hint.rs +19 -25
- data/rust/src/kernel/vocab/mod.rs +6 -6
- data/rust/src/kernel/vocab/query_comparator.rs +23 -24
- data/rust/src/kernel/vocab/refusal_template.rs +501 -297
- data/rust/src/lib.rs +50 -0
- data/rust/src/main.rs +4 -1
- data/rust/web/Cargo.toml +36 -0
- data/rust/web/clippy.toml +4 -0
- metadata +85 -15
- data/examples/pizzas/bluebook/hecksagon/mock_stripe_payment_adapter.rb +0 -36
- data/lib/hecks/adapters/driven/google_authentication.adapter +0 -3
- data/lib/hecks/adapters/driven/google_authentication.rb +0 -99
- data/lib/hecks/adapters/driven/mock_stripe_adapter.adapter +0 -3
- data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +0 -32
- data/lib/hecks/ports/authentication.port +0 -6
- data/lib/hecks/ports/authentication.rb +0 -67
- data/lib/hecks/ports/persistence/plugins/era/translation/approval_file.rb +0 -255
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +0 -22
- data/rust/host/src/approval.rs +0 -652
|
@@ -123,6 +123,8 @@ pub fn entity_at_path_mut<'a>(
|
|
|
123
123
|
entities: &'a mut [ir::Entity],
|
|
124
124
|
path: &[usize],
|
|
125
125
|
) -> &'a mut ir::Entity {
|
|
126
|
+
// reason: documented under `# Panics`; paths are built internally, never from input.
|
|
127
|
+
#[allow(clippy::expect_used)]
|
|
126
128
|
let (first, rest) = path
|
|
127
129
|
.split_first()
|
|
128
130
|
.expect("entity_path must have at least one segment");
|
|
@@ -140,6 +142,7 @@ pub fn entity_at_path_mut<'a>(
|
|
|
140
142
|
/// type-form `identified_by` resolves against it. `entity_named_givens` is one pool shared by
|
|
141
143
|
/// every piece under the root aggregate; a sibling's command reads it back through
|
|
142
144
|
/// `command::try_reference_named_given`.
|
|
145
|
+
#[allow(clippy::too_many_arguments)] // established signature
|
|
143
146
|
pub fn parse_body(
|
|
144
147
|
file: &str,
|
|
145
148
|
lines: &[SourceLine],
|
|
@@ -175,12 +178,9 @@ pub fn parse_body(
|
|
|
175
178
|
loop {
|
|
176
179
|
// Peeked before `next_line`: the grammar row still requires a block for `given`, so a
|
|
177
180
|
// bare reference must be consumed here.
|
|
178
|
-
if let Some(outcome) =
|
|
179
|
-
file,
|
|
180
|
-
|
|
181
|
-
pos,
|
|
182
|
-
chapter_entity_named_givens,
|
|
183
|
-
)? {
|
|
181
|
+
if let Some(outcome) =
|
|
182
|
+
try_reference_named_chapter_entity_given(file, lines, pos, chapter_entity_named_givens)?
|
|
183
|
+
{
|
|
184
184
|
match outcome {
|
|
185
185
|
ChapterEntityGivenLookup::Resolved(given) => {
|
|
186
186
|
entity.preconditions.push(given.clone());
|
|
@@ -230,7 +230,7 @@ pub fn parse_body(
|
|
|
230
230
|
"description",
|
|
231
231
|
&gated.args,
|
|
232
232
|
1,
|
|
233
|
-
)?)
|
|
233
|
+
)?);
|
|
234
234
|
}
|
|
235
235
|
"attribute" => entity
|
|
236
236
|
.attributes
|
|
@@ -359,9 +359,13 @@ pub fn parse_body(
|
|
|
359
359
|
}
|
|
360
360
|
// Chapter-wide pool, keyed by "Aggregate.Entity" plus description.
|
|
361
361
|
let owner = format!("{aggregate_name}.{name}");
|
|
362
|
-
if !chapter_entity_named_givens
|
|
363
|
-
|
|
364
|
-
|
|
362
|
+
if !chapter_entity_named_givens
|
|
363
|
+
.iter()
|
|
364
|
+
.any(|(candidate_owner, g)| {
|
|
365
|
+
candidate_owner == &owner
|
|
366
|
+
&& g.description.as_deref() == Some(description.as_str())
|
|
367
|
+
})
|
|
368
|
+
{
|
|
365
369
|
chapter_entity_named_givens.push((owner, built));
|
|
366
370
|
}
|
|
367
371
|
}
|
|
@@ -517,7 +521,13 @@ pub fn parse_body(
|
|
|
517
521
|
}
|
|
518
522
|
}
|
|
519
523
|
|
|
520
|
-
lifecycle::seal_commands(
|
|
524
|
+
lifecycle::seal_commands(
|
|
525
|
+
file,
|
|
526
|
+
*pos,
|
|
527
|
+
&entity.name,
|
|
528
|
+
entity.lifecycle.as_ref(),
|
|
529
|
+
&entity.commands,
|
|
530
|
+
)?;
|
|
521
531
|
|
|
522
532
|
Ok((
|
|
523
533
|
entity,
|
|
@@ -32,7 +32,7 @@ pub fn apply(
|
|
|
32
32
|
) -> ParseResult<()> {
|
|
33
33
|
loop {
|
|
34
34
|
let line = *lines.get(*pos).ok_or_else(|| {
|
|
35
|
-
let last = lines.last().
|
|
35
|
+
let last = lines.last().map_or(0, |l| l.number);
|
|
36
36
|
Diagnostic::new(
|
|
37
37
|
file,
|
|
38
38
|
last,
|
|
@@ -115,8 +115,13 @@ pub fn apply(
|
|
|
115
115
|
// fact collected for `resolve`, absent from `ir.json`. Only `from: :vendor`
|
|
116
116
|
// is admitted as a source, and anything else is refused.
|
|
117
117
|
"attaches" => {
|
|
118
|
-
let name =
|
|
119
|
-
|
|
118
|
+
let name = super::positional_text(
|
|
119
|
+
file,
|
|
120
|
+
gated.line.number,
|
|
121
|
+
"attaches",
|
|
122
|
+
&gated.args,
|
|
123
|
+
1,
|
|
124
|
+
)?;
|
|
120
125
|
match super::named_symbol(&gated.args, "from").as_deref() {
|
|
121
126
|
None => gem_chapter_names.push(name),
|
|
122
127
|
Some("vendor") => vendored_bluebook_names.push(name),
|
|
@@ -149,6 +154,7 @@ pub fn apply(
|
|
|
149
154
|
|
|
150
155
|
/// `<Domain>::<Aggregate>.<verb>`: `port` attaches a port to the named aggregate; any other
|
|
151
156
|
/// verb is a generic adapter-bind, shape-matched and dropped.
|
|
157
|
+
#[allow(clippy::too_many_arguments)] // established signature
|
|
152
158
|
fn apply_aggregate_qualified(
|
|
153
159
|
file: &str,
|
|
154
160
|
lines: &[SourceLine],
|
|
@@ -216,7 +222,7 @@ fn skip_dropped_body(file: &str, lines: &[SourceLine], pos: &mut usize) -> Parse
|
|
|
216
222
|
let mut depth = 1;
|
|
217
223
|
while depth > 0 {
|
|
218
224
|
let line = *lines.get(*pos).ok_or_else(|| {
|
|
219
|
-
let last = lines.last().
|
|
225
|
+
let last = lines.last().map_or(0, |l| l.number);
|
|
220
226
|
Diagnostic::new(
|
|
221
227
|
file,
|
|
222
228
|
last,
|
|
@@ -41,10 +41,10 @@ pub fn parse_body(
|
|
|
41
41
|
let (command_raw, to_state_raw) = super::split_top_level_rocket(pair_text);
|
|
42
42
|
let command = text_value(command_raw);
|
|
43
43
|
let to_state = text_value(to_state_raw);
|
|
44
|
-
let from_states =
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
let from_states = super::named_raw(&gated.args, "from").map_or_else(
|
|
45
|
+
|| vec![None],
|
|
46
|
+
|raw| from_values(raw).into_iter().map(Some).collect(),
|
|
47
|
+
);
|
|
48
48
|
|
|
49
49
|
for from_state in from_states {
|
|
50
50
|
lifecycle.transitions.push(ir::StateTransitionRow {
|
|
@@ -117,7 +117,11 @@ fn is_lowercase_word(name: &str) -> bool {
|
|
|
117
117
|
|
|
118
118
|
// Refuses a mark naming a state that is neither the default nor a transition target. Wording
|
|
119
119
|
// matches Ruby's.
|
|
120
|
-
fn refuse_unknown_mark_states(
|
|
120
|
+
fn refuse_unknown_mark_states(
|
|
121
|
+
file: &str,
|
|
122
|
+
line: usize,
|
|
123
|
+
lifecycle: &ir::Lifecycle,
|
|
124
|
+
) -> ParseResult<()> {
|
|
121
125
|
let mut known: Vec<&str> = vec![lifecycle.default.as_str()];
|
|
122
126
|
for row in &lifecycle.transitions {
|
|
123
127
|
if !known.contains(&row.to_state.as_str()) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
//! Word, body, and argument gates, plus the recursive body walker.
|
|
2
2
|
//! Per-construct modules build IR; a word they do not build fails with `not_yet_implemented`.
|
|
3
3
|
|
|
4
|
+
pub mod adapter;
|
|
4
5
|
pub mod aggregate;
|
|
5
6
|
pub mod chapter;
|
|
6
7
|
pub mod command;
|
|
@@ -15,6 +16,7 @@ pub mod process_manager;
|
|
|
15
16
|
pub mod query;
|
|
16
17
|
pub mod read_model;
|
|
17
18
|
pub mod value_object;
|
|
19
|
+
pub mod world;
|
|
18
20
|
|
|
19
21
|
use crate::diag::{Diagnostic, ParseResult};
|
|
20
22
|
use crate::ir;
|
|
@@ -176,11 +178,7 @@ pub(crate) fn classify_lexical_kind(token: &str) -> &'static str {
|
|
|
176
178
|
if t.starts_with('{') && t.ends_with('}') {
|
|
177
179
|
return "pairs";
|
|
178
180
|
}
|
|
179
|
-
if t.chars()
|
|
180
|
-
.next()
|
|
181
|
-
.map(|c| c.is_ascii_uppercase())
|
|
182
|
-
.unwrap_or(false)
|
|
183
|
-
{
|
|
181
|
+
if t.chars().next().is_some_and(|c| c.is_ascii_uppercase()) {
|
|
184
182
|
return "constant";
|
|
185
183
|
}
|
|
186
184
|
if type_context_call_word(t).is_some() {
|
|
@@ -564,12 +562,7 @@ pub(crate) fn split_top_level_rocket(segment: &str) -> (&str, &str) {
|
|
|
564
562
|
|
|
565
563
|
/// The field name of a pair key, a bare or quoted Symbol literal (`:status`, `:"a.b"`).
|
|
566
564
|
/// Any other key is refused; pair keys are always symbols.
|
|
567
|
-
fn rocket_key_name<
|
|
568
|
-
file: &str,
|
|
569
|
-
word: &str,
|
|
570
|
-
key_text: &'a str,
|
|
571
|
-
line: usize,
|
|
572
|
-
) -> ParseResult<String> {
|
|
565
|
+
fn rocket_key_name(file: &str, word: &str, key_text: &str, line: usize) -> ParseResult<String> {
|
|
573
566
|
let Some(rest) = key_text.strip_prefix(':') else {
|
|
574
567
|
return Err(Diagnostic::new(file, line, format!("'{word}'s key '{key_text}' is not a symbol — a pair's key is always :name or :\"a.dotted.path\"")));
|
|
575
568
|
};
|
|
@@ -677,7 +670,7 @@ pub(crate) fn next_line<'src>(
|
|
|
677
670
|
context: &'static str,
|
|
678
671
|
) -> ParseResult<Option<GatedLine<'src>>> {
|
|
679
672
|
let line = *lines.get(*pos).ok_or_else(|| {
|
|
680
|
-
let last = lines.last().
|
|
673
|
+
let last = lines.last().map_or(0, |l| l.number);
|
|
681
674
|
Diagnostic::new(
|
|
682
675
|
file,
|
|
683
676
|
last,
|
|
@@ -807,7 +800,7 @@ pub(crate) fn positional_constant<'a>(
|
|
|
807
800
|
args: &'a ArgumentGateResult,
|
|
808
801
|
at: usize,
|
|
809
802
|
) -> ParseResult<&'a str> {
|
|
810
|
-
positional_raw(file, line, word, args, at).map(
|
|
803
|
+
positional_raw(file, line, word, args, at).map(str::trim)
|
|
811
804
|
}
|
|
812
805
|
|
|
813
806
|
/// A command reference: bare constant (`trigger Account::Debit`) or quoted (`"Account.Debit"`).
|
|
@@ -847,7 +840,7 @@ pub(crate) fn named_raw<'a>(args: &'a ArgumentGateResult, name: &str) -> Option<
|
|
|
847
840
|
|
|
848
841
|
/// A named constant argument (`to: Payment`): raw text, trimmed; resolved downstream.
|
|
849
842
|
pub(crate) fn named_constant<'a>(args: &'a ArgumentGateResult, name: &str) -> Option<&'a str> {
|
|
850
|
-
named_raw(args, name).map(
|
|
843
|
+
named_raw(args, name).map(str::trim)
|
|
851
844
|
}
|
|
852
845
|
|
|
853
846
|
/// A named symbol argument without its colon (`as: :name`); bare or quoted.
|
|
@@ -865,9 +858,7 @@ pub(crate) fn named_text(args: &ArgumentGateResult, name: &str) -> Option<String
|
|
|
865
858
|
|
|
866
859
|
/// A named flag (`true`/`false`) argument — `optional: true`.
|
|
867
860
|
pub(crate) fn named_flag(args: &ArgumentGateResult, name: &str) -> bool {
|
|
868
|
-
named_raw(args, name)
|
|
869
|
-
.map(|raw| raw.trim() == "true")
|
|
870
|
-
.unwrap_or(false)
|
|
861
|
+
named_raw(args, name).is_some_and(|raw| raw.trim() == "true")
|
|
871
862
|
}
|
|
872
863
|
|
|
873
864
|
/// Raw text of a `source`-shaped body written as `{ ... }` or `do ... end`.
|
|
@@ -1036,6 +1027,7 @@ pub(crate) enum PendingIdentity {
|
|
|
1036
1027
|
/// Parses `identified_by` in its three forms: a value object type (`PizzaName, as: :name`), one or
|
|
1037
1028
|
/// more field symbols (`:a, :b`), or a `{ ... }`/`do ... end` block declaring an inline value
|
|
1038
1029
|
/// object named `inline_type_name`.
|
|
1030
|
+
#[allow(clippy::too_many_arguments)] // established signature
|
|
1039
1031
|
pub(crate) fn parse_identified_by(
|
|
1040
1032
|
file: &str,
|
|
1041
1033
|
lines: &[SourceLine],
|
|
@@ -1072,9 +1064,10 @@ pub(crate) fn parse_identified_by(
|
|
|
1072
1064
|
}
|
|
1073
1065
|
// An identity block's own `one_of(...)` sets are not installed (its builder is not
|
|
1074
1066
|
// the aggregate's `value_object` verb).
|
|
1075
|
-
let (value_object, _) =
|
|
1076
|
-
|
|
1077
|
-
|
|
1067
|
+
let (value_object, _) =
|
|
1068
|
+
parse_nested_body(file, lines, pos, opener, line, |f, l, p| {
|
|
1069
|
+
value_object::parse_body(f, l, p, inline_type_name, owner_value_objects)
|
|
1070
|
+
})?;
|
|
1078
1071
|
if value_object.attributes.is_empty() {
|
|
1079
1072
|
return Err(Diagnostic::new(
|
|
1080
1073
|
file,
|
|
@@ -1138,7 +1131,11 @@ pub(crate) fn build_attribute(
|
|
|
1138
1131
|
let pattern = match named_symbol(args, "matches") {
|
|
1139
1132
|
Some(given) => {
|
|
1140
1133
|
if named_text(args, "pattern").is_some() {
|
|
1141
|
-
return Err(Diagnostic::new(
|
|
1134
|
+
return Err(Diagnostic::new(
|
|
1135
|
+
file,
|
|
1136
|
+
line,
|
|
1137
|
+
"matches: and pattern: both state the pattern".to_string(),
|
|
1138
|
+
));
|
|
1142
1139
|
}
|
|
1143
1140
|
match crate::build::named_pattern::source(&given) {
|
|
1144
1141
|
Some(source) => Some(source.to_string()),
|
|
@@ -1199,6 +1196,8 @@ pub(crate) fn resolve_type_expression(
|
|
|
1199
1196
|
let trimmed = text.trim();
|
|
1200
1197
|
match type_context_call_word(trimmed) {
|
|
1201
1198
|
Some("list_of") => {
|
|
1199
|
+
// reason: `type_context_call_word` already confirmed a '(' in `trimmed`.
|
|
1200
|
+
#[allow(clippy::expect_used)]
|
|
1202
1201
|
let open = trimmed
|
|
1203
1202
|
.find('(')
|
|
1204
1203
|
.expect("type_context_call_word already confirmed a '('");
|
|
@@ -1213,6 +1212,8 @@ pub(crate) fn resolve_type_expression(
|
|
|
1213
1212
|
Ok((inner.to_string(), true, None))
|
|
1214
1213
|
}
|
|
1215
1214
|
Some("one_of") => {
|
|
1215
|
+
// reason: `type_context_call_word` already confirmed a '(' in `trimmed`.
|
|
1216
|
+
#[allow(clippy::expect_used)]
|
|
1216
1217
|
let open = trimmed
|
|
1217
1218
|
.find('(')
|
|
1218
1219
|
.expect("type_context_call_word already confirmed a '('");
|
|
@@ -1251,73 +1252,3 @@ pub(crate) fn resolve_type_expression(
|
|
|
1251
1252
|
None => Ok((trimmed.to_string(), false, None)),
|
|
1252
1253
|
}
|
|
1253
1254
|
}
|
|
1254
|
-
|
|
1255
|
-
/// Walks lines inside an open `context` up to and including the matching `end`, gating each one.
|
|
1256
|
-
/// The first gate failure or not-yet-implemented construct, at any depth, aborts the walk.
|
|
1257
|
-
pub fn walk_body(
|
|
1258
|
-
file: &str,
|
|
1259
|
-
lines: &[SourceLine],
|
|
1260
|
-
pos: &mut usize,
|
|
1261
|
-
context: &'static str,
|
|
1262
|
-
) -> ParseResult<()> {
|
|
1263
|
-
loop {
|
|
1264
|
-
let line = *lines.get(*pos).ok_or_else(|| {
|
|
1265
|
-
let last = lines.last().map(|l| l.number).unwrap_or(0);
|
|
1266
|
-
Diagnostic::new(
|
|
1267
|
-
file,
|
|
1268
|
-
last,
|
|
1269
|
-
format!("unexpected end of file — still inside {context}"),
|
|
1270
|
-
)
|
|
1271
|
-
})?;
|
|
1272
|
-
|
|
1273
|
-
let shape = lex::classify(file, &line)?;
|
|
1274
|
-
*pos += 1;
|
|
1275
|
-
|
|
1276
|
-
match shape {
|
|
1277
|
-
LineShape::End => return Ok(()),
|
|
1278
|
-
LineShape::Call(call) => handle_call(file, lines, pos, context, &line, call)?,
|
|
1279
|
-
}
|
|
1280
|
-
}
|
|
1281
|
-
}
|
|
1282
|
-
|
|
1283
|
-
fn handle_call(
|
|
1284
|
-
file: &str,
|
|
1285
|
-
lines: &[SourceLine],
|
|
1286
|
-
pos: &mut usize,
|
|
1287
|
-
context: &'static str,
|
|
1288
|
-
line: &SourceLine,
|
|
1289
|
-
call: Call,
|
|
1290
|
-
) -> ParseResult<()> {
|
|
1291
|
-
let candidates = word_gate(file, &call.word, context, line.number)?;
|
|
1292
|
-
let row = body_gate(file, &candidates, &call.opener, line.number, &call.word)?;
|
|
1293
|
-
argument_gate(file, &call.word, context, &call.args, line.number)?;
|
|
1294
|
-
|
|
1295
|
-
match &call.opener {
|
|
1296
|
-
Opener::DoBlock { .. } => {
|
|
1297
|
-
if row.inner.is_empty() {
|
|
1298
|
-
return Err(dispatch_stub(context, file, line.number, &call.word));
|
|
1299
|
-
}
|
|
1300
|
-
let inner_context = keywords::CONTEXTS
|
|
1301
|
-
.iter()
|
|
1302
|
-
.find(|c| **c == row.inner)
|
|
1303
|
-
.copied()
|
|
1304
|
-
.ok_or_else(|| {
|
|
1305
|
-
Diagnostic::new(
|
|
1306
|
-
file,
|
|
1307
|
-
line.number,
|
|
1308
|
-
format!(
|
|
1309
|
-
"'{}' opens an undeclared context '{}'",
|
|
1310
|
-
call.word, row.inner
|
|
1311
|
-
),
|
|
1312
|
-
)
|
|
1313
|
-
})?;
|
|
1314
|
-
walk_body(file, lines, pos, inner_context)?;
|
|
1315
|
-
// The nested body gated cleanly but builds nothing yet, so the failure is reported at
|
|
1316
|
-
// the construct just entered rather than the outer `context`.
|
|
1317
|
-
Err(dispatch_stub(inner_context, file, line.number, &call.word))
|
|
1318
|
-
}
|
|
1319
|
-
Opener::BraceBlock { .. } | Opener::None => {
|
|
1320
|
-
Err(dispatch_stub(context, file, line.number, &call.word))
|
|
1321
|
-
}
|
|
1322
|
-
}
|
|
1323
|
-
}
|
|
@@ -22,7 +22,10 @@ pub fn declare(
|
|
|
22
22
|
) -> ParseResult<()> {
|
|
23
23
|
let fact = super::positional_symbol(file, line, "needs", args, 1)?;
|
|
24
24
|
if !NEEDABLE_FACTS.contains(&fact.as_str()) {
|
|
25
|
-
let supplied: Vec<String> = NEEDABLE_FACTS
|
|
25
|
+
let supplied: Vec<String> = NEEDABLE_FACTS
|
|
26
|
+
.iter()
|
|
27
|
+
.map(|known| format!(":{known}"))
|
|
28
|
+
.collect();
|
|
26
29
|
return Err(Diagnostic::new(
|
|
27
30
|
file,
|
|
28
31
|
line,
|
|
@@ -33,7 +36,11 @@ pub fn declare(
|
|
|
33
36
|
));
|
|
34
37
|
}
|
|
35
38
|
if needs.contains(&fact) {
|
|
36
|
-
return Err(Diagnostic::new(
|
|
39
|
+
return Err(Diagnostic::new(
|
|
40
|
+
file,
|
|
41
|
+
line,
|
|
42
|
+
format!("{owner} declares needs :{fact} twice"),
|
|
43
|
+
));
|
|
37
44
|
}
|
|
38
45
|
needs.push(fact);
|
|
39
46
|
Ok(())
|
|
@@ -47,14 +54,16 @@ pub fn refuse_undeclared(
|
|
|
47
54
|
needs: &[String],
|
|
48
55
|
attributes: &[ir::Attribute],
|
|
49
56
|
) -> ParseResult<()> {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
needs
|
|
58
|
+
.iter()
|
|
59
|
+
.find(|fact| !attributes.iter().any(|a| &a.name == *fact))
|
|
60
|
+
.map_or(Ok(()), |fact| {
|
|
61
|
+
Err(Diagnostic::new(
|
|
62
|
+
file,
|
|
63
|
+
line,
|
|
64
|
+
format!(
|
|
65
|
+
"{owner} needs :{fact} but declares no attribute :{fact} for the runtime to fill — add `attribute :{fact}, <type>`"
|
|
66
|
+
),
|
|
67
|
+
))
|
|
68
|
+
})
|
|
60
69
|
}
|
|
@@ -36,7 +36,7 @@ pub fn parse_body(
|
|
|
36
36
|
"on",
|
|
37
37
|
&gated.args,
|
|
38
38
|
1,
|
|
39
|
-
)?)
|
|
39
|
+
)?);
|
|
40
40
|
}
|
|
41
41
|
"trigger" => {
|
|
42
42
|
policy.trigger_command = Some(super::positional_command_ref(
|
|
@@ -82,7 +82,7 @@ pub fn parse_body(
|
|
|
82
82
|
"for_each",
|
|
83
83
|
&gated.args,
|
|
84
84
|
1,
|
|
85
|
-
)?)
|
|
85
|
+
)?);
|
|
86
86
|
}
|
|
87
87
|
_ => {
|
|
88
88
|
return Err(super::not_built_yet(
|
|
@@ -34,7 +34,7 @@ pub fn parse_body(
|
|
|
34
34
|
// A dotted symbol (`:"reference.value"`), which only `positional_symbol` can spell.
|
|
35
35
|
"correlates_by" => {
|
|
36
36
|
pm.correlates_by =
|
|
37
|
-
super::positional_symbol(file, line, "correlates_by", &gated.args, 1)
|
|
37
|
+
super::positional_symbol(file, line, "correlates_by", &gated.args, 1)?;
|
|
38
38
|
}
|
|
39
39
|
// Event references keep only the bare final segment (`Naming.event_name_ref`);
|
|
40
40
|
// `SagaInterpreter` matches them against a bare `event.name`.
|
|
@@ -45,7 +45,7 @@ pub fn parse_body(
|
|
|
45
45
|
"starts_on",
|
|
46
46
|
&gated.args,
|
|
47
47
|
1,
|
|
48
|
-
)?)
|
|
48
|
+
)?);
|
|
49
49
|
}
|
|
50
50
|
"ends_on" => {
|
|
51
51
|
pm.ends_on = Some(super::positional_event_name_ref(
|
|
@@ -54,17 +54,15 @@ pub fn parse_body(
|
|
|
54
54
|
"ends_on",
|
|
55
55
|
&gated.args,
|
|
56
56
|
1,
|
|
57
|
-
)?)
|
|
57
|
+
)?);
|
|
58
58
|
}
|
|
59
59
|
"transition" => {
|
|
60
60
|
for handler in parse_transition(file, lines, pos, line, &gated)? {
|
|
61
61
|
// Two legs on one (event, state) pair would be picked by declaration order;
|
|
62
62
|
// mirrors `ProcessManagerBuilder#refuse_ambiguous_legs!`.
|
|
63
|
-
if let Some(earlier) = pm
|
|
64
|
-
.
|
|
65
|
-
|
|
66
|
-
.find(|h| h.event_type == handler.event_type && h.from_state == handler.from_state)
|
|
67
|
-
{
|
|
63
|
+
if let Some(earlier) = pm.handlers.iter().find(|h| {
|
|
64
|
+
h.event_type == handler.event_type && h.from_state == handler.from_state
|
|
65
|
+
}) {
|
|
68
66
|
return Err(Diagnostic::new(
|
|
69
67
|
file,
|
|
70
68
|
line,
|
|
@@ -208,9 +206,9 @@ fn parse_dispatches(
|
|
|
208
206
|
Opener::DoBlock { .. } => {
|
|
209
207
|
parse_compensates_block(file, lines, pos)?.map(Box::new)
|
|
210
208
|
}
|
|
211
|
-
Opener::BraceBlock { .. } =>
|
|
212
|
-
"body_gate never admits a BraceBlock for 'dispatch'/Handler"
|
|
213
|
-
|
|
209
|
+
Opener::BraceBlock { .. } => {
|
|
210
|
+
unreachable!("body_gate never admits a BraceBlock for 'dispatch'/Handler")
|
|
211
|
+
}
|
|
214
212
|
};
|
|
215
213
|
dispatches.push(ir::DispatchSpec {
|
|
216
214
|
command_name,
|
|
@@ -39,7 +39,7 @@ pub fn parse_body(
|
|
|
39
39
|
"description",
|
|
40
40
|
&gated.args,
|
|
41
41
|
1,
|
|
42
|
-
)?)
|
|
42
|
+
)?);
|
|
43
43
|
}
|
|
44
44
|
"attribute" => query
|
|
45
45
|
.attributes
|
|
@@ -48,7 +48,7 @@ pub fn parse_body(
|
|
|
48
48
|
query_derive::refuse_on_target(file, line, "where", &gated.args.named)?;
|
|
49
49
|
query
|
|
50
50
|
.wheres
|
|
51
|
-
.extend(query_derive::where_clauses(&gated.args.named, None))
|
|
51
|
+
.extend(query_derive::where_clauses(&gated.args.named, None));
|
|
52
52
|
}
|
|
53
53
|
"order_by" => {
|
|
54
54
|
// The gate already refuses an undeclared `on:`; only `where` needs a check.
|
|
@@ -57,7 +57,11 @@ pub fn parse_body(
|
|
|
57
57
|
Some((_, text)) => text.trim().trim_start_matches(':').to_string(),
|
|
58
58
|
None => "asc".to_string(),
|
|
59
59
|
};
|
|
60
|
-
query.order_by = Some(ir::OrderBy {
|
|
60
|
+
query.order_by = Some(ir::OrderBy {
|
|
61
|
+
field,
|
|
62
|
+
direction,
|
|
63
|
+
target: None,
|
|
64
|
+
});
|
|
61
65
|
}
|
|
62
66
|
// `positional_constant` only fetches raw text; the gate already checked it is numeric.
|
|
63
67
|
"limit" => {
|
|
@@ -87,7 +91,7 @@ pub fn parse_body(
|
|
|
87
91
|
}
|
|
88
92
|
"needs" => super::needs::declare(file, line, name, &gated.args, &mut query.needs)?,
|
|
89
93
|
word if OPTION_WORDS.contains(&word) => {
|
|
90
|
-
query_options::apply(file, line, word, &gated.args, &mut query.options)
|
|
94
|
+
query_options::apply(file, line, word, &gated.args, &mut query.options)?;
|
|
91
95
|
}
|
|
92
96
|
_ => {
|
|
93
97
|
return Err(super::not_built_yet(
|
|
@@ -41,10 +41,7 @@ pub fn parse_body(
|
|
|
41
41
|
let mut options = ir::QueryOptions::default();
|
|
42
42
|
let mut last_line = 0usize;
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
let Some(gated) = super::next_line(file, lines, pos, "ReadModel")? else {
|
|
46
|
-
break;
|
|
47
|
-
};
|
|
44
|
+
while let Some(gated) = super::next_line(file, lines, pos, "ReadModel")? {
|
|
48
45
|
last_line = gated.line.number;
|
|
49
46
|
|
|
50
47
|
match gated.row.word {
|
|
@@ -55,7 +52,7 @@ pub fn parse_body(
|
|
|
55
52
|
"description",
|
|
56
53
|
&gated.args,
|
|
57
54
|
1,
|
|
58
|
-
)?)
|
|
55
|
+
)?);
|
|
59
56
|
}
|
|
60
57
|
// Ruby refuses a second `reference_to`; that seal is out of scope for this parser.
|
|
61
58
|
"reference_to" => {
|
|
@@ -93,19 +90,43 @@ pub fn parse_body(
|
|
|
93
90
|
"median",
|
|
94
91
|
&gated.args,
|
|
95
92
|
1,
|
|
96
|
-
)?)
|
|
93
|
+
)?);
|
|
97
94
|
}
|
|
98
95
|
"sum" => {
|
|
99
|
-
sum_field = Some(super::positional_symbol(
|
|
96
|
+
sum_field = Some(super::positional_symbol(
|
|
97
|
+
file,
|
|
98
|
+
last_line,
|
|
99
|
+
"sum",
|
|
100
|
+
&gated.args,
|
|
101
|
+
1,
|
|
102
|
+
)?);
|
|
100
103
|
}
|
|
101
104
|
"avg" => {
|
|
102
|
-
avg_field = Some(super::positional_symbol(
|
|
105
|
+
avg_field = Some(super::positional_symbol(
|
|
106
|
+
file,
|
|
107
|
+
last_line,
|
|
108
|
+
"avg",
|
|
109
|
+
&gated.args,
|
|
110
|
+
1,
|
|
111
|
+
)?);
|
|
103
112
|
}
|
|
104
113
|
"min" => {
|
|
105
|
-
min_field = Some(super::positional_symbol(
|
|
114
|
+
min_field = Some(super::positional_symbol(
|
|
115
|
+
file,
|
|
116
|
+
last_line,
|
|
117
|
+
"min",
|
|
118
|
+
&gated.args,
|
|
119
|
+
1,
|
|
120
|
+
)?);
|
|
106
121
|
}
|
|
107
122
|
"max" => {
|
|
108
|
-
max_field = Some(super::positional_symbol(
|
|
123
|
+
max_field = Some(super::positional_symbol(
|
|
124
|
+
file,
|
|
125
|
+
last_line,
|
|
126
|
+
"max",
|
|
127
|
+
&gated.args,
|
|
128
|
+
1,
|
|
129
|
+
)?);
|
|
109
130
|
}
|
|
110
131
|
"percentile" => {
|
|
111
132
|
percentile_field = Some(super::positional_symbol(
|
|
@@ -116,14 +137,30 @@ pub fn parse_body(
|
|
|
116
137
|
1,
|
|
117
138
|
)?);
|
|
118
139
|
percentile_at = Some(super::named_text(&gated.args, "at").ok_or_else(|| {
|
|
119
|
-
Diagnostic::new(
|
|
140
|
+
Diagnostic::new(
|
|
141
|
+
file,
|
|
142
|
+
last_line,
|
|
143
|
+
"'percentile' needs an at: argument".to_string(),
|
|
144
|
+
)
|
|
120
145
|
})?);
|
|
121
146
|
}
|
|
122
147
|
"any" => {
|
|
123
|
-
any_field = Some(super::positional_symbol(
|
|
148
|
+
any_field = Some(super::positional_symbol(
|
|
149
|
+
file,
|
|
150
|
+
last_line,
|
|
151
|
+
"any",
|
|
152
|
+
&gated.args,
|
|
153
|
+
1,
|
|
154
|
+
)?);
|
|
124
155
|
}
|
|
125
156
|
"all" => {
|
|
126
|
-
all_field = Some(super::positional_symbol(
|
|
157
|
+
all_field = Some(super::positional_symbol(
|
|
158
|
+
file,
|
|
159
|
+
last_line,
|
|
160
|
+
"all",
|
|
161
|
+
&gated.args,
|
|
162
|
+
1,
|
|
163
|
+
)?);
|
|
127
164
|
}
|
|
128
165
|
"where" => {
|
|
129
166
|
// ADR 0055: `on:` is a recognized named argument for `where` in ReadModel
|
|
@@ -138,7 +175,7 @@ pub fn parse_body(
|
|
|
138
175
|
.filter(|(name, _)| name != "on")
|
|
139
176
|
.cloned()
|
|
140
177
|
.collect();
|
|
141
|
-
wheres.extend(query_derive::where_clauses(&field_pairs, target.as_deref()))
|
|
178
|
+
wheres.extend(query_derive::where_clauses(&field_pairs, target.as_deref()));
|
|
142
179
|
}
|
|
143
180
|
"order_by" => {
|
|
144
181
|
let field = super::positional_symbol(file, last_line, "order_by", &gated.args, 1)?;
|
|
@@ -147,7 +184,11 @@ pub fn parse_body(
|
|
|
147
184
|
None => "asc".to_string(),
|
|
148
185
|
};
|
|
149
186
|
let target = super::named_constant(&gated.args, "on").map(naming::demodulise);
|
|
150
|
-
order_by = Some(ir::OrderBy {
|
|
187
|
+
order_by = Some(ir::OrderBy {
|
|
188
|
+
field,
|
|
189
|
+
direction,
|
|
190
|
+
target,
|
|
191
|
+
});
|
|
151
192
|
}
|
|
152
193
|
"limit" => {
|
|
153
194
|
let raw = super::positional_constant(file, last_line, "limit", &gated.args, 1)?;
|
|
@@ -158,7 +199,7 @@ pub fn parse_body(
|
|
|
158
199
|
});
|
|
159
200
|
}
|
|
160
201
|
word if OPTION_WORDS.contains(&word) => {
|
|
161
|
-
query_options::apply(file, last_line, word, &gated.args, &mut options)
|
|
202
|
+
query_options::apply(file, last_line, word, &gated.args, &mut options)?;
|
|
162
203
|
}
|
|
163
204
|
_ => {
|
|
164
205
|
return Err(super::not_built_yet(
|
|
@@ -98,7 +98,8 @@ pub fn parse_body(
|
|
|
98
98
|
// An inline type-position `one_of(...)` set is returned beside the value object (see
|
|
99
99
|
// build/closed_sets.rs); `ValueObject` has no nested-value-objects field.
|
|
100
100
|
"attribute" => {
|
|
101
|
-
let (attribute, one_of, closed_set) =
|
|
101
|
+
let (attribute, one_of, closed_set) =
|
|
102
|
+
build_value_object_attribute(file, &gated.args)?;
|
|
102
103
|
synthesized.extend(closed_set);
|
|
103
104
|
if let Some(values) = one_of {
|
|
104
105
|
let vo_name = vo.name.clone();
|
|
@@ -140,7 +141,11 @@ pub fn parse_body(
|
|
|
140
141
|
fn build_value_object_attribute(
|
|
141
142
|
file: &str,
|
|
142
143
|
args: &super::ArgumentGateResult,
|
|
143
|
-
) -> ParseResult<(
|
|
144
|
+
) -> ParseResult<(
|
|
145
|
+
ir::Attribute,
|
|
146
|
+
Option<Vec<ruby_value::Value>>,
|
|
147
|
+
Option<ir::ValueObject>,
|
|
148
|
+
)> {
|
|
144
149
|
let (attribute, closed_set) = super::build_attribute(file, 0, "attribute", args)?;
|
|
145
150
|
let one_of = super::named_raw(args, "one_of").map(|raw| {
|
|
146
151
|
let trimmed = raw.trim();
|