pgn_traits/lib.rs
1//! Data types for text representations of game positions and moves, which may be used for [Portable game notation][1].
2//!
3//! The terminology used in this module is specific to chess and chess variants, but it can be implemented for any game.
4//!
5//! [1]: https://en.wikipedia.org/wiki/Portable_Game_Notation
6
7extern crate board_game_traits;
8
9use board_game_traits::{GameResult, Position};
10use std::error;
11use std::fmt;
12
13/// A list of general categories of errors related to pgn parsing.
14///
15/// This list is intended to grow over time and it is not recommended to exhaustively match against it.
16///
17/// It is used with the [`Error`] type.
18///
19/// [`Error`]: struct.Error.html
20#[derive(Clone, Copy, Eq, PartialEq, Debug, PartialOrd, Ord)]
21pub enum ErrorKind {
22 ParseError,
23 AmbiguousMove,
24 IllegalMove,
25 IllegalPosition,
26 IoError,
27 Other,
28}
29
30/// The error type for operations on a `PgnPosition`.
31///
32/// The error can be created with an arbitrary payload and optionally an underlying source error for error chaining.
33#[derive(Debug)]
34pub struct Error {
35 kind: ErrorKind,
36 error: Box<dyn error::Error + Send + Sync>,
37 source: Option<Box<dyn error::Error + Send + Sync>>,
38}
39
40impl Error {
41 /// Returns a new error of the specific `ErrorKind` with an arbitrary payload.
42 pub fn new<E>(kind: ErrorKind, error: E) -> Error
43 where
44 E: Into<Box<dyn error::Error + Send + Sync>>,
45 {
46 Error {
47 kind,
48 error: error.into(),
49 source: None,
50 }
51 }
52
53 /// Returns a new error of the specific `ErrorKind` with an arbitrary payload and source error.
54 pub fn new_caused_by<E, F>(kind: ErrorKind, error: E, source: F) -> Error
55 where
56 E: Into<Box<dyn error::Error + Send + Sync>>,
57 F: Into<Box<dyn error::Error + Send + Sync>>,
58 {
59 Error {
60 kind,
61 error: error.into(),
62 source: Some(source.into()),
63 }
64 }
65
66 /// Convenience function that returns a `ParseError` with an arbitrary payload. Equivalent to calling `Error::new(ErrorKind::ParseError, error)`
67 pub fn new_parse_error<E>(error: E) -> Error
68 where
69 E: Into<Box<dyn error::Error + Send + Sync>>,
70 {
71 Error {
72 kind: ErrorKind::ParseError,
73 error: error.into(),
74 source: None,
75 }
76 }
77}
78
79impl error::Error for Error {}
80
81impl fmt::Display for Error {
82 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
83 match self.kind {
84 ErrorKind::ParseError => write!(fmt, "Parse error. "),
85 ErrorKind::AmbiguousMove => write!(fmt, "Ambiguous move. "),
86 ErrorKind::IllegalMove => write!(fmt, "Illegal move. "),
87 ErrorKind::IllegalPosition => write!(fmt, "Illegal position. "),
88 ErrorKind::IoError => write!(fmt, "IO error. "),
89 ErrorKind::Other => Ok(()),
90 }?;
91 write!(fmt, "{}", self.error)?;
92 if let Some(ref source) = self.source {
93 write!(fmt, "\nCaused by: {}", source)?;
94 }
95 Ok(())
96 }
97}
98
99/// Trait for text representations of game positions and moves.
100///
101/// The terminology used in this trait is specific to chess and chess variants, but it can be implemented for any game.
102pub trait PgnPosition: Sized + Position + PartialEq {
103 /// The required tags, and their default values, for pgn files
104 const REQUIRED_TAGS: &'static [(&'static str, &'static str)];
105
106 /// The name of a special tag that allows non-standard start positions for a game
107 /// For such games, parsers need to handle this tag to work correctly
108 /// In standard chess PGN, this would be `Some("FEN")`
109 /// Can be `None` to disable its usage
110 const START_POSITION_TAG_NAME: Option<&'static str>;
111
112 /// Each possible game result in the pgn
113 const POSSIBLE_GAME_RESULTS: &'static [(&'static str, Option<GameResult>)] = &[
114 ("*", None),
115 ("1-0", Some(GameResult::WhiteWin)),
116 ("0-1", Some(GameResult::BlackWin)),
117 ("1/2-1/2", Some(GameResult::Draw)),
118 ];
119
120 /// Returns a more detailed game result string, for games that use these.
121 /// Must correspond with `POSSIBLE_GAME_RESULTS`
122 #[inline]
123 fn pgn_game_result(&self) -> Option<&'static str> {
124 self.game_result().map(|game_result| match game_result {
125 GameResult::WhiteWin => "1-0",
126 GameResult::BlackWin => "0-1",
127 GameResult::Draw => "1/2-1/2",
128 })
129 }
130
131 /// Each possible move annotation that can appear at the end of a move
132 /// A move can have multiple annotations.
133 /// If one annotation is a substring of another, the longer one must be written first
134 const POSSIBLE_MOVE_ANNOTATIONS: &'static [&'static str] = &["!!", "!?", "?!", "??", "!", "?"];
135
136 /// Constructs a position from [Forsyth–Edwards Notation][1].
137 ///
138 /// Extensions to this notation exist for all large chess variants
139 ///
140 /// [1]: https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation
141 #[inline]
142 fn from_fen(fen: &str) -> Result<Self, Error> {
143 Self::from_fen_with_settings(fen, &Self::Settings::default())
144 }
145
146 /// The number of full moves in the position
147 /// It starts at 1 and is incremented after the second player's move
148 #[inline]
149 fn full_move_number(&self) -> Option<u32> {
150 None
151 }
152
153 /// Constructs a position from [Forsyth–Edwards Notation][1] with the given settings.
154 ///
155 /// Extensions to this notation exist for all large chess variants
156 ///
157 /// [1]: https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation
158 fn from_fen_with_settings(fen: &str, settings: &Self::Settings) -> Result<Self, Error>;
159
160 /// Returns a string representation of the position in [Forsyth–Edwards Notation][1].
161 ///
162 /// Extensions to this notation exist for all large chess variants.
163 ///
164 /// [1]: https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation
165 fn to_fen(&self) -> String;
166
167 /// Construct a game move from [Standard Algebraic Notation][1], specifically the format used in [pgn notation][2].
168 ///
169 /// Extensions to this notation exist for all large chess variants.
170 ///
171 /// [1]: https://en.wikipedia.org/wiki/Algebraic_notation_(chess)
172 /// [2]: https://en.wikipedia.org/wiki/Portable_Game_Notation
173 fn move_from_san(&self, input: &str) -> Result<Self::Move, Error>;
174
175 /// Returns a string representation of the move in [Standard Algebraic Notation][1], specifically the format used in [pgn notation][2].
176 ///
177 /// Extensions to this notation exist for all large chess variants.
178 ///
179 /// [1]: https://en.wikipedia.org/wiki/Algebraic_notation_(chess)
180 /// [2]: https://en.wikipedia.org/wiki/Portable_Game_Notation
181 fn move_to_san(&self, mv: &Self::Move) -> String;
182
183 /// Construct a move from an alternative, [long algebraic notation][1].
184 ///
185 /// This is mostly used for chess and chess variations in the uci interface, or for convenient debugging.
186 /// Implementations may simply wrap this function around `move_from_san` where appropriate.
187 ///
188 /// [1]: https://en.wikipedia.org/wiki/Algebraic_notation_(chess)#Long_algebraic_notation
189 fn move_from_lan(&self, input: &str) -> Result<Self::Move, Error>;
190
191 /// Returns a string representation of the move in an alternative, [long algebraic notation][1].
192 ///
193 /// This is mostly used for chess and chess variations in the uci interface, or for convenient debugging.
194 /// Implementations may simply wrap this function around `move_to_san` where appropriate.
195 ///
196 /// [1]: https://en.wikipedia.org/wiki/Algebraic_notation_(chess)#Long_algebraic_notation
197 fn move_to_lan(&self, mv: &Self::Move) -> String;
198}