pgn_reader/lib.rs
1//! A fast non-allocating and streaming reader for chess games in PGN notation.
2//!
3//! [`BufferedReader`] parses games and calls methods of a user provided
4//! [`Visitor`]. Implementing custom visitors allows for maximum flexibility:
5//!
6//! * The reader itself does not allocate (besides a single fixed-size buffer).
7//! The visitor can decide if and how to represent games in memory.
8//! * The reader does not validate move legality. This allows implementing
9//! support for custom chess variants, or delaying move validation.
10//! * The visitor can signal to the reader that it does not care about a game
11//! or variation.
12//!
13//! # Flow
14//!
15//! Visitor methods are called in this order:
16//!
17//! 
18//!
19//! # Examples
20//!
21//! A visitor that counts the number of syntactically valid moves in mainline
22//! of each game.
23//!
24//! ```
25//! use std::io;
26//! use pgn_reader::{Visitor, Skip, BufferedReader, SanPlus};
27//!
28//! struct MoveCounter {
29//! moves: usize,
30//! }
31//!
32//! impl MoveCounter {
33//! fn new() -> MoveCounter {
34//! MoveCounter { moves: 0 }
35//! }
36//! }
37//!
38//! impl Visitor for MoveCounter {
39//! type Result = usize;
40//!
41//! fn begin_game(&mut self) {
42//! self.moves = 0;
43//! }
44//!
45//! fn san(&mut self, _san_plus: SanPlus) {
46//! self.moves += 1;
47//! }
48//!
49//! fn begin_variation(&mut self) -> Skip {
50//! Skip(true) // stay in the mainline
51//! }
52//!
53//! fn end_game(&mut self) -> Self::Result {
54//! self.moves
55//! }
56//! }
57//!
58//! fn main() -> io::Result<()> {
59//! let pgn = b"1. e4 e5 2. Nf3 (2. f4)
60//! { game paused due to bad weather }
61//! 2... Nf6 *";
62//!
63//! let mut reader = BufferedReader::new_cursor(&pgn[..]);
64//!
65//! let mut counter = MoveCounter::new();
66//! let moves = reader.read_game(&mut counter)?;
67//!
68//! assert_eq!(moves, Some(4));
69//! Ok(())
70//! }
71//! ```
72//!
73//! A visitor that returns the final position using [Shakmaty].
74//!
75//! ```
76//! use std::io;
77//!
78//! use shakmaty::{CastlingMode, Chess, Position};
79//! use shakmaty::fen::Fen;
80//!
81//! use pgn_reader::{Visitor, Skip, RawHeader, BufferedReader, SanPlus};
82//!
83//! struct LastPosition {
84//! pos: Chess,
85//! }
86//!
87//! impl LastPosition {
88//! fn new() -> LastPosition {
89//! LastPosition { pos: Chess::default() }
90//! }
91//! }
92//!
93//! impl Visitor for LastPosition {
94//! type Result = Chess;
95//!
96//! fn header(&mut self, key: &[u8], value: RawHeader<'_>) {
97//! // Support games from a non-standard starting position.
98//! if key == b"FEN" {
99//! let pos = Fen::from_ascii(value.as_bytes()).ok()
100//! .and_then(|f| f.into_position(CastlingMode::Standard).ok());
101//!
102//! if let Some(pos) = pos {
103//! self.pos = pos;
104//! }
105//! }
106//! }
107//!
108//! fn begin_variation(&mut self) -> Skip {
109//! Skip(true) // stay in the mainline
110//! }
111//!
112//! fn san(&mut self, san_plus: SanPlus) {
113//! if let Ok(m) = san_plus.san.to_move(&self.pos) {
114//! self.pos.play_unchecked(&m);
115//! }
116//! }
117//!
118//! fn end_game(&mut self) -> Self::Result {
119//! ::std::mem::replace(&mut self.pos, Chess::default())
120//! }
121//! }
122//!
123//! fn main() -> io::Result<()> {
124//! let pgn = b"1. f3 e5 2. g4 Qh4#";
125//!
126//! let mut reader = BufferedReader::new_cursor(&pgn[..]);
127//!
128//! let mut visitor = LastPosition::new();
129//! let pos = reader.read_game(&mut visitor)?;
130//!
131//! assert!(pos.map_or(false, |p| p.is_checkmate()));
132//! Ok(())
133//! }
134//! ```
135//!
136//! [Shakmaty]: ../shakmaty/index.html
137
138#![doc(html_root_url = "https://docs.rs/pgn-reader/0.26.0")]
139#![forbid(unsafe_op_in_unsafe_fn)]
140#![warn(missing_debug_implementations)]
141
142mod reader;
143mod types;
144mod visitor;
145
146pub use reader::{BufferedReader, IntoIter};
147pub use shakmaty::{
148 san::{San, SanPlus},
149 CastlingSide, Color, File, Outcome, Rank, Role, Square,
150};
151pub use types::{Nag, RawComment, RawHeader, Skip};
152pub use visitor::Visitor;