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//! ![Flow](https://github.com/niklasf/rust-pgn-reader/blob/master/docs/visitor.png?raw=true)
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, RawTag, 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 tag(&mut self, name: &[u8], value: RawTag<'_>) {
97//!         // Support games from a non-standard starting position.
98//!         if name == 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.27.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, RawTag, Skip};
152pub use visitor::Visitor;