use core::sq::SQ;
use core::{PieceType, File, Rank};
use std::fmt;
pub enum GameResult {
WhiteWins,
BlackWins,
Draw,
Other
}
pub enum ChessDate {
Unknown,
Year(u16),
YearMonth(u16,u8),
Full(u16,u8,u8)
}
impl ChessDate {
pub fn parse_chess_date(date: &str) -> Self {
let mut args = date[1..(date.len() - 1)].split('.');
let y = args.next().map(|m: &str| m.parse::<u16>());
if y.is_none() {
return ChessDate::Unknown;
}
let y_err = y.unwrap();
if y_err.is_err() {
return ChessDate::Unknown;
}
let year = y_err.unwrap();
let m = args.next().map(|m: &str| m.parse::<u8>());
if m.is_none() {
return ChessDate::Year(year);
}
let m_err = m.unwrap();
if m_err.is_err() {
return ChessDate::Year(year);
}
let month = m_err.unwrap();
let d = args.next().map(|m: &str| m.parse::<u8>());
if d.is_none() {
return ChessDate::YearMonth(year, month);
}
let d_err = d.unwrap();
if d_err.is_err() {
return ChessDate::YearMonth(year, month);
}
let day = d_err.unwrap();
ChessDate::Full(year,month,day)
}
pub fn to_string(&self) -> String {
match *self {
ChessDate::Unknown => {("\"??\"").to_owned()},
ChessDate::Year(y) => {
let mut s = ("\"").to_owned();
s.push_str(y.to_string().as_ref());
s.push_str(".??.??\"");
s
},
ChessDate::YearMonth(y,m) => {
let mut s = ("\"").to_owned();
s.push_str(y.to_string().as_ref());
s.push('.');
s.push_str(m.to_string().as_ref());
s.push_str(".??\"");
s
},
ChessDate::Full(y,m,d) => {
let mut s = ("\"").to_owned();
s.push_str(y.to_string().as_ref());
s.push('.');
s.push_str(m.to_string().as_ref());
s.push('.');
s.push_str(d.to_string().as_ref());
s.push('"');
s
},
}
}
}
pub struct ChessRound {
rounds: Vec<u32>
}
impl Default for ChessRound {
fn default() -> ChessRound {
ChessRound {
rounds: Vec::new()
}
}
}
impl ChessRound {
pub fn parse_chess_round(round: &str) -> ChessRound {
let mut cr = ChessRound::default();
let args = round[1..(round.len() - 1)].split('.');
args.for_each(|r: &str| {
if let Ok(m) = r.parse() {
cr.rounds.push(m)
}
});
cr
}
pub fn to_string(&self) -> String {
let mut s = "\"".to_string();
for (i, x) in self.rounds.iter().enumerate() {
s.push_str(x.to_string().as_ref());
if i != self.rounds.len() - 1 {
s.push('.');
}
}
s.push('"');
s
}
}
pub struct PGNTags {
event: String,
site: String,
date: ChessDate,
round: ChessRound,
white: String,
black: String,
result: String,
}
impl fmt::Display for PGNTags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s = self.to_string();
f.pad(&s)
}
}
impl PGNTags {
pub fn to_string(&self) -> String {
let mut s: String = "[Event ".to_owned();
s.push_str(self.event.as_ref());
s.push_str("]\n[Site ");
s.push_str(self.site.as_ref());
s.push_str("]\n[Date ");
s.push_str(self.date.to_string().as_ref());
s.push_str("]\n[Round ");
s.push_str(self.round.to_string().as_ref());
s.push_str("]\n[White ");
s.push_str(self.white.as_ref());
s.push_str("]\n[Black ");
s.push_str(self.black.as_ref());
s.push_str("]\n[Result ");
s.push_str(self.result.as_ref());
s.push_str("]\n");
s
}
pub fn add(mut self, input: &str) -> Result<PGNTags,PGNError> {
let first_char = input.chars().nth(0).ok_or(PGNError::TagParse)?;
let last_char = input.chars().last().ok_or(PGNError::TagParse)?;
if input.len() < 3 || first_char != '[' || last_char != ']' {
return Err(PGNError::TagParse);
}
let r = &input[1..(input.len() - 1)];
let quote_first = r.find('"')
.ok_or(PGNError::TagParse)?;
let quote_second = r.rfind('"')
.ok_or(PGNError::TagParse)?;
if quote_first >= quote_second - 2 {
return Err(PGNError::TagParse)
}
let in_quote = r[(quote_first)..(quote_second + 1)].to_owned();
let no_quote = r[..(quote_first - 1)]
.trim()
.split_whitespace()
.next()
.ok_or(PGNError::TagParse)?;
self = self.parse_tag(no_quote, in_quote)?;
Ok(self)
}
pub fn parse_tag(mut self, tag: &str, data: String) -> Result<PGNTags,PGNError> {
match tag {
"Event" => {self.event = data},
"Site" => {self.site = data},
"Date" => {self.date = ChessDate::parse_chess_date(data.as_ref())},
"Round" => {self.round = ChessRound::parse_chess_round(data.as_ref())},
"White" => {self.white = data},
"Black" => {self.black = data},
"Result" => {self.result = data},
_ => {return Err(PGNError::TagParse)}
}
Ok(self)
}
}
impl Default for PGNTags {
fn default() -> Self {
PGNTags {
event: String::new(),
site: String::new(),
date: ChessDate::Unknown,
round: ChessRound::default(),
white: String::new(),
black: String::new(),
result: String::new(),
}
}
}
pub enum PGNMoveTag {
None, Good, Excellent, Bad, Blunder, Interesting, Doubtful }
pub enum CheckType {
Check,
CheckMate
}
pub struct PGNMoveSpecifier {
rank: Option<Rank>,
file: Option<File>,
square: Option<SQ>
}
pub struct PGNRegMove {
piece: Option<PieceType>,
specifier: Option<PGNMoveSpecifier>,
dest: SQ,
promo: Option<PieceType>,
capture: bool,
}
pub enum PGNMoveType {
KingSideCastle, QueenSideCastle, Reg(PGNRegMove)
}
pub struct PGNMove {
move_type: PGNMoveType,
check: Option<CheckType>,
tag: PGNMoveTag
}
impl PGNMove {
pub fn parse(_input: &str) -> Result<PGNMove, PGNError> {
unimplemented!()
}
}
pub struct PGNRound {
move_num: u32,
white_move: PGNMove,
black_move: Option<PGNMove>,
}
#[derive(Debug)]
pub enum PGNError {
TagParse,
Length,
}
pub struct PGN {
tags: PGNTags,
moves: Vec<PGNRound>
}
impl PGN {
pub fn parse(input: &str) -> Result<PGN, PGNError> {
let mut tags = PGNTags::default();
let mut lines = input.lines();
loop {
let mut line = lines.next().ok_or(PGNError::Length)?;
if line == "" {
break;
}
line = line.trim();
tags = tags.add(line)?;
}
Ok(PGN {
tags,
moves: Vec::new()
})
}
}
#[cfg(test)]
mod tests {
static TEST_WHITE: &'static str = "[White \"David Sr. Johnson\"]";
static TEST_BLACK: &'static str = "[Black \"Grace Foo Bar\"]";
static TEST_DATE: &'static str = "[Date \"2017.4.2\"]";
static TEST_ROUND: &'static str = "[Round \"0.0\"]";
static TEST_RESULT: &'static str = "[Round \"1-0\"]";
extern crate rand;
use super::*;
#[test]
fn tags_test() {
PGNTags::default()
.add(TEST_WHITE).unwrap()
.add(TEST_BLACK).unwrap()
.add(TEST_DATE).unwrap()
.add(TEST_ROUND).unwrap();
}
}