mod date; mod game; pub use game::{GameNode, GameRecord, MoveNode, Player}; mod parser; pub use parser::{parse_collection, Move}; mod types; pub use types::*; pub use date::Date; use std::{fs::File, io::Read}; use thiserror::Error; #[derive(Debug)] pub enum Error { InvalidField, InvalidBoardSize, Incomplete, InvalidSgf(VerboseNomError), } #[derive(Debug)] pub struct VerboseNomError(nom::error::VerboseError); impl From> for VerboseNomError { fn from(err: nom::error::VerboseError<&str>) -> Self { VerboseNomError(nom::error::VerboseError { errors: err .errors .into_iter() .map(|err| (err.0.to_owned(), err.1)) .collect(), }) } } impl From>> for Error { fn from(err: nom::Err>) -> Self { match err { nom::Err::Incomplete(_) => Error::Incomplete, nom::Err::Error(e) => Error::InvalidSgf(VerboseNomError::from(e)), nom::Err::Failure(e) => Error::InvalidSgf(VerboseNomError::from(e)), } } } #[derive(Debug, PartialEq, Error)] pub enum ParseError { #[error("An unknown error was found")] NomError(nom::error::Error), } impl From> for ParseError { fn from(err: nom::error::Error<&str>) -> Self { Self::NomError(nom::error::Error { input: err.input.to_owned(), code: err.code, }) } } /// Given raw text of an SGF file, parse all of the games within that file. /// /// The outermost Result is for any errors that happen in opening and reading the file, or if hte /// outermost part of the file format is invalid. /// /// The inner Result is for errors in each individual game in the file. All of the other games can /// still be kept as valid. pub fn parse_sgf(input: &str) -> Result>, Error> { let (_, games) = parse_collection::>(input)?; let games = games .into_iter() .map(|game| GameRecord::try_from(game)) .collect::>>(); Ok(games) } /// Given a path, parse all of the games stored in that file. /// /// See also `parse_sgf` pub fn parse_sgf_file( path: &std::path::Path, ) -> Result>, Error> { let mut file = File::open(path).unwrap(); let mut text = String::new(); let _ = file.read_to_string(&mut text); parse_sgf(&text) } /* pub fn parse_sgf(_input: &str) -> Result, Error> { Ok(vec![Game::new( GameType::Go, Size { width: 19, height: 19, }, Player { name: None, rank: None, team: None, }, Player { name: None, rank: None, team: None, }, )]) } */