hecks 3.14.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/examples/pizzas/adapters/fake_processor.rb +34 -0
- data/examples/pizzas/adapters/payment_signature.rb +66 -0
- data/examples/pizzas/adapters/payment_webhook.rb +87 -0
- data/examples/pizzas/bluebook/pizzas.behaviors +33 -0
- data/examples/pizzas/bluebook/pizzas.hecksagon +5 -3
- data/examples/pizzas/config.ru +22 -0
- data/exe/hecks +1 -1
- data/lib/hecks/adapters/driven/prism.rb +0 -2
- data/lib/hecks/adapters/driven.rb +0 -2
- data/lib/hecks/adapters/driving/json.rb +9 -16
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +2 -2
- data/lib/hecks/bluebook/dsl/derivation_words.rb +58 -0
- data/lib/hecks/bluebook/dsl/translation_builder.rb +6 -37
- data/lib/hecks/bluebook/expression/ast_json.rb +0 -9
- data/lib/hecks/bluebook/hecksagon.rb +1 -1
- data/lib/hecks/bluebook/meta_validator/translation_judge/rules.rb +7 -10
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +4 -8
- data/lib/hecks/bluebook/meta_validator.rb +0 -11
- data/lib/hecks/bluebook/translation.rb +18 -10
- data/lib/hecks/bluebook/translation_expression/evaluator.rb +63 -0
- data/lib/hecks/bluebook/translation_expression/parser.rb +127 -0
- data/lib/hecks/bluebook/translation_expression.rb +39 -0
- data/lib/hecks/bluebook/translation_plan.rb +70 -0
- data/lib/hecks/bluebook.rb +2 -0
- data/lib/hecks/chapters.rb +3 -3
- data/lib/hecks/cli/project_cli/launcher_source.rb +1 -1
- data/lib/hecks/composer/adapters/claude_composer.adapter +3 -0
- data/lib/hecks/composer/adapters/claude_composer.rb +83 -0
- data/lib/hecks/composer/adapters/fake_agent.adapter +3 -0
- data/lib/hecks/composer/adapters/fake_agent.rb +55 -0
- data/lib/hecks/composer/adapters/jev.adapter +3 -0
- data/lib/hecks/composer/adapters/jev.rb +66 -0
- data/lib/hecks/composer/agent.rb +113 -0
- data/lib/hecks/composer/assistant.rb +110 -0
- data/lib/hecks/composer/behavior_run.rb +53 -0
- data/lib/hecks/composer/bluebook/composer.bluebook +178 -0
- data/lib/hecks/composer/bluebook/composer.hecksagon +16 -0
- data/lib/hecks/composer/board.rb +65 -0
- data/lib/hecks/composer/board_edit/command_text.rb +39 -0
- data/lib/hecks/composer/board_edit/source.rb +84 -0
- data/lib/hecks/composer/board_edit.rb +144 -0
- data/lib/hecks/composer/check.rb +89 -0
- data/lib/hecks/composer/domains.rb +81 -0
- data/lib/hecks/composer/jobs.rb +134 -0
- data/lib/hecks/composer/layout.rb +37 -0
- data/lib/hecks/composer/live.rb +150 -0
- data/lib/hecks/composer/outline.rb +48 -0
- data/lib/hecks/composer/page.html +989 -0
- data/lib/hecks/composer/panels.rb +142 -0
- data/lib/hecks/composer/parse.rb +83 -0
- data/lib/hecks/composer/plain.rb +28 -0
- data/lib/hecks/composer/ports/composer_agent.port +7 -0
- data/lib/hecks/composer/session.rb +141 -0
- data/lib/hecks/composer/toolbox.rb +166 -0
- data/lib/hecks/composer/web.rb +156 -0
- data/lib/hecks/composer/workbench.rb +51 -0
- data/lib/hecks/composer/workspace.rb +81 -0
- data/lib/hecks/composer.rb +17 -0
- data/lib/hecks/corpus/adapters.rb +22 -0
- data/lib/hecks/corpus/rust.rb +1 -1
- data/lib/hecks/corpus/worlds.rb +19 -0
- data/lib/hecks/corpus.rb +5 -0
- data/lib/hecks/forms/app/commands.rb +2 -3
- data/lib/hecks/forms/app/queries.rb +2 -3
- data/lib/hecks/forms/app/records.rb +17 -5
- data/lib/hecks/forms/app/responses.rb +3 -1
- data/lib/hecks/forms/command_form_renderer.rb +42 -16
- data/lib/hecks/forms/field_controls.rb +2 -2
- data/lib/hecks/forms/field_renderer.rb +1 -1
- data/lib/hecks/forms/field_shape.rb +36 -17
- data/lib/hecks/forms/index_renderer.rb +2 -1
- data/lib/hecks/forms/page.rb +1 -1
- data/lib/hecks/forms/params.rb +0 -13
- data/lib/hecks/forms/query_form_renderer.rb +7 -6
- data/lib/hecks/forms/record_renderer.rb +11 -7
- data/lib/hecks/forms/rule_words.rb +100 -0
- data/lib/hecks/forms/value_object_shape.rb +2 -1
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +0 -3
- data/lib/hecks/fuzzing/isolated_boot/worlds.rb +3 -6
- data/lib/hecks/fuzzing/replay/filters.rb +2 -6
- data/lib/hecks/gate/stages.yml +25 -0
- data/lib/hecks/grammar/evolve/seeds.rb +0 -6
- data/lib/hecks/grammar/evolve.rb +0 -3
- data/lib/hecks/grammar/translation.bluebook +1 -1
- data/lib/hecks/hecks/adapters/codebase/composing.rb +56 -0
- data/lib/hecks/hecks/adapters/codebase/source_tree.rb +3 -1
- data/lib/hecks/hecks/adapters/first_admin.rb +3 -3
- data/lib/hecks/hecks/adapters/journal_store/changes.rb +0 -26
- data/lib/hecks/hecks/adapters/journal_store/examination.rb +2 -23
- data/lib/hecks/hecks/adapters/journal_store/readings.rb +3 -11
- data/lib/hecks/hecks/adapters/journal_store.rb +0 -11
- data/lib/hecks/hecks/adapters/rehearsal/inquiry.rb +4 -11
- data/lib/hecks/hecks/adapters/rehearsal/memory_store.rb +0 -9
- data/lib/hecks/hecks/adapters/rehearsal/postgres_reading.rb +0 -4
- data/lib/hecks/hecks/codebase.behaviors +6 -0
- data/lib/hecks/hecks/codebase.bluebook +21 -0
- data/lib/hecks/hecks/composer.behaviors +68 -0
- data/lib/hecks/hecks/context_map.hecksagon +1 -1
- data/lib/hecks/hecks/custodian.behaviors +0 -6
- data/lib/hecks/hecks/custodian.bluebook +3 -80
- data/lib/hecks/hecks/hecks.hecksagon +2 -2
- data/lib/hecks/hecks/hecks.world +1 -1
- data/lib/hecks/hecks/rehearsal.bluebook +7 -13
- data/lib/hecks/kernel_capabilities/templates.rb +4 -4
- data/lib/hecks/kernel_capabilities.rb +1 -1
- data/lib/hecks/language/bluebook/vocabulary.bluebook +2 -3
- data/lib/hecks/language/translation/translation_aggregate.bluebook +21 -24
- data/lib/hecks/ports/access_control.rb +2 -2
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +3 -41
- data/lib/hecks/ports/persistence/plugins/era/lineage/coverage.rb +7 -7
- data/lib/hecks/ports/persistence/plugins/era/lineage/rewrite.rb +25 -2
- data/lib/hecks/ports/persistence/plugins/era/lineage/rules.rb +4 -4
- data/lib/hecks/ports/persistence/plugins/era/lineage.rb +17 -25
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/base_sql.rb +0 -12
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/domain_rename.rb +1 -1
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +2 -22
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +0 -1
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +9 -3
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +3 -29
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/refusals.rb +2 -26
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +6 -33
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +3 -3
- data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +2 -4
- data/lib/hecks/ports/persistence/plugins/era/translation/expression_sql.rb +52 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +23 -29
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +1 -1
- data/lib/hecks/ports/persistence/plugins/era/translation.rb +0 -1
- data/lib/hecks/ports/persistence/plugins/era.rb +3 -6
- data/lib/hecks/ports.rb +0 -1
- data/lib/hecks/projection_files.rb +3 -1
- data/lib/hecks/projections/deploy/templates/lambda/nat_comment_oauth.tmpl +1 -1
- data/lib/hecks/projections/glossary/entries.rb +10 -4
- data/lib/hecks/projections/glossary/groups.rb +42 -0
- data/lib/hecks/projections/glossary/html/page.rb +7 -2
- data/lib/hecks/projections/glossary/html/parser.rb +1 -1
- data/lib/hecks/projections/glossary/html/sections.rb +31 -11
- data/lib/hecks/projections/glossary/html.rb +1 -2
- data/lib/hecks/projections/glossary/markdown.rb +31 -28
- data/lib/hecks/projections/glossary/mermaid.rb +3 -0
- data/lib/hecks/projections/glossary/page.css +143 -153
- data/lib/hecks/projections/glossary/page.js +63 -3
- data/lib/hecks/projections/glossary/sentences/actions.rb +88 -0
- data/lib/hecks/projections/glossary/sentences/paths.rb +77 -0
- data/lib/hecks/projections/glossary/sentences/queries.rb +90 -0
- data/lib/hecks/projections/glossary/sentences/value_objects.rb +32 -5
- data/lib/hecks/projections/glossary/sentences.rb +13 -3
- data/lib/hecks/projections/glossary/slugs.rb +4 -1
- data/lib/hecks/projections/glossary.rb +2 -1
- data/lib/hecks/projections/plain_english.rb +97 -0
- data/lib/hecks/projections/rust_vocabulary/arguments.rb +2 -2
- data/lib/hecks/projections/rust_vocabulary/enum_body.rb +20 -25
- data/lib/hecks/projections/rust_vocabulary/tests.rb +2 -2
- data/lib/hecks/projections/rust_vocabulary.rb +14 -6
- data/lib/hecks/projections/site/cms_editor/color.rb +0 -6
- data/lib/hecks/projections/site/cms_editor/templates/src/host.ts.tmpl +7 -2
- data/lib/hecks/projections/statements/actions.rb +99 -0
- data/lib/hecks/projections/statements/relationships.rb +86 -0
- data/lib/hecks/projections/statements.rb +15 -24
- data/lib/hecks/projector/exporter/adapters.rb +112 -0
- data/lib/hecks/projector/exporter/translations.rb +18 -35
- data/lib/hecks/projector/exporter/worlds.rb +103 -0
- data/lib/hecks/projector/exporter.rb +5 -1
- data/lib/hecks/quality_control/adapters/agent.rb +6 -7
- data/lib/hecks/quality_control/adapters/agent_profile.rb +16 -114
- data/lib/hecks/quality_control/cli/qa_mine_combinations.rb +1 -1
- data/lib/hecks/runtime/loader/boot_steps.rb +0 -20
- data/lib/hecks/runtime/outbox.rb +2 -9
- data/lib/hecks/runtime/port_operation_interpreter.rb +1 -9
- data/lib/hecks/runtime/remote_dispatcher.rb +2 -2
- data/lib/hecks/runtime/value/unwrapping.rb +60 -0
- data/lib/hecks/runtime/value.rb +10 -21
- data/lib/hecks/rust_build/project_rust/target_ir.rb +23 -4
- data/lib/hecks/rust_build/wasm.rb +0 -12
- data/lib/hecks/rust_build/write_if_changed.rb +12 -12
- data/lib/hecks/rust_format.rb +77 -0
- data/lib/hecks/tools/fuzz_sweep/findings.rb +3 -9
- data/lib/hecks/tools/fuzz_sweep.rb +1 -3
- data/lib/hecks/tools/translation_audit/verdicts.rb +3 -28
- data/lib/hecks/tools/translation_audit.rb +8 -10
- data/lib/hecks/tools/translation_scaffold/reporting.rb +1 -1
- data/lib/hecks/version.rb +1 -1
- data/lib/hecks/vocabulary.rb +2 -1
- data/lib/hecks.rb +3 -0
- data/prebuilt/{1a6f596be124228be93eba1d86dbc9d2933190fa389b284331241cb9f27115a0.marshal → ad68f9e06c8068c2a4176b57abf3083d591d5d8980c2b3e897b97331f965b643.marshal} +0 -0
- data/prebuilt/{a883a7e568667aa5ac904c82daff356bdf9e996252a36af6def4a4a43efb967e.marshal → ee22ec8642c4006717ba2b80f46e5efb81de459eef9dfec043e838c0ad4fd720.marshal} +0 -0
- data/prebuilt/{verdicts-872993b1aa26d73fe48f28251374b8585fed145d02aab42be0f00de386b6ff80.json → verdicts-b69f5dcbba79c20daa0493476fa7c317004a30aa2bc3d3dc6a722ea70ae2b43b.json} +1 -1
- data/rust/Cargo.toml +40 -0
- data/rust/build/Cargo.toml +36 -0
- data/rust/build/clippy.toml +4 -0
- data/rust/build/src/adapters_pass.rs +196 -0
- data/rust/build/src/binding_pass.rs +54 -13
- data/rust/build/src/build_artifact.rs +23 -5
- data/rust/build/src/cargo_sync.rs +69 -26
- data/rust/build/src/lineage_pass.rs +101 -87
- data/rust/build/src/main.rs +11 -2
- data/rust/build/src/pipeline.rs +80 -19
- data/rust/build/src/reserved_names.rs +6 -50
- data/rust/build/src/resolve.rs +82 -35
- data/rust/build/src/rustfmt.rs +100 -0
- data/rust/build/src/seams_pass.rs +85 -16
- data/rust/build/src/subprocess.rs +30 -6
- data/rust/build/src/tmp.rs +5 -2
- data/rust/build/src/translation_pass.rs +72 -22
- data/rust/build/src/world_pass.rs +109 -0
- data/rust/clippy.toml +4 -0
- data/rust/codegen/Cargo.toml +36 -0
- data/rust/codegen/clippy.toml +4 -0
- data/rust/codegen/src/asks.rs +40 -14
- data/rust/codegen/src/attr.rs +2 -2
- data/rust/codegen/src/bridging.rs +676 -117
- data/rust/codegen/src/commands.rs +979 -373
- data/rust/codegen/src/constraints.rs +88 -25
- data/rust/codegen/src/dependency_planning.rs +128 -34
- data/rust/codegen/src/domain_generator.rs +306 -170
- data/rust/codegen/src/exemplar.rs +87 -32
- data/rust/codegen/src/expr_emitter.rs +54 -6
- data/rust/codegen/src/fielded.rs +193 -41
- data/rust/codegen/src/hecks_naming.rs +124 -16
- data/rust/codegen/src/hecks_naming_tests.rs +81 -0
- data/rust/codegen/src/json.rs +80 -44
- data/rust/codegen/src/json_codec.rs +585 -149
- data/rust/codegen/src/literal.rs +104 -138
- data/rust/codegen/src/main.rs +151 -52
- data/rust/codegen/src/manifest.rs +76 -14
- data/rust/codegen/src/mutations.rs +1212 -239
- data/rust/codegen/src/naming.rs +111 -60
- data/rust/codegen/src/optional_pass.rs +90 -28
- data/rust/codegen/src/ports.rs +5 -4
- data/rust/codegen/src/prelude.rs +139 -49
- data/rust/codegen/src/presenters.rs +335 -0
- data/rust/codegen/src/queries.rs +601 -226
- data/rust/codegen/src/reactions.rs +268 -74
- data/rust/codegen/src/read_models.rs +574 -229
- data/rust/codegen/src/reference_specs.rs +16 -3
- data/rust/codegen/src/registry.rs +128 -79
- data/rust/codegen/src/reserved_names.rs +6 -50
- data/rust/codegen/src/shared.rs +71 -22
- data/rust/codegen/src/sidecars.rs +15 -5
- data/rust/codegen/src/skip_reason.rs +8 -2
- data/rust/codegen/src/types.rs +306 -108
- data/rust/host/Cargo.lock +1 -0
- data/rust/host/Cargo.toml +40 -4
- data/rust/host/HECKS_RELEASE +1 -1
- data/rust/host/clippy.toml +4 -0
- data/rust/host/src/api.rs +698 -218
- data/rust/host/src/auth.rs +491 -214
- data/rust/host/src/b64.rs +91 -0
- data/rust/host/src/bin/expr_harness.rs +6 -2
- data/rust/host/src/bin/lineage_harness.rs +47 -11
- data/rust/host/src/bin/mint_harness.rs +70 -37
- data/rust/host/src/bin/validate_harness.rs +16 -8
- data/rust/host/src/boot.rs +144 -68
- data/rust/host/src/boundary_fuzz/api.rs +130 -32
- data/rust/host/src/boundary_fuzz/auth.rs +108 -23
- data/rust/host/src/boundary_fuzz/expr_json.rs +85 -11
- data/rust/host/src/boundary_fuzz/ir.rs +164 -12
- data/rust/host/src/boundary_fuzz/server.rs +283 -36
- data/rust/host/src/boundary_fuzz/web.rs +56 -8
- data/rust/host/src/checkout.rs +78 -32
- data/rust/host/src/clock.rs +8 -0
- data/rust/host/src/commerce_ir.rs +333 -95
- data/rust/host/src/crypto.rs +33 -0
- data/rust/host/src/dispatch.rs +1037 -331
- data/rust/host/src/expr_json.rs +1021 -273
- data/rust/host/src/extension.rs +24 -4
- data/rust/host/src/field_hints.rs +11 -7
- data/rust/host/src/fuzz_support.rs +85 -13
- data/rust/host/src/host.rs +100 -0
- data/rust/host/src/http.rs +22 -0
- data/rust/host/src/ir.rs +326 -16
- data/rust/host/src/journal.rs +277 -176
- data/rust/host/src/lambda_client.rs +219 -53
- data/rust/host/src/lib.rs +16 -4
- data/rust/host/src/log.rs +62 -11
- data/rust/host/src/mint.rs +1153 -326
- data/rust/host/src/needs.rs +185 -44
- data/rust/host/src/payments/keystore.rs +197 -45
- data/rust/host/src/payments/roles.rs +39 -7
- data/rust/host/src/payments/tests.rs +1427 -282
- data/rust/host/src/payments.rs +391 -112
- data/rust/host/src/platform_signin/attack_tests.rs +5 -5
- data/rust/host/src/platform_signin/config.rs +11 -11
- data/rust/host/src/platform_signin/cookie.rs +2 -2
- data/rust/host/src/platform_signin/extension.rs +2 -1
- data/rust/host/src/platform_signin/handler.rs +9 -8
- data/rust/host/src/platform_signin/jti.rs +4 -2
- data/rust/host/src/platform_signin/jwks.rs +2 -1
- data/rust/host/src/platform_signin/jws.rs +2 -37
- data/rust/host/src/platform_signin/known_answer_tests.rs +5 -1
- data/rust/host/src/platform_signin/mode_tests.rs +5 -3
- data/rust/host/src/platform_signin/pages.rs +20 -20
- data/rust/host/src/platform_signin/session.rs +8 -1
- data/rust/host/src/platform_signin/settings.rs +6 -6
- data/rust/host/src/platform_signin/strict_json.rs +12 -9
- data/rust/host/src/platform_signin/verify.rs +41 -41
- data/rust/host/src/platform_signin/wiring_support.rs +13 -18
- data/rust/host/src/presentation.rs +207 -63
- data/rust/host/src/presentation_write.rs +502 -187
- data/rust/host/src/query_step.rs +69 -18
- data/rust/host/src/rate_limit.rs +541 -142
- data/rust/host/src/reference_transform.rs +77 -25
- data/rust/host/src/reference_validate.rs +443 -104
- data/rust/host/src/resend.rs +190 -55
- data/rust/host/src/secrets.rs +116 -21
- data/rust/host/src/server.rs +555 -104
- data/rust/host/src/storage_shape.rs +88 -21
- data/rust/host/src/test_pg.rs +17 -7
- data/rust/host/src/ui_schema.rs +495 -207
- data/rust/host/src/wasm_runner.rs +21 -6
- data/rust/host/src/web/commerce.rs +101 -23
- data/rust/host/src/web/newsletter/cooldown.rs +10 -3
- data/rust/host/src/web/newsletter/template.rs +26 -7
- data/rust/host/src/web/newsletter/tests.rs +112 -23
- data/rust/host/src/web/newsletter.rs +209 -59
- data/rust/host/src/web/newsletter_send.rs +251 -79
- data/rust/host/src/web/registration_receipt/tests.rs +16 -4
- data/rust/host/src/web/registration_receipt.rs +100 -24
- data/rust/host/src/web/registrations.rs +1384 -332
- data/rust/host/src/web/resend_webhook.rs +138 -80
- data/rust/host/src/web.rs +2416 -508
- data/rust/lsp/Cargo.toml +36 -0
- data/rust/lsp/clippy.toml +4 -0
- data/rust/lsp/src/json.rs +29 -22
- data/rust/lsp/src/main.rs +81 -31
- data/rust/lsp/src/outline.rs +23 -7
- data/rust/lsp/src/rpc.rs +17 -4
- data/rust/parser/Cargo.toml +36 -0
- data/rust/parser/clippy.toml +4 -0
- data/rust/parser/src/build/closed_sets.rs +6 -1
- data/rust/parser/src/build/identity.rs +4 -3
- data/rust/parser/src/build/mod.rs +1 -1
- data/rust/parser/src/build/named_pattern.rs +4 -1
- data/rust/parser/src/build/naming.rs +2 -80
- data/rust/parser/src/build/pattern_subset.rs +6 -8
- data/rust/parser/src/build/query_derive.rs +3 -1
- data/rust/parser/src/build/query_inference.rs +14 -5
- data/rust/parser/src/build/read_model.rs +4 -1
- data/rust/parser/src/build/references.rs +4 -3
- data/rust/parser/src/canonical.rs +34 -15
- data/rust/parser/src/emit.rs +187 -105
- data/rust/parser/src/expr/ast_json.rs +134 -42
- data/rust/parser/src/expr/evaluator.rs +27 -9
- data/rust/parser/src/expr/mod.rs +54 -13
- data/rust/parser/src/expr/resolver.rs +299 -92
- data/rust/parser/src/ir.rs +35 -3
- data/rust/parser/src/keywords.rs +7168 -516
- data/rust/parser/src/lex.rs +11 -25
- data/rust/parser/src/lib.rs +1 -0
- data/rust/parser/src/main.rs +65 -6
- data/rust/parser/src/parse/adapter.rs +157 -0
- data/rust/parser/src/parse/aggregate.rs +19 -10
- data/rust/parser/src/parse/chapter.rs +16 -17
- data/rust/parser/src/parse/command.rs +35 -16
- data/rust/parser/src/parse/domain_port.rs +4 -7
- data/rust/parser/src/parse/entity.rs +21 -11
- data/rust/parser/src/parse/hecksagon.rs +10 -4
- data/rust/parser/src/parse/lifecycle.rs +9 -5
- data/rust/parser/src/parse/mod.rs +22 -91
- data/rust/parser/src/parse/needs.rs +21 -12
- data/rust/parser/src/parse/policy.rs +2 -2
- data/rust/parser/src/parse/process_manager.rs +9 -11
- data/rust/parser/src/parse/query.rs +8 -4
- data/rust/parser/src/parse/read_model.rs +57 -16
- data/rust/parser/src/parse/value_object.rs +7 -2
- data/rust/parser/src/parse/world.rs +322 -0
- data/rust/parser/src/ruby_value.rs +7 -6
- data/rust/src/exemplar/json.rs +4 -4
- data/rust/src/exemplar/mutations.rs +18 -0
- data/rust/src/exemplar/read_models.rs +1 -10
- data/rust/src/kernel/attribute_shapes/composite.rs +54 -14
- data/rust/src/kernel/attribute_shapes/list.rs +2 -2
- data/rust/src/kernel/attribute_shapes/mod.rs +8 -8
- data/rust/src/kernel/attribute_shapes/scalar.rs +2 -2
- data/rust/src/kernel/cli.rs +244 -114
- data/rust/src/kernel/dispatch.rs +323 -115
- data/rust/src/kernel/dispatch_guard_tests.rs +327 -65
- data/rust/src/kernel/expr.rs +203 -86
- data/rust/src/kernel/expression_operators/arithmetic.rs +138 -25
- data/rust/src/kernel/expression_operators/comparison.rs +87 -2
- data/rust/src/kernel/expression_operators/enumeration.rs +106 -22
- data/rust/src/kernel/expression_operators/logical.rs +9 -3
- data/rust/src/kernel/expression_operators/membership.rs +3 -1
- data/rust/src/kernel/expression_operators/mod.rs +34 -21
- data/rust/src/kernel/expression_operators/pattern_match.rs +46 -11
- data/rust/src/kernel/expression_operators/positional.rs +84 -30
- data/rust/src/kernel/expression_operators/presence.rs +52 -13
- data/rust/src/kernel/expression_operators/sign_test.rs +3 -1
- data/rust/src/kernel/expression_operators/sized.rs +24 -8
- data/rust/src/kernel/expression_operators/text.rs +120 -27
- data/rust/src/kernel/expression_operators/to_string.rs +10 -4
- data/rust/src/kernel/json.rs +244 -148
- data/rust/src/kernel/mod.rs +47 -33
- data/rust/src/kernel/named_query.rs +63 -15
- data/rust/src/kernel/needs.rs +112 -25
- data/rust/src/kernel/orchestrate.rs +980 -581
- data/rust/src/kernel/orchestrate_guard_tests.rs +538 -140
- data/rust/src/kernel/pattern.rs +107 -34
- data/rust/src/kernel/present.rs +261 -0
- data/rust/src/kernel/query_comparators.rs +118 -41
- data/rust/src/kernel/query_ordering.rs +13 -5
- data/rust/src/kernel/read_model.rs +479 -181
- data/rust/src/kernel/reference_lookup.rs +122 -26
- data/rust/src/kernel/repository.rs +293 -66
- data/rust/src/kernel/routing.rs +58 -42
- data/rust/src/kernel/ruby_float.rs +139 -0
- data/rust/src/kernel/vocab/aggregate_dispatch_order.rs +57 -57
- data/rust/src/kernel/vocab/entity_dispatch_order.rs +54 -54
- data/rust/src/kernel/vocab/field_hint.rs +19 -25
- data/rust/src/kernel/vocab/mod.rs +6 -6
- data/rust/src/kernel/vocab/query_comparator.rs +23 -24
- data/rust/src/kernel/vocab/refusal_template.rs +501 -297
- data/rust/src/lib.rs +50 -0
- data/rust/src/main.rs +4 -1
- data/rust/web/Cargo.toml +36 -0
- data/rust/web/clippy.toml +4 -0
- metadata +85 -15
- data/examples/pizzas/bluebook/hecksagon/mock_stripe_payment_adapter.rb +0 -36
- data/lib/hecks/adapters/driven/google_authentication.adapter +0 -3
- data/lib/hecks/adapters/driven/google_authentication.rb +0 -99
- data/lib/hecks/adapters/driven/mock_stripe_adapter.adapter +0 -3
- data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +0 -32
- data/lib/hecks/ports/authentication.port +0 -6
- data/lib/hecks/ports/authentication.rb +0 -67
- data/lib/hecks/ports/persistence/plugins/era/translation/approval_file.rb +0 -255
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +0 -22
- data/rust/host/src/approval.rs +0 -652
data/rust/src/kernel/expr.rs
CHANGED
|
@@ -19,20 +19,20 @@ pub enum Value {
|
|
|
19
19
|
/// above. Produced by an array literal or `.split`; never used for
|
|
20
20
|
/// `.include?`'s haystack (`expr_emitter.rb`'s `emit_include` rewrites
|
|
21
21
|
/// that shape at codegen time instead).
|
|
22
|
-
Array(Vec<
|
|
22
|
+
Array(Vec<Self>),
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
impl Value {
|
|
26
26
|
/// Ruby's `Evaluator.truthy?`: false only for `Nil`/`Bool(false)`.
|
|
27
|
-
pub fn truthy(&self) -> bool {
|
|
28
|
-
!matches!(self,
|
|
27
|
+
pub const fn truthy(&self) -> bool {
|
|
28
|
+
!matches!(self, Self::Nil | Self::Bool(false))
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
/// The text of a `Str`, else `None`; a generated `SetProjectedField` arm narrows a
|
|
32
32
|
/// projected scalar to its field's type with one of the `into_*` readers.
|
|
33
33
|
pub fn into_string(self) -> Option<String> {
|
|
34
34
|
match self {
|
|
35
|
-
|
|
35
|
+
Self::Str(s) => Some(s),
|
|
36
36
|
_ => None,
|
|
37
37
|
}
|
|
38
38
|
}
|
|
@@ -40,7 +40,7 @@ impl Value {
|
|
|
40
40
|
/// The integer of an `Int`, else `None`.
|
|
41
41
|
pub fn into_i64(self) -> Option<i64> {
|
|
42
42
|
match self {
|
|
43
|
-
|
|
43
|
+
Self::Int(n) => Some(n),
|
|
44
44
|
_ => None,
|
|
45
45
|
}
|
|
46
46
|
}
|
|
@@ -48,7 +48,7 @@ impl Value {
|
|
|
48
48
|
/// The float of a `Float`, else `None`.
|
|
49
49
|
pub fn into_f64(self) -> Option<f64> {
|
|
50
50
|
match self {
|
|
51
|
-
|
|
51
|
+
Self::Float(f) => Some(f),
|
|
52
52
|
_ => None,
|
|
53
53
|
}
|
|
54
54
|
}
|
|
@@ -56,7 +56,7 @@ impl Value {
|
|
|
56
56
|
/// The boolean of a `Bool`, else `None`.
|
|
57
57
|
pub fn into_bool(self) -> Option<bool> {
|
|
58
58
|
match self {
|
|
59
|
-
|
|
59
|
+
Self::Bool(b) => Some(b),
|
|
60
60
|
_ => None,
|
|
61
61
|
}
|
|
62
62
|
}
|
|
@@ -94,7 +94,7 @@ pub struct Bound<'a> {
|
|
|
94
94
|
pub rest: &'a dyn Fielded,
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
impl
|
|
97
|
+
impl Fielded for Bound<'_> {
|
|
98
98
|
fn field(&self, name: &str) -> Option<Field<'_>> {
|
|
99
99
|
if name == self.name {
|
|
100
100
|
return Some(match &self.value {
|
|
@@ -130,7 +130,7 @@ pub struct WithOld<'a> {
|
|
|
130
130
|
pub old: &'a dyn Fielded,
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
impl
|
|
133
|
+
impl Fielded for WithOld<'_> {
|
|
134
134
|
fn field(&self, name: &str) -> Option<Field<'_>> {
|
|
135
135
|
if name == "old" {
|
|
136
136
|
return Some(Field::Nested(self.old));
|
|
@@ -154,7 +154,7 @@ pub struct StateFirst<'a> {
|
|
|
154
154
|
pub settled: &'a dyn Fielded,
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
-
impl
|
|
157
|
+
impl Fielded for StateFirst<'_> {
|
|
158
158
|
fn field(&self, name: &str) -> Option<Field<'_>> {
|
|
159
159
|
if self.settled.field(name).is_some() {
|
|
160
160
|
return None;
|
|
@@ -178,7 +178,7 @@ pub struct WithParent<'a> {
|
|
|
178
178
|
pub parent: &'a dyn Fielded,
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
-
impl
|
|
181
|
+
impl Fielded for WithParent<'_> {
|
|
182
182
|
fn field(&self, name: &str) -> Option<Field<'_>> {
|
|
183
183
|
if name == "parent" {
|
|
184
184
|
return Some(Field::Nested(self.parent));
|
|
@@ -210,11 +210,11 @@ pub enum BlockMode {
|
|
|
210
210
|
impl BlockMode {
|
|
211
211
|
/// The operator's own spelling, for the "expects a list" wording
|
|
212
212
|
/// Ruby renders from `"#{node.mode}?"`.
|
|
213
|
-
pub fn ruby_name(&self) -> &'static str {
|
|
213
|
+
pub const fn ruby_name(&self) -> &'static str {
|
|
214
214
|
match self {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
215
|
+
Self::All => "all?",
|
|
216
|
+
Self::Any => "any?",
|
|
217
|
+
Self::None => "none?",
|
|
218
218
|
}
|
|
219
219
|
}
|
|
220
220
|
}
|
|
@@ -225,61 +225,106 @@ impl BlockMode {
|
|
|
225
225
|
#[derive(Debug, Clone)]
|
|
226
226
|
#[allow(dead_code)]
|
|
227
227
|
pub enum Expr {
|
|
228
|
-
Or(Box<
|
|
229
|
-
And(Box<
|
|
230
|
-
Not(Box<
|
|
231
|
-
Compare {
|
|
232
|
-
|
|
228
|
+
Or(Box<Self>, Box<Self>),
|
|
229
|
+
And(Box<Self>, Box<Self>),
|
|
230
|
+
Not(Box<Self>),
|
|
231
|
+
Compare {
|
|
232
|
+
op: Comparison,
|
|
233
|
+
left: Box<Self>,
|
|
234
|
+
right: Box<Self>,
|
|
235
|
+
},
|
|
236
|
+
Include {
|
|
237
|
+
haystack: Box<Self>,
|
|
238
|
+
needle: Box<Self>,
|
|
239
|
+
},
|
|
233
240
|
Int(i64),
|
|
234
241
|
Float(f64),
|
|
235
242
|
Str(String),
|
|
236
243
|
Bool(bool),
|
|
237
244
|
Nil,
|
|
238
|
-
Add(Box<
|
|
239
|
-
Sub(Box<
|
|
240
|
-
Mul(Box<
|
|
245
|
+
Add(Box<Self>, Box<Self>),
|
|
246
|
+
Sub(Box<Self>, Box<Self>),
|
|
247
|
+
Mul(Box<Self>, Box<Self>),
|
|
241
248
|
/// Floor division on integers; a zero divisor is an evaluation fault.
|
|
242
|
-
Div(Box<
|
|
243
|
-
SignTest {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
249
|
+
Div(Box<Self>, Box<Self>),
|
|
250
|
+
SignTest {
|
|
251
|
+
op: Comparison,
|
|
252
|
+
receiver: Box<Self>,
|
|
253
|
+
},
|
|
254
|
+
Empty(Box<Self>),
|
|
255
|
+
ToS(Box<Self>),
|
|
256
|
+
Modulo {
|
|
257
|
+
receiver: Box<Self>,
|
|
258
|
+
divisor: Box<Self>,
|
|
259
|
+
},
|
|
260
|
+
Size(Box<Self>),
|
|
248
261
|
Lookup(&'static str),
|
|
249
262
|
/// `receiver.any?`/`.all?`/`.none? { |param| predicate }` —
|
|
250
263
|
/// evaluated once per element with `param` bound (see `Bound`).
|
|
251
|
-
BlockPredicate {
|
|
264
|
+
BlockPredicate {
|
|
265
|
+
mode: BlockMode,
|
|
266
|
+
receiver: Box<Self>,
|
|
267
|
+
param: &'static str,
|
|
268
|
+
predicate: Box<Self>,
|
|
269
|
+
},
|
|
252
270
|
/// `receiver.find { |param| predicate }.a.b` — first accepted
|
|
253
271
|
/// element, projected through `path`, or nil when nothing matches.
|
|
254
|
-
Find {
|
|
272
|
+
Find {
|
|
273
|
+
receiver: Box<Self>,
|
|
274
|
+
param: &'static str,
|
|
275
|
+
predicate: Box<Self>,
|
|
276
|
+
path: &'static [&'static str],
|
|
277
|
+
},
|
|
255
278
|
/// `[a, b, c]` — a leaf like `Int`/`Str`/`Nil` above, not an
|
|
256
279
|
/// operator, so `interpret` handles it directly, not via
|
|
257
280
|
/// `dispatch_operator`.
|
|
258
|
-
Array(Vec<
|
|
281
|
+
Array(Vec<Self>),
|
|
259
282
|
/// `receiver.match?(/pattern/flags)` — `pattern` is a real
|
|
260
283
|
/// Ruby-Regexp source, taken as-is between the slashes.
|
|
261
|
-
MatchesRegex {
|
|
284
|
+
MatchesRegex {
|
|
285
|
+
receiver: Box<Self>,
|
|
286
|
+
pattern: String,
|
|
287
|
+
flags: String,
|
|
288
|
+
},
|
|
262
289
|
/// `receiver.present?` / `.blank?` — one node for the pair,
|
|
263
290
|
/// `negated` distinguishing them (`.blank?` is `.present?` negated).
|
|
264
|
-
Presence {
|
|
291
|
+
Presence {
|
|
292
|
+
receiver: Box<Self>,
|
|
293
|
+
negated: bool,
|
|
294
|
+
},
|
|
265
295
|
/// `receiver.set?` / `.unset?` — narrower than `Presence`: only
|
|
266
296
|
/// `!receiver.is_nil()`. An assigned-but-empty value is `.set?`,
|
|
267
297
|
/// unlike `Presence`'s Rails-standard emptiness reading.
|
|
268
|
-
Assignment {
|
|
298
|
+
Assignment {
|
|
299
|
+
receiver: Box<Self>,
|
|
300
|
+
negated: bool,
|
|
301
|
+
},
|
|
269
302
|
/// `receiver.split("SEP")` — `Resolver::Split`. Produces a real
|
|
270
303
|
/// `Value::Array`, not `Value::List` — see that variant's own header.
|
|
271
|
-
Split {
|
|
304
|
+
Split {
|
|
305
|
+
receiver: Box<Self>,
|
|
306
|
+
separator: String,
|
|
307
|
+
},
|
|
272
308
|
/// `receiver.strip` / `.lstrip` / `.rstrip` — `Resolver::Strip`. Trims Ruby's own whitespace
|
|
273
309
|
/// set (see `StripSide::trim`), not Unicode spaces.
|
|
274
|
-
Strip {
|
|
310
|
+
Strip {
|
|
311
|
+
receiver: Box<Self>,
|
|
312
|
+
side: StripSide,
|
|
313
|
+
},
|
|
275
314
|
/// `receiver.start_with?("prefix")` — `Resolver::StartsWith`.
|
|
276
|
-
StartsWith {
|
|
315
|
+
StartsWith {
|
|
316
|
+
receiver: Box<Self>,
|
|
317
|
+
substring: String,
|
|
318
|
+
},
|
|
277
319
|
/// `receiver.end_with?("suffix")` — `Resolver::EndsWith`.
|
|
278
|
-
EndsWith {
|
|
320
|
+
EndsWith {
|
|
321
|
+
receiver: Box<Self>,
|
|
322
|
+
substring: String,
|
|
323
|
+
},
|
|
279
324
|
/// `receiver.first` — `Resolver::First`.
|
|
280
|
-
First(Box<
|
|
325
|
+
First(Box<Self>),
|
|
281
326
|
/// `receiver.last` — `Resolver::Last`.
|
|
282
|
-
Last(Box<
|
|
327
|
+
Last(Box<Self>),
|
|
283
328
|
}
|
|
284
329
|
|
|
285
330
|
/// Which ends of a string `Expr::Strip` trims: `.strip` both, `.lstrip` the start, `.rstrip` the end.
|
|
@@ -295,11 +340,12 @@ impl StripSide {
|
|
|
295
340
|
/// return and space, never a Unicode space. Rust's own `trim` would also drop U+00A0 and the
|
|
296
341
|
/// rest, so the set is spelled out.
|
|
297
342
|
pub fn trim(self, text: &str) -> String {
|
|
298
|
-
let is_stripped =
|
|
343
|
+
let is_stripped =
|
|
344
|
+
|c: char| matches!(c, '\0' | '\t' | '\n' | '\u{b}' | '\u{c}' | '\r' | ' ');
|
|
299
345
|
match self {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
346
|
+
Self::Both => text.trim_matches(is_stripped),
|
|
347
|
+
Self::Left => text.trim_start_matches(is_stripped),
|
|
348
|
+
Self::Right => text.trim_end_matches(is_stripped),
|
|
303
349
|
}
|
|
304
350
|
.to_string()
|
|
305
351
|
}
|
|
@@ -315,7 +361,7 @@ pub struct EvalContext<'a> {
|
|
|
315
361
|
/// Leaves (literals, `Lookup`) have no `OperatorCategory`, so they're
|
|
316
362
|
/// handled directly; everything else routes through `dispatch_operator`.
|
|
317
363
|
pub fn interpret(expr: &Expr, ctx: &EvalContext) -> Result<Value, Refusal> {
|
|
318
|
-
use Expr
|
|
364
|
+
use Expr::{Array, Bool, Float, Int, Lookup, Nil, Str};
|
|
319
365
|
match expr {
|
|
320
366
|
Int(v) => Ok(Value::Int(*v)),
|
|
321
367
|
Float(v) => Ok(Value::Float(*v)),
|
|
@@ -323,7 +369,12 @@ pub fn interpret(expr: &Expr, ctx: &EvalContext) -> Result<Value, Refusal> {
|
|
|
323
369
|
Bool(v) => Ok(Value::Bool(*v)),
|
|
324
370
|
Nil => Ok(Value::Nil),
|
|
325
371
|
Lookup(path) => lookup(path, ctx),
|
|
326
|
-
Array(elements) => Ok(Value::Array(
|
|
372
|
+
Array(elements) => Ok(Value::Array(
|
|
373
|
+
elements
|
|
374
|
+
.iter()
|
|
375
|
+
.map(|e| interpret(e, ctx))
|
|
376
|
+
.collect::<Result<Vec<_>, _>>()?,
|
|
377
|
+
)),
|
|
327
378
|
_ => dispatch_operator(category_of(expr), expr, ctx),
|
|
328
379
|
}
|
|
329
380
|
}
|
|
@@ -332,7 +383,11 @@ pub fn interpret(expr: &Expr, ctx: &EvalContext) -> Result<Value, Refusal> {
|
|
|
332
383
|
/// hand-written association `interpret` never calls for a leaf, hence
|
|
333
384
|
/// the `unreachable!` there rather than a wrong category.
|
|
334
385
|
fn category_of(expr: &Expr) -> OperatorCategory {
|
|
335
|
-
use Expr
|
|
386
|
+
use Expr::{
|
|
387
|
+
Add, And, Array, Assignment, BlockPredicate, Bool, Compare, Div, Empty, EndsWith, Find,
|
|
388
|
+
First, Float, Include, Int, Last, Lookup, MatchesRegex, Modulo, Mul, Nil, Not, Or,
|
|
389
|
+
Presence, SignTest, Size, Split, StartsWith, Str, Strip, Sub, ToS,
|
|
390
|
+
};
|
|
336
391
|
match expr {
|
|
337
392
|
Or(..) | And(..) | Not(..) => OperatorCategory::Logical,
|
|
338
393
|
Include { .. } => OperatorCategory::Membership,
|
|
@@ -356,22 +411,35 @@ fn category_of(expr: &Expr) -> OperatorCategory {
|
|
|
356
411
|
/// removing a variant (hecks project_kernel_capabilities) stops this
|
|
357
412
|
/// compiling until a matching arm exists. Never add one back — it would
|
|
358
413
|
/// silently route a new category to the wrong operator file.
|
|
359
|
-
fn dispatch_operator(
|
|
360
|
-
|
|
414
|
+
fn dispatch_operator(
|
|
415
|
+
category: OperatorCategory,
|
|
416
|
+
expr: &Expr,
|
|
417
|
+
ctx: &EvalContext,
|
|
418
|
+
) -> Result<Value, Refusal> {
|
|
419
|
+
use expression_operators::{
|
|
420
|
+
arithmetic, comparison, enumeration, logical, membership, pattern_match, positional,
|
|
421
|
+
presence, sign_test, sized, text, to_string, OperatorCategory,
|
|
422
|
+
};
|
|
361
423
|
match category {
|
|
362
424
|
OperatorCategory::Logical => logical::interpret(expr, ctx),
|
|
363
425
|
OperatorCategory::Membership => membership::interpret(expr, ctx),
|
|
364
426
|
OperatorCategory::Comparison => comparison::interpret(expr, ctx),
|
|
365
427
|
OperatorCategory::Arithmetic => match expr {
|
|
366
|
-
Expr::Add(..) | Expr::Sub(..) | Expr::Mul(..) | Expr::Div(..) =>
|
|
428
|
+
Expr::Add(..) | Expr::Sub(..) | Expr::Mul(..) | Expr::Div(..) => {
|
|
429
|
+
arithmetic::binary(expr, ctx)
|
|
430
|
+
}
|
|
367
431
|
Expr::Modulo { .. } => arithmetic::modulo(expr, ctx),
|
|
368
|
-
_ => Err(Refusal::TypeMismatch(format!(
|
|
432
|
+
_ => Err(Refusal::TypeMismatch(format!(
|
|
433
|
+
"dispatch_operator(Arithmetic, ..) called with {expr:?} — a router bug"
|
|
434
|
+
))),
|
|
369
435
|
},
|
|
370
436
|
OperatorCategory::SignTest => sign_test::interpret(expr, ctx),
|
|
371
437
|
OperatorCategory::Sized => match expr {
|
|
372
438
|
Expr::Empty(..) => sized::empty(expr, ctx),
|
|
373
439
|
Expr::Size(..) => sized::size(expr, ctx),
|
|
374
|
-
_ => Err(Refusal::TypeMismatch(format!(
|
|
440
|
+
_ => Err(Refusal::TypeMismatch(format!(
|
|
441
|
+
"dispatch_operator(Sized, ..) called with {expr:?} — a router bug"
|
|
442
|
+
))),
|
|
375
443
|
},
|
|
376
444
|
OperatorCategory::ToString => to_string::interpret(expr, ctx),
|
|
377
445
|
OperatorCategory::Enumeration => enumeration::interpret(expr, ctx),
|
|
@@ -379,7 +447,9 @@ fn dispatch_operator(category: OperatorCategory, expr: &Expr, ctx: &EvalContext)
|
|
|
379
447
|
OperatorCategory::Presence => match expr {
|
|
380
448
|
Expr::Presence { .. } => presence::interpret(expr, ctx),
|
|
381
449
|
Expr::Assignment { .. } => presence::interpret_assignment(expr, ctx),
|
|
382
|
-
_ => Err(Refusal::TypeMismatch(format!(
|
|
450
|
+
_ => Err(Refusal::TypeMismatch(format!(
|
|
451
|
+
"dispatch_operator(Presence, ..) called with {expr:?} — a router bug"
|
|
452
|
+
))),
|
|
383
453
|
},
|
|
384
454
|
OperatorCategory::Text => text::interpret(expr, ctx),
|
|
385
455
|
OperatorCategory::Positional => positional::interpret(expr, ctx),
|
|
@@ -398,15 +468,19 @@ fn lookup(path: &str, ctx: &EvalContext) -> Result<Value, Refusal> {
|
|
|
398
468
|
/// nested object, which `.set?` reads as present without needing it to be one value.
|
|
399
469
|
pub(crate) fn lookup_field<'a>(path: &str, ctx: &EvalContext<'a>) -> Result<Field<'a>, Refusal> {
|
|
400
470
|
let mut segments = path.split('.');
|
|
401
|
-
let head = segments.next().
|
|
471
|
+
let head = segments.next().unwrap_or_default();
|
|
402
472
|
let mut current = ctx
|
|
403
473
|
.args
|
|
404
474
|
.field(head)
|
|
405
475
|
.or_else(|| ctx.instance.field(head))
|
|
406
|
-
.ok_or_else(||
|
|
476
|
+
.ok_or_else(|| {
|
|
477
|
+
eval_error(format!(
|
|
478
|
+
"cannot resolve {head:?} — no such attribute or argument"
|
|
479
|
+
))
|
|
480
|
+
})?;
|
|
407
481
|
|
|
408
482
|
for seg in segments {
|
|
409
|
-
current = composite::step(current, seg, head)?;
|
|
483
|
+
current = composite::step(¤t, seg, head)?;
|
|
410
484
|
}
|
|
411
485
|
|
|
412
486
|
Ok(current)
|
|
@@ -415,34 +489,54 @@ pub(crate) fn lookup_field<'a>(path: &str, ctx: &EvalContext<'a>) -> Result<Fiel
|
|
|
415
489
|
/// The list-elements reading of a `Lookup` path — same head resolution
|
|
416
490
|
/// as `lookup`, then `Fielded::items` on the last segment (a list is
|
|
417
491
|
/// always the end of a path). `op` names the caller for error wording.
|
|
418
|
-
pub(crate) fn lookup_items<'a>(
|
|
419
|
-
|
|
420
|
-
|
|
492
|
+
pub(crate) fn lookup_items<'a>(
|
|
493
|
+
path: &str,
|
|
494
|
+
ctx: &EvalContext<'a>,
|
|
495
|
+
op: &str,
|
|
496
|
+
) -> Result<Vec<Field<'a>>, Refusal> {
|
|
497
|
+
let mut segments = path.split('.');
|
|
498
|
+
let head = segments.next().unwrap_or_default();
|
|
499
|
+
let rest: Vec<&str> = segments.collect();
|
|
421
500
|
|
|
422
|
-
|
|
501
|
+
let Some((last, middle)) = rest.split_last() else {
|
|
423
502
|
// args first when the key is present there, state second — a
|
|
424
503
|
// non-list argument refuses rather than falling through to a
|
|
425
504
|
// same-named instance list.
|
|
426
|
-
let side: &'a dyn Fielded = if ctx.args.field(head).is_some() {
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
.
|
|
430
|
-
|
|
505
|
+
let side: &'a dyn Fielded = if ctx.args.field(head).is_some() {
|
|
506
|
+
ctx.args
|
|
507
|
+
} else {
|
|
508
|
+
ctx.instance
|
|
509
|
+
};
|
|
510
|
+
return side.items(head).ok_or_else(|| {
|
|
511
|
+
eval_error(format!(
|
|
512
|
+
"{op} expects a list, got {}",
|
|
513
|
+
describe_field(side.field(head))
|
|
514
|
+
))
|
|
515
|
+
});
|
|
516
|
+
};
|
|
431
517
|
|
|
432
|
-
let (last, middle) = rest.split_last().unwrap();
|
|
433
518
|
let mut current = ctx
|
|
434
519
|
.args
|
|
435
520
|
.field(head)
|
|
436
521
|
.or_else(|| ctx.instance.field(head))
|
|
437
|
-
.ok_or_else(||
|
|
522
|
+
.ok_or_else(|| {
|
|
523
|
+
eval_error(format!(
|
|
524
|
+
"cannot resolve {head:?} — no such attribute or argument"
|
|
525
|
+
))
|
|
526
|
+
})?;
|
|
438
527
|
for seg in middle {
|
|
439
|
-
current = composite::step(current, seg, head)?;
|
|
528
|
+
current = composite::step(¤t, seg, head)?;
|
|
440
529
|
}
|
|
441
530
|
match current {
|
|
442
|
-
Field::Nested(obj) => obj
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
531
|
+
Field::Nested(obj) => obj.items(last).ok_or_else(|| {
|
|
532
|
+
eval_error(format!(
|
|
533
|
+
"{op} expects a list, got {}",
|
|
534
|
+
describe_field(obj.field(last))
|
|
535
|
+
))
|
|
536
|
+
}),
|
|
537
|
+
Field::Value(v) => Err(eval_error(format!(
|
|
538
|
+
"{path} — cannot look up {last:?} on scalar {v:?}"
|
|
539
|
+
))),
|
|
446
540
|
}
|
|
447
541
|
}
|
|
448
542
|
|
|
@@ -454,7 +548,7 @@ fn describe_field(field: Option<Field<'_>>) -> String {
|
|
|
454
548
|
}
|
|
455
549
|
}
|
|
456
550
|
|
|
457
|
-
pub(crate) fn eval_error(message: String) -> Refusal {
|
|
551
|
+
pub(crate) const fn eval_error(message: String) -> Refusal {
|
|
458
552
|
Refusal::Fault(message)
|
|
459
553
|
}
|
|
460
554
|
|
|
@@ -473,10 +567,9 @@ mod lookup_tests {
|
|
|
473
567
|
fn field(&self, name: &str) -> Option<Field<'_>> {
|
|
474
568
|
match name {
|
|
475
569
|
"previous_sessions" => Some(Field::Value(Value::Bool(self.previous_sessions))),
|
|
476
|
-
"first_time" => Some(
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
}),
|
|
570
|
+
"first_time" => Some(Field::Value(
|
|
571
|
+
self.first_time.map_or(Value::Nil, Value::Bool),
|
|
572
|
+
)),
|
|
480
573
|
_ => None,
|
|
481
574
|
}
|
|
482
575
|
}
|
|
@@ -484,7 +577,10 @@ mod lookup_tests {
|
|
|
484
577
|
|
|
485
578
|
fn not_nil(field: &'static str, instance: &dyn Fielded) -> Value {
|
|
486
579
|
let expr = Expr::Not(Box::new(Expr::Lookup(field)));
|
|
487
|
-
let ctx = EvalContext {
|
|
580
|
+
let ctx = EvalContext {
|
|
581
|
+
args: &NoFields,
|
|
582
|
+
instance,
|
|
583
|
+
};
|
|
488
584
|
interpret(&expr, &ctx).expect("evaluates without refusing")
|
|
489
585
|
}
|
|
490
586
|
|
|
@@ -493,8 +589,14 @@ mod lookup_tests {
|
|
|
493
589
|
// `!previous_sessions.nil?` — the live crash, `previous_sessions:
|
|
494
590
|
// true`: `.nil?` is not a dedicated node in this grammar, so this
|
|
495
591
|
// is really `Lookup("previous_sessions.nil?")` negated.
|
|
496
|
-
let attendee = Attendee {
|
|
497
|
-
|
|
592
|
+
let attendee = Attendee {
|
|
593
|
+
previous_sessions: true,
|
|
594
|
+
first_time: None,
|
|
595
|
+
};
|
|
596
|
+
assert_eq!(
|
|
597
|
+
not_nil("previous_sessions.nil?", &attendee),
|
|
598
|
+
Value::Bool(true)
|
|
599
|
+
);
|
|
498
600
|
}
|
|
499
601
|
|
|
500
602
|
#[test]
|
|
@@ -502,8 +604,14 @@ mod lookup_tests {
|
|
|
502
604
|
// The other half of the live crash: `false` is exactly as
|
|
503
605
|
// present as `true` — `walk_path` never inspects the value,
|
|
504
606
|
// only whether the head attribute resolved at all.
|
|
505
|
-
let attendee = Attendee {
|
|
506
|
-
|
|
607
|
+
let attendee = Attendee {
|
|
608
|
+
previous_sessions: false,
|
|
609
|
+
first_time: None,
|
|
610
|
+
};
|
|
611
|
+
assert_eq!(
|
|
612
|
+
not_nil("previous_sessions.nil?", &attendee),
|
|
613
|
+
Value::Bool(true)
|
|
614
|
+
);
|
|
507
615
|
}
|
|
508
616
|
|
|
509
617
|
#[test]
|
|
@@ -511,7 +619,10 @@ mod lookup_tests {
|
|
|
511
619
|
// Matches Ruby parity: `walk_path` breaks to `nil` off any
|
|
512
620
|
// scalar before inspecting `.nil?`'s segment name, so `!x.nil?`
|
|
513
621
|
// reads `true` for any head attribute that resolved, set or not.
|
|
514
|
-
let attendee = Attendee {
|
|
622
|
+
let attendee = Attendee {
|
|
623
|
+
previous_sessions: true,
|
|
624
|
+
first_time: None,
|
|
625
|
+
};
|
|
515
626
|
assert_eq!(not_nil("first_time.nil?", &attendee), Value::Bool(true));
|
|
516
627
|
}
|
|
517
628
|
|
|
@@ -519,9 +630,15 @@ mod lookup_tests {
|
|
|
519
630
|
fn a_genuinely_missing_head_attribute_still_refuses() {
|
|
520
631
|
// An undeclared head attribute is still a codegen bug, not a
|
|
521
632
|
// legitimate absent value — see `lookup`'s own doc comment.
|
|
522
|
-
let attendee = Attendee {
|
|
633
|
+
let attendee = Attendee {
|
|
634
|
+
previous_sessions: true,
|
|
635
|
+
first_time: None,
|
|
636
|
+
};
|
|
523
637
|
let expr = Expr::Not(Box::new(Expr::Lookup("not_a_real_attribute.nil?")));
|
|
524
|
-
let ctx = EvalContext {
|
|
638
|
+
let ctx = EvalContext {
|
|
639
|
+
args: &NoFields,
|
|
640
|
+
instance: &attendee,
|
|
641
|
+
};
|
|
525
642
|
assert!(interpret(&expr, &ctx).is_err());
|
|
526
643
|
}
|
|
527
644
|
}
|