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.
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
- # Yields [:format|:element|:end, payload, line_number].
44
- # The first event is always :format with payload
45
- # { list_type: <String>, version: <String> }.
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; as the first effective line.
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; as the first effective line.
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; as the first effective line.
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
@@ -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
- # Validation result container
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
@@ -8,6 +8,8 @@ module Dsv7
8
8
  # Validates Wettkampfergebnisliste attribute counts and datatypes.
9
9
  # Accepts synonymous element names found in the wild
10
10
  # (e.g., STAFFELERGEBNIS/STERGEBNIS).
11
+ #
12
+ # @api private
11
13
  class ErgSchema < SchemaBase
12
14
  include WkTypeChecks
13
15
 
@@ -6,6 +6,8 @@ require_relative 'base'
6
6
  module Dsv7
7
7
  class Validator
8
8
  # Validates Vereinsmeldeliste attribute counts and datatypes.
9
+ #
10
+ # @api private
9
11
  class VmlSchema < SchemaBase
10
12
  include WkTypeChecks
11
13
 
@@ -8,6 +8,8 @@ module Dsv7
8
8
  # Validates Vereinsergebnisliste attribute counts and datatypes.
9
9
  # Accepts synonymous element names found in the wild
10
10
  # (e.g., STAFFELERGEBNIS/STERGEBNIS).
11
+ #
12
+ # @api private
11
13
  class VrlSchema < SchemaBase
12
14
  include WkTypeChecks
13
15
 
@@ -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