hecks 3.2.0 → 3.3.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/lib/hecks/bluebook/assembly/contracts.rb +2 -1
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +1 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +2 -39
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +3 -3
- data/lib/hecks/bluebook/dsl/need_word.rb +50 -0
- data/lib/hecks/bluebook/dsl/query_builder.rb +6 -1
- data/lib/hecks/bluebook/query.rb +8 -5
- data/lib/hecks/cli/check_era.rb +3 -39
- data/lib/hecks/cli/interview_draft.rb +86 -31
- data/lib/hecks/cli/interview_session.rb +77 -26
- data/lib/hecks/cli/interview_shape.rb +179 -0
- data/lib/hecks/doors/cli_runner.rb +14 -1
- data/lib/hecks/doors/launcher_options.rb +13 -3
- data/lib/hecks/doors/mcp_door.rb +16 -2
- data/lib/hecks/doors/mcp_door_scope.rb +45 -5
- data/lib/hecks/doors/mcp_guide_cache.rb +75 -0
- data/lib/hecks/doors.rb +1 -1
- data/lib/hecks/embryonaut_bluebook/manifest.rb +117 -0
- data/lib/hecks/embryonaut_bluebook/registry.rb +209 -0
- data/lib/hecks/embryonaut_bluebook/vendor.rb +1 -1
- data/lib/hecks/embryonaut_bluebook.rb +2 -0
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +17 -1
- data/lib/hecks/fuzzing/qa_settings.rb +15 -34
- data/lib/hecks/gate/stages.yml +113 -8
- data/lib/hecks/hecks/adapters/codebase/regeneration.rb +10 -1
- data/lib/hecks/hecks/adapters/codebase/test_runner.rb +10 -2
- data/lib/hecks/hecks/adapters/first_admin.rb +108 -0
- data/lib/hecks/hecks/adapters/git.rb +59 -0
- data/lib/hecks/hecks/adapters/in_process_access.rb +25 -0
- data/lib/hecks/hecks/adapters/in_process_boot.rb +2 -0
- data/lib/hecks/hecks/adapters/rust_toolchain.rb +18 -3
- data/lib/hecks/hecks/adapters/rust_workspace.rb +1 -1
- data/lib/hecks/hecks/adapters/site_toolchain.rb +88 -11
- data/lib/hecks/hecks/codebase.behaviors +6 -0
- data/lib/hecks/hecks/codebase.bluebook +21 -0
- data/lib/hecks/hecks/custodian.behaviors +6 -0
- data/lib/hecks/hecks/custodian.bluebook +217 -0
- data/lib/hecks/hecks/hecks.hecksagon +24 -0
- data/lib/hecks/hecks/hecks.world +8 -3
- data/lib/hecks/hecks/site.world +5 -2
- data/lib/hecks/language/bluebook/query.bluebook +25 -0
- data/lib/hecks/language/bluebook/vocabulary.bluebook +220 -7
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +1 -1
- data/lib/hecks/language/oidc.json +5 -0
- data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +3 -3
- data/lib/hecks/projections/deploy/box/hosting.rb +170 -0
- data/lib/hecks/projections/deploy/box/hosting_settings.rb +123 -0
- data/lib/hecks/projections/deploy/box/settings.rb +40 -6
- data/lib/hecks/projections/deploy/box/templates/deploy-box.sh.tmpl +2 -0
- data/lib/hecks/projections/deploy/box/templates/deploy-service.services-roll.part +33 -0
- data/lib/hecks/projections/deploy/box/templates/deploy-service.sh.tmpl +79 -0
- data/lib/hecks/projections/deploy/box/templates/deploy-service.taskdef-roll.part +77 -0
- data/lib/hecks/projections/deploy/box/templates/deploy-service.taskdef.part +12 -0
- data/lib/hecks/projections/deploy/box/templates/hosting.mk.tmpl +24 -0
- data/lib/hecks/projections/deploy/box/templates/render-compose-taskdef.sh.tmpl +17 -0
- data/lib/hecks/projections/deploy/box/templates/smoke-after-deploy.sh.tmpl +271 -0
- data/lib/hecks/projections/deploy/box.rb +28 -4
- data/lib/hecks/projections/deploy/fargate.rb +3 -3
- data/lib/hecks/projections/deploy/lambda.rb +8 -8
- data/lib/hecks/projections/model/deviations.rb +1 -1
- data/lib/hecks/projections/site/edge/behaviours.rb +5 -3
- data/lib/hecks/projections/site/edge/checks.rb +31 -5
- data/lib/hecks/projections/site/edge.rb +25 -11
- data/lib/hecks/projections/site/live_edge.rb +162 -0
- data/lib/hecks/projections/site/routes_ts.rb +50 -27
- data/lib/hecks/projections/site/site_cdn.rb +7 -4
- data/lib/hecks/projections/site/table/checks.rb +53 -7
- data/lib/hecks/projections/site/table/link_builder.rb +64 -0
- data/lib/hecks/projections/site/table/row_builder.rb +21 -6
- data/lib/hecks/projections/site/table.rb +16 -5
- data/lib/hecks/quality_control/adapters/git_pr.rb +0 -14
- data/lib/hecks/quality_control/cli/qa_discover_external_domains.rb +1 -1
- data/lib/hecks/quality_control/cli/qa_open_pr.rb +32 -16
- data/lib/hecks/quality_control/cli/qa_seed_angles.rb +2 -2
- data/lib/hecks/quality_control/cli/qa_seed_targets.rb +1 -2
- data/lib/hecks/quality_control/quality_control.bluebook +76 -124
- data/lib/hecks/runtime/interpreting.rb +3 -22
- data/lib/hecks/runtime/needs.rb +48 -0
- data/lib/hecks/runtime/query_interpreter.rb +3 -0
- data/lib/hecks/runtime/registry/verification.rb +3 -3
- data/lib/hecks/rust_build/host.rb +147 -0
- data/lib/hecks/rust_build/kernel_input.rb +51 -17
- data/lib/hecks/rust_build/project_rust/codegen_run.rb +0 -18
- data/lib/hecks/rust_build/project_rust.rb +7 -140
- data/lib/hecks/rust_build.rb +1 -0
- data/lib/hecks/site/bluebook/site.bluebook +80 -14
- data/lib/hecks/site/bluebook/site.ports.hecksagon +6 -0
- data/lib/hecks/sme/bluebook/sme.bluebook +128 -1
- data/lib/hecks/storehouse.rb +35 -1
- data/lib/hecks/tools/comment_style.rb +4 -2
- data/lib/hecks/tools/gate.rb +1 -1
- data/lib/hecks/tools/rust_comment_style.rb +2 -4
- data/lib/hecks/tools/site_routes.rb +69 -28
- data/lib/hecks/tools/tools_doc.rb +204 -0
- data/lib/hecks/tools.rb +1 -0
- data/lib/hecks/version.rb +1 -1
- data/lib/hecks/vocabulary.rb +152 -1
- data/lib/hecks.rb +1 -0
- data/qa/settings.yml +1 -2
- data/rust/build/src/lineage_pass.rs +31 -16
- data/rust/build/src/main.rs +4 -2
- data/rust/build/src/pipeline.rs +9 -20
- data/rust/codegen/src/attr.rs +1 -1
- data/rust/codegen/src/bridging.rs +8 -8
- data/rust/codegen/src/commands.rs +1 -1
- data/rust/codegen/src/constraints.rs +1 -1
- data/rust/codegen/src/dependency_planning.rs +1 -1
- data/rust/codegen/src/domain_generator.rs +2 -2
- data/rust/codegen/src/exemplar.rs +1 -1
- data/rust/codegen/src/expr_emitter.rs +1 -1
- data/rust/codegen/src/fielded.rs +1 -1
- data/rust/codegen/src/json.rs +214 -23
- data/rust/codegen/src/json_codec.rs +1 -1
- data/rust/codegen/src/literal.rs +6 -3
- data/rust/codegen/src/main.rs +23 -13
- data/rust/codegen/src/mutations.rs +1 -1
- data/rust/codegen/src/naming.rs +5 -3
- data/rust/codegen/src/optional_pass.rs +226 -0
- data/rust/codegen/src/ports.rs +1 -1
- data/rust/codegen/src/queries.rs +3 -3
- data/rust/codegen/src/reactions.rs +1 -1
- data/rust/codegen/src/read_models.rs +2 -2
- data/rust/codegen/src/reference_specs.rs +1 -1
- data/rust/codegen/src/registry.rs +4 -4
- data/rust/codegen/src/shared.rs +1 -1
- data/rust/{build → codegen}/src/sidecars.rs +9 -5
- data/rust/codegen/src/skip_reason.rs +1 -1
- data/rust/codegen/src/types.rs +4 -4
- data/rust/host/HECKS_RELEASE +1 -1
- data/rust/host/src/api.rs +1 -1
- data/rust/host/src/dispatch.rs +5 -0
- data/rust/host/src/needs.rs +193 -29
- data/rust/host/src/resend.rs +4 -4
- data/rust/host/src/ui_schema.rs +1 -1
- data/rust/parser/src/emit.rs +12 -0
- data/rust/parser/src/ir.rs +2 -0
- data/rust/parser/src/keywords.rs +2 -0
- data/rust/parser/src/main.rs +1 -0
- data/rust/parser/src/parse/command.rs +2 -44
- data/rust/parser/src/parse/mod.rs +1 -0
- data/rust/parser/src/parse/needs.rs +60 -0
- data/rust/parser/src/parse/query.rs +3 -0
- data/rust/src/exemplar/commands.rs +1 -1
- data/rust/src/exemplar/constraints.rs +1 -1
- data/rust/src/exemplar/fielded.rs +1 -1
- data/rust/src/exemplar/json.rs +1 -1
- data/rust/src/exemplar/mod.rs +1 -1
- data/rust/src/exemplar/mutations.rs +1 -1
- data/rust/src/exemplar/queries.rs +1 -1
- data/rust/src/exemplar/reactions.rs +1 -1
- data/rust/src/exemplar/read_models.rs +1 -1
- data/rust/src/exemplar/registry.rs +1 -1
- data/rust/src/exemplar/types.rs +1 -1
- data/rust/src/kernel/cli.rs +18 -8
- data/rust/src/kernel/needs.rs +104 -9
- data/rust/src/kernel/read_model.rs +1 -1
- data/rust/src/lib.rs +1 -1
- metadata +24 -30
- data/lib/hecks/rust_build/append_optionals.rb +0 -79
- data/lib/hecks/rust_build/rust_literal.rb +0 -33
- data/lib/hecks/three_zero/forms.yml +0 -127
- data/lib/hecks/three_zero.rb +0 -12
- data/rust/build/src/json.rs +0 -421
- data/rust/build/src/optional_pass.rs +0 -150
- data/rust/project/bridging.rb +0 -257
- data/rust/project/commands.rb +0 -877
- data/rust/project/constraints.rb +0 -86
- data/rust/project/dependency_planning.rb +0 -178
- data/rust/project/domain_generator.rb +0 -983
- data/rust/project/exemplar.rb +0 -148
- data/rust/project/expr_emitter.rb +0 -72
- data/rust/project/fielded.rb +0 -141
- data/rust/project/json_codec.rb +0 -641
- data/rust/project/mutations.rb +0 -695
- data/rust/project/naming.rb +0 -223
- data/rust/project/ports.rb +0 -65
- data/rust/project/queries.rb +0 -493
- data/rust/project/reactions.rb +0 -317
- data/rust/project/read_models.rb +0 -690
- data/rust/project/reference_specs.rb +0 -27
- data/rust/project/registry.rb +0 -396
- data/rust/project/skip_reason.rb +0 -29
- data/rust/project/types.rb +0 -225
- data/rust/project/write_if_changed.rb +0 -7
- data/rust/project.rb +0 -30
- data/rust/project_rust_pipeline.rb +0 -391
data/rust/project/exemplar.rb
DELETED
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
module RustProjection
|
|
2
|
-
# Loads the `// TMPL:<id> BEGIN/END` shapes in rust/src/exemplar/*.rs and renders them
|
|
3
|
-
# by substituting the fixed `Tmpl`/`tmpl_`/`TMPL_` placeholders with real domain names.
|
|
4
|
-
module Exemplar
|
|
5
|
-
# Matches any placeholder token. After substitution nothing may match, so an
|
|
6
|
-
# unfilled placeholder raises instead of leaking `TmplType` into generated output.
|
|
7
|
-
LEFTOVER_PLACEHOLDER = /\b(?:Tmpl\w*|tmpl_\w*|TMPL_\w*)\b/
|
|
8
|
-
|
|
9
|
-
DEFAULT_DIR = File.expand_path("../src/exemplar", __dir__)
|
|
10
|
-
|
|
11
|
-
class DriftError < StandardError; end
|
|
12
|
-
|
|
13
|
-
class << self
|
|
14
|
-
# Overridable so specs can point at a scratch fixture tree.
|
|
15
|
-
def dir
|
|
16
|
-
@dir ||= DEFAULT_DIR
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
# `id` -> raw shape text (dedented, marker lines excluded).
|
|
20
|
-
# A nested region leaves a `<<TMPL_SLOT:<id>:<sub>>>` sentinel; see `compose`.
|
|
21
|
-
def shapes
|
|
22
|
-
@shapes ||= parse_all
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
# Renders `shape_id`, replacing each `subs` key (a literal substring) with its value.
|
|
26
|
-
# Raises DriftError on a key absent from the shape or a placeholder left over.
|
|
27
|
-
#
|
|
28
|
-
# Uses the block form of gsub: the two-arg form interprets backslash sequences in
|
|
29
|
-
# the replacement, which corrupts escaped regex sources.
|
|
30
|
-
def render(shape_id, subs)
|
|
31
|
-
text = raw(shape_id)
|
|
32
|
-
|
|
33
|
-
subs.keys.sort_by { |k| -k.length }.each do |marker|
|
|
34
|
-
raise DriftError, "Exemplar.render(#{shape_id.inspect}): marker #{marker.inspect} not found in shape text — " \
|
|
35
|
-
"exemplar and Ruby caller have drifted" unless text.include?(marker)
|
|
36
|
-
|
|
37
|
-
value = subs[marker]
|
|
38
|
-
text = text.gsub(marker) { value }
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
if (leftover = text.scan(LEFTOVER_PLACEHOLDER)).any?
|
|
42
|
-
raise DriftError, "Exemplar.render(#{shape_id.inspect}): unrendered placeholder(s) #{leftover.uniq.inspect} — " \
|
|
43
|
-
"subs is missing a marker this shape actually contains"
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
text
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
# Renders `shape_id` once per entry in `subs_list`, joined by `join_with`.
|
|
50
|
-
def render_each(shape_id, subs_list, join_with: "\n")
|
|
51
|
-
subs_list.map { |subs| render(shape_id, subs) }.join(join_with)
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
# Fills every nested slot of `outer_id` (`slots` maps field_id => rendered content),
|
|
55
|
-
# reindenting each to its marker's depth, then applies `outer_subs` last so a marker
|
|
56
|
-
# cannot match text that came from a slot.
|
|
57
|
-
def assemble(outer_id, outer_subs, slots:)
|
|
58
|
-
outer_text = raw(outer_id)
|
|
59
|
-
|
|
60
|
-
slots.each do |field_id, content|
|
|
61
|
-
slot = "<<TMPL_SLOT:#{field_id}>>"
|
|
62
|
-
slot_line = outer_text.match(/^([ \t]*)#{Regexp.escape(slot)}$/)
|
|
63
|
-
raise DriftError, "Exemplar.assemble(#{outer_id.inspect}): no nested slot for #{field_id.inspect} — " \
|
|
64
|
-
"expected a `// TMPL:#{field_id} BEGIN/END` region nested inside `// TMPL:#{outer_id} BEGIN/END`" unless slot_line
|
|
65
|
-
|
|
66
|
-
# The nested marker's indentation in the exemplar sets the depth, not the caller.
|
|
67
|
-
indent = slot_line[1]
|
|
68
|
-
reindented = content.lines.map { |l| l.strip.empty? ? l : "#{indent}#{l}" }.join
|
|
69
|
-
outer_text = outer_text.sub(slot_line[0]) { reindented } # block form: see render
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
outer_subs.keys.sort_by { |k| -k.length }.each do |marker|
|
|
73
|
-
raise DriftError, "Exemplar.assemble(#{outer_id.inspect}): marker #{marker.inspect} not found in outer shape text" unless outer_text.include?(marker)
|
|
74
|
-
|
|
75
|
-
value = outer_subs[marker]
|
|
76
|
-
outer_text = outer_text.gsub(marker) { value }
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
if (leftover = outer_text.scan(LEFTOVER_PLACEHOLDER)).any?
|
|
80
|
-
raise DriftError, "Exemplar.assemble(#{outer_id.inspect}): unrendered placeholder(s) #{leftover.uniq.inspect}"
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
outer_text
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
# `assemble` for the single-slot case: renders the inner shape `field_id` once per
|
|
87
|
-
# entry in `field_subs_list` and splices the joined result into `outer_id`.
|
|
88
|
-
def compose(outer_id, outer_subs, field_id:, field_subs_list:, join_with: "\n")
|
|
89
|
-
assemble(outer_id, outer_subs, slots: { field_id => render_each(field_id, field_subs_list, join_with: join_with) })
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
def raw(shape_id)
|
|
93
|
-
shapes.fetch(shape_id) do
|
|
94
|
-
raise DriftError, "Exemplar: no shape #{shape_id.inspect} — " \
|
|
95
|
-
"checked rust/src/exemplar/*.rs for `// TMPL:#{shape_id} BEGIN`"
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
# Test-only: repoints `dir` and clears the memoized shapes.
|
|
100
|
-
def reset!(dir: DEFAULT_DIR)
|
|
101
|
-
@dir = dir
|
|
102
|
-
@shapes = nil
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
private
|
|
106
|
-
|
|
107
|
-
def parse_all
|
|
108
|
-
result = {}
|
|
109
|
-
Dir.glob(File.join(dir, "*.rs")).sort.each { |path| parse_file(path, result) }
|
|
110
|
-
result
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
def parse_file(path, result)
|
|
114
|
-
stack = [] # each frame: [id, lines, begin_indent]
|
|
115
|
-
File.readlines(path).each do |line|
|
|
116
|
-
if (id = begin_marker(line))
|
|
117
|
-
stack.push([id, [], line[/^[ \t]*/]])
|
|
118
|
-
elsif (id = end_marker(line))
|
|
119
|
-
raise "#{path}: TMPL:#{id} END with no matching BEGIN (stack: #{stack.map(&:first).inspect})" if stack.empty? || stack.last[0] != id
|
|
120
|
-
|
|
121
|
-
_, lines, begin_indent = stack.pop
|
|
122
|
-
result[id] = dedent(lines)
|
|
123
|
-
# Keep the nested marker's indentation; it sets the depth of the spliced lines.
|
|
124
|
-
stack.last[1] << "#{begin_indent}<<TMPL_SLOT:#{id}>>\n" if stack.any?
|
|
125
|
-
elsif stack.any?
|
|
126
|
-
stack.last[1] << line
|
|
127
|
-
end
|
|
128
|
-
end
|
|
129
|
-
raise "#{path}: unclosed TMPL region(s): #{stack.map(&:first).inspect}" if stack.any?
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
def begin_marker(line)
|
|
133
|
-
line[%r{//\s*TMPL:(\S+)\s+BEGIN\s*$}, 1]
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
def end_marker(line)
|
|
137
|
-
line[%r{//\s*TMPL:(\S+)\s+END\s*$}, 1]
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
# Drops the leading whitespace shared by all non-blank lines, like `<<~`.
|
|
141
|
-
def dedent(lines)
|
|
142
|
-
indents = lines.reject { |l| l.strip.empty? }.map { |l| l[/^[ \t]*/].length }
|
|
143
|
-
margin = indents.min || 0
|
|
144
|
-
lines.map { |l| l.empty? ? l : l[margin..] }.join.rstrip
|
|
145
|
-
end
|
|
146
|
-
end
|
|
147
|
-
end
|
|
148
|
-
end
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
module RustProjection
|
|
2
|
-
# Emits Rust `Expr` data-literal source from the `ast` tree of an IR rule row.
|
|
3
|
-
# Mirrors rust/codegen/src/expr_emitter.rs node for node; see `AstJson::OPS`.
|
|
4
|
-
# `include` arm below only ever sees a real field/string haystack.
|
|
5
|
-
module ExprEmitter
|
|
6
|
-
module_function
|
|
7
|
-
|
|
8
|
-
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
|
|
9
|
-
def emit_ast(node)
|
|
10
|
-
case at(node, :op)
|
|
11
|
-
when "or" then "Expr::Or(Box::new(#{emit_ast(at(node, :left))}), Box::new(#{emit_ast(at(node, :right))}))"
|
|
12
|
-
when "and" then "Expr::And(Box::new(#{emit_ast(at(node, :left))}), Box::new(#{emit_ast(at(node, :right))}))"
|
|
13
|
-
when "not" then "Expr::Not(Box::new(#{emit_ast(at(node, :expr))}))"
|
|
14
|
-
when "compare"
|
|
15
|
-
"Expr::Compare { op: #{emit_comparison(at(node, :cmp))}, left: Box::new(#{emit_ast(at(node, :left))}), right: Box::new(#{emit_ast(at(node, :right))}) }"
|
|
16
|
-
when "include"
|
|
17
|
-
"Expr::Include { haystack: Box::new(#{emit_ast(at(node, :haystack))}), needle: Box::new(#{emit_ast(at(node, :needle))}) }"
|
|
18
|
-
when "int" then "Expr::Int(#{at(node, :value)})"
|
|
19
|
-
when "float" then "Expr::Float(#{at(node, :value)}f64)"
|
|
20
|
-
when "str" then "Expr::Str(#{at(node, :value).inspect}.to_string())"
|
|
21
|
-
when "bool" then "Expr::Bool(#{at(node, :value)})"
|
|
22
|
-
when "nil" then "Expr::Nil"
|
|
23
|
-
when "lookup" then "Expr::Lookup(#{at(node, :path).join('.').inspect})"
|
|
24
|
-
when "add" then "Expr::Add(Box::new(#{emit_ast(at(node, :left))}), Box::new(#{emit_ast(at(node, :right))}))"
|
|
25
|
-
when "sign_test"
|
|
26
|
-
"Expr::SignTest { op: #{emit_comparison(at(node, :cmp))}, receiver: Box::new(#{emit_ast(at(node, :receiver))}) }"
|
|
27
|
-
when "empty" then "Expr::Empty(Box::new(#{emit_ast(at(node, :receiver))}))"
|
|
28
|
-
when "to_s" then "Expr::ToS(Box::new(#{emit_ast(at(node, :receiver))}))"
|
|
29
|
-
when "modulo"
|
|
30
|
-
"Expr::Modulo { receiver: Box::new(#{emit_ast(at(node, :receiver))}), divisor: Box::new(#{emit_ast(at(node, :divisor))}) }"
|
|
31
|
-
when "size" then "Expr::Size(Box::new(#{emit_ast(at(node, :receiver))}))"
|
|
32
|
-
when "block_predicate"
|
|
33
|
-
"Expr::BlockPredicate { mode: crate::kernel::BlockMode::#{at(node, :mode).capitalize}, receiver: Box::new(#{emit_ast(at(node, :receiver))}), " \
|
|
34
|
-
"param: #{at(node, :param).inspect}, predicate: Box::new(#{emit_ast(at(node, :predicate))}) }"
|
|
35
|
-
when "find"
|
|
36
|
-
"Expr::Find { receiver: Box::new(#{emit_ast(at(node, :receiver))}), param: #{at(node, :param).inspect}, " \
|
|
37
|
-
"predicate: Box::new(#{emit_ast(at(node, :predicate))}), path: &[#{at(node, :path).map(&:inspect).join(', ')}] }"
|
|
38
|
-
when "array"
|
|
39
|
-
"Expr::Array(vec![#{at(node, :elements).map { |element| emit_ast(element) }.join(', ')}])"
|
|
40
|
-
when "matches_regex"
|
|
41
|
-
"Expr::MatchesRegex { receiver: Box::new(#{emit_ast(at(node, :receiver))}), pattern: #{at(node, :pattern).inspect}.to_string(), flags: #{at(node, :flags).inspect}.to_string() }"
|
|
42
|
-
when "presence"
|
|
43
|
-
"Expr::Presence { receiver: Box::new(#{emit_ast(at(node, :receiver))}), negated: #{at(node, :negated)} }"
|
|
44
|
-
when "assignment"
|
|
45
|
-
"Expr::Assignment { receiver: Box::new(#{emit_ast(at(node, :receiver))}), negated: #{at(node, :negated)} }"
|
|
46
|
-
when "split"
|
|
47
|
-
"Expr::Split { receiver: Box::new(#{emit_ast(at(node, :receiver))}), separator: #{at(node, :separator).inspect}.to_string() }"
|
|
48
|
-
when "starts_with"
|
|
49
|
-
"Expr::StartsWith { receiver: Box::new(#{emit_ast(at(node, :receiver))}), substring: #{at(node, :substring).inspect}.to_string() }"
|
|
50
|
-
when "ends_with"
|
|
51
|
-
"Expr::EndsWith { receiver: Box::new(#{emit_ast(at(node, :receiver))}), substring: #{at(node, :substring).inspect}.to_string() }"
|
|
52
|
-
when "first" then "Expr::First(Box::new(#{emit_ast(at(node, :receiver))}))"
|
|
53
|
-
when "last" then "Expr::Last(Box::new(#{emit_ast(at(node, :receiver))}))"
|
|
54
|
-
else
|
|
55
|
-
# An op with no arm above means `AstJson::OPS` grew and this file did not.
|
|
56
|
-
raise "unhandled ast op #{at(node, :op).inspect} — no Rust rendering exists for it in this generator (rust/project/expr_emitter.rb#emit_ast)"
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
|
|
60
|
-
|
|
61
|
-
# Fully qualified: the grammar's own "Comparison" closed set would collide with the
|
|
62
|
-
# kernel struct of the same name in a file that needs both.
|
|
63
|
-
def emit_comparison(cmp)
|
|
64
|
-
"crate::kernel::Comparison { less_than: #{at(cmp, :less_than)}, equal: #{at(cmp, :equal)}, negated: #{at(cmp, :negated)} }"
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
# Reads a key from a symbol-keyed (round-tripped) or string-keyed (`AstJson`) node.
|
|
68
|
-
def at(node, key)
|
|
69
|
-
node.key?(key) ? node[key] : node.fetch(key.to_s)
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
end
|
data/rust/project/fielded.rb
DELETED
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
module RustProjection
|
|
2
|
-
module Projector
|
|
3
|
-
module_function
|
|
4
|
-
|
|
5
|
-
# One `Fielded` match arm per attribute, shared by value objects, args structs and
|
|
6
|
-
# (via emit_fielded_record) aggregate records.
|
|
7
|
-
# `extra_arms:` are raw `"key" => ...,` lines appended verbatim, e.g. an entity's lifecycle
|
|
8
|
-
# field. Arms render from the leaf shapes in rust/src/exemplar/fielded.rs with a fixed
|
|
9
|
-
# 12-space prefix, using plain `render` because the leaves are shared across outer shapes.
|
|
10
|
-
def emit_fielded_flat(struct_name, attributes, value_objects_by_name, extra_arms: [], entity_names: [])
|
|
11
|
-
arms = attributes.filter_map do |attr|
|
|
12
|
-
key = rust_field(attr[:name])
|
|
13
|
-
ident = rust_ident_field(attr[:name])
|
|
14
|
-
scalar = effective_scalar_type(attr[:type])
|
|
15
|
-
if attr[:list] && attr[:optional]
|
|
16
|
-
Exemplar.render("fielded_arm_list_optional", '"tmpl_field"' => key.inspect, "tmpl_ident" => ident)
|
|
17
|
-
elsif attr[:list]
|
|
18
|
-
Exemplar.render("fielded_arm_list", '"tmpl_field"' => key.inspect, "tmpl_ident" => ident)
|
|
19
|
-
elsif attr[:optional] && scalar
|
|
20
|
-
Exemplar.render(
|
|
21
|
-
"fielded_arm_optional_scalar",
|
|
22
|
-
'"tmpl_field"' => key.inspect,
|
|
23
|
-
"tmpl_ident" => ident,
|
|
24
|
-
"tmpl_value_expr_placeholder(v)" => scalar_to_value(scalar, "v")
|
|
25
|
-
)
|
|
26
|
-
elsif attr[:optional]
|
|
27
|
-
nested = value_objects_by_name[attr[:type]]
|
|
28
|
-
next nil unless nested && fielded_capable_nested?(nested)
|
|
29
|
-
|
|
30
|
-
Exemplar.render("fielded_arm_optional_nested", '"tmpl_field"' => key.inspect, "tmpl_ident" => ident)
|
|
31
|
-
elsif scalar
|
|
32
|
-
Exemplar.render(
|
|
33
|
-
"fielded_arm_scalar",
|
|
34
|
-
'"tmpl_field"' => key.inspect,
|
|
35
|
-
"tmpl_value_expr_placeholder(&self.tmpl_ident)" => scalar_to_value(scalar, "self.#{ident}")
|
|
36
|
-
)
|
|
37
|
-
else
|
|
38
|
-
nested = value_objects_by_name[attr[:type]]
|
|
39
|
-
next nil unless nested && fielded_capable_nested?(nested)
|
|
40
|
-
|
|
41
|
-
Exemplar.render("fielded_arm_nested", '"tmpl_field"' => key.inspect, "tmpl_ident" => ident)
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
arms = arms.map { |a| " #{a}" }
|
|
45
|
-
arms += extra_arms
|
|
46
|
-
|
|
47
|
-
# The trailing "\n" keeps the blank-line count callers expect; render's dedent rstrips.
|
|
48
|
-
"#{Exemplar.render(
|
|
49
|
-
'fielded_flat',
|
|
50
|
-
'TmplFlatType' => struct_name,
|
|
51
|
-
'"tmpl_arms_placeholder" => tmpl_arms_block(),' => arms.join("\n"),
|
|
52
|
-
# `entity_names`: a list of entities is enumerated the same way the record does.
|
|
53
|
-
'"tmpl_items_placeholder" => tmpl_items_block(),' => items_arms(attributes, value_objects_by_name, entity_names, optional: ->(attr) { attr[:optional] }).join("\n"),
|
|
54
|
-
"tmpl_as_scalar_placeholder()" => as_scalar_expr(attributes),
|
|
55
|
-
# Import only what the arms use, so a struct with no arms leaves no unused `use`.
|
|
56
|
-
'use crate::kernel::Field;' => (arms.any? { |arm| arm.include?('Field') } ? 'use crate::kernel::Field;' : ''),
|
|
57
|
-
'use crate::kernel::Value;' => (arms.any? { |arm| arm.include?('Value') } ? 'use crate::kernel::Value;' : '')
|
|
58
|
-
)}\n"
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
# `Fielded::items`: one arm per list attribute whose element is a scalar or a fielded-capable
|
|
62
|
-
# nested type or entity. `optional` says whether the list is `Option`-wrapped.
|
|
63
|
-
def items_arms(attributes, value_objects_by_name, entity_names, optional:)
|
|
64
|
-
attributes.filter_map do |attr|
|
|
65
|
-
next nil unless attr[:list]
|
|
66
|
-
|
|
67
|
-
key = rust_field(attr[:name])
|
|
68
|
-
ident = rust_ident_field(attr[:name])
|
|
69
|
-
scalar = effective_scalar_type(attr[:type])
|
|
70
|
-
nested = value_objects_by_name[attr[:type]]
|
|
71
|
-
fielded_element = scalar || (nested && fielded_capable_nested?(nested)) || entity_names.include?(attr[:type])
|
|
72
|
-
next nil unless fielded_element
|
|
73
|
-
|
|
74
|
-
shape = "fielded_items_arm_list_#{optional.call(attr) ? 'optional_' : ''}#{scalar ? 'scalar' : 'nested'}"
|
|
75
|
-
subs = { '"tmpl_field"' => key.inspect, "tmpl_ident" => ident }
|
|
76
|
-
subs["tmpl_value_expr_placeholder(v)"] = scalar_to_value(scalar, "v") if scalar
|
|
77
|
-
" #{Exemplar.render(shape, subs)}"
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
# Record variant of emit_fielded_flat: non-list attributes are `Option`-wrapped, so `None`
|
|
82
|
-
# reads as `Value::Nil`. Adds lifecycle and corrects-flag arms.
|
|
83
|
-
def emit_fielded_record(aggregate, value_objects_by_name)
|
|
84
|
-
name = rust_ident(aggregate[:name])
|
|
85
|
-
arms = aggregate[:attributes].filter_map do |attr|
|
|
86
|
-
key = rust_field(attr[:name])
|
|
87
|
-
ident = rust_ident_field(attr[:name])
|
|
88
|
-
scalar = effective_scalar_type(attr[:type])
|
|
89
|
-
if attr[:list] && list_attr_creation_optional?(aggregate, attr[:name], value_objects_by_name)
|
|
90
|
-
Exemplar.render("fielded_arm_list_optional", '"tmpl_field"' => key.inspect, "tmpl_ident" => ident)
|
|
91
|
-
elsif attr[:list]
|
|
92
|
-
Exemplar.render("fielded_arm_list", '"tmpl_field"' => key.inspect, "tmpl_ident" => ident)
|
|
93
|
-
elsif scalar
|
|
94
|
-
Exemplar.render(
|
|
95
|
-
"fielded_arm_optional_scalar",
|
|
96
|
-
'"tmpl_field"' => key.inspect,
|
|
97
|
-
"tmpl_ident" => ident,
|
|
98
|
-
"tmpl_value_expr_placeholder(v)" => scalar_to_value(scalar, "v")
|
|
99
|
-
)
|
|
100
|
-
else
|
|
101
|
-
nested = value_objects_by_name[attr[:type]]
|
|
102
|
-
next nil unless nested && fielded_capable_nested?(nested)
|
|
103
|
-
|
|
104
|
-
Exemplar.render("fielded_arm_optional_nested", '"tmpl_field"' => key.inspect, "tmpl_ident" => ident)
|
|
105
|
-
end
|
|
106
|
-
end
|
|
107
|
-
if aggregate[:lifecycle]
|
|
108
|
-
key = rust_field(aggregate[:lifecycle][:field])
|
|
109
|
-
ident = rust_ident_field(aggregate[:lifecycle][:field])
|
|
110
|
-
arms << Exemplar.render("fielded_lifecycle_arm", '"tmpl_field"' => key.inspect, "tmpl_ident" => ident)
|
|
111
|
-
end
|
|
112
|
-
correctable_event_names(aggregate).each do |ev|
|
|
113
|
-
ident = corrects_flag_field(ev)
|
|
114
|
-
arms << Exemplar.render("fielded_corrects_flag_arm", '"tmpl_field"' => ident.inspect, "tmpl_ident" => ident)
|
|
115
|
-
end
|
|
116
|
-
arms = arms.map { |a| " #{a}" }
|
|
117
|
-
|
|
118
|
-
# Trailing "\n": see emit_fielded_flat.
|
|
119
|
-
entity_names = (aggregate[:entities] || []).map { |e| e[:name] }
|
|
120
|
-
"#{Exemplar.render(
|
|
121
|
-
'fielded_record',
|
|
122
|
-
'TmplRecordType' => name,
|
|
123
|
-
'"tmpl_arms_placeholder" => tmpl_arms_block(),' => arms.join("\n"),
|
|
124
|
-
'"tmpl_items_placeholder" => tmpl_items_block(),' => items_arms(
|
|
125
|
-
aggregate[:attributes], value_objects_by_name, entity_names,
|
|
126
|
-
optional: ->(attr) { list_attr_creation_optional?(aggregate, attr[:name], value_objects_by_name) }
|
|
127
|
-
).join("\n"),
|
|
128
|
-
"tmpl_as_scalar_placeholder()" => as_scalar_expr(aggregate[:attributes])
|
|
129
|
-
)}\n"
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
# `Resolver#unwrap_scalar`: a struct with exactly one scalar attribute reads as that
|
|
133
|
-
# attribute's value whatever its name; anything else answers `None`.
|
|
134
|
-
def as_scalar_expr(attributes)
|
|
135
|
-
sole = attributes.size == 1 && !attributes.first[:list] ? attributes.first : nil
|
|
136
|
-
return "None" unless sole && effective_scalar_type(sole[:type])
|
|
137
|
-
|
|
138
|
-
"match self.field(#{rust_field(sole[:name]).inspect}) { Some(crate::kernel::Field::Value(v)) => Some(v), _ => None }"
|
|
139
|
-
end
|
|
140
|
-
end
|
|
141
|
-
end
|