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}