hecks 3.13.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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.bluebook +4 -4
- data/examples/pizzas/bluebook/pizzas.hecksagon +5 -3
- data/examples/pizzas/config.ru +22 -0
- data/lib/hecks/adapters/driven/prism.rb +0 -2
- data/lib/hecks/adapters/driven.rb +0 -2
- data/lib/hecks/adapters/driving/json.rb +1 -7
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +15 -5
- 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/named_pattern.rb +33 -0
- 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 +3 -0
- data/lib/hecks/chapters.rb +3 -3
- data/lib/hecks/cli/domain_stub.rb +1 -1
- data/lib/hecks/cli/interview_aggregate.rb +4 -4
- 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/deploy/bluebook/deploy.bluebook +34 -34
- 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/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/framework/bluebook/change.bluebook +2 -2
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +0 -3
- data/lib/hecks/fuzzing/domain_generator/block_rendering.rb +1 -1
- 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/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 +58 -37
- 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 +15 -92
- data/lib/hecks/hecks/hecks.bluebook +2 -2
- data/lib/hecks/hecks/hecks.hecksagon +2 -2
- data/lib/hecks/hecks/hecks.world +1 -1
- data/lib/hecks/hecks/rehearsal.bluebook +10 -16
- data/lib/hecks/hecks/tooling.bluebook +22 -22
- data/lib/hecks/kernel_capabilities/templates.rb +4 -4
- data/lib/hecks/kernel_capabilities.rb +1 -1
- data/lib/hecks/language/bluebook/aggregate.bluebook +1 -0
- data/lib/hecks/language/bluebook/command.bluebook +1 -0
- data/lib/hecks/language/bluebook/entity.bluebook +1 -0
- data/lib/hecks/language/bluebook/query.bluebook +1 -0
- data/lib/hecks/language/bluebook/shape.bluebook +8 -7
- data/lib/hecks/language/bluebook/vocabulary.bluebook +2 -3
- data/lib/hecks/language/hecksagon/port_operation.bluebook +1 -0
- 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/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/quality_control/quality_control.bluebook +51 -51
- 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.rb +13 -0
- 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/site/bluebook/site.bluebook +5 -5
- data/lib/hecks/sme/bluebook/sme.bluebook +12 -12
- data/lib/hecks/tickets/bluebook/tickets.bluebook +3 -3
- 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/{58208399bada10b0db99928c48e38789288fc58f7b71a72b21bfa386680e32ed.marshal → 02c09c73b272c1fb5a9ae6d87bb5e5e529208e00f6b4b597dc2c0fd70a9c0964.marshal} +0 -0
- data/prebuilt/{2b59737fbdbd5f44edda0fddce2611f45d76087b3a7c782d1f1a17ea58c77948.marshal → bb84a835cf6a15136bad5ca03aa5bf93a5b92f5f72444618d3ef2412b987fb32.marshal} +0 -0
- data/prebuilt/verdicts-f1f10da14c8e8f01b4c9b7e3e2d74be8cd2bf4676364ed17fa16975d2a076d75.json +1 -0
- 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 +300 -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 +145 -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/queries.rs +601 -226
- data/rust/codegen/src/reactions.rs +268 -74
- data/rust/codegen/src/read_models.rs +606 -205
- 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 +990 -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 +191 -0
- data/rust/host/src/payments/tests.rs +1445 -275
- data/rust/host/src/payments.rs +409 -121
- 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 +483 -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 -0
- data/rust/parser/src/build/named_pattern.rs +26 -0
- 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 -510
- 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 +43 -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 +229 -112
- 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 +230 -148
- data/rust/src/kernel/mod.rs +46 -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/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/prebuilt/verdicts-48318ca172edd21443b1ad02801c697fff100b746e828f09db987647641a5df2.json +0 -1
- data/rust/host/src/approval.rs +0 -652
|
@@ -17,8 +17,14 @@ pub fn reference_specs(domain_name: &str, attributes: &[Json]) -> Vec<ReferenceS
|
|
|
17
17
|
.filter_map(|attr| {
|
|
18
18
|
let target = crate::naming::reference_target(crate::attr::type_name(attr))?;
|
|
19
19
|
let field = crate::attr::name(attr).to_string();
|
|
20
|
-
let as_name = field
|
|
21
|
-
|
|
20
|
+
let as_name = field
|
|
21
|
+
.strip_suffix("_id")
|
|
22
|
+
.map_or_else(|| field.clone(), str::to_string);
|
|
23
|
+
Some(ReferenceSpec {
|
|
24
|
+
field,
|
|
25
|
+
as_name,
|
|
26
|
+
target: format!("{domain_name}::{target}"),
|
|
27
|
+
})
|
|
22
28
|
})
|
|
23
29
|
.collect()
|
|
24
30
|
}
|
|
@@ -36,5 +42,12 @@ pub fn emit_reference_specs_literal(specs: &[ReferenceSpec]) -> String {
|
|
|
36
42
|
if specs.is_empty() {
|
|
37
43
|
return "&[]".to_string();
|
|
38
44
|
}
|
|
39
|
-
format!(
|
|
45
|
+
format!(
|
|
46
|
+
"&[{}]",
|
|
47
|
+
specs
|
|
48
|
+
.iter()
|
|
49
|
+
.map(emit_reference_spec)
|
|
50
|
+
.collect::<Vec<_>>()
|
|
51
|
+
.join(", ")
|
|
52
|
+
)
|
|
40
53
|
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
use crate::exemplar::Exemplar;
|
|
5
5
|
use crate::naming;
|
|
6
6
|
use crate::reference_specs::{self, ReferenceSpec};
|
|
7
|
+
use std::fmt::Write as _;
|
|
7
8
|
|
|
8
9
|
#[derive(Clone)]
|
|
9
10
|
pub struct ReferenceCheck {
|
|
@@ -77,7 +78,6 @@ pub struct NestedEntityCommandEntry {
|
|
|
77
78
|
pub args_struct: String,
|
|
78
79
|
pub reference_checks: Vec<ReferenceCheck>,
|
|
79
80
|
pub reference_specs: Vec<ReferenceSpec>,
|
|
80
|
-
pub attributes: Vec<String>,
|
|
81
81
|
pub invariant_check_lines: Vec<String>,
|
|
82
82
|
pub role: Option<String>,
|
|
83
83
|
pub entity_name: String,
|
|
@@ -91,7 +91,6 @@ pub struct NestedEntityCommandEntry {
|
|
|
91
91
|
|
|
92
92
|
pub struct PortEntry {
|
|
93
93
|
pub verb: String,
|
|
94
|
-
pub name: String,
|
|
95
94
|
pub fn_name: String,
|
|
96
95
|
pub args_struct: String,
|
|
97
96
|
pub reference_checks: Vec<ReferenceCheck>,
|
|
@@ -146,32 +145,64 @@ pub fn emit_role_check(
|
|
|
146
145
|
|
|
147
146
|
/// Port of the retired Ruby generator's `registry.rb#emit_tenant_boundary_check`.
|
|
148
147
|
/// Hand-built: the target accessor differs for a single-attribute value object and a bare scalar.
|
|
148
|
+
// reason: the emitted Rust contains literal `{:?}` format templates, which the lint mistakes for unfilled arguments.
|
|
149
|
+
#[allow(clippy::literal_string_with_formatting_args)]
|
|
149
150
|
pub fn emit_tenant_boundary_check(check: &TenantBoundaryCheck) -> String {
|
|
150
151
|
let ref_ident = naming::rust_ident_field(&check.reference_field);
|
|
151
|
-
let own_expr = format!(
|
|
152
|
+
let own_expr = format!(
|
|
153
|
+
"args.{}.clone()",
|
|
154
|
+
naming::rust_ident_field(&check.own_accessor)
|
|
155
|
+
);
|
|
152
156
|
let target_expr = match check.target_accessor.split_once('.') {
|
|
153
157
|
Some((head, inner)) => format!(
|
|
154
158
|
"record.{}.as_ref().map(|v| v.{}.clone())",
|
|
155
159
|
naming::rust_ident_field(head),
|
|
156
160
|
naming::rust_ident_field(inner)
|
|
157
161
|
),
|
|
158
|
-
None => format!(
|
|
162
|
+
None => format!(
|
|
163
|
+
"record.{}.clone()",
|
|
164
|
+
naming::rust_ident_field(&check.target_accessor)
|
|
165
|
+
),
|
|
159
166
|
};
|
|
160
167
|
|
|
161
168
|
let mut out = String::new();
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
169
|
+
let _ = write!(
|
|
170
|
+
out,
|
|
171
|
+
"if let Some(record) = store.{}.find(&args.{ref_ident}) {{ ",
|
|
172
|
+
check.target_mod
|
|
173
|
+
);
|
|
174
|
+
let _ = write!(out, "if let Some(target_tenant) = {target_expr} {{ ");
|
|
175
|
+
let _ = write!(out, "let own_tenant = {own_expr}; ");
|
|
165
176
|
out.push_str("if target_tenant != own_tenant { ");
|
|
166
177
|
out.push_str(
|
|
167
178
|
"return Err(crate::kernel::Refusal::Unauthorized(crate::kernel::refusal_wording::UnauthorizedCrossTenantReferenceArgs { ",
|
|
168
179
|
);
|
|
169
|
-
|
|
170
|
-
|
|
180
|
+
let _ = write!(
|
|
181
|
+
out,
|
|
182
|
+
"aggregate: {}, ",
|
|
183
|
+
naming::ruby_inspect_string(&check.aggregate_name)
|
|
184
|
+
);
|
|
185
|
+
let _ = write!(
|
|
186
|
+
out,
|
|
187
|
+
"field: {}, ",
|
|
188
|
+
naming::ruby_inspect_string(&check.own_tenant_field)
|
|
189
|
+
);
|
|
171
190
|
out.push_str("tenant: &format!(\"{:?}\", own_tenant), ");
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
191
|
+
let _ = write!(
|
|
192
|
+
out,
|
|
193
|
+
"attribute: {}, ",
|
|
194
|
+
naming::ruby_inspect_string(&check.reference_field)
|
|
195
|
+
);
|
|
196
|
+
let _ = write!(
|
|
197
|
+
out,
|
|
198
|
+
"target: {}, ",
|
|
199
|
+
naming::ruby_inspect_string(&check.target_name)
|
|
200
|
+
);
|
|
201
|
+
let _ = write!(
|
|
202
|
+
out,
|
|
203
|
+
"target_field: {}, ",
|
|
204
|
+
naming::ruby_inspect_string(&check.target_tenant_field)
|
|
205
|
+
);
|
|
175
206
|
out.push_str("other: &format!(\"{:?}\", target_tenant)");
|
|
176
207
|
out.push_str(" }.render_args())); ");
|
|
177
208
|
out.push_str("} } }");
|
|
@@ -180,20 +211,32 @@ pub fn emit_tenant_boundary_check(check: &TenantBoundaryCheck) -> String {
|
|
|
180
211
|
|
|
181
212
|
/// `kernel::ArgumentGates` literal for one command (`registry.rb#emit_argument_gates_literal`).
|
|
182
213
|
/// The last two fields are closures because they need `store`, which exists only at the router.
|
|
183
|
-
fn emit_argument_gates_literal(
|
|
214
|
+
fn emit_argument_gates_literal(
|
|
215
|
+
args_path: &str,
|
|
216
|
+
invariant_check_lines: &[String],
|
|
217
|
+
role_line: Option<String>,
|
|
218
|
+
reference_lines: &[String],
|
|
219
|
+
) -> String {
|
|
184
220
|
let mut normalize = vec![format!("let args = {args_path}::from_json(v)?;")];
|
|
185
221
|
normalize.extend(invariant_check_lines.iter().map(|l| squeeze(l)));
|
|
186
222
|
normalize.push("Ok(args)".to_string());
|
|
187
223
|
|
|
188
|
-
let role =
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
let references: Vec<String> = reference_lines
|
|
224
|
+
let role = role_line.filter(|l| !l.is_empty()).map_or_else(
|
|
225
|
+
|| "&|| Ok(())".to_string(),
|
|
226
|
+
|line| format!("&|| {{ {} Ok(()) }}", squeeze(&line)),
|
|
227
|
+
);
|
|
228
|
+
let references: Vec<String> = reference_lines
|
|
229
|
+
.iter()
|
|
230
|
+
.filter(|l| !l.is_empty())
|
|
231
|
+
.map(|l| squeeze(l))
|
|
232
|
+
.collect();
|
|
193
233
|
let references = if references.is_empty() {
|
|
194
234
|
format!("&|_args: &{args_path}| Ok(())")
|
|
195
235
|
} else {
|
|
196
|
-
format!(
|
|
236
|
+
format!(
|
|
237
|
+
"&|args: &{args_path}| {{ {} Ok(()) }}",
|
|
238
|
+
references.join("\n")
|
|
239
|
+
)
|
|
197
240
|
};
|
|
198
241
|
|
|
199
242
|
format!(
|
|
@@ -201,13 +244,18 @@ fn emit_argument_gates_literal(args_path: &str, invariant_check_lines: &[String]
|
|
|
201
244
|
refuse_unknown_arguments: &{args_path}::refuse_unknown_arguments, \
|
|
202
245
|
refuse_absent_arguments: &{args_path}::refuse_absent_arguments, \
|
|
203
246
|
normalize_args: &|v: &crate::kernel::Json| {{ {} }}, refuse_role_mismatch: {role}, resolve_references: {references} }}",
|
|
204
|
-
normalize.join("
|
|
247
|
+
normalize.join("\n")
|
|
205
248
|
)
|
|
206
249
|
}
|
|
207
250
|
|
|
208
|
-
///
|
|
251
|
+
/// Trims the lines of an emitted check so it can sit inside a closure expression. The lines stay
|
|
252
|
+
/// separate: joined onto one, `}` and the `if` after it would read as a missing `else`.
|
|
209
253
|
fn squeeze(text: &str) -> String {
|
|
210
|
-
text.lines()
|
|
254
|
+
text.lines()
|
|
255
|
+
.map(str::trim)
|
|
256
|
+
.filter(|l| !l.is_empty())
|
|
257
|
+
.collect::<Vec<_>>()
|
|
258
|
+
.join("\n")
|
|
211
259
|
}
|
|
212
260
|
|
|
213
261
|
pub fn emit_reference_check(exemplar: &Exemplar, check: &ReferenceCheck) -> String {
|
|
@@ -259,6 +307,18 @@ fn chapter_path(a: &AggregateEntry) -> String {
|
|
|
259
307
|
format!("crate::generated::{}::{}", a.chapter_mod, a.module_name)
|
|
260
308
|
}
|
|
261
309
|
|
|
310
|
+
/// One `"verb" => { .. }` arm of the registry's dispatch match, its body lines indented.
|
|
311
|
+
fn verb_arm(verb: &str, body: &[String]) -> String {
|
|
312
|
+
format!(
|
|
313
|
+
" {} => {{\n{}\n }}",
|
|
314
|
+
naming::ruby_inspect_string(verb),
|
|
315
|
+
body.iter()
|
|
316
|
+
.map(|l| format!(" {l}"))
|
|
317
|
+
.collect::<Vec<_>>()
|
|
318
|
+
.join("\n")
|
|
319
|
+
)
|
|
320
|
+
}
|
|
321
|
+
|
|
262
322
|
pub fn emit_registry(exemplar: &Exemplar, aggregates: &[AggregateEntry]) -> String {
|
|
263
323
|
let store_fields: Vec<String> = aggregates
|
|
264
324
|
.iter()
|
|
@@ -352,10 +412,13 @@ pub fn emit_registry(exemplar: &Exemplar, aggregates: &[AggregateEntry]) -> Stri
|
|
|
352
412
|
)
|
|
353
413
|
})
|
|
354
414
|
.collect();
|
|
355
|
-
let extra_pass: String =
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
415
|
+
let extra_pass: String =
|
|
416
|
+
extra_idents
|
|
417
|
+
.iter()
|
|
418
|
+
.fold(String::new(), |mut passed, ident| {
|
|
419
|
+
let _ = write!(passed, "&{ident}, ");
|
|
420
|
+
passed
|
|
421
|
+
});
|
|
359
422
|
|
|
360
423
|
// A creating command's dispatch fn takes `route` so it can tell create from find.
|
|
361
424
|
let dispatch_call = format!(
|
|
@@ -394,8 +457,7 @@ pub fn emit_registry(exemplar: &Exemplar, aggregates: &[AggregateEntry]) -> Stri
|
|
|
394
457
|
&format!("{mod_path}::{}", c.args_struct),
|
|
395
458
|
&c.invariant_check_lines,
|
|
396
459
|
emit_role_check(exemplar, c.role.as_deref(), &c.name),
|
|
397
|
-
&c
|
|
398
|
-
.reference_checks
|
|
460
|
+
&c.reference_checks
|
|
399
461
|
.iter()
|
|
400
462
|
.map(|check| emit_reference_check(exemplar, check))
|
|
401
463
|
.collect::<Vec<_>>()
|
|
@@ -414,7 +476,8 @@ pub fn emit_registry(exemplar: &Exemplar, aggregates: &[AggregateEntry]) -> Stri
|
|
|
414
476
|
.map(emit_tenant_boundary_check)
|
|
415
477
|
.collect();
|
|
416
478
|
let tenant_boundary_check_line = if tenant_boundary_check_bodies.is_empty() {
|
|
417
|
-
"let tenant_boundary_check: Result<(), crate::kernel::Refusal> = Ok(());"
|
|
479
|
+
"let tenant_boundary_check: Result<(), crate::kernel::Refusal> = Ok(());"
|
|
480
|
+
.to_string()
|
|
418
481
|
} else {
|
|
419
482
|
format!(
|
|
420
483
|
"let tenant_boundary_check: Result<(), crate::kernel::Refusal> = \
|
|
@@ -462,14 +525,7 @@ pub fn emit_registry(exemplar: &Exemplar, aggregates: &[AggregateEntry]) -> Stri
|
|
|
462
525
|
"{dispatch_call}.map(|(_, events)| stamp_payload(events, &payload))"
|
|
463
526
|
));
|
|
464
527
|
|
|
465
|
-
aggregate_arms.push(
|
|
466
|
-
" {} => {{\n{}\n }}",
|
|
467
|
-
naming::ruby_inspect_string(&c.verb),
|
|
468
|
-
body.iter()
|
|
469
|
-
.map(|l| format!(" {l}"))
|
|
470
|
-
.collect::<Vec<_>>()
|
|
471
|
-
.join("\n")
|
|
472
|
-
));
|
|
528
|
+
aggregate_arms.push(verb_arm(&c.verb, &body));
|
|
473
529
|
}
|
|
474
530
|
}
|
|
475
531
|
|
|
@@ -484,8 +540,7 @@ pub fn emit_registry(exemplar: &Exemplar, aggregates: &[AggregateEntry]) -> Stri
|
|
|
484
540
|
&format!("{mod_path}::{}", c.args_struct),
|
|
485
541
|
&c.invariant_check_lines,
|
|
486
542
|
emit_role_check(exemplar, c.role.as_deref(), &c.name),
|
|
487
|
-
&c
|
|
488
|
-
.reference_checks
|
|
543
|
+
&c.reference_checks
|
|
489
544
|
.iter()
|
|
490
545
|
.map(|check| emit_reference_check(exemplar, check))
|
|
491
546
|
.collect::<Vec<_>>()
|
|
@@ -501,7 +556,10 @@ pub fn emit_registry(exemplar: &Exemplar, aggregates: &[AggregateEntry]) -> Stri
|
|
|
501
556
|
// A missing identity is wrapped as NotFound, as in the aggregate arm's `id_line`.
|
|
502
557
|
let entity_parent_no_identity_message = format!(
|
|
503
558
|
"{} acts on a {}'s {} — pass {}:",
|
|
504
|
-
c.name,
|
|
559
|
+
c.name,
|
|
560
|
+
a.record,
|
|
561
|
+
c.entity_name,
|
|
562
|
+
a.identified_by.join(", ")
|
|
505
563
|
);
|
|
506
564
|
let entity_element_no_identity_message = format!(
|
|
507
565
|
"{} acts on one {} — pass {}:",
|
|
@@ -519,7 +577,8 @@ pub fn emit_registry(exemplar: &Exemplar, aggregates: &[AggregateEntry]) -> Stri
|
|
|
519
577
|
);
|
|
520
578
|
|
|
521
579
|
let mut body: Vec<String> = vec![
|
|
522
|
-
"let invocation = crate::kernel::CommandInvocation::from_json(args_json)?;"
|
|
580
|
+
"let invocation = crate::kernel::CommandInvocation::from_json(args_json)?;"
|
|
581
|
+
.to_string(),
|
|
523
582
|
"let route = invocation.route();".to_string(),
|
|
524
583
|
"let facts_json = invocation.facts();".to_string(),
|
|
525
584
|
"if let Some(route) = route { route.require_depth(1)?; }".to_string(),
|
|
@@ -547,14 +606,7 @@ pub fn emit_registry(exemplar: &Exemplar, aggregates: &[AggregateEntry]) -> Stri
|
|
|
547
606
|
);
|
|
548
607
|
body.push(dispatch_call);
|
|
549
608
|
|
|
550
|
-
entity_arms.push(
|
|
551
|
-
" {} => {{\n{}\n }}",
|
|
552
|
-
naming::ruby_inspect_string(&c.verb),
|
|
553
|
-
body.iter()
|
|
554
|
-
.map(|l| format!(" {l}"))
|
|
555
|
-
.collect::<Vec<_>>()
|
|
556
|
-
.join("\n")
|
|
557
|
-
));
|
|
609
|
+
entity_arms.push(verb_arm(&c.verb, &body));
|
|
558
610
|
}
|
|
559
611
|
}
|
|
560
612
|
|
|
@@ -571,8 +623,7 @@ pub fn emit_registry(exemplar: &Exemplar, aggregates: &[AggregateEntry]) -> Stri
|
|
|
571
623
|
&format!("{mod_path}::{}", c.args_struct),
|
|
572
624
|
&c.invariant_check_lines,
|
|
573
625
|
emit_role_check(exemplar, c.role.as_deref(), &c.name),
|
|
574
|
-
&c
|
|
575
|
-
.reference_checks
|
|
626
|
+
&c.reference_checks
|
|
576
627
|
.iter()
|
|
577
628
|
.map(|check| emit_reference_check(exemplar, check))
|
|
578
629
|
.collect::<Vec<_>>()
|
|
@@ -587,7 +638,11 @@ pub fn emit_registry(exemplar: &Exemplar, aggregates: &[AggregateEntry]) -> Stri
|
|
|
587
638
|
// wrong-depth route first. A missing identity wraps into a per-hop NotFound message.
|
|
588
639
|
let entity_parent_no_identity_message = format!(
|
|
589
640
|
"{} acts on a {}'s {}.{} — pass {}:",
|
|
590
|
-
c.name,
|
|
641
|
+
c.name,
|
|
642
|
+
a.record,
|
|
643
|
+
c.entity_name,
|
|
644
|
+
c.nested_name,
|
|
645
|
+
a.identified_by.join(", ")
|
|
591
646
|
);
|
|
592
647
|
let hop1_no_identity_message = format!(
|
|
593
648
|
"{} acts on one {} — pass {}:",
|
|
@@ -622,7 +677,8 @@ pub fn emit_registry(exemplar: &Exemplar, aggregates: &[AggregateEntry]) -> Stri
|
|
|
622
677
|
};
|
|
623
678
|
|
|
624
679
|
let mut body: Vec<String> = vec![
|
|
625
|
-
"let invocation = crate::kernel::CommandInvocation::from_json(args_json)?;"
|
|
680
|
+
"let invocation = crate::kernel::CommandInvocation::from_json(args_json)?;"
|
|
681
|
+
.to_string(),
|
|
626
682
|
"let route = invocation.route();".to_string(),
|
|
627
683
|
"let facts_json = invocation.facts();".to_string(),
|
|
628
684
|
];
|
|
@@ -645,14 +701,7 @@ pub fn emit_registry(exemplar: &Exemplar, aggregates: &[AggregateEntry]) -> Stri
|
|
|
645
701
|
);
|
|
646
702
|
body.push(dispatch_call);
|
|
647
703
|
|
|
648
|
-
nested_entity_arms.push(
|
|
649
|
-
" {} => {{\n{}\n }}",
|
|
650
|
-
naming::ruby_inspect_string(&c.verb),
|
|
651
|
-
body.iter()
|
|
652
|
-
.map(|l| format!(" {l}"))
|
|
653
|
-
.collect::<Vec<_>>()
|
|
654
|
-
.join("\n")
|
|
655
|
-
));
|
|
704
|
+
nested_entity_arms.push(verb_arm(&c.verb, &body));
|
|
656
705
|
}
|
|
657
706
|
}
|
|
658
707
|
|
|
@@ -670,21 +719,19 @@ pub fn emit_registry(exemplar: &Exemplar, aggregates: &[AggregateEntry]) -> Stri
|
|
|
670
719
|
.legacy_receiver_field
|
|
671
720
|
.as_deref()
|
|
672
721
|
.map(naming::ruby_inspect_string)
|
|
673
|
-
.
|
|
674
|
-
.unwrap_or_else(|| "None".to_string());
|
|
722
|
+
.map_or_else(|| "None".to_string(), |field| format!("Some({field})"));
|
|
675
723
|
let to_receiver = p
|
|
676
724
|
.to_receiver_field
|
|
677
725
|
.as_deref()
|
|
678
726
|
.map(naming::ruby_inspect_string)
|
|
679
|
-
.
|
|
680
|
-
.unwrap_or_else(|| "None".to_string());
|
|
727
|
+
.map_or_else(|| "None".to_string(), |field| format!("Some({field})"));
|
|
681
728
|
|
|
682
729
|
let mut body: Vec<String> = vec![
|
|
683
730
|
"let invocation = crate::kernel::CommandInvocation::from_json(args_json)?;".to_string(),
|
|
684
731
|
format!("let (id, port_facts) = invocation.split_aggregate_receiver({legacy_receiver}, {to_receiver})?;"),
|
|
685
732
|
"let facts_json = &port_facts;".to_string(),
|
|
686
733
|
format!(
|
|
687
|
-
"let _instance = store.{}.find(&id).ok_or_else(|| crate::kernel::Refusal::NotFound(format!(\"{} {{:?}} does not exist\"
|
|
734
|
+
"let _instance = store.{}.find(&id).ok_or_else(|| crate::kernel::Refusal::NotFound(format!(\"{} {{id:?}} does not exist\")))?;",
|
|
688
735
|
a.module_name, a.name
|
|
689
736
|
),
|
|
690
737
|
format!("let args = {mod_path}::{}::from_json(facts_json)?;", p.args_struct),
|
|
@@ -696,14 +743,7 @@ pub fn emit_registry(exemplar: &Exemplar, aggregates: &[AggregateEntry]) -> Stri
|
|
|
696
743
|
);
|
|
697
744
|
body.push(dispatch_call);
|
|
698
745
|
|
|
699
|
-
port_arms.push(
|
|
700
|
-
" {} => {{\n{}\n }}",
|
|
701
|
-
naming::ruby_inspect_string(&p.verb),
|
|
702
|
-
body.iter()
|
|
703
|
-
.map(|l| format!(" {l}"))
|
|
704
|
-
.collect::<Vec<_>>()
|
|
705
|
-
.join("\n")
|
|
706
|
-
));
|
|
746
|
+
port_arms.push(verb_arm(&p.verb, &body));
|
|
707
747
|
}
|
|
708
748
|
}
|
|
709
749
|
|
|
@@ -729,7 +769,10 @@ pub fn emit_registry(exemplar: &Exemplar, aggregates: &[AggregateEntry]) -> Stri
|
|
|
729
769
|
("tmpl_dump_arm_placeholder();", dump_arms.join("\n")),
|
|
730
770
|
("tmpl_seed_arm_placeholder();", seed_arms.join("\n")),
|
|
731
771
|
("tmpl_query_arm_placeholder();", query_arms.join("\n")),
|
|
732
|
-
(
|
|
772
|
+
(
|
|
773
|
+
"tmpl_scan_each_arm_placeholder();",
|
|
774
|
+
scan_each_arms.join("\n"),
|
|
775
|
+
),
|
|
733
776
|
(
|
|
734
777
|
"\"tmpl_verb\" => { tmpl_dispatch_arm_placeholder() }",
|
|
735
778
|
dispatch_arms.join("\n"),
|
|
@@ -841,7 +884,6 @@ mod tests {
|
|
|
841
884
|
nested_entity_commands: Vec::new(),
|
|
842
885
|
ports: vec![PortEntry {
|
|
843
886
|
verb: "Banking::SafeDepositBox.PaymentGateway.Receive".to_string(),
|
|
844
|
-
name: "Receive".to_string(),
|
|
845
887
|
fn_name: "paymentgateway_receive".to_string(),
|
|
846
888
|
args_struct: "PaymentGatewayReceiveArgs".to_string(),
|
|
847
889
|
reference_checks: Vec::new(),
|
|
@@ -862,7 +904,9 @@ mod tests {
|
|
|
862
904
|
assert!(generated.contains("let box_number = facts_json.dig(\"box_number\")"));
|
|
863
905
|
// `from_json` is reached through the `normalize_args` hook; route depth leads the body and
|
|
864
906
|
// identity resolves after every gate.
|
|
865
|
-
assert!(generated.contains(
|
|
907
|
+
assert!(generated.contains(
|
|
908
|
+
"crate::kernel::decode_aggregate_arguments(facts_json, &crate::kernel::ArgumentGates {"
|
|
909
|
+
));
|
|
866
910
|
assert!(generated.contains("decode_arguments: &crate::generated::banking::safedepositbox::RentArgs::decode_arguments"));
|
|
867
911
|
assert!(generated.contains("refuse_unknown_arguments: &crate::generated::banking::safedepositbox::RentArgs::refuse_unknown_arguments"));
|
|
868
912
|
assert!(generated.contains("refuse_absent_arguments: &crate::generated::banking::safedepositbox::RentArgs::refuse_absent_arguments"));
|
|
@@ -870,7 +914,9 @@ mod tests {
|
|
|
870
914
|
assert!(generated.contains("if let Some(route) = route { route.require_depth(0)?; }"));
|
|
871
915
|
assert!(generated.contains("Some(route) => route.aggregate().to_string()"));
|
|
872
916
|
assert!(generated.contains("if let Some(route) = route { route.require_depth(1)?; }"));
|
|
873
|
-
assert!(generated.contains(
|
|
917
|
+
assert!(generated.contains(
|
|
918
|
+
"crate::kernel::decode_entity_arguments(facts_json, &crate::kernel::ArgumentGates {"
|
|
919
|
+
));
|
|
874
920
|
assert!(generated.contains("let element_id = route.entities()[0].clone()"));
|
|
875
921
|
assert!(generated.contains("VisitAnnotateArgs::from_json(v)?"));
|
|
876
922
|
assert!(!generated.contains("RentArgs::from_json(facts_json)?"));
|
|
@@ -950,7 +996,10 @@ mod tests {
|
|
|
950
996
|
chapter_mod: "banking".to_string(),
|
|
951
997
|
domain_name: "Banking".to_string(),
|
|
952
998
|
reference_specs: Vec::new(),
|
|
953
|
-
identified_by: vec![
|
|
999
|
+
identified_by: vec![
|
|
1000
|
+
"branch_code.value".to_string(),
|
|
1001
|
+
"box_number.value".to_string(),
|
|
1002
|
+
],
|
|
954
1003
|
entities: Vec::new(),
|
|
955
1004
|
};
|
|
956
1005
|
|
|
@@ -2,57 +2,13 @@
|
|
|
2
2
|
// CargoReservedName vocabularies (lib/hecks/language/bluebook/
|
|
3
3
|
// vocabulary.bluebook). Do not hand-edit — re-run hecks project_reserved_names.
|
|
4
4
|
|
|
5
|
-
/// The `RustReservedWord` vocabulary,
|
|
5
|
+
/// The `RustReservedWord` vocabulary, 51 entries.
|
|
6
6
|
pub const RUST_KEYWORDS: &[&str] = &[
|
|
7
|
-
"as",
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"dyn",
|
|
13
|
-
"else",
|
|
14
|
-
"enum",
|
|
15
|
-
"extern",
|
|
16
|
-
"false",
|
|
17
|
-
"fn",
|
|
18
|
-
"for",
|
|
19
|
-
"if",
|
|
20
|
-
"impl",
|
|
21
|
-
"in",
|
|
22
|
-
"let",
|
|
23
|
-
"loop",
|
|
24
|
-
"match",
|
|
25
|
-
"mod",
|
|
26
|
-
"move",
|
|
27
|
-
"mut",
|
|
28
|
-
"pub",
|
|
29
|
-
"ref",
|
|
30
|
-
"return",
|
|
31
|
-
"self",
|
|
32
|
-
"Self",
|
|
33
|
-
"static",
|
|
34
|
-
"struct",
|
|
35
|
-
"super",
|
|
36
|
-
"trait",
|
|
37
|
-
"true",
|
|
38
|
-
"type",
|
|
39
|
-
"unsafe",
|
|
40
|
-
"use",
|
|
41
|
-
"where",
|
|
42
|
-
"while",
|
|
43
|
-
"abstract",
|
|
44
|
-
"become",
|
|
45
|
-
"box",
|
|
46
|
-
"do",
|
|
47
|
-
"final",
|
|
48
|
-
"macro",
|
|
49
|
-
"override",
|
|
50
|
-
"priv",
|
|
51
|
-
"typeof",
|
|
52
|
-
"unsized",
|
|
53
|
-
"virtual",
|
|
54
|
-
"yield",
|
|
55
|
-
"try",
|
|
7
|
+
"as", "async", "await", "break", "const", "continue", "crate", "dyn", "else", "enum", "extern",
|
|
8
|
+
"false", "fn", "for", "if", "impl", "in", "let", "loop", "match", "mod", "move", "mut", "pub",
|
|
9
|
+
"ref", "return", "self", "Self", "static", "struct", "super", "trait", "true", "type",
|
|
10
|
+
"unsafe", "use", "where", "while", "abstract", "become", "box", "do", "final", "macro",
|
|
11
|
+
"override", "priv", "typeof", "unsized", "virtual", "yield", "try",
|
|
56
12
|
];
|
|
57
13
|
|
|
58
14
|
/// The `CargoReservedName` vocabulary, 16 entries.
|
data/rust/codegen/src/shared.rs
CHANGED
|
@@ -1,13 +1,55 @@
|
|
|
1
1
|
//! Predicates ported from the retired Ruby generator's `mutations.rb` that `types.rs` and `json_codec.rs` share.
|
|
2
2
|
|
|
3
3
|
use crate::json::Json;
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
/// Mirrors Ruby's `puts`: appends "\n" only when the string does not already end in one.
|
|
6
|
+
pub fn puts_str(out: &mut String, s: &str) {
|
|
7
|
+
out.push_str(s);
|
|
8
|
+
if !s.ends_with('\n') {
|
|
9
|
+
out.push('\n');
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
pub fn puts_blank(out: &mut String) {
|
|
14
|
+
out.push('\n');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/// The extra `(field, json)` pair a lifecycle contributes to a node's record fields.
|
|
18
|
+
pub fn lifecycle_extra_field(node: &Json) -> Vec<(String, String)> {
|
|
19
|
+
node.get("lifecycle")
|
|
20
|
+
.map_or_else(std::vec::Vec::new, |lifecycle| {
|
|
21
|
+
let field = lifecycle.get("field").and_then(Json::as_str).unwrap_or("");
|
|
22
|
+
let ident = crate::naming::rust_ident_field(field);
|
|
23
|
+
vec![(
|
|
24
|
+
crate::naming::rust_field(field),
|
|
25
|
+
format!("crate::kernel::Json::Str(self.{ident}.clone())"),
|
|
26
|
+
)]
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/// Ruby's `String#to_i`: parses a leading integer prefix, 0 when there is none.
|
|
31
|
+
pub fn ruby_to_i(s: &str) -> i64 {
|
|
32
|
+
let trimmed = s.trim_start();
|
|
33
|
+
let bytes = trimmed.as_bytes();
|
|
34
|
+
let mut end = 0;
|
|
35
|
+
if end < bytes.len() && (bytes[end] == b'-' || bytes[end] == b'+') {
|
|
36
|
+
end += 1;
|
|
37
|
+
}
|
|
38
|
+
let digits_start = end;
|
|
39
|
+
while end < bytes.len() && bytes[end].is_ascii_digit() {
|
|
40
|
+
end += 1;
|
|
41
|
+
}
|
|
42
|
+
if end == digits_start {
|
|
43
|
+
return 0;
|
|
44
|
+
}
|
|
45
|
+
trimmed[..end].parse().unwrap_or(0)
|
|
46
|
+
}
|
|
5
47
|
|
|
6
48
|
/// Argument names claimed by an append, so an append's element-field argument never bare-name-
|
|
7
49
|
/// matches an unrelated owner field. Shared by `creates_owner` and `identity_components`.
|
|
8
50
|
pub fn append_claimed_names(command: &Json) -> std::collections::HashSet<String> {
|
|
9
51
|
let mut claimed = std::collections::HashSet::new();
|
|
10
|
-
for m in command.get("mutations").
|
|
52
|
+
for m in command.get("mutations").map_or_default(Json::each) {
|
|
11
53
|
if m.get("op").map(Json::to_s).as_deref() != Some("append") {
|
|
12
54
|
continue;
|
|
13
55
|
}
|
|
@@ -28,26 +70,32 @@ pub fn append_claimed_names(command: &Json) -> std::collections::HashSet<String>
|
|
|
28
70
|
/// A command with `references` acts on an existing record. Otherwise it creates when it supplies,
|
|
29
71
|
/// via a `:set` mutation or a same-named argument, a required non-list owner field, ignoring
|
|
30
72
|
/// arguments claimed by an append (`append_claimed_names`).
|
|
31
|
-
pub fn creates_owner(aggregate: &Json, command: &Json
|
|
73
|
+
pub fn creates_owner(aggregate: &Json, command: &Json) -> bool {
|
|
32
74
|
if command.get("references").is_some() {
|
|
33
75
|
return false;
|
|
34
76
|
}
|
|
35
77
|
|
|
36
|
-
let attrs = aggregate.get("attributes").
|
|
37
|
-
let owner_fields: std::collections::HashSet<String> = attrs
|
|
38
|
-
|
|
39
|
-
|
|
78
|
+
let attrs = aggregate.get("attributes").map_or_default(Json::each);
|
|
79
|
+
let owner_fields: std::collections::HashSet<String> = attrs
|
|
80
|
+
.iter()
|
|
81
|
+
.map(|a| crate::attr::name(a).to_string())
|
|
82
|
+
.collect();
|
|
83
|
+
let required_fields: std::collections::HashSet<String> = attrs
|
|
84
|
+
.iter()
|
|
85
|
+
.filter(|a| !crate::attr::list(a) && !crate::attr::optional(a))
|
|
86
|
+
.map(|a| crate::attr::name(a).to_string())
|
|
87
|
+
.collect();
|
|
40
88
|
|
|
41
89
|
let append_claimed = append_claimed_names(command);
|
|
42
90
|
|
|
43
91
|
let mut known_writes: std::collections::HashSet<String> = std::collections::HashSet::new();
|
|
44
|
-
for attr in command.get("attributes").
|
|
92
|
+
for attr in command.get("attributes").map_or_default(Json::each) {
|
|
45
93
|
let name = crate::attr::name(attr).to_string();
|
|
46
94
|
if owner_fields.contains(&name) && !append_claimed.contains(&name) {
|
|
47
95
|
known_writes.insert(name);
|
|
48
96
|
}
|
|
49
97
|
}
|
|
50
|
-
for m in command.get("mutations").
|
|
98
|
+
for m in command.get("mutations").map_or_default(Json::each) {
|
|
51
99
|
if m.get("op").map(Json::to_s).as_deref() != Some("set") {
|
|
52
100
|
continue;
|
|
53
101
|
}
|
|
@@ -57,37 +105,38 @@ pub fn creates_owner(aggregate: &Json, command: &Json, _value_objects_by_name: &
|
|
|
57
105
|
}
|
|
58
106
|
}
|
|
59
107
|
|
|
60
|
-
required_fields
|
|
108
|
+
required_fields
|
|
109
|
+
.iter()
|
|
110
|
+
.any(|field| known_writes.contains(field))
|
|
61
111
|
}
|
|
62
112
|
|
|
63
113
|
/// Whether a list-typed attribute reads `nil` rather than `[]` in Ruby; see
|
|
64
114
|
/// `mutations.rb#list_attr_creation_optional?`.
|
|
65
|
-
pub fn list_attr_creation_optional(aggregate: &Json, attr_name: &str
|
|
66
|
-
let commands = aggregate.get("commands").
|
|
115
|
+
pub fn list_attr_creation_optional(aggregate: &Json, attr_name: &str) -> bool {
|
|
116
|
+
let commands = aggregate.get("commands").map_or_default(Json::each);
|
|
67
117
|
commands.iter().any(|command| {
|
|
68
|
-
if !creates_owner(aggregate, command
|
|
118
|
+
if !creates_owner(aggregate, command) {
|
|
69
119
|
return false;
|
|
70
120
|
}
|
|
71
|
-
let mutations = command.get("mutations").
|
|
121
|
+
let mutations = command.get("mutations").map_or_default(Json::each);
|
|
72
122
|
mutations.iter().any(|m| {
|
|
73
123
|
let op = m.get("op").map(Json::to_s).unwrap_or_default();
|
|
74
124
|
let target = m.get("target").map(Json::to_s).unwrap_or_default();
|
|
75
125
|
if op != "set" || target != attr_name {
|
|
76
126
|
return false;
|
|
77
127
|
}
|
|
78
|
-
let source =
|
|
79
|
-
|
|
80
|
-
None => return false,
|
|
128
|
+
let Some(source) = m.get("source") else {
|
|
129
|
+
return false;
|
|
81
130
|
};
|
|
82
131
|
if source.get("kind").map(Json::to_s).unwrap_or_default() != "argument" {
|
|
83
132
|
return false;
|
|
84
133
|
}
|
|
85
134
|
let source_name = source.get("name").map(Json::to_s).unwrap_or_default();
|
|
86
|
-
let attrs = command.get("attributes").
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
135
|
+
let attrs = command.get("attributes").map_or_default(Json::each);
|
|
136
|
+
attrs
|
|
137
|
+
.iter()
|
|
138
|
+
.find(|a| crate::attr::name(a) == source_name)
|
|
139
|
+
.is_some_and(crate::attr::optional)
|
|
91
140
|
})
|
|
92
141
|
})
|
|
93
142
|
}
|