1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
//! A fast non-allocating and streaming reader for chess games in PGN notation.
//!
//! [`Reader`] parses games and calls methods of a user provided
//! [`Visitor`]. Implementing custom visitors allows for maximum flexibility:
//!
//! * The reader itself does not allocate (besides a single fixed-size buffer).
//! The visitor can decide if and how to represent games in memory.
//! * The reader does not validate move legality. This allows implementing
//! support for custom chess variants, or delaying move validation.
//! * The visitor can short-circuit and let the reader use a fast path for
//! skipping games or variations.
//!
//! # Flow
//!
//! Visitor methods are called in these phases:
//!
//! 1. [`Visitor::begin_tags()`]
//! - [`Visitor::tag()`]
//! 2. [`Visitor::begin_movetext()`]
//! - [`Visitor::san()`]
//! - [`Visitor::nag()`]
//! - [`Visitor::comment()`]
//! - [`Visitor::begin_variation()`] or skip
//! - [`Visitor::end_variation()`]
//! - [`Visitor::outcome()`]
//! 3. [`Visitor::end_game()`]
//!
//! # Examples
//!
//! A visitor that counts the number of syntactically valid moves in mainline
//! of each game.
//!
//! ```
//! use std::{io, ops::ControlFlow};
//! use pgn_reader::{Visitor, Reader, SanPlus};
//!
//! struct MoveCounter;
//!
//! impl Visitor for MoveCounter {
//! type Tags = ();
//! type Movetext = usize;
//! type Output = usize;
//!
//! fn begin_tags(&mut self) -> ControlFlow<Self::Output, Self::Tags> {
//! ControlFlow::Continue(())
//! }
//!
//! fn begin_movetext(&mut self, _tags: Self::Tags) -> ControlFlow<Self::Output, Self::Movetext> {
//! ControlFlow::Continue(0)
//! }
//!
//! fn san(&mut self, movetext: &mut Self::Movetext, _san_plus: SanPlus) -> ControlFlow<Self::Output> {
//! *movetext += 1;
//! ControlFlow::Continue(())
//! }
//!
//! fn end_game(&mut self, movetext: Self::Movetext) -> Self::Output {
//! movetext
//! }
//! }
//!
//! fn main() -> io::Result<()> {
//! let pgn = b"1. e4 e5 2. Nf3 (2. f4)
//! { game paused due to bad weather }
//! 2... Nf6 *";
//!
//! let mut reader = Reader::new(io::Cursor::new(&pgn));
//!
//! let moves = reader.read_game(&mut MoveCounter)?;
//! assert_eq!(moves, Some(4));
//!
//! Ok(())
//! }
//! ```
//!
//! A visitor that returns the final position using [`shakmaty`].
//!
//! ```
//! use std::{error::Error, io, mem, ops::ControlFlow};
//!
//! use shakmaty::{CastlingMode, Chess, Position};
//! use shakmaty::fen::Fen;
//!
//! use pgn_reader::{Visitor, RawTag, Reader, SanPlus};
//!
//! struct LastPosition;
//!
//! impl Visitor for LastPosition {
//! type Tags = Option<Chess>;
//! type Movetext = Chess;
//! type Output = Result<Chess, Box<dyn Error>>;
//!
//! fn begin_tags(&mut self) -> ControlFlow<Self::Output, Self::Tags> {
//! ControlFlow::Continue(None)
//! }
//!
//! fn tag(&mut self, tags: &mut Self::Tags, name: &[u8], value: RawTag<'_>) -> ControlFlow<Self::Output> {
//! // Support games from a non-standard starting position.
//! if name == b"FEN" {
//! let fen = match Fen::from_ascii(value.as_bytes()) {
//! Ok(fen) => fen,
//! Err(err) => return ControlFlow::Break(Err(err.into())),
//! };
//! let pos = match fen.into_position(CastlingMode::Standard) {
//! Ok(pos) => pos,
//! Err(err) => return ControlFlow::Break(Err(err.into())),
//! };
//! tags.replace(pos);
//! }
//! ControlFlow::Continue(())
//! }
//!
//! fn begin_movetext(&mut self, tags: Self::Tags) -> ControlFlow<Self::Output, Self::Movetext> {
//! ControlFlow::Continue(tags.unwrap_or_default())
//! }
//!
//! fn san(&mut self, movetext: &mut Self::Movetext, san_plus: SanPlus) -> ControlFlow<Self::Output> {
//! match san_plus.san.to_move(movetext) {
//! Ok(m) => {
//! movetext.play_unchecked(m);
//! ControlFlow::Continue(())
//! }
//! Err(err) => ControlFlow::Break(Err(err.into()))
//! }
//! }
//!
//! fn end_game(&mut self, movetext: Self::Movetext) -> Self::Output {
//! Ok(movetext)
//! }
//! }
//!
//! fn main() -> io::Result<()> {
//! let pgn = b"1. f3 e5 2. g4 Qh4#";
//!
//! let mut reader = Reader::new(io::Cursor::new(&pgn));
//!
//! let pos = reader
//! .read_game(&mut LastPosition)?
//! .expect("game found")
//! .expect("valid and legal");
//!
//! assert!(pos.is_checkmate());
//! Ok(())
//! }
//! ```
pub use RawComment;
pub use Nag;
pub use Reader;
pub use ;
pub use RawTag;
pub use ;