Skip to content

Commit cb900ec

Browse files
committed
docs: update README and add nocov markers for defensive code
- Add comprehensive README with accurate API reference - Document dump method, error hierarchy, and all public classes - Add :nocov: markers to unreachable defensive validations in parser - Improve inline documentation for piece_placement parser
1 parent 8ad8507 commit cb900ec

61 files changed

Lines changed: 8186 additions & 4357 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 145 additions & 93 deletions
Large diffs are not rendered by default.

lib/sashite/feen.rb

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,14 @@
33
require "sashite/epin"
44
require "sashite/sin"
55

6-
require_relative "feen/constants"
7-
require_relative "feen/errors"
6+
require_relative "feen/error"
7+
require_relative "feen/errors/parse_error"
8+
require_relative "feen/errors/piece_placement_error"
9+
require_relative "feen/errors/hands_error"
10+
require_relative "feen/errors/style_turn_error"
11+
require_relative "feen/errors/cardinality_error"
812
require_relative "feen/parser"
13+
require_relative "feen/dumper"
914
require_relative "feen/position"
1015

1116
module Sashite
@@ -37,6 +42,14 @@ module Sashite
3742
# Sashite::Feen.valid?("lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL / S/s") # => true
3843
# Sashite::Feen.valid?("invalid") # => false
3944
#
45+
# # Dump structured data to FEEN string
46+
# Sashite::Feen.dump(
47+
# piece_placement: { segments: [[8], [8], ...], separators: ["/", ...] },
48+
# hands: { first: [], second: [] },
49+
# style_turn: { active: "C", inactive: "c" }
50+
# )
51+
# # => "8/8/8/8/8/8/8/8 / C/c"
52+
#
4053
# @see https://sashite.dev/specs/feen/1.0.0/
4154
# @api public
4255
module Feen
@@ -45,7 +58,7 @@ module Feen
4558
# @api public
4659
# @param feen_string [String] The FEEN string to parse
4760
# @return [Position] A new Position instance
48-
# @raise [ArgumentError] If the string is not a valid FEEN
61+
# @raise [ParseError] If the string is not a valid FEEN
4962
#
5063
# @example Parsing a Chess position
5164
# position = Sashite::Feen.parse("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR / C/c")
@@ -56,11 +69,11 @@ module Feen
5669
# position = Sashite::Feen.parse("lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL / S/s")
5770
# position.piece_placement.dimensions # => 2
5871
#
59-
# @example Invalid input raises ArgumentError
60-
# Sashite::Feen.parse("invalid") # => raises ArgumentError
72+
# @example Invalid input raises ParseError
73+
# Sashite::Feen.parse("invalid") # => raises ParseError
6174
def self.parse(feen_string)
6275
components = Parser.parse(feen_string)
63-
Position.new(**components)
76+
Position.send(:new, **components)
6477
end
6578

6679
# Reports whether a string is a valid FEEN position.
@@ -82,5 +95,42 @@ def self.parse(feen_string)
8295
def self.valid?(feen_string)
8396
Parser.valid?(feen_string)
8497
end
98+
99+
# Serializes structured position data to a FEEN string.
100+
#
101+
# @api public
102+
# @param piece_placement [Hash] Piece placement with :segments and :separators
103+
# @param hands [Hash] Hands with :first and :second
104+
# @param style_turn [Hash] Style-turn with :active and :inactive
105+
# @return [String] Canonical FEEN string
106+
#
107+
# @example Dumping an empty Chess board
108+
# Sashite::Feen.dump(
109+
# piece_placement: {
110+
# segments: [[8], [8], [8], [8], [8], [8], [8], [8]],
111+
# separators: ["/", "/", "/", "/", "/", "/", "/"]
112+
# },
113+
# hands: { first: [], second: [] },
114+
# style_turn: { active: "C", inactive: "c" }
115+
# )
116+
# # => "8/8/8/8/8/8/8/8 / C/c"
117+
#
118+
# @example Dumping a position with hands
119+
# Sashite::Feen.dump(
120+
# piece_placement: { segments: [["K", 6, "k"]], separators: [] },
121+
# hands: {
122+
# first: [{ piece: "P", count: 2 }],
123+
# second: [{ piece: "p", count: 1 }]
124+
# },
125+
# style_turn: { active: "S", inactive: "s" }
126+
# )
127+
# # => "K6k 2P/p S/s"
128+
def self.dump(piece_placement:, hands:, style_turn:)
129+
Dumper.dump(
130+
piece_placement: piece_placement,
131+
hands: hands,
132+
style_turn: style_turn
133+
)
134+
end
85135
end
86136
end

lib/sashite/feen/constants.rb

Lines changed: 0 additions & 35 deletions
This file was deleted.

