expressir 2.1.29 → 2.1.31
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/.github/workflows/docs.yml +98 -0
- data/.github/workflows/links.yml +100 -0
- data/.github/workflows/rake.yml +4 -0
- data/.github/workflows/release.yml +5 -0
- data/.github/workflows/validate_schemas.yml +1 -1
- data/.gitignore +3 -0
- data/.rubocop.yml +1 -1
- data/.rubocop_todo.yml +209 -55
- data/Gemfile +2 -1
- data/README.adoc +650 -83
- data/docs/Gemfile +12 -0
- data/docs/_config.yml +141 -0
- data/docs/_guides/changes/changes-format.adoc +778 -0
- data/docs/_guides/changes/importing-eengine.adoc +898 -0
- data/docs/_guides/changes/index.adoc +396 -0
- data/docs/_guides/changes/programmatic-usage.adoc +1038 -0
- data/docs/_guides/changes/validating-changes.adoc +681 -0
- data/docs/_guides/cli/benchmark-performance.adoc +834 -0
- data/docs/_guides/cli/coverage-analysis.adoc +921 -0
- data/docs/_guides/cli/format-schemas.adoc +547 -0
- data/docs/_guides/cli/index.adoc +8 -0
- data/docs/_guides/cli/managing-changes.adoc +927 -0
- data/docs/_guides/cli/validate-ascii.adoc +645 -0
- data/docs/_guides/cli/validate-schemas.adoc +534 -0
- data/docs/_guides/index.adoc +165 -0
- data/docs/_guides/ler/creating-packages.adoc +664 -0
- data/docs/_guides/ler/index.adoc +305 -0
- data/docs/_guides/ler/loading-packages.adoc +707 -0
- data/docs/_guides/ler/package-formats.adoc +748 -0
- data/docs/_guides/ler/querying-packages.adoc +826 -0
- data/docs/_guides/ler/validating-packages.adoc +750 -0
- data/docs/_guides/liquid/basic-templates.adoc +813 -0
- data/docs/_guides/liquid/documentation-generation.adoc +1042 -0
- data/docs/_guides/liquid/drops-reference.adoc +829 -0
- data/docs/_guides/liquid/filters-and-tags.adoc +912 -0
- data/docs/_guides/liquid/index.adoc +468 -0
- data/docs/_guides/manifests/creating-manifests.adoc +483 -0
- data/docs/_guides/manifests/index.adoc +307 -0
- data/docs/_guides/manifests/resolving-manifests.adoc +557 -0
- data/docs/_guides/manifests/validating-manifests.adoc +713 -0
- data/docs/_guides/ruby-api/formatting-schemas.adoc +605 -0
- data/docs/_guides/ruby-api/index.adoc +257 -0
- data/docs/_guides/ruby-api/parsing-files.adoc +421 -0
- data/docs/_guides/ruby-api/search-engine.adoc +609 -0
- data/docs/_guides/ruby-api/working-with-repository.adoc +577 -0
- data/docs/_pages/data-model.adoc +665 -0
- data/docs/_pages/express-language.adoc +506 -0
- data/docs/_pages/getting-started.adoc +414 -0
- data/docs/_pages/index.adoc +116 -0
- data/docs/_pages/introduction.adoc +256 -0
- data/docs/_pages/ler-packages.adoc +837 -0
- data/docs/_pages/parsers.adoc +683 -0
- data/docs/_pages/schema-manifests.adoc +431 -0
- data/docs/_references/index.adoc +228 -0
- data/docs/_tutorials/creating-ler-package.adoc +735 -0
- data/docs/_tutorials/documentation-coverage.adoc +795 -0
- data/docs/_tutorials/index.adoc +221 -0
- data/docs/_tutorials/liquid-templates.adoc +806 -0
- data/docs/_tutorials/parsing-your-first-schema.adoc +522 -0
- data/docs/_tutorials/querying-schemas.adoc +751 -0
- data/docs/_tutorials/working-with-multiple-schemas.adoc +676 -0
- data/docs/index.adoc +242 -0
- data/docs/lychee.toml +84 -0
- data/examples/demo_ler_usage.sh +86 -0
- data/examples/ler/README.md +111 -0
- data/examples/ler/simple_example.ler +0 -0
- data/examples/ler/simple_schema.exp +33 -0
- data/examples/ler_build.rb +75 -0
- data/examples/ler_cli.rb +79 -0
- data/examples/ler_demo_complete.rb +276 -0
- data/examples/ler_query.rb +91 -0
- data/examples/ler_query_examples.rb +305 -0
- data/examples/ler_stats.rb +81 -0
- data/examples/phase3_demo.rb +159 -0
- data/examples/query_demo_simple.rb +131 -0
- data/expressir.gemspec +2 -0
- data/lib/expressir/changes/schema_change.rb +32 -22
- data/lib/expressir/changes/{edition_change.rb → version_change.rb} +3 -3
- data/lib/expressir/cli.rb +12 -4
- data/lib/expressir/commands/changes_import_eengine.rb +2 -2
- data/lib/expressir/commands/changes_validate.rb +1 -1
- data/lib/expressir/commands/manifest.rb +427 -0
- data/lib/expressir/commands/package.rb +1274 -0
- data/lib/expressir/commands/validate.rb +70 -37
- data/lib/expressir/commands/validate_ascii.rb +607 -0
- data/lib/expressir/commands/validate_load.rb +88 -0
- data/lib/expressir/express/formatter.rb +5 -1
- data/lib/expressir/express/formatters/remark_item_formatter.rb +25 -0
- data/lib/expressir/express/parser.rb +33 -0
- data/lib/expressir/manifest/resolver.rb +213 -0
- data/lib/expressir/manifest/validator.rb +195 -0
- data/lib/expressir/model/declarations/entity.rb +6 -0
- data/lib/expressir/model/dependency_resolver.rb +270 -0
- data/lib/expressir/model/indexes/entity_index.rb +103 -0
- data/lib/expressir/model/indexes/reference_index.rb +148 -0
- data/lib/expressir/model/indexes/type_index.rb +149 -0
- data/lib/expressir/model/interface_validator.rb +384 -0
- data/lib/expressir/model/repository.rb +400 -5
- data/lib/expressir/model/repository_validator.rb +295 -0
- data/lib/expressir/model/search_engine.rb +525 -0
- data/lib/expressir/model.rb +4 -94
- data/lib/expressir/package/builder.rb +200 -0
- data/lib/expressir/package/metadata.rb +81 -0
- data/lib/expressir/package/reader.rb +165 -0
- data/lib/expressir/schema_manifest.rb +11 -1
- data/lib/expressir/version.rb +1 -1
- data/lib/expressir.rb +16 -3
- metadata +115 -5
- data/docs/benchmarking.adoc +0 -107
- data/docs/liquid_drops.adoc +0 -1547
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Expressir
|
|
4
|
+
module Model
|
|
5
|
+
# Validates EXPRESS interface statements (USE FROM, REFERENCE FROM)
|
|
6
|
+
# Single responsibility: Interface-specific validation only
|
|
7
|
+
# Handles schema reference validation, interface item validation, and type compatibility checks
|
|
8
|
+
class InterfaceValidator
|
|
9
|
+
# Error types returned by validation
|
|
10
|
+
ERROR_TYPES = {
|
|
11
|
+
missing_schema: :missing_schema_reference,
|
|
12
|
+
missing_item: :missing_interface_item,
|
|
13
|
+
type_mismatch: :interface_type_mismatch,
|
|
14
|
+
duplicate_alias: :duplicate_interface_alias,
|
|
15
|
+
self_reference: :self_reference_interface,
|
|
16
|
+
empty_items: :empty_interface_items,
|
|
17
|
+
}.freeze
|
|
18
|
+
|
|
19
|
+
attr_reader :repository, :schemas
|
|
20
|
+
|
|
21
|
+
# Initialize validator with a repository
|
|
22
|
+
# @param repository [Repository] Repository containing schemas to validate
|
|
23
|
+
def initialize(repository)
|
|
24
|
+
@repository = repository
|
|
25
|
+
@schemas = repository.schemas
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Main validation entry point
|
|
29
|
+
# @param options [Hash] Validation options
|
|
30
|
+
# @option options [Boolean] :detailed Show detailed reports with fix suggestions
|
|
31
|
+
# @option options [Boolean] :check_duplicates Check for duplicate aliases
|
|
32
|
+
# @option options [Boolean] :check_self_references Check for self-referencing interfaces
|
|
33
|
+
# @option options [Boolean] :strict Enable strict validation (warnings become errors)
|
|
34
|
+
# @return [Hash] Validation results with :valid?, :errors, :warnings, :interface_report
|
|
35
|
+
def validate(options = {})
|
|
36
|
+
errors = []
|
|
37
|
+
warnings = []
|
|
38
|
+
stats = { use_from: 0, reference_from: 0, total_items: 0 }
|
|
39
|
+
|
|
40
|
+
schemas.each do |schema|
|
|
41
|
+
validate_schema_interfaces(schema, errors, warnings, stats, options)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
result = {
|
|
45
|
+
valid?: errors.empty?,
|
|
46
|
+
errors: errors,
|
|
47
|
+
warnings: warnings,
|
|
48
|
+
stats: stats,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if options[:detailed]
|
|
52
|
+
result[:interface_report] =
|
|
53
|
+
build_detailed_report(errors, warnings, stats)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
result
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Validate interfaces in a specific schema
|
|
60
|
+
# @param schema_name [String] Schema name
|
|
61
|
+
# @param options [Hash] Validation options
|
|
62
|
+
# @return [Hash] Validation results for the schema
|
|
63
|
+
def validate_schema(schema_name, options = {})
|
|
64
|
+
schema = find_schema(schema_name)
|
|
65
|
+
unless schema
|
|
66
|
+
return { valid?: false,
|
|
67
|
+
errors: [{ type: :schema_not_found,
|
|
68
|
+
message: "Schema '#{schema_name}' not found" }] }
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
errors = []
|
|
72
|
+
warnings = []
|
|
73
|
+
stats = { use_from: 0, reference_from: 0, total_items: 0 }
|
|
74
|
+
|
|
75
|
+
validate_schema_interfaces(schema, errors, warnings, stats, options)
|
|
76
|
+
|
|
77
|
+
{
|
|
78
|
+
valid?: errors.empty?,
|
|
79
|
+
errors: errors,
|
|
80
|
+
warnings: warnings,
|
|
81
|
+
stats: stats,
|
|
82
|
+
}
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
private
|
|
86
|
+
|
|
87
|
+
# Validate all interfaces in a schema
|
|
88
|
+
# @param schema [Declarations::Schema] Schema to validate
|
|
89
|
+
# @param errors [Array] Accumulator for errors
|
|
90
|
+
# @param warnings [Array] Accumulator for warnings
|
|
91
|
+
# @param stats [Hash] Statistics accumulator
|
|
92
|
+
# @param options [Hash] Validation options
|
|
93
|
+
# @return [void]
|
|
94
|
+
def validate_schema_interfaces(schema, errors, warnings, stats, options)
|
|
95
|
+
return unless schema.interfaces
|
|
96
|
+
|
|
97
|
+
schema.interfaces.each do |interface|
|
|
98
|
+
case interface.kind
|
|
99
|
+
when Declarations::Interface::USE
|
|
100
|
+
stats[:use_from] += 1
|
|
101
|
+
validate_use_from(schema, interface, errors, warnings, options)
|
|
102
|
+
when Declarations::Interface::REFERENCE
|
|
103
|
+
stats[:reference_from] += 1
|
|
104
|
+
validate_reference_from(schema, interface, errors, warnings,
|
|
105
|
+
options)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
stats[:total_items] += interface.items.size
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Additional validations if enabled
|
|
112
|
+
if options[:check_duplicates]
|
|
113
|
+
check_duplicate_aliases(schema, errors)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
if options[:check_self_references]
|
|
117
|
+
check_self_references(schema, warnings)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Validate USE FROM interface
|
|
122
|
+
# @param schema [Declarations::Schema] Source schema
|
|
123
|
+
# @param interface [Declarations::Interface] Interface to validate
|
|
124
|
+
# @param errors [Array] Accumulator for errors
|
|
125
|
+
# @param warnings [Array] Accumulator for warnings
|
|
126
|
+
# @param options [Hash] Validation options
|
|
127
|
+
# @return [void]
|
|
128
|
+
def validate_use_from(schema, interface, errors, warnings, _options)
|
|
129
|
+
referenced_schema = find_schema(interface.schema.id)
|
|
130
|
+
|
|
131
|
+
if referenced_schema.nil?
|
|
132
|
+
errors << build_error(
|
|
133
|
+
type: ERROR_TYPES[:missing_schema],
|
|
134
|
+
schema: schema.id,
|
|
135
|
+
interface_type: "USE FROM",
|
|
136
|
+
referenced_schema: interface.schema.id,
|
|
137
|
+
message: "Schema '#{schema.id}' uses non-existent schema '#{interface.schema.id}'",
|
|
138
|
+
fix_suggestion: "Ensure schema '#{interface.schema.id}' exists in the repository or remove the USE FROM statement",
|
|
139
|
+
)
|
|
140
|
+
return
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Validate interface items
|
|
144
|
+
validate_interface_items(schema, interface, referenced_schema, errors,
|
|
145
|
+
warnings, "USE FROM")
|
|
146
|
+
|
|
147
|
+
# Warn if USE FROM has no items (imports everything)
|
|
148
|
+
if interface.items.empty?
|
|
149
|
+
warnings << {
|
|
150
|
+
type: ERROR_TYPES[:empty_items],
|
|
151
|
+
schema: schema.id,
|
|
152
|
+
interface_type: "USE FROM",
|
|
153
|
+
referenced_schema: interface.schema.id,
|
|
154
|
+
message: "USE FROM imports all items from '#{interface.schema.id}' (consider specifying explicit items)",
|
|
155
|
+
fix_suggestion: "Add explicit item list: USE FROM #{interface.schema.id} (item1, item2, ...);",
|
|
156
|
+
}
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Validate REFERENCE FROM interface
|
|
161
|
+
# @param schema [Declarations::Schema] Source schema
|
|
162
|
+
# @param interface [Declarations::Interface] Interface to validate
|
|
163
|
+
# @param errors [Array] Accumulator for errors
|
|
164
|
+
# @param warnings [Array] Accumulator for warnings
|
|
165
|
+
# @param options [Hash] Validation options
|
|
166
|
+
# @return [void]
|
|
167
|
+
def validate_reference_from(schema, interface, errors, warnings, _options)
|
|
168
|
+
referenced_schema = find_schema(interface.schema.id)
|
|
169
|
+
|
|
170
|
+
if referenced_schema.nil?
|
|
171
|
+
errors << build_error(
|
|
172
|
+
type: ERROR_TYPES[:missing_schema],
|
|
173
|
+
schema: schema.id,
|
|
174
|
+
interface_type: "REFERENCE FROM",
|
|
175
|
+
referenced_schema: interface.schema.id,
|
|
176
|
+
message: "Schema '#{schema.id}' references non-existent schema '#{interface.schema.id}'",
|
|
177
|
+
fix_suggestion: "Ensure schema '#{interface.schema.id}' exists in the repository or remove the REFERENCE FROM statement",
|
|
178
|
+
)
|
|
179
|
+
return
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Validate interface items
|
|
183
|
+
validate_interface_items(schema, interface, referenced_schema, errors,
|
|
184
|
+
warnings, "REFERENCE FROM")
|
|
185
|
+
|
|
186
|
+
# REFERENCE FROM should always have explicit items
|
|
187
|
+
if interface.items.empty?
|
|
188
|
+
errors << build_error(
|
|
189
|
+
type: ERROR_TYPES[:empty_items],
|
|
190
|
+
schema: schema.id,
|
|
191
|
+
interface_type: "REFERENCE FROM",
|
|
192
|
+
referenced_schema: interface.schema.id,
|
|
193
|
+
message: "REFERENCE FROM requires explicit item list",
|
|
194
|
+
fix_suggestion: "Add item list: REFERENCE FROM #{interface.schema.id} (item1, item2, ...);",
|
|
195
|
+
)
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Validate interface items exist in referenced schema
|
|
200
|
+
# @param schema [Declarations::Schema] Source schema
|
|
201
|
+
# @param interface [Declarations::Interface] Interface to validate
|
|
202
|
+
# @param referenced_schema [Declarations::Schema] Referenced schema
|
|
203
|
+
# @param errors [Array] Accumulator for errors
|
|
204
|
+
# @param warnings [Array] Accumulator for warnings
|
|
205
|
+
# @param interface_type [String] Type of interface (USE FROM or REFERENCE FROM)
|
|
206
|
+
# @return [void]
|
|
207
|
+
def validate_interface_items(schema, interface, referenced_schema,
|
|
208
|
+
errors, _warnings, interface_type)
|
|
209
|
+
interface.items.each do |item|
|
|
210
|
+
ref_id = item.ref.id.safe_downcase
|
|
211
|
+
item_type = find_item_type(referenced_schema, ref_id)
|
|
212
|
+
|
|
213
|
+
unless item_type
|
|
214
|
+
errors << build_error(
|
|
215
|
+
type: ERROR_TYPES[:missing_item],
|
|
216
|
+
schema: schema.id,
|
|
217
|
+
interface_type: interface_type,
|
|
218
|
+
referenced_schema: interface.schema.id,
|
|
219
|
+
item: item.ref.id,
|
|
220
|
+
message: "Interface item '#{item.ref.id}' not found in schema '#{interface.schema.id}'",
|
|
221
|
+
fix_suggestion: "Remove '#{item.ref.id}' from interface or check spelling. Available items in '#{interface.schema.id}': #{list_available_items(referenced_schema)}",
|
|
222
|
+
)
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
# Check for duplicate interface aliases
|
|
228
|
+
# @param schema [Declarations::Schema] Schema to check
|
|
229
|
+
# @param errors [Array] Accumulator for errors
|
|
230
|
+
# @return [void]
|
|
231
|
+
def check_duplicate_aliases(schema, errors)
|
|
232
|
+
return unless schema.interfaces
|
|
233
|
+
|
|
234
|
+
aliases = {}
|
|
235
|
+
schema.interfaces.each do |interface|
|
|
236
|
+
interface.items.each do |item|
|
|
237
|
+
alias_id = (item.id || item.ref.id).safe_downcase
|
|
238
|
+
if aliases[alias_id]
|
|
239
|
+
errors << build_error(
|
|
240
|
+
type: ERROR_TYPES[:duplicate_alias],
|
|
241
|
+
schema: schema.id,
|
|
242
|
+
alias: alias_id,
|
|
243
|
+
message: "Duplicate interface alias '#{item.id || item.ref.id}' in schema '#{schema.id}'",
|
|
244
|
+
fix_suggestion: "Use unique aliases: USE FROM schema (item AS unique_name)",
|
|
245
|
+
)
|
|
246
|
+
else
|
|
247
|
+
aliases[alias_id] = true
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
# Check for self-referencing interfaces
|
|
254
|
+
# @param schema [Declarations::Schema] Schema to check
|
|
255
|
+
# @param warnings [Array] Accumulator for warnings
|
|
256
|
+
# @return [void]
|
|
257
|
+
def check_self_references(schema, warnings)
|
|
258
|
+
return unless schema.interfaces
|
|
259
|
+
|
|
260
|
+
schema.interfaces.each do |interface|
|
|
261
|
+
if interface.schema.id.safe_downcase == schema.id.safe_downcase
|
|
262
|
+
warnings << {
|
|
263
|
+
type: ERROR_TYPES[:self_reference],
|
|
264
|
+
schema: schema.id,
|
|
265
|
+
message: "Schema '#{schema.id}' references itself (likely unnecessary)",
|
|
266
|
+
fix_suggestion: "Remove self-referencing interface statement",
|
|
267
|
+
}
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
# Find schema by name
|
|
273
|
+
# @param schema_name [String] Schema name
|
|
274
|
+
# @return [Declarations::Schema, nil] Found schema or nil
|
|
275
|
+
def find_schema(schema_name)
|
|
276
|
+
schemas.find { |s| s.id.safe_downcase == schema_name.safe_downcase }
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
# Find item type in schema
|
|
280
|
+
# @param schema [Declarations::Schema] Schema to search
|
|
281
|
+
# @param ref_id [String] Item reference ID (normalized)
|
|
282
|
+
# @return [Symbol, nil] Item type or nil if not found
|
|
283
|
+
def find_item_type(schema, ref_id)
|
|
284
|
+
return :entity if schema.entities&.any? do |e|
|
|
285
|
+
e.id.safe_downcase == ref_id
|
|
286
|
+
end
|
|
287
|
+
return :type if schema.types&.any? { |t| t.id.safe_downcase == ref_id }
|
|
288
|
+
return :function if schema.functions&.any? do |f|
|
|
289
|
+
f.id.safe_downcase == ref_id
|
|
290
|
+
end
|
|
291
|
+
return :procedure if schema.procedures&.any? do |p|
|
|
292
|
+
p.id.safe_downcase == ref_id
|
|
293
|
+
end
|
|
294
|
+
return :constant if schema.constants&.any? do |c|
|
|
295
|
+
c.id.safe_downcase == ref_id
|
|
296
|
+
end
|
|
297
|
+
return :rule if schema.rules&.any? { |r| r.id.safe_downcase == ref_id }
|
|
298
|
+
|
|
299
|
+
nil
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
# List available items in schema
|
|
303
|
+
# @param schema [Declarations::Schema] Schema to list items from
|
|
304
|
+
# @param limit [Integer] Maximum number of items to list
|
|
305
|
+
# @return [String] Comma-separated list of items
|
|
306
|
+
def list_available_items(schema, limit = 10)
|
|
307
|
+
items = []
|
|
308
|
+
items.concat(schema.entities.map(&:id)) if schema.entities
|
|
309
|
+
items.concat(schema.types.map(&:id)) if schema.types
|
|
310
|
+
items.concat(schema.functions.map(&:id)) if schema.functions
|
|
311
|
+
items.concat(schema.procedures.map(&:id)) if schema.procedures
|
|
312
|
+
items.concat(schema.constants.map(&:id)) if schema.constants
|
|
313
|
+
|
|
314
|
+
items = items.take(limit)
|
|
315
|
+
result = items.join(", ")
|
|
316
|
+
result += ", ..." if items.size == limit
|
|
317
|
+
result
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
# Build error hash with consistent structure
|
|
321
|
+
# @param params [Hash] Error parameters
|
|
322
|
+
# @return [Hash] Error hash
|
|
323
|
+
def build_error(**params)
|
|
324
|
+
params
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
# Build detailed report from errors and warnings
|
|
328
|
+
# @param errors [Array<Hash>] Validation errors
|
|
329
|
+
# @param warnings [Array<Hash>] Validation warnings
|
|
330
|
+
# @param stats [Hash] Statistics
|
|
331
|
+
# @return [String] Formatted report
|
|
332
|
+
def build_detailed_report(errors, warnings, stats)
|
|
333
|
+
report = []
|
|
334
|
+
report << ("=" * 80)
|
|
335
|
+
report << "Interface Validation Report"
|
|
336
|
+
report << ("=" * 80)
|
|
337
|
+
report << ""
|
|
338
|
+
|
|
339
|
+
# Statistics
|
|
340
|
+
report << "Statistics:"
|
|
341
|
+
report << " Total USE FROM statements: #{stats[:use_from]}"
|
|
342
|
+
report << " Total REFERENCE FROM statements: #{stats[:reference_from]}"
|
|
343
|
+
report << " Total interface items: #{stats[:total_items]}"
|
|
344
|
+
report << ""
|
|
345
|
+
|
|
346
|
+
# Errors
|
|
347
|
+
if errors.any?
|
|
348
|
+
report << "Errors (#{errors.size}):"
|
|
349
|
+
report << ("-" * 80)
|
|
350
|
+
errors.each_with_index do |error, i|
|
|
351
|
+
report << "#{i + 1}. [#{error[:type]}] #{error[:message]}"
|
|
352
|
+
report << " Schema: #{error[:schema]}" if error[:schema]
|
|
353
|
+
report << " Referenced Schema: #{error[:referenced_schema]}" if error[:referenced_schema]
|
|
354
|
+
report << " Item: #{error[:item]}" if error[:item]
|
|
355
|
+
report << " Fix: #{error[:fix_suggestion]}" if error[:fix_suggestion]
|
|
356
|
+
report << ""
|
|
357
|
+
end
|
|
358
|
+
else
|
|
359
|
+
report << "✓ No errors found"
|
|
360
|
+
report << ""
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
# Warnings
|
|
364
|
+
if warnings.any?
|
|
365
|
+
report << "Warnings (#{warnings.size}):"
|
|
366
|
+
report << ("-" * 80)
|
|
367
|
+
warnings.each_with_index do |warning, i|
|
|
368
|
+
report << "#{i + 1}. [#{warning[:type]}] #{warning[:message]}"
|
|
369
|
+
report << " Schema: #{warning[:schema]}" if warning[:schema]
|
|
370
|
+
report << " Referenced Schema: #{warning[:referenced_schema]}" if warning[:referenced_schema]
|
|
371
|
+
report << " Fix: #{warning[:fix_suggestion]}" if warning[:fix_suggestion]
|
|
372
|
+
report << ""
|
|
373
|
+
end
|
|
374
|
+
else
|
|
375
|
+
report << "✓ No warnings"
|
|
376
|
+
report << ""
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
report << ("=" * 80)
|
|
380
|
+
report.join("\n")
|
|
381
|
+
end
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
end
|