hecks 1.4.0 → 1.5.1
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/lib/hecks/adapters/driven/claude_code.rb +65 -0
- data/lib/hecks/adapters/driven/folder.rb +73 -0
- data/lib/hecks/adapters/driven/google_authentication.rb +25 -4
- data/lib/hecks/adapters/driven/governance_authorization.rb +57 -6
- data/lib/hecks/adapters/driven/heki/journal.rb +8 -0
- data/lib/hecks/adapters/driven/heki/saga_store.rb +53 -7
- data/lib/hecks/adapters/driven/heki/snapshot.rb +1 -1
- data/lib/hecks/adapters/driven/heki.rb +93 -2
- data/lib/hecks/adapters/driven/identity_registry.rb +10 -0
- data/lib/hecks/adapters/driven/in_memory_ordering.rb +22 -0
- data/lib/hecks/adapters/driven/in_process_key_vault.adapter +3 -0
- data/lib/hecks/adapters/driven/in_process_key_vault.rb +53 -0
- data/lib/hecks/adapters/driven/lambda/client.rb +35 -7
- data/lib/hecks/adapters/driven/lambda.rb +43 -2
- data/lib/hecks/adapters/driven/local_storage.rb +67 -1
- data/lib/hecks/adapters/driven/memory.rb +13 -13
- data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +20 -0
- data/lib/hecks/adapters/driven/prism.rb +46 -7
- data/lib/hecks/adapters/driven/secure_random_identity.rb +3 -0
- data/lib/hecks/adapters/driven/system_clock.rb +3 -0
- data/lib/hecks/adapters/driven/tenant_provisioner.adapter +3 -0
- data/lib/hecks/adapters/driven/tenant_provisioner.rb +66 -0
- data/lib/hecks/adapters/driven.rb +2 -0
- data/lib/hecks/adapters/driving/github_webhook.rb +14 -1
- data/lib/hecks/behaviors/dsl.rb +58 -0
- data/lib/hecks/behaviors/expectations.rb +168 -7
- data/lib/hecks/behaviors/ir.rb +11 -0
- data/lib/hecks/behaviors/rspec.rb +8 -0
- data/lib/hecks/behaviors/runner.rb +19 -0
- data/lib/hecks/behaviors.rb +8 -0
- data/lib/hecks/bluebook/aggregate.rb +30 -0
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +7 -0
- data/lib/hecks/bluebook/assembly/build.rb +25 -4
- data/lib/hecks/bluebook/assembly/contract.rb +66 -14
- data/lib/hecks/bluebook/assembly/contracts.rb +24 -19
- data/lib/hecks/bluebook/assembly/marks.rb +143 -14
- data/lib/hecks/bluebook/assembly/specializer.rb +19 -0
- data/lib/hecks/bluebook/assembly.rb +25 -7
- data/lib/hecks/bluebook/attribute.rb +17 -3
- data/lib/hecks/bluebook/behaviour/aggregate.rb +32 -0
- data/lib/hecks/bluebook/behaviour/attribute.rb +13 -0
- data/lib/hecks/bluebook/behaviour/chapter.rb +55 -2
- data/lib/hecks/bluebook/behaviour/command.rb +33 -3
- data/lib/hecks/bluebook/behaviour/domain_port.rb +20 -0
- data/lib/hecks/bluebook/behaviour/entity.rb +12 -0
- data/lib/hecks/bluebook/behaviour/hexagon.rb +26 -0
- data/lib/hecks/bluebook/behaviour/lifecycle.rb +23 -2
- data/lib/hecks/bluebook/behaviour/policy.rb +34 -9
- data/lib/hecks/bluebook/behaviour/process_manager.rb +32 -1
- data/lib/hecks/bluebook/behaviour/query.rb +5 -0
- data/lib/hecks/bluebook/behaviour/read_model.rb +21 -0
- data/lib/hecks/bluebook/behaviour/traits.rb +36 -0
- data/lib/hecks/bluebook/behaviour/value_object.rb +17 -5
- data/lib/hecks/bluebook/chapter.rb +23 -0
- data/lib/hecks/bluebook/command.rb +53 -8
- data/lib/hecks/bluebook/domain_port.rb +25 -0
- data/lib/hecks/bluebook/dsl/aggregate_builder.rb +196 -37
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +79 -42
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +59 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +385 -53
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +135 -21
- data/lib/hecks/bluebook/dsl/command_builder.rb +189 -27
- data/lib/hecks/bluebook/dsl/entity_builder.rb +139 -9
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +41 -0
- data/lib/hecks/bluebook/dsl/identity_declaration.rb +23 -6
- data/lib/hecks/bluebook/dsl/policy_builder.rb +60 -19
- data/lib/hecks/bluebook/dsl/process_manager_builder.rb +79 -15
- data/lib/hecks/bluebook/dsl/query_builder.rb +33 -4
- data/lib/hecks/bluebook/dsl/read_model_builder.rb +102 -27
- data/lib/hecks/bluebook/dsl/rule_reference.rb +60 -8
- data/lib/hecks/bluebook/dsl/translation_builder.rb +146 -40
- data/lib/hecks/bluebook/dsl/value_object_builder.rb +55 -7
- data/lib/hecks/bluebook/entity.rb +29 -0
- data/lib/hecks/bluebook/expression/ast_json.rb +115 -23
- data/lib/hecks/bluebook/expression/ast_reader.rb +29 -0
- data/lib/hecks/bluebook/expression/canonical_form.rb +48 -9
- data/lib/hecks/bluebook/expression/evaluator.rb +207 -29
- data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +36 -0
- data/lib/hecks/bluebook/expression/resolver.rb +320 -77
- data/lib/hecks/bluebook/hexagon.rb +34 -0
- data/lib/hecks/bluebook/lifecycle.rb +11 -0
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -0
- data/lib/hecks/bluebook/meta_validator/judge.rb +30 -26
- data/lib/hecks/bluebook/meta_validator/plan.rb +43 -8
- data/lib/hecks/bluebook/meta_validator/port_judge.rb +1 -0
- data/lib/hecks/bluebook/meta_validator/readings.rb +157 -7
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +24 -4
- data/lib/hecks/bluebook/meta_validator/shapes.rb +141 -0
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +149 -23
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +5 -4
- data/lib/hecks/bluebook/meta_validator/world_judge.rb +1 -0
- data/lib/hecks/bluebook/meta_validator.rb +180 -84
- data/lib/hecks/bluebook/model_check.rb +268 -24
- data/lib/hecks/bluebook/pattern_subset.rb +23 -1
- data/lib/hecks/bluebook/process_manager.rb +13 -0
- data/lib/hecks/bluebook/project_discovery.rb +5 -0
- data/lib/hecks/bluebook/project_loader.rb +40 -0
- data/lib/hecks/bluebook/project_register.rb +44 -0
- data/lib/hecks/bluebook/query.rb +27 -0
- data/lib/hecks/bluebook/read_model.rb +21 -1
- data/lib/hecks/bluebook/reference.rb +21 -8
- data/lib/hecks/bluebook/smoke_test.rb +29 -6
- data/lib/hecks/bluebook/synthesizer.rb +34 -0
- data/lib/hecks/bluebook/translation.rb +30 -1
- data/lib/hecks/bluebook/value_object.rb +23 -5
- data/lib/hecks/bluebook.rb +3 -4
- data/lib/hecks/codemod.rb +107 -20
- data/lib/hecks/construct.rb +15 -1
- data/lib/hecks/corpus.rb +146 -25
- data/lib/hecks/deploy/bluebook/deploy.bluebook +105 -0
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +19 -0
- data/lib/hecks/deploy/oidc.json +5 -0
- data/lib/hecks/doc/reference.rb +185 -16
- data/lib/hecks/embryonaut_bluebook.rb +32 -9
- data/lib/hecks/facade/handle.rb +76 -3
- data/lib/hecks/facade/surface/aggregate_door.rb +8 -0
- data/lib/hecks/forms/field_shape.rb +3 -0
- data/lib/hecks/forms/page.rb +14 -0
- data/lib/hecks/forms/port_argument.rb +12 -0
- data/lib/hecks/forms/query_form_renderer.rb +63 -0
- data/lib/hecks/forms/record_renderer.rb +58 -0
- data/lib/hecks/forms/record_table.rb +27 -0
- data/lib/hecks/forms/reference_options.rb +24 -0
- data/lib/hecks/forms/value_object_shape.rb +10 -0
- data/lib/hecks/fqn.rb +58 -0
- data/lib/hecks/framework/bluebook/compliance.bluebook +221 -0
- data/lib/hecks/framework/bluebook/privacy.bluebook +155 -0
- data/lib/hecks/framework/oidc.json +15 -0
- data/lib/hecks/framework.rb +43 -20
- data/lib/hecks/freezer.rb +17 -1
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +159 -24
- data/lib/hecks/fuzzing/combination_miner.rb +59 -0
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +109 -8
- data/lib/hecks/fuzzing/coverage_campaign.rb +56 -13
- data/lib/hecks/fuzzing/differential.rb +34 -0
- data/lib/hecks/fuzzing/domain_generator.rb +188 -11
- data/lib/hecks/fuzzing/era_boundary.rb +45 -15
- data/lib/hecks/fuzzing/form_census.rb +86 -0
- data/lib/hecks/fuzzing/generated_domain_check.rb +76 -0
- data/lib/hecks/fuzzing/invalid_value_generator.rb +39 -0
- data/lib/hecks/fuzzing/isolated_boot.rb +79 -22
- data/lib/hecks/fuzzing/nondeterministic.rb +13 -1
- data/lib/hecks/fuzzing/persistence_parity.rb +95 -3
- data/lib/hecks/fuzzing/properties/corrections.rb +25 -0
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +158 -14
- data/lib/hecks/fuzzing/properties/guards.rb +44 -0
- data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +48 -0
- data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +18 -0
- data/lib/hecks/fuzzing/properties/outbox.rb +49 -11
- data/lib/hecks/fuzzing/properties/querying.rb +68 -14
- data/lib/hecks/fuzzing/properties.rb +24 -15
- data/lib/hecks/fuzzing/qa_settings.rb +12 -0
- data/lib/hecks/fuzzing/replay.rb +137 -29
- data/lib/hecks/fuzzing/rotation_priority.rb +41 -21
- data/lib/hecks/fuzzing/rust_gap_manifest.rb +46 -20
- data/lib/hecks/fuzzing/self_consistency.rb +189 -40
- data/lib/hecks/fuzzing/sequence_generator/adversary.rb +12 -6
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +1 -1
- data/lib/hecks/fuzzing/sequence_generator.rb +47 -0
- data/lib/hecks/fuzzing/shrinker.rb +114 -2
- data/lib/hecks/fuzzing/structural_skips.rb +18 -4
- data/lib/hecks/fuzzing/sweep_depth.rb +8 -0
- data/lib/hecks/fuzzing/target_capabilities.rb +61 -14
- data/lib/hecks/fuzzing/value_generator.rb +98 -10
- data/lib/hecks/grammar/evolve.rb +178 -2
- data/lib/hecks/grammar.rb +46 -0
- data/lib/hecks/ir.rb +38 -7
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +11 -0
- data/lib/hecks/literal.rb +32 -0
- data/lib/hecks/naming.rb +88 -7
- data/lib/hecks/ports/access_control.rb +5 -10
- data/lib/hecks/ports/authorization.rb +3 -6
- data/lib/hecks/ports/identity_assignment.rb +1 -2
- data/lib/hecks/ports/identity_resolution.rb +1 -2
- data/lib/hecks/ports/key_vault.port +6 -0
- data/lib/hecks/ports/key_vault.rb +85 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +6 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +38 -2
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +6 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +40 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +92 -1
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +15 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +8 -0
- data/lib/hecks/ports/query/in_memory.rb +39 -6
- data/lib/hecks/ports/query/ordering.rb +15 -0
- data/lib/hecks/ports.rb +1 -0
- data/lib/hecks/projections/bootstrap_table.rb +43 -8
- data/lib/hecks/projections/deploy/fargate.rb +666 -0
- data/lib/hecks/projections/deploy/lambda.rb +2423 -0
- data/lib/hecks/projections/deploy/shared.rb +624 -0
- data/lib/hecks/projections/diagrams.rb +243 -7
- data/lib/hecks/projections/glossary/html.rb +88 -0
- data/lib/hecks/projections/glossary/markdown.rb +47 -0
- data/lib/hecks/projections/glossary/mermaid.rb +48 -0
- data/lib/hecks/projections/glossary/sentences.rb +105 -0
- data/lib/hecks/projections/glossary.rb +161 -19
- data/lib/hecks/projections/model/deviations.rb +44 -0
- data/lib/hecks/projections/model.rb +51 -1
- data/lib/hecks/projections/oidc.rb +40 -3
- data/lib/hecks/projections/parser_table.rb +49 -8
- data/lib/hecks/projections/reference.rb +12 -0
- data/lib/hecks/projections/rust_vocabulary.rb +219 -16
- data/lib/hecks/projections/shape.rb +8 -0
- data/lib/hecks/projections/statements.rb +63 -16
- data/lib/hecks/projections/vocabulary.rb +17 -0
- data/lib/hecks/projections.rb +3 -0
- data/lib/hecks/projector/cli_projector.rb +218 -10
- data/lib/hecks/projector/docs_projector.rb +145 -19
- data/lib/hecks/projector/exporter.rb +65 -11
- data/lib/hecks/projector/ir_projector.rb +6 -0
- data/lib/hecks/projector/narrate_projector.rb +136 -15
- data/lib/hecks/projector/target.rb +47 -10
- data/lib/hecks/projector.rb +101 -19
- data/lib/hecks/query_ir.rb +47 -0
- data/lib/hecks/query_specification/common/null_policy.rb +5 -3
- data/lib/hecks/rendering.rb +6 -0
- data/lib/hecks/router/namespace_installer.rb +13 -0
- data/lib/hecks/router.rb +55 -0
- data/lib/hecks/runtime/aggregate_lock.rb +9 -0
- data/lib/hecks/runtime/boot_gates.rb +18 -0
- data/lib/hecks/runtime/caller.rb +32 -0
- data/lib/hecks/runtime/capability_graph.rb +11 -0
- data/lib/hecks/runtime/command_interpreter/argument_gate.rb +23 -21
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +14 -15
- data/lib/hecks/runtime/command_interpreter.rb +42 -17
- data/lib/hecks/runtime/command_rules/admissibility.rb +165 -14
- data/lib/hecks/runtime/command_rules/arithmetic.rb +17 -5
- data/lib/hecks/runtime/command_rules/references.rb +118 -28
- data/lib/hecks/runtime/dependency_planning.rb +45 -0
- data/lib/hecks/runtime/dispatcher.rb +28 -50
- data/lib/hecks/runtime/entity_element.rb +161 -8
- data/lib/hecks/runtime/entity_interpreter.rb +44 -9
- data/lib/hecks/runtime/errors.rb +18 -4
- data/lib/hecks/runtime/event.rb +10 -5
- data/lib/hecks/runtime/identity.rb +71 -3
- data/lib/hecks/runtime/instance.rb +67 -7
- data/lib/hecks/runtime/interpreting.rb +13 -5
- data/lib/hecks/runtime/invocation.rb +118 -36
- data/lib/hecks/runtime/loader.rb +94 -8
- data/lib/hecks/runtime/outbox.rb +145 -7
- data/lib/hecks/runtime/policy_interpreter.rb +22 -9
- data/lib/hecks/runtime/port_operation_interpreter.rb +20 -0
- data/lib/hecks/runtime/query_interpreter.rb +40 -12
- data/lib/hecks/runtime/reaction_invocation.rb +53 -8
- data/lib/hecks/runtime/read_model_interpreter.rb +23 -7
- data/lib/hecks/runtime/rebuild_sweep.rb +28 -0
- data/lib/hecks/runtime/reference_hop.rb +42 -0
- data/lib/hecks/runtime/refusal_wording.rb +50 -0
- data/lib/hecks/runtime/registry/saga_persistence.rb +11 -0
- data/lib/hecks/runtime/registry/verification.rb +119 -4
- data/lib/hecks/runtime/registry.rb +157 -4
- data/lib/hecks/runtime/remote_dispatcher.rb +92 -6
- data/lib/hecks/runtime/routing.rb +27 -2
- data/lib/hecks/runtime/saga_interpreter/correlation.rb +10 -12
- data/lib/hecks/runtime/saga_interpreter.rb +27 -13
- data/lib/hecks/runtime/tenant_check.rb +26 -6
- data/lib/hecks/runtime/tenant_scope.rb +18 -0
- data/lib/hecks/runtime/value/coercion.rb +255 -33
- data/lib/hecks/runtime/value/entity_list_coercion.rb +102 -30
- data/lib/hecks/runtime/value.rb +50 -0
- data/lib/hecks/runtime.rb +32 -0
- data/lib/hecks/storehouse.rb +305 -9
- data/lib/hecks/tenancy/bluebook/tenancy.bluebook +130 -0
- data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +32 -0
- data/lib/hecks/version.rb +1 -1
- data/lib/hecks.rb +79 -1
- data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +8 -0
- data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +12 -2
- data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +29 -5
- metadata +14 -5
- data/lib/hecks/codemod/legacy_dispatch_args.rb +0 -299
- data/lib/hecks/codemod/legacy_dispatch_recorder.rb +0 -186
- data/lib/hecks/deprecation.rb +0 -95
- data/lib/hecks/framework/bluebook/compliance.bluebook +0 -1
|
@@ -16,25 +16,47 @@ module Hecks
|
|
|
16
16
|
|
|
17
17
|
module_function
|
|
18
18
|
|
|
19
|
+
# Projects the Rust parser's keyword and closed-set tables for one chapter.
|
|
20
|
+
#
|
|
21
|
+
# @param bluebook [Bluebook::Chapter] the chapter to project; the language's own
|
|
22
|
+
# for the ordinary `bin/project_parser_table` run
|
|
23
|
+
# @param options [Hash{Symbol => Object}] ignored; present to satisfy the
|
|
24
|
+
# `Projector::Target` calling convention
|
|
25
|
+
# @return [String] the rendered Rust source
|
|
19
26
|
def call(bluebook:, options: {}) = render(bluebook)
|
|
20
27
|
|
|
21
28
|
module_function
|
|
22
29
|
|
|
23
|
-
# The chapter is handed over, not reached for
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
30
|
+
# The chapter is handed over, not reached for — reaching for the grammar
|
|
31
|
+
# registry directly would pin the projection to one chapter, the language's
|
|
32
|
+
# own, and the projector protocol already hands over the chapter as an
|
|
33
|
+
# argument, at no cost.
|
|
34
|
+
#
|
|
35
|
+
# @param bluebook [Bluebook::Chapter] the chapter to read the Syntax aggregate
|
|
36
|
+
# from
|
|
37
|
+
# @return [Bluebook::Aggregate, nil] the chapter's `"Syntax"` aggregate, `nil` if
|
|
38
|
+
# it declares none
|
|
27
39
|
def syntax(bluebook) = bluebook.aggregate("Syntax")
|
|
28
40
|
|
|
29
41
|
# Every cell as text — exactly spec/syntax_conformance_spec.rb's own
|
|
30
42
|
# `rows` helper, reused rather than re-derived: a member's fields decode
|
|
31
43
|
# back through typed literal decoding on the way out of reconstruction,
|
|
32
44
|
# and this reads it back as what was written.
|
|
45
|
+
#
|
|
46
|
+
# @param bluebook [Bluebook::Chapter] the chapter to read from
|
|
47
|
+
# @param name [String] the closed-set value object's name, such as `"Context"`
|
|
48
|
+
# @return [Array<Hash{Symbol => String}>] one row per declared member, every
|
|
49
|
+
# field stringified
|
|
33
50
|
def rows(bluebook, name)
|
|
34
51
|
syntax(bluebook).value_objects.find { |vo| vo.hecks_name == name }
|
|
35
52
|
.members.map { |row| row.to_h.transform_values(&:to_s) }
|
|
36
53
|
end
|
|
37
54
|
|
|
55
|
+
# Reads one closed set's member names, for a Rust `&[&str]` constant.
|
|
56
|
+
#
|
|
57
|
+
# @param bluebook [Bluebook::Chapter] the chapter to read from
|
|
58
|
+
# @param name [String] the closed-set value object's name
|
|
59
|
+
# @return [Array<String>] every declared member's `:name` field
|
|
38
60
|
def closed_set_members(bluebook, name) = rows(bluebook, name).map { |row| row[:name] }
|
|
39
61
|
|
|
40
62
|
KEYWORD_FIELDS = %i[word context body inner opens fills status was resolves_via disambiguator].freeze
|
|
@@ -45,18 +67,34 @@ module Hecks
|
|
|
45
67
|
# plain ASCII (a word, a context name, a digit, "true"/"false"), so this
|
|
46
68
|
# only has to be safe against the two characters Rust string literals
|
|
47
69
|
# themselves reserve.
|
|
70
|
+
#
|
|
71
|
+
# @param value [Object] the field value to render; stringified before escaping
|
|
72
|
+
# @return [String] a quoted Rust string literal
|
|
48
73
|
def rust_string(value) = "\"#{value.to_s.gsub('\\', '\\\\\\\\').gsub('"', '\\"')}\""
|
|
49
74
|
|
|
75
|
+
# Renders one `KEYWORDS` table entry.
|
|
76
|
+
#
|
|
77
|
+
# @param row [Hash{Symbol => String}] one keyword row, keyed by `KEYWORD_FIELDS`
|
|
78
|
+
# @return [String] one `KeywordRow { ... },` line
|
|
50
79
|
def keyword_row(row)
|
|
51
80
|
fields = KEYWORD_FIELDS.map { |field| rust_string(row[field]) }
|
|
52
81
|
" KeywordRow { #{KEYWORD_FIELDS.zip(fields).map { |name, value| "#{name}: #{value}" }.join(', ')} },"
|
|
53
82
|
end
|
|
54
83
|
|
|
84
|
+
# Renders one `ARGUMENTS` table entry.
|
|
85
|
+
#
|
|
86
|
+
# @param row [Hash{Symbol => String}] one argument row, keyed by `ARGUMENT_FIELDS`
|
|
87
|
+
# @return [String] one `ArgumentRow { ... },` line
|
|
55
88
|
def argument_row(row)
|
|
56
89
|
fields = ARGUMENT_FIELDS.map { |field| rust_string(row[field]) }
|
|
57
90
|
" ArgumentRow { #{ARGUMENT_FIELDS.zip(fields).map { |name, value| "#{name}: #{value}" }.join(', ')} },"
|
|
58
91
|
end
|
|
59
92
|
|
|
93
|
+
# Renders one `pub static NAME: &[&str]` constant.
|
|
94
|
+
#
|
|
95
|
+
# @param name [String] the Rust constant's name
|
|
96
|
+
# @param values [Array<String>] the constant's member strings
|
|
97
|
+
# @return [String] the rendered `pub static` declaration
|
|
60
98
|
def const_str_array(name, values)
|
|
61
99
|
lines = values.map { |value| " #{rust_string(value)}," }
|
|
62
100
|
"pub static #{name}: &[&str] = &[\n#{lines.join("\n")}\n];\n"
|
|
@@ -66,10 +104,13 @@ module Hecks
|
|
|
66
104
|
# now, dispatched (not merely declared) so their own `status`
|
|
67
105
|
# really is a lifecycle. `SyntaxBoot.call` reads the still-static
|
|
68
106
|
# seed rows (`KeywordSeed`/`ArgumentSeed`), dispatches each one
|
|
69
|
-
# through the real admission/lifecycle door, and hands back
|
|
70
|
-
#
|
|
71
|
-
#
|
|
72
|
-
#
|
|
107
|
+
# through the real admission/lifecycle door, and hands back rows in
|
|
108
|
+
# the same shape `rows` reads for every other closed set here —
|
|
109
|
+
# symbol keys, string values, `status` included — so nothing else
|
|
110
|
+
# in this file needs to change.
|
|
111
|
+
#
|
|
112
|
+
# @param bluebook [Bluebook::Chapter] the chapter to project
|
|
113
|
+
# @return [String] the rendered `KEYWORDS`/`ARGUMENTS`/closed-set Rust source
|
|
73
114
|
def render(bluebook)
|
|
74
115
|
table = Hecks::Bluebook::MetaValidator::SyntaxBoot.call
|
|
75
116
|
keywords = table[:keywords]
|
|
@@ -25,6 +25,18 @@ module Hecks
|
|
|
25
25
|
|
|
26
26
|
module_function
|
|
27
27
|
|
|
28
|
+
# Renders one reference page per DSL keyword context, carrying prose
|
|
29
|
+
# over from whatever is already committed under `options[:from]`.
|
|
30
|
+
#
|
|
31
|
+
# @param bluebook [Bluebook::Behaviour::Chapter] the chapter declaring the
|
|
32
|
+
# Syntax aggregate to render pages from; unused beyond admission, since the
|
|
33
|
+
# keyword table this reads comes from the global `Syntax` grammar
|
|
34
|
+
# @param options [Hash] must include `:from`
|
|
35
|
+
# @option options [String] :from directory holding the already-committed
|
|
36
|
+
# reference pages, read to harvest their prose
|
|
37
|
+
# @return [Hash{String => String}] each page's filename => its rendered
|
|
38
|
+
# Markdown, including `"index.md"`
|
|
39
|
+
# @raise [ArgumentError] if `options[:from]` is missing or falsy
|
|
28
40
|
def call(bluebook:, options: {})
|
|
29
41
|
from = options[:from] or
|
|
30
42
|
raise ArgumentError,
|
|
@@ -4,7 +4,7 @@ require_relative "vocabulary"
|
|
|
4
4
|
|
|
5
5
|
module Hecks
|
|
6
6
|
module Projections
|
|
7
|
-
#
|
|
7
|
+
# The language's closed sets, projected into the Rust kernel — one
|
|
8
8
|
# `rust/src/kernel/vocab/<table>.rs` per Vocabulary table, each a
|
|
9
9
|
# `pub enum` plus `match`-based accessors with no wildcard arm, so a
|
|
10
10
|
# row the chapter gains is a compile error at every exhaustive match
|
|
@@ -18,21 +18,24 @@ module Hecks
|
|
|
18
18
|
# which read Runtime::RefusalWording::TEMPLATES — a hand-typed copy of
|
|
19
19
|
# the RefusalTemplate rows. This reads the chapter itself.
|
|
20
20
|
#
|
|
21
|
-
#
|
|
22
|
-
# and its generated extras; a table not listed is not projected. The
|
|
23
|
-
# enum names that differ from the table name keep existing Rust call
|
|
24
|
-
# sites (`RefusalSite`) and the dispatch-step names D1 consumes
|
|
25
|
-
# (`AggregateStep`, `EntityStep`).
|
|
21
|
+
# ## Only `TABLES` the kernel needs
|
|
26
22
|
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
#
|
|
23
|
+
# Each entry in `TABLES` names its enum and its generated extras; a
|
|
24
|
+
# table not listed is not projected. The enum names that differ from
|
|
25
|
+
# the table name keep existing Rust call sites (`RefusalSite`) and the
|
|
26
|
+
# dispatch-step names D1 consumes (`AggregateStep`, `EntityStep`).
|
|
27
|
+
#
|
|
28
|
+
# ## Typed refusal arguments
|
|
29
|
+
#
|
|
30
|
+
# The templates table also reads RefusalSiteArgument: every site gets a
|
|
31
|
+
# `<Variant>Args` struct whose fields are exactly the site's declared
|
|
32
|
+
# arguments (a `&str` per scalar, a `&[&str]` per list) and a
|
|
33
|
+
# `render_args` that formats each one by its row. `render` itself stays
|
|
34
|
+
# private to the generated module, so no call site can pass an argument
|
|
35
|
+
# list by hand — leaving one out does not compile. The generated test
|
|
36
|
+
# pins every site's `render_args` output (empty, single and multiple
|
|
37
|
+
# unsorted lists) against Runtime::RefusalWording.render_with, computed
|
|
38
|
+
# here in Ruby.
|
|
36
39
|
module RustVocabulary
|
|
37
40
|
extend Projector::Target
|
|
38
41
|
|
|
@@ -62,8 +65,24 @@ module Hecks
|
|
|
62
65
|
|
|
63
66
|
module_function
|
|
64
67
|
|
|
68
|
+
# The projector protocol.
|
|
69
|
+
#
|
|
70
|
+
# @param bluebook [Bluebook::Behaviour::Chapter] the chapter declaring the
|
|
71
|
+
# Vocabulary aggregate to project
|
|
72
|
+
# @param options [Hash] unused; accepted to satisfy the registry's call shape
|
|
73
|
+
# @return [Hash{String => String}] each generated file's path, relative to
|
|
74
|
+
# `rust/src/kernel/`, mapped to its full Rust source
|
|
65
75
|
def call(bluebook:, options: {}) = render(bluebook)
|
|
66
76
|
|
|
77
|
+
# Renders every file `TABLES` projects: `vocab/mod.rs` plus one `.rs` file
|
|
78
|
+
# per table, each an exhaustive enum with accessors and a pinned test module.
|
|
79
|
+
#
|
|
80
|
+
# @param bluebook [Bluebook::Behaviour::Chapter] the chapter declaring the
|
|
81
|
+
# Vocabulary aggregate to render
|
|
82
|
+
# @return [Hash{String => String}] each generated file's path, relative to
|
|
83
|
+
# `rust/src/kernel/`, mapped to its full Rust source
|
|
84
|
+
# @raise [ArgumentError] if `bluebook` does not declare a `TABLES`-named table,
|
|
85
|
+
# or the `:arguments` table a `:templates` entry names
|
|
67
86
|
def render(bluebook)
|
|
68
87
|
tables = Vocabulary.tables(bluebook)
|
|
69
88
|
files = TABLES.to_h do |table, spec|
|
|
@@ -76,6 +95,11 @@ module Hecks
|
|
|
76
95
|
{ "vocab/mod.rs" => mod_file }.merge(files)
|
|
77
96
|
end
|
|
78
97
|
|
|
98
|
+
# The generated-file banner every projected file opens with.
|
|
99
|
+
#
|
|
100
|
+
# @param table [String] the Vocabulary table name being projected, such as
|
|
101
|
+
# `"RefusalTemplate"`
|
|
102
|
+
# @return [String] the banner, as Rust line comments
|
|
79
103
|
def header(table)
|
|
80
104
|
<<~RUST
|
|
81
105
|
// GENERATED by #{GENERATOR} (Hecks::Projections::RustVocabulary,
|
|
@@ -85,6 +109,10 @@ module Hecks
|
|
|
85
109
|
RUST
|
|
86
110
|
end
|
|
87
111
|
|
|
112
|
+
# Renders `vocab/mod.rs`, the entry point that re-exports every projected enum.
|
|
113
|
+
#
|
|
114
|
+
# @return [String] `vocab/mod.rs`'s full source: one `pub mod` and `pub use`
|
|
115
|
+
# per entry in `TABLES`
|
|
88
116
|
def mod_file
|
|
89
117
|
mods = TABLES.values.map { |spec| "pub mod #{spec[:file]};" }.join("\n")
|
|
90
118
|
exports = TABLES.values.map { |spec| "pub use #{spec[:file]}::#{spec[:enum]};" }.join("\n")
|
|
@@ -104,6 +132,18 @@ module Hecks
|
|
|
104
132
|
RUST
|
|
105
133
|
end
|
|
106
134
|
|
|
135
|
+
# One table's full `.rs` source: its exhaustive enum, per-field accessors,
|
|
136
|
+
# `from_name`, its `:order`/`:templates` extras, and its `#[cfg(test)]` module.
|
|
137
|
+
#
|
|
138
|
+
# @param table [String] the Vocabulary table name, such as `"RefusalTemplate"`
|
|
139
|
+
# @param spec [Hash] the table's `TABLES` entry (`:enum`, `:variant_from`,
|
|
140
|
+
# `:file`, `:kind`, and, for `:templates`, `:arguments`)
|
|
141
|
+
# @param rows [Array<Hash{String => String}>] the table's rows, as read off
|
|
142
|
+
# the chapter by `Vocabulary.tables`
|
|
143
|
+
# @param argument_rows [Array<Hash{String => String}>, nil] the
|
|
144
|
+
# `RefusalSiteArgument` rows, only when `spec[:kind] == :templates`
|
|
145
|
+
# @return [String] the table's full `.rs` file source
|
|
146
|
+
# @raise [ArgumentError] if two rows would generate the same enum variant name
|
|
107
147
|
def table_file(table, spec, rows, argument_rows = nil)
|
|
108
148
|
variants = rows.map { |row| variant_name(row, spec[:variant_from]) }
|
|
109
149
|
duplicates = variants.tally.select { |_, count| count > 1 }.keys
|
|
@@ -132,6 +172,14 @@ module Hecks
|
|
|
132
172
|
"#{body.flatten.join("\n").rstrip}\n"
|
|
133
173
|
end
|
|
134
174
|
|
|
175
|
+
# Renders the enum's declared-order constant: `ORDER` for `:order`, `ALL` otherwise.
|
|
176
|
+
#
|
|
177
|
+
# @param enum [String] the Rust enum name, such as `"RefusalSite"`
|
|
178
|
+
# @param variants [Array<String>] each row's generated variant name, in
|
|
179
|
+
# declared order
|
|
180
|
+
# @param kind [Symbol] the table's `:kind` (`:order`, `:templates`, or `:set`)
|
|
181
|
+
# @return [Array<String>] the lines declaring the enum's `ORDER` constant
|
|
182
|
+
# (`:order`) or `ALL` constant (otherwise)
|
|
135
183
|
def constants(enum, variants, kind)
|
|
136
184
|
list = variants.map { |variant| " #{enum}::#{variant}," }
|
|
137
185
|
if kind == :order
|
|
@@ -143,6 +191,16 @@ module Hecks
|
|
|
143
191
|
end
|
|
144
192
|
end
|
|
145
193
|
|
|
194
|
+
# Renders one field's `pub fn` accessor, an exhaustive match over every variant.
|
|
195
|
+
#
|
|
196
|
+
# @param enum [String] the Rust enum name
|
|
197
|
+
# @param field [String] the row field this accessor reads, such as `"name"`
|
|
198
|
+
# @param variants [Array<String>] each row's generated variant name, in
|
|
199
|
+
# declared order, matching `rows`
|
|
200
|
+
# @param rows [Array<Hash{String => String}>] the table's rows, in the same
|
|
201
|
+
# order as `variants`
|
|
202
|
+
# @return [Array<String>] the lines for the field's `pub fn` accessor, one
|
|
203
|
+
# match arm per variant
|
|
146
204
|
def accessor(enum, field, variants, rows)
|
|
147
205
|
arms = variants.zip(rows).map { |variant, row| " #{enum}::#{variant} => #{rust_string(row.fetch(field))}," }
|
|
148
206
|
[" /// The row's declared `#{field}`.",
|
|
@@ -156,6 +214,10 @@ module Hecks
|
|
|
156
214
|
|
|
157
215
|
# Built on the enum's own list and name accessor, not a string
|
|
158
216
|
# match, so it needs no wildcard arm either.
|
|
217
|
+
#
|
|
218
|
+
# @param enum [String] the Rust enum name
|
|
219
|
+
# @param kind [Symbol] the table's `:kind` (`:order`, `:templates`, or `:set`)
|
|
220
|
+
# @return [Array<String>] the lines for the enum's `from_name` associated function
|
|
159
221
|
def from_name(enum, kind)
|
|
160
222
|
list = kind == :order ? "ORDER" : "ALL"
|
|
161
223
|
field = kind == :templates ? "site" : name_field(kind)
|
|
@@ -166,6 +228,14 @@ module Hecks
|
|
|
166
228
|
""]
|
|
167
229
|
end
|
|
168
230
|
|
|
231
|
+
# Renders the extra methods a table's `:kind` adds beyond the shared accessors.
|
|
232
|
+
#
|
|
233
|
+
# @param enum [String] the Rust enum name
|
|
234
|
+
# @param variants [Array<String>] each row's generated variant name, in
|
|
235
|
+
# declared order
|
|
236
|
+
# @param kind [Symbol] the table's `:kind` (`:order`, `:templates`, or `:set`)
|
|
237
|
+
# @return [Array<String>] `:order`'s `position` method, `:templates`'s private
|
|
238
|
+
# `render` method, or `[]` for `:set`
|
|
169
239
|
def extras(enum, variants, kind)
|
|
170
240
|
case kind
|
|
171
241
|
when :order
|
|
@@ -196,6 +266,17 @@ module Hecks
|
|
|
196
266
|
# [refusal, site] => that site's argument rows, in declared order —
|
|
197
267
|
# refused unless they name exactly the template's own placeholders,
|
|
198
268
|
# in the order each first appears, with only known formatting rules.
|
|
269
|
+
#
|
|
270
|
+
# @param rows [Array<Hash{String => String}>] the `RefusalTemplate` rows,
|
|
271
|
+
# in declared order
|
|
272
|
+
# @param argument_rows [Array<Hash{String => String}>] the `RefusalSiteArgument`
|
|
273
|
+
# rows for every site
|
|
274
|
+
# @return [Hash{Array<String> => Array<Hash{String => String}>}] each
|
|
275
|
+
# `[refusal, site]` pair mapped to its declared argument rows, in declared order
|
|
276
|
+
# @raise [ArgumentError] if an argument row names a `refusal`/`site` no
|
|
277
|
+
# `RefusalTemplate` row declares, if a site's declared arguments do not match
|
|
278
|
+
# its template's `{placeholder}`s in order, or if `check_rule!` rejects a
|
|
279
|
+
# row's `"shape"`, `"quoting"`, or `"sorted"` value
|
|
199
280
|
def site_arguments(rows, argument_rows)
|
|
200
281
|
grouped = argument_rows.group_by { |row| [row.fetch("refusal"), row.fetch("site")] }
|
|
201
282
|
orphans = grouped.keys - rows.map { |row| [row.fetch("refusal"), row.fetch("site")] }
|
|
@@ -215,6 +296,18 @@ module Hecks
|
|
|
215
296
|
end
|
|
216
297
|
end
|
|
217
298
|
|
|
299
|
+
# Refuses a `RefusalSiteArgument` row whose formatting fields are not
|
|
300
|
+
# recognized rules.
|
|
301
|
+
#
|
|
302
|
+
# @param key [Array<String>] the `[refusal, site]` pair `spec` belongs to,
|
|
303
|
+
# used in the raised message
|
|
304
|
+
# @param spec [Hash{String => String}] one `RefusalSiteArgument` row
|
|
305
|
+
# (`"shape"`, `"quoting"`, `"sorted"`)
|
|
306
|
+
# @return [void]
|
|
307
|
+
# @raise [ArgumentError] if `spec`'s `"shape"` is not in
|
|
308
|
+
# `Runtime::RefusalWording::SHAPES`, its `"quoting"` is not in
|
|
309
|
+
# `Runtime::RefusalWording::QUOTINGS`, or its `"sorted"` is not
|
|
310
|
+
# `"true"`/`"false"`
|
|
218
311
|
def check_rule!(key, spec)
|
|
219
312
|
where = "#{key.join('/')}.#{spec.fetch('argument')}"
|
|
220
313
|
unless Runtime::RefusalWording::SHAPES.include?(spec.fetch("shape"))
|
|
@@ -228,6 +321,17 @@ module Hecks
|
|
|
228
321
|
raise ArgumentError, "#{where}: sorted must be \"true\" or \"false\""
|
|
229
322
|
end
|
|
230
323
|
|
|
324
|
+
# Renders every `:templates` variant's typed argument struct, plus the two
|
|
325
|
+
# helper functions (`quoted`, `list`) they share.
|
|
326
|
+
#
|
|
327
|
+
# @param enum [String] the Rust enum name
|
|
328
|
+
# @param variants [Array<String>] each row's generated variant name, matching `rows`
|
|
329
|
+
# @param rows [Array<Hash{String => String}>] the `RefusalTemplate` rows, in
|
|
330
|
+
# the same order as `variants`
|
|
331
|
+
# @param by_site [Hash{Array<String> => Array<Hash{String => String}>}] each
|
|
332
|
+
# `[refusal, site]` pair's declared argument rows, as built by `site_arguments`
|
|
333
|
+
# @return [Array<String>] the shared `quoted`/`list` helper functions, followed
|
|
334
|
+
# by every variant's `<Variant>Args` struct and `render_args` implementation
|
|
231
335
|
def argument_types(enum, variants, rows, by_site)
|
|
232
336
|
support = [
|
|
233
337
|
"/// Ruby's `#inspect` of a name, as a refusal quotes it: `{:?}` on a",
|
|
@@ -258,6 +362,15 @@ module Hecks
|
|
|
258
362
|
end
|
|
259
363
|
end
|
|
260
364
|
|
|
365
|
+
# Renders one variant's `<Variant>Args` struct and its `render_args` method.
|
|
366
|
+
#
|
|
367
|
+
# @param enum [String] the Rust enum name
|
|
368
|
+
# @param variant [String] the row's generated variant name
|
|
369
|
+
# @param row [Hash{String => String}] the `RefusalTemplate` row this variant projects
|
|
370
|
+
# @param specs [Array<Hash{String => String}>] the variant's declared
|
|
371
|
+
# `RefusalSiteArgument` rows, in declared order
|
|
372
|
+
# @return [Array<String>] the lines for the variant's `<Variant>Args` struct
|
|
373
|
+
# and its `render_args` method
|
|
261
374
|
def args_struct(enum, variant, row, specs)
|
|
262
375
|
fields = specs.flat_map do |spec|
|
|
263
376
|
type = spec.fetch("shape") == "list" ? "&'a [&'a str]" : "&'a str"
|
|
@@ -288,6 +401,15 @@ module Hecks
|
|
|
288
401
|
""]
|
|
289
402
|
end
|
|
290
403
|
|
|
404
|
+
# The `let` binding, if any, `args_struct` inserts before a variant's
|
|
405
|
+
# `render_args` computes its argument pairs.
|
|
406
|
+
#
|
|
407
|
+
# @param spec [Hash{String => String}] one `RefusalSiteArgument` row
|
|
408
|
+
# (`"argument"`, `"shape"`, `"quoting"`, and, for a list, `"sorted"`,
|
|
409
|
+
# `"separator"`, `"when_empty"`)
|
|
410
|
+
# @return [String, nil] the `let` line formatting the argument through
|
|
411
|
+
# `list`/`quoted`, or `nil` if the argument needs no formatting local
|
|
412
|
+
# (a scalar written as-is)
|
|
291
413
|
def formatted_local(spec)
|
|
292
414
|
name = spec.fetch("argument")
|
|
293
415
|
field = "self.#{rust_field(name)}"
|
|
@@ -299,6 +421,13 @@ module Hecks
|
|
|
299
421
|
end
|
|
300
422
|
end
|
|
301
423
|
|
|
424
|
+
# Describes one argument's formatting rule in English, for its struct field's doc comment.
|
|
425
|
+
#
|
|
426
|
+
# @param spec [Hash{String => String}] one `RefusalSiteArgument` row
|
|
427
|
+
# (`"shape"`, `"quoting"`, and, for a list, `"sorted"`, `"separator"`,
|
|
428
|
+
# `"when_empty"`)
|
|
429
|
+
# @return [String] a short English reading of the row's formatting rule,
|
|
430
|
+
# used as the struct field's own doc comment
|
|
302
431
|
def rule_reading(spec)
|
|
303
432
|
quoting = spec.fetch("quoting") == "inspect" ? ", quoted" : ""
|
|
304
433
|
return "scalar#{quoting}" unless spec.fetch("shape") == "list"
|
|
@@ -307,10 +436,33 @@ module Hecks
|
|
|
307
436
|
"list#{sorted}#{quoting}, joined #{spec.fetch('separator').inspect}, empty reads #{spec.fetch('when_empty').inspect}"
|
|
308
437
|
end
|
|
309
438
|
|
|
439
|
+
# The local variable name `formatted_local` binds an argument's formatted value to.
|
|
440
|
+
#
|
|
441
|
+
# @param argument [String] the `RefusalSiteArgument` row's `"argument"` name
|
|
442
|
+
# @return [String] the Rust local variable name `formatted_local` binds the
|
|
443
|
+
# formatted value to
|
|
310
444
|
def local_name(argument) = "#{argument}_text"
|
|
311
445
|
|
|
446
|
+
# An argument name, escaped for use as a Rust field/parameter identifier.
|
|
447
|
+
#
|
|
448
|
+
# @param argument [String] the `RefusalSiteArgument` row's `"argument"` name
|
|
449
|
+
# @return [String] `argument`, prefixed with `r#` if it collides with a Rust
|
|
450
|
+
# keyword in `RUST_KEYWORDS`
|
|
312
451
|
def rust_field(argument) = RUST_KEYWORDS.include?(argument) ? "r##{argument}" : argument
|
|
313
452
|
|
|
453
|
+
# Renders the table's `#[cfg(test)]` module: every kind's `from_name` round-trip
|
|
454
|
+
# test, plus `:order`'s and `:templates`' own tests.
|
|
455
|
+
#
|
|
456
|
+
# @param enum [String] the Rust enum name
|
|
457
|
+
# @param kind [Symbol] the table's `:kind` (`:order`, `:templates`, or `:set`)
|
|
458
|
+
# @param variants [Array<String>] each row's generated variant name, only
|
|
459
|
+
# read when `kind == :templates`
|
|
460
|
+
# @param rows [Array<Hash{String => String}>] the table's rows, only read
|
|
461
|
+
# when `kind == :templates`
|
|
462
|
+
# @param by_site [Hash{Array<String> => Array<Hash{String => String}>}, nil]
|
|
463
|
+
# each `[refusal, site]` pair's declared argument rows, only read when
|
|
464
|
+
# `kind == :templates`
|
|
465
|
+
# @return [Array<String>] the `#[cfg(test)]` module's full source lines
|
|
314
466
|
def tests(enum, kind, variants = [], rows = [], by_site = nil)
|
|
315
467
|
list = kind == :order ? "ORDER" : "ALL"
|
|
316
468
|
name = accessor_name(kind == :templates ? "site" : name_field(kind))
|
|
@@ -336,6 +488,11 @@ module Hecks
|
|
|
336
488
|
lines + ["}"]
|
|
337
489
|
end
|
|
338
490
|
|
|
491
|
+
# The `:order` kind's own test, checked into every order table's test module.
|
|
492
|
+
#
|
|
493
|
+
# @param enum [String] the Rust enum name
|
|
494
|
+
# @return [Array<String>] the test asserting `ORDER`'s index matches each
|
|
495
|
+
# variant's `position`
|
|
339
496
|
def order_test(enum)
|
|
340
497
|
[" #[test]",
|
|
341
498
|
" fn position_is_the_index_in_order() {",
|
|
@@ -348,6 +505,10 @@ module Hecks
|
|
|
348
505
|
|
|
349
506
|
# Placeholders are read off each template's own text, never a second
|
|
350
507
|
# hand-kept list.
|
|
508
|
+
#
|
|
509
|
+
# @param enum [String] the Rust enum name
|
|
510
|
+
# @return [Array<String>] the test asserting every site's `render` leaves
|
|
511
|
+
# no `{...}` placeholder unfilled
|
|
351
512
|
def placeholder_test(enum)
|
|
352
513
|
[" #[test]",
|
|
353
514
|
" fn every_site_renders_with_no_leftover_placeholder() {",
|
|
@@ -377,12 +538,21 @@ module Hecks
|
|
|
377
538
|
" }"]
|
|
378
539
|
end
|
|
379
540
|
|
|
380
|
-
#
|
|
541
|
+
# The Ruby oracle, pinned. Every site renders through its typed
|
|
381
542
|
# `render_args` for each edge case a list argument has — empty, one
|
|
382
543
|
# item, several out of order — and must equal what
|
|
383
544
|
# Runtime::RefusalWording.render_with answers for the same values,
|
|
384
545
|
# computed here at generation time. Scalars carry a `"` so quoting
|
|
385
546
|
# is compared too.
|
|
547
|
+
#
|
|
548
|
+
# @param variants [Array<String>] each row's generated variant name, matching `rows`
|
|
549
|
+
# @param rows [Array<Hash{String => String}>] the `RefusalTemplate` rows, in
|
|
550
|
+
# the same order as `variants`
|
|
551
|
+
# @param by_site [Hash{Array<String> => Array<Hash{String => String}>}] each
|
|
552
|
+
# `[refusal, site]` pair's declared argument rows, as built by `site_arguments`
|
|
553
|
+
# @return [Array<String>] the test asserting every variant's `render_args`
|
|
554
|
+
# output for each `argument_cases` case matches
|
|
555
|
+
# `Runtime::RefusalWording.render_with`, computed here in Ruby
|
|
386
556
|
def render_args_test(variants, rows, by_site)
|
|
387
557
|
asserts = variants.zip(rows).flat_map do |variant, row|
|
|
388
558
|
specs = by_site.fetch([row.fetch("refusal"), row.fetch("site")])
|
|
@@ -407,6 +577,14 @@ module Hecks
|
|
|
407
577
|
|
|
408
578
|
LIST_CASES = [[], ["only \"one\""], %w[zeta alpha mid]].freeze
|
|
409
579
|
|
|
580
|
+
# Builds the argument-value cases `render_args_test` checks a site's
|
|
581
|
+
# `render_args` against.
|
|
582
|
+
#
|
|
583
|
+
# @param specs [Array<Hash{String => String}>] one site's declared
|
|
584
|
+
# `RefusalSiteArgument` rows
|
|
585
|
+
# @return [Array<Hash{Symbol => Object}>] one argument Hash per test case —
|
|
586
|
+
# `LIST_CASES` (empty, one item, several unsorted) if any spec is
|
|
587
|
+
# list-shaped, else a single scalar case, each scalar value carrying a `"`
|
|
410
588
|
def argument_cases(specs)
|
|
411
589
|
cases = specs.any? { |spec| spec.fetch("shape") == "list" } ? LIST_CASES : [nil]
|
|
412
590
|
cases.map do |items|
|
|
@@ -417,12 +595,29 @@ module Hecks
|
|
|
417
595
|
end
|
|
418
596
|
end
|
|
419
597
|
|
|
598
|
+
# The row field a table's `from_name`/round-trip test treats as its "name".
|
|
599
|
+
#
|
|
600
|
+
# @param kind [Symbol] the table's `:kind` (`:order`, `:templates`, or `:set`)
|
|
601
|
+
# @return [String] the row field used as this kind's "name" — `"step"` for
|
|
602
|
+
# `:order`, `"name"` otherwise
|
|
420
603
|
def name_field(kind) = kind == :order ? "step" : "name"
|
|
421
604
|
|
|
422
605
|
# `refusal` is a Rust-safe word but reads ambiguously beside the
|
|
423
606
|
# kernel's own `Refusal` enum; the accessor says what it answers.
|
|
607
|
+
#
|
|
608
|
+
# @param field [String] a row field name, such as `"name"` or `"refusal"`
|
|
609
|
+
# @return [String] the Rust accessor method name for `field` —
|
|
610
|
+
# `"refusal_class"` for `"refusal"`, `field` otherwise
|
|
424
611
|
def accessor_name(field) = field == "refusal" ? "refusal_class" : field
|
|
425
612
|
|
|
613
|
+
# Builds one row's Rust enum variant name from its declared `variant_from` fields.
|
|
614
|
+
#
|
|
615
|
+
# @param row [Hash{String => String}] the table row to name
|
|
616
|
+
# @param fields [Array<String>] the row fields, PascalCased and concatenated
|
|
617
|
+
# in order to build the variant name
|
|
618
|
+
# @return [String] the row's generated Rust enum variant name
|
|
619
|
+
# @raise [ArgumentError] if the built name is not a valid Rust variant
|
|
620
|
+
# identifier (uppercase first letter, alphanumeric only)
|
|
426
621
|
def variant_name(row, fields)
|
|
427
622
|
name = fields.map { |field| pascal(row.fetch(field)) }.join
|
|
428
623
|
raise ArgumentError, "#{name.inspect} is not a Rust variant name" unless name.match?(/\A[A-Z][A-Za-z0-9]*\z/)
|
|
@@ -430,10 +625,18 @@ module Hecks
|
|
|
430
625
|
name
|
|
431
626
|
end
|
|
432
627
|
|
|
628
|
+
# PascalCases one field value for use inside a generated variant name.
|
|
629
|
+
#
|
|
630
|
+
# @param text [String, Symbol, nil] the value to PascalCase; each
|
|
631
|
+
# `_`-separated part's first letter is upcased
|
|
632
|
+
# @return [String] `text` converted to PascalCase, or `""` if `text` is `nil`
|
|
433
633
|
def pascal(text) = text.to_s.split("_").map { |part| part[0].to_s.upcase + part[1..].to_s }.join
|
|
434
634
|
|
|
435
635
|
# A Rust string literal: only `\`, `"` and newlines need escaping in
|
|
436
636
|
# the text any vocabulary row holds.
|
|
637
|
+
#
|
|
638
|
+
# @param text [String] the value to render as a Rust string literal
|
|
639
|
+
# @return [String] `text`, backslash/quote/newline-escaped and wrapped in `"..."`
|
|
437
640
|
def rust_string(text)
|
|
438
641
|
escaped = text.to_s.gsub("\\") { "\\\\" }.gsub('"') { "\\\"" }.gsub("\n") { "\\n" }
|
|
439
642
|
"\"#{escaped}\""
|
|
@@ -31,6 +31,14 @@ module Hecks
|
|
|
31
31
|
# is "every file here registers," not "every projection always
|
|
32
32
|
# succeeds") but refuses clearly if asked to run with the plugin
|
|
33
33
|
# unloaded, rather than raising on an undefined constant.
|
|
34
|
+
# Projects the storage shape of `bluebook` — the structural form an era
|
|
35
|
+
# bump/no-bump question is answered by diffing.
|
|
36
|
+
#
|
|
37
|
+
# @param bluebook [Bluebook::Chapter] the bluebook to reduce to its storage shape
|
|
38
|
+
# @param options [Hash] unused; accepted to satisfy the registry's call shape
|
|
39
|
+
# @return [Hash{String => Object}] `Runtime::StorageShape.project`'s Hash: `"name"`
|
|
40
|
+
# and a sorted `"aggregates"` array
|
|
41
|
+
# @raise [RuntimeError] if the era persistence plugin is not loaded
|
|
34
42
|
def call(bluebook:, options: {})
|
|
35
43
|
unless Ports::Persistence.plugin?(:era)
|
|
36
44
|
raise "the :shape projection needs the era persistence plugin loaded " \
|