hecks 3.14.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/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/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/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/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/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/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.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/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/{a883a7e568667aa5ac904c82daff356bdf9e996252a36af6def4a4a43efb967e.marshal → 02c09c73b272c1fb5a9ae6d87bb5e5e529208e00f6b4b597dc2c0fd70a9c0964.marshal} +0 -0
- data/prebuilt/{1a6f596be124228be93eba1d86dbc9d2933190fa389b284331241cb9f27115a0.marshal → bb84a835cf6a15136bad5ca03aa5bf93a5b92f5f72444618d3ef2412b987fb32.marshal} +0 -0
- data/prebuilt/{verdicts-872993b1aa26d73fe48f28251374b8585fed145d02aab42be0f00de386b6ff80.json → verdicts-f1f10da14c8e8f01b4c9b7e3e2d74be8cd2bf4676364ed17fa16975d2a076d75.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 +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 +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 +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 -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 +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 +82 -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
data/rust/codegen/src/types.rs
CHANGED
|
@@ -5,43 +5,75 @@ use crate::exemplar::Exemplar;
|
|
|
5
5
|
use crate::fielded;
|
|
6
6
|
use crate::json::Json;
|
|
7
7
|
use crate::naming;
|
|
8
|
+
use crate::shared::ruby_to_i;
|
|
8
9
|
use std::collections::HashMap;
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
#[allow(
|
|
12
|
+
clippy::panic,
|
|
13
|
+
reason = "a drifted exemplar or invalid IR aborts generation on purpose"
|
|
14
|
+
)]
|
|
15
|
+
pub fn emit_check_invariants(
|
|
16
|
+
exemplar: &Exemplar,
|
|
17
|
+
vo: &Json,
|
|
18
|
+
value_objects_by_name: &HashMap<String, &Json>,
|
|
19
|
+
aggregates_by_name: &HashMap<String, &Json>,
|
|
20
|
+
) -> String {
|
|
11
21
|
let name = naming::rust_ident(vo.get("name").and_then(Json::as_str).unwrap_or(""));
|
|
12
|
-
let type_name = vo
|
|
13
|
-
|
|
22
|
+
let type_name = vo
|
|
23
|
+
.get("name")
|
|
24
|
+
.and_then(Json::as_str)
|
|
25
|
+
.unwrap_or("")
|
|
26
|
+
.to_string();
|
|
27
|
+
let invariants = vo.get("invariants").map_or_default(Json::each);
|
|
14
28
|
|
|
15
29
|
// The block is flush left to match the Ruby heredoc's dedent; re-indenting it mismatches.
|
|
16
30
|
// Order: nested value objects, then `admits`/`pattern`, then invariants.
|
|
17
31
|
let mut body: Vec<String> = Vec::new();
|
|
18
|
-
let attributes = vo.get("attributes").
|
|
32
|
+
let attributes = vo.get("attributes").map_or_default(Json::each);
|
|
19
33
|
|
|
20
34
|
for attr in attributes {
|
|
21
35
|
if naming::effective_scalar_type(crate::attr::type_name(attr)).is_some() {
|
|
22
36
|
continue;
|
|
23
37
|
}
|
|
24
|
-
let is_plain_nested = matches!(value_objects_by_name.get(crate::attr::type_name(attr)), Some(v) if !v.get("closed_set").
|
|
38
|
+
let is_plain_nested = matches!(value_objects_by_name.get(crate::attr::type_name(attr)), Some(v) if !v.get("closed_set").is_some_and(Json::as_bool));
|
|
25
39
|
if !is_plain_nested {
|
|
26
40
|
continue;
|
|
27
41
|
}
|
|
28
42
|
let field = naming::rust_ident_field(crate::attr::name(attr));
|
|
29
|
-
body.push(nested_invariant_line(
|
|
43
|
+
body.push(nested_invariant_line(
|
|
44
|
+
&field,
|
|
45
|
+
crate::attr::list(attr),
|
|
46
|
+
crate::attr::optional(attr),
|
|
47
|
+
));
|
|
30
48
|
}
|
|
31
49
|
|
|
32
50
|
for attr in attributes {
|
|
33
51
|
if crate::attr::list(attr) {
|
|
34
52
|
let field = format!("self.{}", naming::rust_ident_field(crate::attr::name(attr)));
|
|
35
|
-
if let Some(line) =
|
|
53
|
+
if let Some(line) =
|
|
54
|
+
crate::constraints::emit_list_pattern_check(exemplar, &field, attr, &name)
|
|
55
|
+
{
|
|
36
56
|
body.push(format!(" {line}"));
|
|
37
57
|
}
|
|
38
58
|
continue;
|
|
39
59
|
}
|
|
40
60
|
let field = format!("self.{}", naming::rust_ident_field(crate::attr::name(attr)));
|
|
41
|
-
if let Some(line) = crate::constraints::emit_admits_check(
|
|
61
|
+
if let Some(line) = crate::constraints::emit_admits_check(
|
|
62
|
+
exemplar,
|
|
63
|
+
&field,
|
|
64
|
+
attr,
|
|
65
|
+
aggregates_by_name,
|
|
66
|
+
value_objects_by_name,
|
|
67
|
+
) {
|
|
42
68
|
body.push(format!(" {line}"));
|
|
43
69
|
}
|
|
44
|
-
if let Some(line) = crate::constraints::emit_pattern_check(
|
|
70
|
+
if let Some(line) = crate::constraints::emit_pattern_check(
|
|
71
|
+
exemplar,
|
|
72
|
+
&field,
|
|
73
|
+
attr,
|
|
74
|
+
&name,
|
|
75
|
+
value_objects_by_name,
|
|
76
|
+
) {
|
|
45
77
|
body.push(format!(" {line}"));
|
|
46
78
|
}
|
|
47
79
|
}
|
|
@@ -78,7 +110,7 @@ fn nested_invariant_line(field: &str, list: bool, optional: bool) -> String {
|
|
|
78
110
|
|
|
79
111
|
pub fn emit_closed_set_table(exemplar: &Exemplar, vo: &Json) -> String {
|
|
80
112
|
let name = naming::rust_ident(vo.get("name").and_then(Json::as_str).unwrap_or(""));
|
|
81
|
-
let attributes = vo.get("attributes").
|
|
113
|
+
let attributes = vo.get("attributes").map_or_default(Json::each);
|
|
82
114
|
|
|
83
115
|
let field_subs_list: Vec<Vec<(&str, String)>> = attributes
|
|
84
116
|
.iter()
|
|
@@ -87,12 +119,24 @@ pub fn emit_closed_set_table(exemplar: &Exemplar, vo: &Json) -> String {
|
|
|
87
119
|
if ty == "String" {
|
|
88
120
|
ty = "&'static str".to_string();
|
|
89
121
|
}
|
|
90
|
-
vec![
|
|
122
|
+
vec![
|
|
123
|
+
("TmplFieldType", ty),
|
|
124
|
+
(
|
|
125
|
+
"tmpl_field",
|
|
126
|
+
naming::rust_ident_field(crate::attr::name(attr)),
|
|
127
|
+
),
|
|
128
|
+
]
|
|
91
129
|
})
|
|
92
130
|
.collect();
|
|
93
|
-
let struct_part = exemplar.compose(
|
|
94
|
-
|
|
95
|
-
|
|
131
|
+
let struct_part = exemplar.compose(
|
|
132
|
+
"plain_struct",
|
|
133
|
+
&[("TmplType", name.clone())],
|
|
134
|
+
"struct_field",
|
|
135
|
+
&field_subs_list,
|
|
136
|
+
"\n",
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
let members = vo.get("members").map_or_default(Json::each);
|
|
96
140
|
let member_literals: Vec<String> = members
|
|
97
141
|
.iter()
|
|
98
142
|
.map(|row| {
|
|
@@ -111,44 +155,36 @@ pub fn emit_closed_set_table(exemplar: &Exemplar, vo: &Json) -> String {
|
|
|
111
155
|
.iter()
|
|
112
156
|
.map(|attr| {
|
|
113
157
|
let field_name = crate::attr::name(attr).to_string();
|
|
114
|
-
let raw_str: String =
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
None => String::new(),
|
|
158
|
+
let raw_str: String = present.get(&field_name).map_or_else(
|
|
159
|
+
|| {
|
|
160
|
+
crate::attr::default(attr)
|
|
161
|
+
.map_or_else(String::new, super::json::Json::to_s)
|
|
119
162
|
},
|
|
120
|
-
|
|
163
|
+
|v| v.to_s(),
|
|
164
|
+
);
|
|
121
165
|
let literal = match crate::attr::type_name(attr) {
|
|
122
166
|
"Integer" => ruby_to_i(&raw_str).to_string(),
|
|
123
167
|
"Float" => format!("{}f64", ruby_to_f(&raw_str)),
|
|
124
168
|
_ => naming::ruby_inspect_string(&raw_str),
|
|
125
169
|
};
|
|
126
|
-
exemplar.render(
|
|
170
|
+
exemplar.render(
|
|
171
|
+
"closed_set_table_row_field",
|
|
172
|
+
&[
|
|
173
|
+
("tmpl_field", naming::rust_ident_field(&field_name)),
|
|
174
|
+
("tmpl_value_placeholder()", literal),
|
|
175
|
+
],
|
|
176
|
+
)
|
|
127
177
|
})
|
|
128
178
|
.collect();
|
|
129
179
|
format!(" {name} {{ {} }},", fields.join(", "))
|
|
130
180
|
})
|
|
131
181
|
.collect();
|
|
132
182
|
|
|
133
|
-
format!(
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
let trimmed = s.trim_start();
|
|
139
|
-
let mut end = 0;
|
|
140
|
-
let bytes = trimmed.as_bytes();
|
|
141
|
-
if end < bytes.len() && (bytes[end] == b'-' || bytes[end] == b'+') {
|
|
142
|
-
end += 1;
|
|
143
|
-
}
|
|
144
|
-
let digits_start = end;
|
|
145
|
-
while end < bytes.len() && bytes[end].is_ascii_digit() {
|
|
146
|
-
end += 1;
|
|
147
|
-
}
|
|
148
|
-
if end == digits_start {
|
|
149
|
-
return 0;
|
|
150
|
-
}
|
|
151
|
-
trimmed[..end].parse().unwrap_or(0)
|
|
183
|
+
format!(
|
|
184
|
+
"{struct_part}\n\npub const {}: &[{name}] = &[\n{}\n];",
|
|
185
|
+
naming::screaming_snake(vo.get("name").and_then(Json::as_str).unwrap_or("")),
|
|
186
|
+
member_literals.join("\n")
|
|
187
|
+
)
|
|
152
188
|
}
|
|
153
189
|
|
|
154
190
|
/// Ruby's `String#to_f`: parses a leading float prefix, 0.0 when there is none.
|
|
@@ -182,17 +218,22 @@ fn ruby_to_f(s: &str) -> f64 {
|
|
|
182
218
|
trimmed[..end].parse().unwrap_or(0.0)
|
|
183
219
|
}
|
|
184
220
|
|
|
185
|
-
pub fn emit_value_object(
|
|
221
|
+
pub fn emit_value_object(
|
|
222
|
+
exemplar: &Exemplar,
|
|
223
|
+
vo: &Json,
|
|
224
|
+
value_objects_by_name: &HashMap<String, &Json>,
|
|
225
|
+
aggregates_by_name: &HashMap<String, &Json>,
|
|
226
|
+
) -> String {
|
|
186
227
|
let name = naming::rust_ident(vo.get("name").and_then(Json::as_str).unwrap_or(""));
|
|
187
|
-
let closed_set = vo.get("closed_set").
|
|
188
|
-
let attributes = vo.get("attributes").
|
|
228
|
+
let closed_set = vo.get("closed_set").is_some_and(Json::as_bool);
|
|
229
|
+
let attributes = vo.get("attributes").map_or_default(Json::each);
|
|
189
230
|
|
|
190
231
|
if closed_set {
|
|
191
232
|
if attributes.len() > 1 {
|
|
192
233
|
return emit_closed_set_table(exemplar, vo);
|
|
193
234
|
}
|
|
194
235
|
|
|
195
|
-
let members = vo.get("members").
|
|
236
|
+
let members = vo.get("members").map_or_default(Json::each);
|
|
196
237
|
let variants: Vec<String> = members
|
|
197
238
|
.iter()
|
|
198
239
|
.map(|row| {
|
|
@@ -202,9 +243,21 @@ pub fn emit_value_object(exemplar: &Exemplar, vo: &Json, value_objects_by_name:
|
|
|
202
243
|
naming::closed_set_variant(&value)
|
|
203
244
|
})
|
|
204
245
|
.collect();
|
|
205
|
-
let field_subs_list: Vec<Vec<(&str, String)>> = variants
|
|
206
|
-
|
|
207
|
-
|
|
246
|
+
let field_subs_list: Vec<Vec<(&str, String)>> = variants
|
|
247
|
+
.iter()
|
|
248
|
+
.map(|v| vec![("TmplMemberA", v.clone())])
|
|
249
|
+
.collect();
|
|
250
|
+
let enum_part = exemplar.compose(
|
|
251
|
+
"closed_set_enum",
|
|
252
|
+
&[("TmplKind", name)],
|
|
253
|
+
"closed_set_enum:VARIANT",
|
|
254
|
+
&field_subs_list,
|
|
255
|
+
"\n",
|
|
256
|
+
);
|
|
257
|
+
return format!(
|
|
258
|
+
"{enum_part}\n\n{}",
|
|
259
|
+
naming::emit_closed_set_fielded_impl(vo)
|
|
260
|
+
);
|
|
208
261
|
}
|
|
209
262
|
|
|
210
263
|
let field_subs_list: Vec<Vec<(&str, String)>> = attributes
|
|
@@ -214,24 +267,48 @@ pub fn emit_value_object(exemplar: &Exemplar, vo: &Json, value_objects_by_name:
|
|
|
214
267
|
if crate::attr::optional(attr) {
|
|
215
268
|
ty = format!("Option<{ty}>");
|
|
216
269
|
}
|
|
217
|
-
vec![
|
|
270
|
+
vec![
|
|
271
|
+
("TmplFieldType", ty),
|
|
272
|
+
(
|
|
273
|
+
"tmpl_field",
|
|
274
|
+
naming::rust_ident_field(crate::attr::name(attr)),
|
|
275
|
+
),
|
|
276
|
+
]
|
|
218
277
|
})
|
|
219
278
|
.collect();
|
|
220
|
-
let struct_part = exemplar.compose(
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
279
|
+
let struct_part = exemplar.compose(
|
|
280
|
+
"plain_struct",
|
|
281
|
+
&[("TmplType", name.clone())],
|
|
282
|
+
"struct_field",
|
|
283
|
+
&field_subs_list,
|
|
284
|
+
"\n",
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
let fielded_part =
|
|
288
|
+
fielded::emit_fielded_flat(exemplar, &name, attributes, value_objects_by_name, &[]);
|
|
289
|
+
let invariants_part =
|
|
290
|
+
emit_check_invariants(exemplar, vo, value_objects_by_name, aggregates_by_name);
|
|
224
291
|
|
|
225
292
|
format!("{struct_part}\n\n{fielded_part}\n\n{invariants_part}")
|
|
226
293
|
}
|
|
227
294
|
|
|
228
295
|
/// Attribute type names on `aggregate` that have no Rust mapping.
|
|
229
|
-
pub fn unsupported_attribute_types(
|
|
230
|
-
|
|
296
|
+
pub fn unsupported_attribute_types(
|
|
297
|
+
aggregate: &Json,
|
|
298
|
+
value_objects_by_name: &HashMap<String, &Json>,
|
|
299
|
+
) -> Vec<String> {
|
|
300
|
+
let entity_names: Vec<&str> = aggregate
|
|
301
|
+
.get("entities")
|
|
302
|
+
.map_or_default(Json::each)
|
|
303
|
+
.iter()
|
|
304
|
+
.map(|e| e.get("name").and_then(Json::as_str).unwrap_or(""))
|
|
305
|
+
.collect();
|
|
231
306
|
let mut seen = Vec::new();
|
|
232
|
-
for attr in aggregate.get("attributes").
|
|
307
|
+
for attr in aggregate.get("attributes").map_or_default(Json::each) {
|
|
233
308
|
let ty = crate::attr::type_name(attr);
|
|
234
|
-
let supported = naming::effective_scalar_type(ty).is_some()
|
|
309
|
+
let supported = naming::effective_scalar_type(ty).is_some()
|
|
310
|
+
|| value_objects_by_name.contains_key(ty)
|
|
311
|
+
|| (crate::attr::list(attr) && entity_names.contains(&ty));
|
|
235
312
|
if !supported && !seen.iter().any(|s| s == ty) {
|
|
236
313
|
seen.push(ty.to_string());
|
|
237
314
|
}
|
|
@@ -239,9 +316,13 @@ pub fn unsupported_attribute_types(aggregate: &Json, value_objects_by_name: &Has
|
|
|
239
316
|
seen
|
|
240
317
|
}
|
|
241
318
|
|
|
242
|
-
pub fn emit_entity(
|
|
319
|
+
pub fn emit_entity(
|
|
320
|
+
exemplar: &Exemplar,
|
|
321
|
+
entity: &Json,
|
|
322
|
+
value_objects_by_name: &HashMap<String, &Json>,
|
|
323
|
+
) -> String {
|
|
243
324
|
let name = naming::rust_ident(entity.get("name").and_then(Json::as_str).unwrap_or(""));
|
|
244
|
-
let attributes = entity.get("attributes").
|
|
325
|
+
let attributes = entity.get("attributes").map_or_default(Json::each);
|
|
245
326
|
|
|
246
327
|
let mut field_subs_list: Vec<Vec<(&str, String)>> = attributes
|
|
247
328
|
.iter()
|
|
@@ -250,49 +331,96 @@ pub fn emit_entity(exemplar: &Exemplar, entity: &Json, value_objects_by_name: &H
|
|
|
250
331
|
if crate::attr::optional(attr) {
|
|
251
332
|
ty = format!("Option<{ty}>");
|
|
252
333
|
}
|
|
253
|
-
vec![
|
|
334
|
+
vec![
|
|
335
|
+
("TmplFieldType", ty),
|
|
336
|
+
(
|
|
337
|
+
"tmpl_field",
|
|
338
|
+
naming::rust_ident_field(crate::attr::name(attr)),
|
|
339
|
+
),
|
|
340
|
+
]
|
|
254
341
|
})
|
|
255
342
|
.collect();
|
|
256
343
|
|
|
257
344
|
let mut lifecycle_arm: Vec<String> = Vec::new();
|
|
258
345
|
if let Some(lifecycle) = entity.get("lifecycle") {
|
|
259
346
|
let field = lifecycle.get("field").and_then(Json::as_str).unwrap_or("");
|
|
260
|
-
field_subs_list.push(vec![
|
|
347
|
+
field_subs_list.push(vec![
|
|
348
|
+
("TmplFieldType", "String".to_string()),
|
|
349
|
+
("tmpl_field", naming::rust_ident_field(field)),
|
|
350
|
+
]);
|
|
261
351
|
let ident = naming::rust_ident_field(field);
|
|
262
|
-
lifecycle_arm.push(format!(
|
|
352
|
+
lifecycle_arm.push(format!(
|
|
353
|
+
" \"{field}\" => Some(Field::Value(Value::Str(self.{ident}.clone()))),"
|
|
354
|
+
));
|
|
263
355
|
}
|
|
264
356
|
|
|
265
|
-
let struct_part = exemplar.compose(
|
|
266
|
-
|
|
357
|
+
let struct_part = exemplar.compose(
|
|
358
|
+
"plain_struct",
|
|
359
|
+
&[("TmplType", name.clone())],
|
|
360
|
+
"struct_field",
|
|
361
|
+
&field_subs_list,
|
|
362
|
+
"\n",
|
|
363
|
+
);
|
|
364
|
+
let fielded_part = fielded::emit_fielded_flat(
|
|
365
|
+
exemplar,
|
|
366
|
+
&name,
|
|
367
|
+
attributes,
|
|
368
|
+
value_objects_by_name,
|
|
369
|
+
&lifecycle_arm,
|
|
370
|
+
);
|
|
267
371
|
|
|
268
372
|
format!("{struct_part}\n\n{fielded_part}")
|
|
269
373
|
}
|
|
270
374
|
|
|
271
|
-
pub fn emit_record(
|
|
375
|
+
pub fn emit_record(
|
|
376
|
+
exemplar: &Exemplar,
|
|
377
|
+
aggregate: &Json,
|
|
378
|
+
value_objects_by_name: &HashMap<String, &Json>,
|
|
379
|
+
) -> String {
|
|
272
380
|
let name = naming::rust_ident(aggregate.get("name").and_then(Json::as_str).unwrap_or(""));
|
|
273
|
-
let attributes = aggregate.get("attributes").
|
|
381
|
+
let attributes = aggregate.get("attributes").map_or_default(Json::each);
|
|
274
382
|
|
|
275
383
|
let mut field_subs_list: Vec<Vec<(&str, String)>> = attributes
|
|
276
384
|
.iter()
|
|
277
385
|
.map(|attr| {
|
|
278
386
|
let list = crate::attr::list(attr);
|
|
279
387
|
let mut ty = naming::rust_type(crate::attr::type_name(attr), list);
|
|
280
|
-
if !list
|
|
388
|
+
if !list
|
|
389
|
+
|| crate::shared::list_attr_creation_optional(aggregate, crate::attr::name(attr))
|
|
390
|
+
{
|
|
281
391
|
ty = format!("Option<{ty}>");
|
|
282
392
|
}
|
|
283
|
-
vec![
|
|
393
|
+
vec![
|
|
394
|
+
("TmplFieldType", ty),
|
|
395
|
+
(
|
|
396
|
+
"tmpl_field",
|
|
397
|
+
naming::rust_ident_field(crate::attr::name(attr)),
|
|
398
|
+
),
|
|
399
|
+
]
|
|
284
400
|
})
|
|
285
401
|
.collect();
|
|
286
402
|
|
|
287
403
|
if let Some(lifecycle) = aggregate.get("lifecycle") {
|
|
288
404
|
let field = lifecycle.get("field").and_then(Json::as_str).unwrap_or("");
|
|
289
|
-
field_subs_list.push(vec![
|
|
405
|
+
field_subs_list.push(vec![
|
|
406
|
+
("TmplFieldType", "String".to_string()),
|
|
407
|
+
("tmpl_field", naming::rust_ident_field(field)),
|
|
408
|
+
]);
|
|
290
409
|
}
|
|
291
410
|
for ev in crate::bridging::correctable_event_names(aggregate) {
|
|
292
|
-
field_subs_list.push(vec![
|
|
411
|
+
field_subs_list.push(vec![
|
|
412
|
+
("TmplFieldType", "bool".to_string()),
|
|
413
|
+
("tmpl_field", crate::bridging::corrects_flag_field(&ev)),
|
|
414
|
+
]);
|
|
293
415
|
}
|
|
294
416
|
|
|
295
|
-
let struct_part = exemplar.compose(
|
|
417
|
+
let struct_part = exemplar.compose(
|
|
418
|
+
"plain_struct",
|
|
419
|
+
&[("TmplType", name)],
|
|
420
|
+
"struct_field",
|
|
421
|
+
&field_subs_list,
|
|
422
|
+
"\n",
|
|
423
|
+
);
|
|
296
424
|
let fielded_part = fielded::emit_fielded_record(exemplar, aggregate, value_objects_by_name);
|
|
297
425
|
|
|
298
426
|
format!("{struct_part}\n\n{fielded_part}")
|
|
@@ -304,12 +432,15 @@ pub fn emit_record(exemplar: &Exemplar, aggregate: &Json, value_objects_by_name:
|
|
|
304
432
|
/// field that resolves to nothing stays `String`.
|
|
305
433
|
pub fn with_resolved_projection_types(ir: &Json) -> Json {
|
|
306
434
|
let mut resolved = ir.clone();
|
|
307
|
-
let aggregates: Vec<Json> = ir.get("aggregates").
|
|
435
|
+
let aggregates: Vec<Json> = ir.get("aggregates").map_or_default(Json::each).to_vec();
|
|
308
436
|
let Some(list) = resolved.get_mut("aggregates").and_then(Json::as_array_mut) else {
|
|
309
437
|
return resolved;
|
|
310
438
|
};
|
|
311
439
|
for (aggregate, original) in list.iter_mut().zip(aggregates.iter()) {
|
|
312
|
-
let Some(fields) = aggregate
|
|
440
|
+
let Some(fields) = aggregate
|
|
441
|
+
.get_mut("projected_fields")
|
|
442
|
+
.and_then(Json::as_array_mut)
|
|
443
|
+
else {
|
|
313
444
|
continue;
|
|
314
445
|
};
|
|
315
446
|
for field in fields.iter_mut() {
|
|
@@ -325,26 +456,45 @@ const PROJECTION_CHAIN_LIMIT: usize = 8;
|
|
|
325
456
|
// The scalar type a projection of `field` copies, following a chain of projections.
|
|
326
457
|
fn projected_scalar_type(aggregates: &[Json], owner: &Json, field: &Json, depth: usize) -> String {
|
|
327
458
|
let reference = field.get("reference").and_then(Json::as_str).unwrap_or("");
|
|
328
|
-
let remote = field
|
|
459
|
+
let remote = field
|
|
460
|
+
.get("remote_field")
|
|
461
|
+
.and_then(Json::as_str)
|
|
462
|
+
.unwrap_or("");
|
|
329
463
|
let target = owner
|
|
330
464
|
.get("attributes")
|
|
331
|
-
.
|
|
332
|
-
.unwrap_or(&[])
|
|
465
|
+
.map_or_default(Json::each)
|
|
333
466
|
.iter()
|
|
334
467
|
.find(|a| crate::attr::name(a) == reference)
|
|
335
468
|
.and_then(|a| naming::reference_target(crate::attr::type_name(a)))
|
|
336
|
-
.and_then(|name|
|
|
337
|
-
|
|
469
|
+
.and_then(|name| {
|
|
470
|
+
aggregates
|
|
471
|
+
.iter()
|
|
472
|
+
.find(|a| a.get("name").and_then(Json::as_str) == Some(name))
|
|
473
|
+
});
|
|
474
|
+
target.map_or_else(
|
|
475
|
+
|| "String".to_string(),
|
|
476
|
+
|t| remote_scalar_type(aggregates, t, remote, depth),
|
|
477
|
+
)
|
|
338
478
|
}
|
|
339
479
|
|
|
340
480
|
fn remote_scalar_type(aggregates: &[Json], target: &Json, remote: &str, depth: usize) -> String {
|
|
341
|
-
let declared = target
|
|
481
|
+
let declared = target
|
|
482
|
+
.get("attributes")
|
|
483
|
+
.map_or_default(Json::each)
|
|
484
|
+
.iter()
|
|
485
|
+
.find(|a| crate::attr::name(a) == remote);
|
|
342
486
|
if let Some(attr) = declared {
|
|
343
487
|
return unwrapped_scalar_type(target, crate::attr::type_name(attr));
|
|
344
488
|
}
|
|
345
|
-
let chained = target
|
|
489
|
+
let chained = target
|
|
490
|
+
.get("projected_fields")
|
|
491
|
+
.map_or_default(Json::each)
|
|
492
|
+
.iter()
|
|
493
|
+
.find(|f| f.get("name").and_then(Json::as_str) == Some(remote));
|
|
346
494
|
match chained {
|
|
347
|
-
Some(next) if depth < PROJECTION_CHAIN_LIMIT =>
|
|
495
|
+
Some(next) if depth < PROJECTION_CHAIN_LIMIT => {
|
|
496
|
+
projected_scalar_type(aggregates, target, next, depth + 1)
|
|
497
|
+
}
|
|
348
498
|
_ => "String".to_string(),
|
|
349
499
|
}
|
|
350
500
|
}
|
|
@@ -353,16 +503,21 @@ fn remote_scalar_type(aggregates: &[Json], target: &Json, remote: &str, depth: u
|
|
|
353
503
|
fn unwrapped_scalar_type(aggregate: &Json, type_name: &str) -> String {
|
|
354
504
|
let sole = aggregate
|
|
355
505
|
.get("value_objects")
|
|
356
|
-
.
|
|
357
|
-
.unwrap_or(&[])
|
|
506
|
+
.map_or_default(Json::each)
|
|
358
507
|
.iter()
|
|
359
508
|
.find(|vo| vo.get("name").and_then(Json::as_str) == Some(type_name))
|
|
360
|
-
.map(|vo| vo.get("attributes").
|
|
509
|
+
.map(|vo| vo.get("attributes").map_or_default(Json::each))
|
|
361
510
|
.filter(|attrs| attrs.len() == 1 && !crate::attr::list(&attrs[0]))
|
|
362
511
|
.map(|attrs| crate::attr::type_name(&attrs[0]).to_string());
|
|
363
512
|
let resolved = sole.unwrap_or_else(|| type_name.to_string());
|
|
364
|
-
let resolved = if resolved == "Boolean" {
|
|
365
|
-
|
|
513
|
+
let resolved = if resolved == "Boolean" {
|
|
514
|
+
"TrueClass".to_string()
|
|
515
|
+
} else {
|
|
516
|
+
resolved
|
|
517
|
+
};
|
|
518
|
+
naming::effective_scalar_type(&resolved)
|
|
519
|
+
.unwrap_or("String")
|
|
520
|
+
.to_string()
|
|
366
521
|
}
|
|
367
522
|
|
|
368
523
|
// The `Value` reader a generated setter narrows with, by scalar type.
|
|
@@ -379,12 +534,19 @@ fn projection_reader(scalar: &str) -> &'static str {
|
|
|
379
534
|
pub fn projected_field_pseudo_attributes(aggregate: &Json) -> Vec<Json> {
|
|
380
535
|
aggregate
|
|
381
536
|
.get("projected_fields")
|
|
382
|
-
.
|
|
383
|
-
.unwrap_or(&[])
|
|
537
|
+
.map_or_default(Json::each)
|
|
384
538
|
.iter()
|
|
385
539
|
.map(|field| {
|
|
386
|
-
let name = field
|
|
387
|
-
|
|
540
|
+
let name = field
|
|
541
|
+
.get("name")
|
|
542
|
+
.and_then(Json::as_str)
|
|
543
|
+
.unwrap_or("")
|
|
544
|
+
.to_string();
|
|
545
|
+
let scalar = field
|
|
546
|
+
.get("type")
|
|
547
|
+
.and_then(Json::as_str)
|
|
548
|
+
.unwrap_or("String")
|
|
549
|
+
.to_string();
|
|
388
550
|
Json::Object(vec![
|
|
389
551
|
("name".to_string(), Json::String(name)),
|
|
390
552
|
("type".to_string(), Json::String(scalar)),
|
|
@@ -398,10 +560,17 @@ pub fn projected_field_pseudo_attributes(aggregate: &Json) -> Vec<Json> {
|
|
|
398
560
|
/// Copy of `aggregate` whose `attributes` also list the projected-field pseudo-attributes, so
|
|
399
561
|
/// `emit_record` and `emit_fielded_record` see them (`domain_generator.rb#record_for_struct`).
|
|
400
562
|
pub fn with_projected_field_pseudo_attributes(aggregate: &Json) -> Json {
|
|
401
|
-
let mut attributes: Vec<Json> = aggregate
|
|
563
|
+
let mut attributes: Vec<Json> = aggregate
|
|
564
|
+
.get("attributes")
|
|
565
|
+
.map_or_default(Json::each)
|
|
566
|
+
.to_vec();
|
|
402
567
|
attributes.extend(projected_field_pseudo_attributes(aggregate));
|
|
403
568
|
let mut pairs: Vec<(String, Json)> = match aggregate {
|
|
404
|
-
Json::Object(pairs) => pairs
|
|
569
|
+
Json::Object(pairs) => pairs
|
|
570
|
+
.iter()
|
|
571
|
+
.filter(|(k, _)| k != "attributes")
|
|
572
|
+
.cloned()
|
|
573
|
+
.collect(),
|
|
405
574
|
_ => Vec::new(),
|
|
406
575
|
};
|
|
407
576
|
pairs.push(("attributes".to_string(), Json::Array(attributes)));
|
|
@@ -413,14 +582,19 @@ pub fn emit_set_projected_field(aggregate: &Json) -> String {
|
|
|
413
582
|
let name = naming::rust_ident(aggregate.get("name").and_then(Json::as_str).unwrap_or(""));
|
|
414
583
|
let arms: Vec<String> = aggregate
|
|
415
584
|
.get("projected_fields")
|
|
416
|
-
.
|
|
417
|
-
.unwrap_or(&[])
|
|
585
|
+
.map_or_default(Json::each)
|
|
418
586
|
.iter()
|
|
419
587
|
.map(|field| {
|
|
420
588
|
let field_name = field.get("name").and_then(Json::as_str).unwrap_or("");
|
|
421
589
|
let ident = naming::rust_ident_field(field_name);
|
|
422
|
-
let reader =
|
|
423
|
-
|
|
590
|
+
let reader =
|
|
591
|
+
projection_reader(field.get("type").and_then(Json::as_str).unwrap_or("String"));
|
|
592
|
+
format!(
|
|
593
|
+
" {} => self.{} = value.and_then(crate::kernel::Value::{}),",
|
|
594
|
+
naming::ruby_inspect_string(field_name),
|
|
595
|
+
ident,
|
|
596
|
+
reader
|
|
597
|
+
)
|
|
424
598
|
})
|
|
425
599
|
.collect();
|
|
426
600
|
format!(
|
|
@@ -434,8 +608,7 @@ pub fn emit_projected_field_table(aggregate: &Json) -> String {
|
|
|
434
608
|
let name = naming::screaming_snake(aggregate.get("name").and_then(Json::as_str).unwrap_or(""));
|
|
435
609
|
let rows: Vec<String> = aggregate
|
|
436
610
|
.get("projected_fields")
|
|
437
|
-
.
|
|
438
|
-
.unwrap_or(&[])
|
|
611
|
+
.map_or_default(Json::each)
|
|
439
612
|
.iter()
|
|
440
613
|
.map(|field| {
|
|
441
614
|
let field_name = field.get("name").and_then(Json::as_str).unwrap_or("");
|
|
@@ -449,7 +622,10 @@ pub fn emit_projected_field_table(aggregate: &Json) -> String {
|
|
|
449
622
|
)
|
|
450
623
|
})
|
|
451
624
|
.collect();
|
|
452
|
-
format!(
|
|
625
|
+
format!(
|
|
626
|
+
"pub static {name}_PROJECTED_FIELDS: &[crate::kernel::ProjectedFieldSpec] = &[\n{}\n];\n",
|
|
627
|
+
rows.join("\n")
|
|
628
|
+
)
|
|
453
629
|
}
|
|
454
630
|
|
|
455
631
|
#[cfg(test)]
|
|
@@ -485,11 +661,14 @@ mod tests {
|
|
|
485
661
|
#[test]
|
|
486
662
|
fn boolean_attributes_round_trip_through_json_as_bool() {
|
|
487
663
|
let vo = flag_value_object();
|
|
488
|
-
let attributes = vo.get("attributes").
|
|
664
|
+
let attributes = vo.get("attributes").map_or_default(Json::each);
|
|
489
665
|
let by_name = HashMap::new();
|
|
490
666
|
let exemplar = Exemplar::load();
|
|
491
|
-
let from_json = crate::json_codec::emit_from_json_flat(
|
|
492
|
-
|
|
667
|
+
let from_json = crate::json_codec::emit_from_json_flat(
|
|
668
|
+
&exemplar, "Entry", attributes, &by_name, None, None, false, false, None, None,
|
|
669
|
+
);
|
|
670
|
+
let to_json =
|
|
671
|
+
crate::json_codec::emit_to_json_flat(&exemplar, "Entry", attributes, false, &[], None);
|
|
493
672
|
|
|
494
673
|
assert!(from_json.contains("x.as_bool()"));
|
|
495
674
|
assert!(!from_json.contains("TrueClass::from_json"));
|
|
@@ -510,12 +689,18 @@ mod tests {
|
|
|
510
689
|
}
|
|
511
690
|
|
|
512
691
|
fn resolved_booking() -> Json {
|
|
513
|
-
with_resolved_projection_types(&event_and_booking())
|
|
692
|
+
with_resolved_projection_types(&event_and_booking())
|
|
693
|
+
.get("aggregates")
|
|
694
|
+
.map_or_default(Json::each)[1]
|
|
695
|
+
.clone()
|
|
514
696
|
}
|
|
515
697
|
|
|
516
698
|
#[test]
|
|
517
699
|
fn a_projection_through_a_single_field_value_object_takes_its_scalar_type() {
|
|
518
|
-
let field = resolved_booking()
|
|
700
|
+
let field = resolved_booking()
|
|
701
|
+
.get("projected_fields")
|
|
702
|
+
.map_or_default(Json::each)[0]
|
|
703
|
+
.clone();
|
|
519
704
|
|
|
520
705
|
assert_eq!(field.get("type").and_then(Json::as_str), Some("Integer"));
|
|
521
706
|
}
|
|
@@ -531,7 +716,9 @@ mod tests {
|
|
|
531
716
|
fn the_projected_setter_narrows_with_the_remote_types_reader() {
|
|
532
717
|
let setter = emit_set_projected_field(&resolved_booking());
|
|
533
718
|
|
|
534
|
-
assert!(setter.contains(
|
|
719
|
+
assert!(setter.contains(
|
|
720
|
+
"\"starts_at\" => self.starts_at = value.and_then(crate::kernel::Value::into_i64),"
|
|
721
|
+
));
|
|
535
722
|
}
|
|
536
723
|
|
|
537
724
|
/// A value object holding a required and an optional value object, and a required and an
|
|
@@ -555,11 +742,22 @@ mod tests {
|
|
|
555
742
|
fn a_nested_value_object_checks_its_invariants_only_when_it_is_present() {
|
|
556
743
|
let note = Json::parse(r#"{"name":"Note","attributes":[{"name":"text","type":"String","list":false,"optional":false}]}"#).unwrap();
|
|
557
744
|
let by_name: HashMap<String, &Json> = HashMap::from([("Note".to_string(), ¬e)]);
|
|
558
|
-
let generated = emit_check_invariants(
|
|
745
|
+
let generated = emit_check_invariants(
|
|
746
|
+
&Exemplar::load(),
|
|
747
|
+
&holder_value_object(),
|
|
748
|
+
&by_name,
|
|
749
|
+
&HashMap::new(),
|
|
750
|
+
);
|
|
559
751
|
|
|
560
752
|
assert!(generated.contains("self.required_note.check_invariants()?;"));
|
|
561
|
-
assert!(
|
|
562
|
-
|
|
563
|
-
|
|
753
|
+
assert!(
|
|
754
|
+
generated.contains("if let Some(value) = &self.note { value.check_invariants()?; }")
|
|
755
|
+
);
|
|
756
|
+
assert!(
|
|
757
|
+
generated.contains("for item in &self.required_notes { item.check_invariants()?; }")
|
|
758
|
+
);
|
|
759
|
+
assert!(generated.contains(
|
|
760
|
+
"if let Some(items) = &self.notes { for item in items { item.check_invariants()?; } }"
|
|
761
|
+
));
|
|
564
762
|
}
|
|
565
763
|
}
|