dsv7-parser 7.0.1 → 7.0.2
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/.yardopts +5 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +7 -1
- data/README.md +46 -35
- data/Rakefile +36 -7
- data/lib/dsv7/lex.rb +15 -10
- data/lib/dsv7/parser/engine.rb +10 -6
- data/lib/dsv7/parser/io_util.rb +26 -6
- data/lib/dsv7/parser/version.rb +4 -1
- data/lib/dsv7/parser.rb +110 -36
- data/lib/dsv7/stream.rb +26 -9
- data/lib/dsv7/validator/core.rb +18 -12
- data/lib/dsv7/validator/line_analyzer.rb +21 -12
- data/lib/dsv7/validator/line_analyzer_common.rb +25 -8
- data/lib/dsv7/validator/result.rb +25 -1
- data/lib/dsv7/validator/schemas/base.rb +26 -17
- data/lib/dsv7/validator/schemas/erg_schema.rb +2 -0
- data/lib/dsv7/validator/schemas/vml_schema.rb +2 -0
- data/lib/dsv7/validator/schemas/vrl_schema.rb +2 -0
- data/lib/dsv7/validator/schemas/wk_schema.rb +4 -0
- data/lib/dsv7/validator/types/common.rb +13 -8
- data/lib/dsv7/validator/types/datetime.rb +12 -7
- data/lib/dsv7/validator/types/enums1.rb +10 -5
- data/lib/dsv7/validator/types/enums2.rb +10 -5
- data/lib/dsv7/validator/types.rb +8 -6
- data/lib/dsv7/validator.rb +49 -38
- metadata +2 -1
data/lib/dsv7/parser.rb
CHANGED
|
@@ -1,33 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# Dsv7::Parser
|
|
4
|
-
#
|
|
5
|
-
# Streaming parser for DSV7 lists. It yields a simple event stream so callers
|
|
6
|
-
# can build their own structures without loading the whole file into memory.
|
|
7
|
-
# The parser is intentionally tolerant (e.g., it scrubs invalid UTF‑8 and
|
|
8
|
-
# accepts BOM) — pair it with `Dsv7::Validator` for strict conformance.
|
|
9
|
-
#
|
|
10
|
-
# Events
|
|
11
|
-
# - `[:format, { list_type: String, version: String }, line_number]` — first
|
|
12
|
-
# effective line must be a FORMAT line.
|
|
13
|
-
# - `[:element, { name: String, attrs: Array<String> }, line_number]` — for
|
|
14
|
-
# each element line between FORMAT and DATEIENDE.
|
|
15
|
-
# - `[:end, nil, line_number]` — emitted after `DATEIENDE` (or EOF if missing).
|
|
16
|
-
#
|
|
17
|
-
# Usage
|
|
18
|
-
# Dsv7::Parser.parse(io_or_path_or_string) do |type, payload, ln|
|
|
19
|
-
# case type
|
|
20
|
-
# when :format then # inspect payload[:list_type], payload[:version]
|
|
21
|
-
# when :element then # payload[:name], payload[:attrs]
|
|
22
|
-
# when :end then # done
|
|
23
|
-
# end
|
|
24
|
-
# end
|
|
25
|
-
#
|
|
26
|
-
# Documenting new helpers
|
|
27
|
-
# - Describe when a helper raises (e.g., wrong list type for a type‑specific
|
|
28
|
-
# parser) and what it yields.
|
|
29
|
-
# - Note streaming/encoding behavior and that comments are stripped inline.
|
|
30
|
-
|
|
31
3
|
require_relative 'parser/version'
|
|
32
4
|
require_relative 'parser/engine'
|
|
33
5
|
require_relative 'validator'
|
|
@@ -35,14 +7,64 @@ require_relative 'stream'
|
|
|
35
7
|
require_relative 'lex'
|
|
36
8
|
|
|
37
9
|
module Dsv7
|
|
10
|
+
##
|
|
11
|
+
# Dsv7::Parser
|
|
12
|
+
#
|
|
13
|
+
# Streaming parser for DSV7 lists. It yields a simple event stream so callers
|
|
14
|
+
# can build their own structures without loading the whole file into memory.
|
|
15
|
+
# The parser is intentionally tolerant (e.g., it scrubs invalid UTF‑8 and
|
|
16
|
+
# accepts BOM) — pair it with {Dsv7::Validator} for strict conformance.
|
|
17
|
+
#
|
|
18
|
+
# Events
|
|
19
|
+
# - `[:format, { list_type: String, version: String }, line_number]` — first
|
|
20
|
+
# effective line must be a FORMAT line.
|
|
21
|
+
# - `[:element, { name: String, attrs: Array<String> }, line_number]` — for
|
|
22
|
+
# each element line between FORMAT and DATEIENDE.
|
|
23
|
+
# - `[:end, nil, line_number]` — emitted after `DATEIENDE` (or EOF if missing).
|
|
24
|
+
#
|
|
25
|
+
# @api public
|
|
26
|
+
# @since 7.0.0
|
|
27
|
+
# @example Enumerate events for any list type
|
|
28
|
+
# Dsv7::Parser.parse(io_or_path_or_string) do |type, payload, ln|
|
|
29
|
+
# case type
|
|
30
|
+
# when :format then # inspect payload[:list_type], payload[:version]
|
|
31
|
+
# when :element then # payload[:name], payload[:attrs]
|
|
32
|
+
# when :end then # done
|
|
33
|
+
# end
|
|
34
|
+
# end
|
|
35
|
+
#
|
|
36
|
+
# Documenting new helpers
|
|
37
|
+
# - Describe when a helper raises (e.g., wrong list type for a type‑specific
|
|
38
|
+
# parser) and what it yields.
|
|
39
|
+
# - Note streaming/encoding behavior and that comments are stripped inline.
|
|
38
40
|
module Parser
|
|
41
|
+
# Error type raised by the parser when the input
|
|
42
|
+
# does not satisfy basic envelope expectations.
|
|
39
43
|
class Error < StandardError; end
|
|
40
44
|
|
|
45
|
+
# @!group Parsers
|
|
46
|
+
#
|
|
41
47
|
# Generic streaming parser that auto-detects the list type from the
|
|
42
48
|
# first effective FORMAT line and yields events for any DSV7 list.
|
|
43
|
-
#
|
|
44
|
-
# The first event is always
|
|
45
|
-
#
|
|
49
|
+
#
|
|
50
|
+
# The first event is always `:format` with payload
|
|
51
|
+
# `{ list_type: <String>, version: <String> }`.
|
|
52
|
+
#
|
|
53
|
+
# @api public
|
|
54
|
+
# @since 7.0.0
|
|
55
|
+
# @overload parse(input, &block)
|
|
56
|
+
# @param input [IO, String] An IO, a file path String, or a String with file content
|
|
57
|
+
# @yield [type, payload, line_number] Emitted for each event
|
|
58
|
+
# @yieldparam type [Symbol] Event type (:format, :element, :end)
|
|
59
|
+
# @yieldparam payload [Hash, nil] Event payload
|
|
60
|
+
# @yieldparam line_number [Integer] 1-based line number of the event
|
|
61
|
+
# @return [void]
|
|
62
|
+
# @overload parse(input)
|
|
63
|
+
# @param input [IO, String]
|
|
64
|
+
# @return [Enumerator] Enumerator over `[type, payload, line_number]`
|
|
65
|
+
# @see Dsv7::Validator
|
|
66
|
+
# @raise [Dsv7::Parser::Error] when the first effective line is not FORMAT
|
|
67
|
+
# @raise [ArgumentError] if the input type is unsupported
|
|
46
68
|
def self.parse(input, &block)
|
|
47
69
|
enum = Enumerator.new { |y| Engine.stream_any(input, y) }
|
|
48
70
|
return enum.each(&block) if block_given?
|
|
@@ -51,8 +73,23 @@ module Dsv7
|
|
|
51
73
|
end
|
|
52
74
|
|
|
53
75
|
# Streaming parser for Wettkampfdefinitionsliste (WKDL).
|
|
54
|
-
# Yields [:format|:element|:end, payload, line_number].
|
|
55
76
|
# Performs inline comment stripping, tolerates BOM, and scrubs UTF-8.
|
|
77
|
+
#
|
|
78
|
+
# @api public
|
|
79
|
+
# @since 7.0.0
|
|
80
|
+
# @overload parse_wettkampfdefinitionsliste(input, &block)
|
|
81
|
+
# @param input [IO, String]
|
|
82
|
+
# @yield [type, payload, line_number]
|
|
83
|
+
# @yieldparam type [Symbol] Event type (:format, :element, :end)
|
|
84
|
+
# @yieldparam payload [Hash, nil]
|
|
85
|
+
# @yieldparam line_number [Integer]
|
|
86
|
+
# @return [void]
|
|
87
|
+
# @overload parse_wettkampfdefinitionsliste(input)
|
|
88
|
+
# @param input [IO, String]
|
|
89
|
+
# @return [Enumerator]
|
|
90
|
+
# @raise [Dsv7::Parser::Error] if the list type is not WKDL
|
|
91
|
+
# @raise [ArgumentError] if the input type is unsupported
|
|
92
|
+
# @see Dsv7::Validator
|
|
56
93
|
def self.parse_wettkampfdefinitionsliste(input, &block)
|
|
57
94
|
enum = Enumerator.new { |y| Engine.stream_list(input, y, 'Wettkampfdefinitionsliste') }
|
|
58
95
|
return enum.each(&block) if block_given?
|
|
@@ -61,8 +98,20 @@ module Dsv7
|
|
|
61
98
|
end
|
|
62
99
|
|
|
63
100
|
# Streaming parser for Vereinsmeldeliste (VML).
|
|
64
|
-
# Same contract as parse_wettkampfdefinitionsliste, but expects
|
|
65
|
-
# FORMAT:Vereinsmeldeliste;7
|
|
101
|
+
# Same contract as {parse_wettkampfdefinitionsliste}, but expects
|
|
102
|
+
# `FORMAT:Vereinsmeldeliste;7;` as the first effective line.
|
|
103
|
+
# @api public
|
|
104
|
+
# @since 7.0.0
|
|
105
|
+
# @overload parse_vereinsmeldeliste(input, &block)
|
|
106
|
+
# @param input [IO, String]
|
|
107
|
+
# @yield [type, payload, line_number]
|
|
108
|
+
# @return [void]
|
|
109
|
+
# @overload parse_vereinsmeldeliste(input)
|
|
110
|
+
# @param input [IO, String]
|
|
111
|
+
# @return [Enumerator]
|
|
112
|
+
# @raise [Dsv7::Parser::Error] if the list type is not VML
|
|
113
|
+
# @raise [ArgumentError] if the input type is unsupported
|
|
114
|
+
# @see Dsv7::Validator
|
|
66
115
|
def self.parse_vereinsmeldeliste(input, &block)
|
|
67
116
|
enum = Enumerator.new { |y| Engine.stream_list(input, y, 'Vereinsmeldeliste') }
|
|
68
117
|
return enum.each(&block) if block_given?
|
|
@@ -72,7 +121,19 @@ module Dsv7
|
|
|
72
121
|
|
|
73
122
|
# Streaming parser for Wettkampfergebnisliste (ERG).
|
|
74
123
|
# Same contract as the other parse_* methods, but expects
|
|
75
|
-
# FORMAT:Wettkampfergebnisliste;7
|
|
124
|
+
# `FORMAT:Wettkampfergebnisliste;7;` as the first effective line.
|
|
125
|
+
# @api public
|
|
126
|
+
# @since 7.0.0
|
|
127
|
+
# @overload parse_wettkampfergebnisliste(input, &block)
|
|
128
|
+
# @param input [IO, String]
|
|
129
|
+
# @yield [type, payload, line_number]
|
|
130
|
+
# @return [void]
|
|
131
|
+
# @overload parse_wettkampfergebnisliste(input)
|
|
132
|
+
# @param input [IO, String]
|
|
133
|
+
# @return [Enumerator]
|
|
134
|
+
# @raise [Dsv7::Parser::Error] if the list type is not ERG
|
|
135
|
+
# @raise [ArgumentError] if the input type is unsupported
|
|
136
|
+
# @see Dsv7::Validator
|
|
76
137
|
def self.parse_wettkampfergebnisliste(input, &block)
|
|
77
138
|
enum = Enumerator.new { |y| Engine.stream_list(input, y, 'Wettkampfergebnisliste') }
|
|
78
139
|
return enum.each(&block) if block_given?
|
|
@@ -82,13 +143,26 @@ module Dsv7
|
|
|
82
143
|
|
|
83
144
|
# Streaming parser for Vereinsergebnisliste (VRL).
|
|
84
145
|
# Same contract as the other parse_* methods, but expects
|
|
85
|
-
# FORMAT:Vereinsergebnisliste;7
|
|
146
|
+
# `FORMAT:Vereinsergebnisliste;7;` as the first effective line.
|
|
147
|
+
# @api public
|
|
148
|
+
# @since 7.0.0
|
|
149
|
+
# @overload parse_vereinsergebnisliste(input, &block)
|
|
150
|
+
# @param input [IO, String]
|
|
151
|
+
# @yield [type, payload, line_number]
|
|
152
|
+
# @return [void]
|
|
153
|
+
# @overload parse_vereinsergebnisliste(input)
|
|
154
|
+
# @param input [IO, String]
|
|
155
|
+
# @return [Enumerator]
|
|
156
|
+
# @raise [Dsv7::Parser::Error] if the list type is not VRL
|
|
157
|
+
# @raise [ArgumentError] if the input type is unsupported
|
|
158
|
+
# @see Dsv7::Validator
|
|
86
159
|
def self.parse_vereinsergebnisliste(input, &block)
|
|
87
160
|
enum = Enumerator.new { |y| Engine.stream_list(input, y, 'Vereinsergebnisliste') }
|
|
88
161
|
return enum.each(&block) if block_given?
|
|
89
162
|
|
|
90
163
|
enum
|
|
91
164
|
end
|
|
165
|
+
# @!endgroup
|
|
92
166
|
# no additional private class methods
|
|
93
167
|
end
|
|
94
168
|
end
|
data/lib/dsv7/stream.rb
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# Low‑level IO helpers for streaming DSV7 content.
|
|
4
|
-
#
|
|
5
|
-
# Responsibilities
|
|
6
|
-
# - Binary mode, BOM detection, and UTF‑8 normalization.
|
|
7
|
-
# - Per‑line sanitization and CR/LF handling.
|
|
8
|
-
# - Inline single‑line comment removal using the `(* ... *)` syntax.
|
|
9
|
-
#
|
|
10
|
-
# These helpers are shared by both the validator and the parser.
|
|
11
|
-
|
|
12
3
|
module Dsv7
|
|
4
|
+
##
|
|
5
|
+
# Low‑level IO helpers for streaming DSV7 content.
|
|
6
|
+
#
|
|
7
|
+
# Responsibilities
|
|
8
|
+
# - Binary mode, BOM detection, and UTF‑8 normalization.
|
|
9
|
+
# - Per‑line sanitization and CR/LF handling.
|
|
10
|
+
# - Inline single‑line comment removal using the `(* ... *)` syntax.
|
|
11
|
+
#
|
|
12
|
+
# These helpers are shared by both the validator and the parser.
|
|
13
|
+
#
|
|
14
|
+
# @api private
|
|
13
15
|
module Stream
|
|
14
16
|
module_function
|
|
15
17
|
|
|
16
18
|
# Puts IO into binary mode when possible (no-op for StringIO)
|
|
19
|
+
# @param io [IO]
|
|
20
|
+
# @return [void]
|
|
17
21
|
def binmode_if_possible(io)
|
|
18
22
|
io.binmode
|
|
19
23
|
rescue StandardError
|
|
@@ -23,6 +27,8 @@ module Dsv7
|
|
|
23
27
|
# Reads potential UTF-8 BOM from the start of IO.
|
|
24
28
|
# Returns true if a BOM was found (and consumed), false otherwise.
|
|
25
29
|
# If no BOM was found, unread the peeked bytes back into the IO.
|
|
30
|
+
# @param io [IO]
|
|
31
|
+
# @return [Boolean]
|
|
26
32
|
def read_bom?(io)
|
|
27
33
|
head = io.read(3)
|
|
28
34
|
return false if head.nil? || head.empty?
|
|
@@ -38,6 +44,9 @@ module Dsv7
|
|
|
38
44
|
# Normalizes a raw line by trimming trailing LF/CR and forcing UTF-8.
|
|
39
45
|
# If invalid encoding is detected, it scrubs replacement chars and
|
|
40
46
|
# calls the optional on_invalid callback.
|
|
47
|
+
# @param raw [String]
|
|
48
|
+
# @param on_invalid [Proc,nil]
|
|
49
|
+
# @return [String]
|
|
41
50
|
def sanitize_line(raw, on_invalid: nil)
|
|
42
51
|
s = raw.delete_suffix("\n").delete_suffix("\r")
|
|
43
52
|
s.force_encoding(Encoding::UTF_8)
|
|
@@ -48,6 +57,8 @@ module Dsv7
|
|
|
48
57
|
end
|
|
49
58
|
|
|
50
59
|
# Removes inline single-line comments in the form: (* ... *)
|
|
60
|
+
# @param line [String]
|
|
61
|
+
# @return [String]
|
|
51
62
|
def strip_inline_comment(line)
|
|
52
63
|
return line unless line.include?('(*') && line.include?('*)')
|
|
53
64
|
|
|
@@ -56,6 +67,12 @@ module Dsv7
|
|
|
56
67
|
|
|
57
68
|
# Iterates sanitized lines, yielding [line, line_number].
|
|
58
69
|
# Returns true if any CRLF lines were observed.
|
|
70
|
+
# @param io [IO]
|
|
71
|
+
# @param on_invalid [Proc,nil]
|
|
72
|
+
# @yield [line, line_number]
|
|
73
|
+
# @yieldparam line [String]
|
|
74
|
+
# @yieldparam line_number [Integer]
|
|
75
|
+
# @return [Boolean] whether any CRLF lines were observed
|
|
59
76
|
def each_sanitized_line(io, on_invalid: nil)
|
|
60
77
|
had_crlf = false
|
|
61
78
|
line_number = 0
|
data/lib/dsv7/validator/core.rb
CHANGED
|
@@ -1,17 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# Core validation pipeline
|
|
4
|
-
#
|
|
5
|
-
# Implements the IO/line streaming for the validator:
|
|
6
|
-
# - puts IO in binary mode and detects BOM
|
|
7
|
-
# - normalizes lines to UTF‑8 and tracks CRLF presence
|
|
8
|
-
# - strips inline comments and delegates per‑line logic to LineAnalyzer
|
|
9
|
-
# - adds a filename warning if the provided path does not match the guidance
|
|
10
|
-
#
|
|
11
|
-
# Notes for maintainers
|
|
12
|
-
# - Keep this class side‑effect free beyond writing to `Result`.
|
|
13
|
-
# - Avoid accumulating state; process line‑by‑line to preserve streaming.
|
|
14
|
-
|
|
15
3
|
require_relative '../stream'
|
|
16
4
|
require_relative '../lex'
|
|
17
5
|
require_relative 'line_analyzer'
|
|
@@ -19,12 +7,30 @@ require_relative 'line_analyzer'
|
|
|
19
7
|
module Dsv7
|
|
20
8
|
class Validator
|
|
21
9
|
# Core pipeline for validator: encoding + line parsing
|
|
10
|
+
##
|
|
11
|
+
# Core validation pipeline.
|
|
12
|
+
#
|
|
13
|
+
# Implements the IO/line streaming for the validator:
|
|
14
|
+
# - puts IO in binary mode and detects BOM
|
|
15
|
+
# - normalizes lines to UTF‑8 and tracks CRLF presence
|
|
16
|
+
# - strips inline comments and delegates per‑line logic to LineAnalyzer
|
|
17
|
+
# - adds a filename warning if the provided path does not match the guidance
|
|
18
|
+
#
|
|
19
|
+
# Notes for maintainers
|
|
20
|
+
# - Keep this class side‑effect free beyond writing to `Result`.
|
|
21
|
+
# - Avoid accumulating state; process line‑by‑line to preserve streaming.
|
|
22
|
+
#
|
|
23
|
+
# @api private
|
|
22
24
|
class Core
|
|
25
|
+
# @param result [Dsv7::Validator::Result]
|
|
26
|
+
# @param filename [String, nil]
|
|
23
27
|
def initialize(result, filename)
|
|
24
28
|
@result = result
|
|
25
29
|
@filename = filename
|
|
26
30
|
end
|
|
27
31
|
|
|
32
|
+
# @param io [IO]
|
|
33
|
+
# @return [Dsv7::Validator::Result]
|
|
28
34
|
def call_io(io)
|
|
29
35
|
Dsv7::Stream.binmode_if_possible(io)
|
|
30
36
|
check_bom_and_rewind(io)
|
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# Streaming line analyzer
|
|
4
|
-
#
|
|
5
|
-
# Orchestrates validation once lines have been sanitized and comments stripped.
|
|
6
|
-
# Tracks the first effective FORMAT line, enforces the final DATEIENDE, and
|
|
7
|
-
# dispatches element lines to list‑specific schema/type checks and cardinality
|
|
8
|
-
# tracking. All findings are written into the shared `Result` instance.
|
|
9
|
-
|
|
10
3
|
require_relative '../stream'
|
|
11
4
|
require_relative 'line_analyzer_common'
|
|
12
5
|
require_relative 'schemas/wk_schema'
|
|
@@ -16,12 +9,22 @@ require_relative 'schemas/vrl_schema'
|
|
|
16
9
|
|
|
17
10
|
module Dsv7
|
|
18
11
|
class Validator
|
|
12
|
+
##
|
|
13
|
+
# Streaming line analyzer.
|
|
14
|
+
#
|
|
15
|
+
# Orchestrates validation once lines have been sanitized and comments stripped.
|
|
16
|
+
# Tracks the first effective FORMAT line, enforces the final DATEIENDE, and
|
|
17
|
+
# dispatches element lines to list‑specific schema/type checks and cardinality
|
|
18
|
+
# tracking. All findings are written into the shared {Dsv7::Validator::Result} instance.
|
|
19
|
+
#
|
|
20
|
+
# @api private
|
|
19
21
|
class LineAnalyzer
|
|
20
22
|
include LineAnalyzerWk
|
|
21
23
|
include LineAnalyzerVml
|
|
22
24
|
include LineAnalyzerErg
|
|
23
25
|
include LineAnalyzerVrl
|
|
24
26
|
|
|
27
|
+
# @param result [Dsv7::Validator::Result]
|
|
25
28
|
def initialize(result)
|
|
26
29
|
@result = result
|
|
27
30
|
@effective_index = 0
|
|
@@ -31,6 +34,7 @@ module Dsv7
|
|
|
31
34
|
init_schemas_and_counters
|
|
32
35
|
end
|
|
33
36
|
|
|
37
|
+
# Initialize schemas and element counters used during streaming.
|
|
34
38
|
def init_schemas_and_counters
|
|
35
39
|
@wk_elements = Hash.new(0)
|
|
36
40
|
@wk_schema = WkSchema.new(@result)
|
|
@@ -42,9 +46,13 @@ module Dsv7
|
|
|
42
46
|
@vrl_schema = VrlSchema.new(@result)
|
|
43
47
|
end
|
|
44
48
|
|
|
49
|
+
# Process a raw input line with its 1-based line number.
|
|
50
|
+
# @param line [String]
|
|
51
|
+
# @param line_number [Integer]
|
|
52
|
+
# @return [void]
|
|
45
53
|
def process_line(line, line_number)
|
|
46
54
|
check_comment_balance(line, line_number)
|
|
47
|
-
trimmed = strip_inline_comment(line)
|
|
55
|
+
trimmed = Dsv7::Stream.strip_inline_comment(line).strip
|
|
48
56
|
return if trimmed.empty?
|
|
49
57
|
|
|
50
58
|
@effective_index += 1
|
|
@@ -54,6 +62,8 @@ module Dsv7
|
|
|
54
62
|
handle_content_line(trimmed, line_number)
|
|
55
63
|
end
|
|
56
64
|
|
|
65
|
+
# Finalize the analysis and run post/summary checks.
|
|
66
|
+
# @return [void]
|
|
57
67
|
def finish
|
|
58
68
|
post_validate_positions
|
|
59
69
|
validate_wk_list_elements if @result.list_type == 'Wettkampfdefinitionsliste'
|
|
@@ -64,6 +74,9 @@ module Dsv7
|
|
|
64
74
|
|
|
65
75
|
private
|
|
66
76
|
|
|
77
|
+
# Internal helper to set up schemas and counters
|
|
78
|
+
private :init_schemas_and_counters
|
|
79
|
+
|
|
67
80
|
def check_comment_balance(line, line_number)
|
|
68
81
|
return unless line.include?('(*') || line.include?('*)')
|
|
69
82
|
|
|
@@ -79,10 +92,6 @@ module Dsv7
|
|
|
79
92
|
check_format_line(trimmed, line_number)
|
|
80
93
|
end
|
|
81
94
|
|
|
82
|
-
def strip_inline_comment(line)
|
|
83
|
-
Dsv7::Stream.strip_inline_comment(line).strip
|
|
84
|
-
end
|
|
85
|
-
|
|
86
95
|
def check_format_line(trimmed, line_number)
|
|
87
96
|
m = Dsv7::Lex.parse_format(trimmed)
|
|
88
97
|
return format_error(line_number) unless m
|
|
@@ -1,13 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# List‑specific analyzer mixins
|
|
4
|
-
#
|
|
5
|
-
# These modules encapsulate per‑list tracking and validation methods used by
|
|
6
|
-
# LineAnalyzer. Each provides three responsibilities for its list type:
|
|
7
|
-
# - track_*_element: counts element occurrences for cardinality checks
|
|
8
|
-
# - validate_*_list_elements: validates the observed counts at finish
|
|
9
|
-
# - validate_*_line: validates a single element’s attributes via the schema
|
|
10
|
-
|
|
11
3
|
require_relative '../lex'
|
|
12
4
|
require_relative 'cardinality'
|
|
13
5
|
|
|
@@ -15,6 +7,19 @@ module Dsv7
|
|
|
15
7
|
class Validator
|
|
16
8
|
# Handles line-by-line structural checks for WKDL-specific logic
|
|
17
9
|
module LineAnalyzerWk
|
|
10
|
+
##
|
|
11
|
+
# List‑specific analyzer mixins.
|
|
12
|
+
#
|
|
13
|
+
# These modules encapsulate per‑list tracking and validation methods used by
|
|
14
|
+
# {Dsv7::Validator::LineAnalyzer}. Each provides three responsibilities for its list type:
|
|
15
|
+
# - track_*_element: counts element occurrences for cardinality checks
|
|
16
|
+
# - validate_*_list_elements: validates the observed counts at finish
|
|
17
|
+
# - validate_*_line: validates a single element’s attributes via the schema
|
|
18
|
+
#
|
|
19
|
+
# @api private
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
18
23
|
def track_wk_element(trimmed)
|
|
19
24
|
return unless @result.list_type == 'Wettkampfdefinitionsliste'
|
|
20
25
|
|
|
@@ -47,6 +52,10 @@ module Dsv7
|
|
|
47
52
|
|
|
48
53
|
# Handles line-by-line structural checks for VML-specific logic
|
|
49
54
|
module LineAnalyzerVml
|
|
55
|
+
# @api private
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
50
59
|
def track_vml_element(trimmed)
|
|
51
60
|
return unless @result.list_type == 'Vereinsmeldeliste'
|
|
52
61
|
|
|
@@ -79,6 +88,10 @@ module Dsv7
|
|
|
79
88
|
|
|
80
89
|
# Handles line-by-line checks for Wettkampfergebnisliste
|
|
81
90
|
module LineAnalyzerErg
|
|
91
|
+
# @api private
|
|
92
|
+
|
|
93
|
+
private
|
|
94
|
+
|
|
82
95
|
def track_erg_element(trimmed)
|
|
83
96
|
return unless @result.list_type == 'Wettkampfergebnisliste'
|
|
84
97
|
|
|
@@ -113,6 +126,10 @@ module Dsv7
|
|
|
113
126
|
|
|
114
127
|
# Handles line-by-line checks for Vereinsergebnisliste
|
|
115
128
|
module LineAnalyzerVrl
|
|
129
|
+
# @api private
|
|
130
|
+
|
|
131
|
+
private
|
|
132
|
+
|
|
116
133
|
def track_vrl_element(trimmed)
|
|
117
134
|
return unless @result.list_type == 'Vereinsergebnisliste'
|
|
118
135
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
##
|
|
4
|
+
# Validation result container.
|
|
4
5
|
#
|
|
5
6
|
# Collects errors and warnings during a validation run and exposes
|
|
6
7
|
# `list_type`/`version` after a valid FORMAT line is seen. `valid?` is true
|
|
@@ -13,7 +14,18 @@
|
|
|
13
14
|
module Dsv7
|
|
14
15
|
class Validator
|
|
15
16
|
# Result container for validation
|
|
17
|
+
#
|
|
18
|
+
# @api public
|
|
19
|
+
# @since 7.0.0
|
|
16
20
|
class Result
|
|
21
|
+
# @!attribute [r] errors
|
|
22
|
+
# @return [Array<String>] Collected human-readable error messages
|
|
23
|
+
# @!attribute [r] warnings
|
|
24
|
+
# @return [Array<String>] Collected warnings; do not affect validity
|
|
25
|
+
# @!attribute [r] list_type
|
|
26
|
+
# @return [String, nil] List type after parsing the FORMAT line
|
|
27
|
+
# @!attribute [r] version
|
|
28
|
+
# @return [String, nil] Format version after parsing the FORMAT line
|
|
17
29
|
attr_reader :errors, :warnings, :list_type, :version
|
|
18
30
|
|
|
19
31
|
def initialize
|
|
@@ -23,19 +35,31 @@ module Dsv7
|
|
|
23
35
|
@version = nil
|
|
24
36
|
end
|
|
25
37
|
|
|
38
|
+
# Add an error message.
|
|
39
|
+
# @param message [String]
|
|
40
|
+
# @return [void]
|
|
26
41
|
def add_error(message)
|
|
27
42
|
@errors << message
|
|
28
43
|
end
|
|
29
44
|
|
|
45
|
+
# Add a warning message.
|
|
46
|
+
# @param message [String]
|
|
47
|
+
# @return [void]
|
|
30
48
|
def add_warning(message)
|
|
31
49
|
@warnings << message
|
|
32
50
|
end
|
|
33
51
|
|
|
52
|
+
# Set FORMAT metadata after a valid FORMAT line was observed.
|
|
53
|
+
# @param list_type [String]
|
|
54
|
+
# @param version [String]
|
|
55
|
+
# @return [void]
|
|
34
56
|
def set_format(list_type, version)
|
|
35
57
|
@list_type = list_type
|
|
36
58
|
@version = version
|
|
37
59
|
end
|
|
38
60
|
|
|
61
|
+
# Whether the validation run produced no errors.
|
|
62
|
+
# @return [Boolean]
|
|
39
63
|
def valid?
|
|
40
64
|
@errors.empty?
|
|
41
65
|
end
|
|
@@ -1,29 +1,37 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# Base class for per‑list schemas.
|
|
4
|
-
#
|
|
5
|
-
# A concrete schema class defines a `SCHEMAS` Hash mapping element names to an
|
|
6
|
-
# Array of attribute specs. Each attribute spec is a tuple:
|
|
7
|
-
# [type, required, opts=nil]
|
|
8
|
-
# where `type` corresponds to a `check_<type>` method mixed in from the
|
|
9
|
-
# type‑check modules, `required` is a boolean, and `opts` can be used by a
|
|
10
|
-
# specific checker.
|
|
11
|
-
#
|
|
12
|
-
# Cross‑field/element rules may be implemented by overriding
|
|
13
|
-
# `validate_cross_rules(name, attrs, line_number)`.
|
|
14
|
-
#
|
|
15
|
-
# Documentation tips when adding/adjusting schemas:
|
|
16
|
-
# - Copy the attribute count and types from the spec and real‑world examples.
|
|
17
|
-
# - Clearly mark intentionally deferred or ambiguous elements in commit msgs.
|
|
18
|
-
# - Add both positive and negative tests for each element and datatype.
|
|
19
|
-
|
|
20
3
|
module Dsv7
|
|
21
4
|
class Validator
|
|
5
|
+
##
|
|
6
|
+
# Base class for per‑list schemas.
|
|
7
|
+
#
|
|
8
|
+
# A concrete schema class defines a `SCHEMAS` Hash mapping element names to an
|
|
9
|
+
# Array of attribute specs. Each attribute spec is a tuple:
|
|
10
|
+
# `[type, required, opts=nil]`
|
|
11
|
+
# where `type` corresponds to a `check_<type>` method mixed in from the
|
|
12
|
+
# type‑check modules, `required` is a boolean, and `opts` can be used by a
|
|
13
|
+
# specific checker.
|
|
14
|
+
#
|
|
15
|
+
# Cross‑field/element rules may be implemented by overriding
|
|
16
|
+
# `validate_cross_rules(name, attrs, line_number)`.
|
|
17
|
+
#
|
|
18
|
+
# Documentation tips when adding/adjusting schemas:
|
|
19
|
+
# - Copy the attribute count and types from the spec and real‑world examples.
|
|
20
|
+
# - Clearly mark intentionally deferred or ambiguous elements in commit msgs.
|
|
21
|
+
# - Add both positive and negative tests for each element and datatype.
|
|
22
|
+
#
|
|
23
|
+
# @see specification/dsv7/dsv7_specification.md Specification reference
|
|
24
|
+
# @api private
|
|
22
25
|
class SchemaBase
|
|
23
26
|
def initialize(result)
|
|
24
27
|
@result = result
|
|
25
28
|
end
|
|
26
29
|
|
|
30
|
+
# Validate a single element against the schema map.
|
|
31
|
+
# @param name [String]
|
|
32
|
+
# @param attrs [Array<String>]
|
|
33
|
+
# @param line_number [Integer]
|
|
34
|
+
# @return [void]
|
|
27
35
|
def validate_element(name, attrs, line_number)
|
|
28
36
|
schema = self.class::SCHEMAS[name]
|
|
29
37
|
return unless schema
|
|
@@ -50,6 +58,7 @@ module Dsv7
|
|
|
50
58
|
end
|
|
51
59
|
end
|
|
52
60
|
|
|
61
|
+
# @return [void]
|
|
53
62
|
def add_error(msg)
|
|
54
63
|
@result.add_error(msg)
|
|
55
64
|
end
|
|
@@ -9,6 +9,8 @@ module Dsv7
|
|
|
9
9
|
#
|
|
10
10
|
# The `SCHEMAS` constant defines the exact attribute counts and types per
|
|
11
11
|
# element according to the current spec interpretation.
|
|
12
|
+
#
|
|
13
|
+
# @api private
|
|
12
14
|
class WkSchema < SchemaBase
|
|
13
15
|
include WkTypeChecks
|
|
14
16
|
|
|
@@ -52,6 +54,8 @@ module Dsv7
|
|
|
52
54
|
'MELDEGELD' => [[:meldegeld_typ, true], [:betrag, true], [:zahl, false]]
|
|
53
55
|
}.freeze
|
|
54
56
|
|
|
57
|
+
private
|
|
58
|
+
|
|
55
59
|
def validate_cross_rules(name, attrs, line_number)
|
|
56
60
|
return unless name == 'MELDEGELD'
|
|
57
61
|
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# Common datatype checks shared across lists.
|
|
4
|
-
#
|
|
5
|
-
# Implementations follow the spec’s informal definitions:
|
|
6
|
-
# - ZK: arbitrary UTF‑8 string (already scrubbed by the stream layer)
|
|
7
|
-
# - Zahl: integer (only digits)
|
|
8
|
-
# - Betrag: monetary amount in the form `x,yy`
|
|
9
|
-
# - Einzelstrecke: distance (1..25000) or 0 where permitted
|
|
10
|
-
|
|
11
3
|
module Dsv7
|
|
12
4
|
class Validator
|
|
5
|
+
##
|
|
6
|
+
# Common datatype checks shared across lists.
|
|
7
|
+
#
|
|
8
|
+
# Implementations follow the spec’s informal definitions:
|
|
9
|
+
# - ZK: arbitrary UTF‑8 string (already scrubbed by the stream layer)
|
|
10
|
+
# - Zahl: integer (only digits)
|
|
11
|
+
# - Betrag: monetary amount in the form `x,yy`
|
|
12
|
+
# - Einzelstrecke: distance (1..25000) or 0 where permitted
|
|
13
|
+
#
|
|
14
|
+
# @see specification/dsv7/dsv7_specification.md Datatypes overview
|
|
15
|
+
# @api private
|
|
13
16
|
module WkTypeChecksCommon
|
|
17
|
+
private
|
|
18
|
+
|
|
14
19
|
def check_zk(_name, _index, _val, _line_number, _opts = nil)
|
|
15
20
|
# any string (already UTF-8 scrubbed); nothing to do
|
|
16
21
|
end
|