lib/sashite/feen/dumper.rb

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# frozen_string_literal: true
2+
3+
require_relative "shared/separators"
4+
require_relative "dumper/piece_placement"
5+
require_relative "dumper/hands"
6+
require_relative "dumper/style_turn"
7+
8+
module Sashite
9+
module Feen
10+
# Serializer for FEEN (Field Expression Encoding Notation) strings.
11+
#
12+
# Converts structured position data into a canonical FEEN string.
13+
# A FEEN string consists of three fields separated by single ASCII spaces:
14+
#
15+
# <PIECE-PLACEMENT> <HANDS> <STYLE-TURN>
16+
#
17+
# This module orchestrates the three sub-dumpers:
18+
# - {Dumper::PiecePlacement} for Field 1
19+
# - {Dumper::Hands} for Field 2
20+
# - {Dumper::StyleTurn} for Field 3
21+
#
22+
# @example Dumping a complete position
23+
# Dumper.dump(
24+
# piece_placement: { segments: [[8], [8], ...], separators: ["/", "/", ...] },
25+
# hands: { first: [], second: [] },
26+
# style_turn: { active: "C", inactive: "c" }
27+
# )
28+
# # => "8/8/8/8/8/8/8/8 / C/c"
29+
#
30+
# @see https://sashite.dev/specs/feen/1.0.0/
31+
# @api private
32+
module Dumper
33+
# Serializes position data to a FEEN string.
34+
#
35+
# @param piece_placement [Hash] Piece placement with :segments and :separators
36+
# @param hands [Hash] Hands with :first and :second
37+
# @param style_turn [Hash] Style-turn with :active and :inactive
38+
# @return [String] Canonical FEEN string
39+
def self.dump(piece_placement:, hands:, style_turn:)
40+
piece_placement_str = PiecePlacement.dump(**piece_placement)
41+
hands_str = Hands.dump(**hands)
42+
style_turn_str = StyleTurn.dump(**style_turn)
43+
44+
[piece_placement_str, hands_str, style_turn_str].join(Separators::FIELD)
45+
end
46+
47+
freeze
48+
end
49+
end
50+
end

lib/sashite/feen/dumper/hand.rb

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# frozen_string_literal: true
2+
3+
module Sashite
4+
module Feen
5+
module Dumper
6+
# Serializer for a single hand within FEEN Hands field.
7+
#
8+
# Converts an array of hand items into a canonical FEEN string.
9+
# Items are expected to be already in canonical order.
10+
#
11+
# Input format:
12+
# - items: Array of Hashes with :piece and :count keys
13+
# - :piece must respond to #to_s
14+
# - :count is an Integer (1 = implicit, ≥2 = explicit prefix)
15+
#
16+
# @example Empty hand
17+
# Dumper::Hand.dump([])
18+
# # => ""
19+
#
20+
# @example Single piece
21+
# Dumper::Hand.dump([{ piece: "P", count: 1 }])
22+
# # => "P"
23+
#
24+
# @example Multiple pieces with counts
25+
# Dumper::Hand.dump([
26+
# { piece: "B", count: 3 },
27+
# { piece: "P", count: 2 },
28+
# { piece: "N", count: 1 }
29+
# ])
30+
# # => "3B2PN"
31+
#
32+
# @see https://sashite.dev/specs/feen/1.0.0/
33+
# @api private
34+
module Hand
35+
# Empty string for count = 1 (implicit).
36+
EMPTY_STRING = ""
37+
38+
# Serializes hand items to a FEEN string.
39+
#
40+
# @param items [Array<Hash>] Hand items with :piece and :count keys
41+
# @return [String] Canonical FEEN hand string
42+
def self.dump(items)
43+
items.map do |item|
44+
dump_item(item[:piece], item[:count])
45+
end.join
46+
end
47+
48+
class << self
49+
private
50+
51+
# Serializes a single hand item.
52+
#
53+
# @param piece [#to_s] The piece identifier
54+
# @param count [Integer] The count (1 = no prefix, ≥2 = prefix)
55+
# @return [String] Serialized hand item
56+
def dump_item(piece, count)
57+
count_str = count > 1 ? count.to_s : EMPTY_STRING
58+
"#{count_str}#{piece}"
59+
end
60+
end
61+
62+
private_class_method :dump_item
63+
64+
freeze
65+
end
66+
end
67+
end
68+
end

lib/sashite/feen/dumper/hands.rb

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# frozen_string_literal: true
2+
3+
require_relative "../shared/separators"
4+
require_relative "hand"
5+
6+
module Sashite
7+
module Feen
8+
module Dumper
9+
# Serializer for FEEN Hands field (Field 2).
10+
#
11+
# Converts structured hands data into a canonical FEEN string.
12+
# Format: <FIRST-HAND>/<SECOND-HAND>
13+
#
14+
# Input format:
15+
# - first: Array of hand items for first player
16+
# - second: Array of hand items for second player
17+
# Each item is a Hash with :piece and :count keys.
18+
#
19+
# @example Empty hands
20+
# Dumper::Hands.dump(first: [], second: [])
21+
# # => "/"
22+
#
23+
# @example First player has pieces
24+
# Dumper::Hands.dump(
25+
# first: [{ piece: "P", count: 2 }, { piece: "N", count: 1 }],
26+
# second: []
27+
# )
28+
# # => "2PN/"
29+
#
30+
# @example Both players have pieces
31+
# Dumper::Hands.dump(
32+
# first: [{ piece: "B", count: 3 }],
33+
# second: [{ piece: "p", count: 2 }]
34+
# )
35+
# # => "3B/2p"
36+
#
37+
# @see https://sashite.dev/specs/feen/1.0.0/
38+
# @api private
39+
module Hands
40+
# Serializes hands data to a FEEN string.
41+
#
42+
# @param first [Array<Hash>] First player's hand items
43+
# @param second [Array<Hash>] Second player's hand items
44+
# @return [String] Canonical FEEN hands string
45+
def self.dump(first:, second:)
46+
first_str = Hand.dump(first)
47+
second_str = Hand.dump(second)
48+
49+
"#{first_str}#{Separators::SEGMENT}#{second_str}"
50+
end
51+
52+
freeze
53+
end
54+
end
55+
end
56+
end

0 commit comments

Comments
 (0)