New Board Parser #83
|
@ -1,6 +1,6 @@
|
||||||
use std::{io::Read, path::PathBuf};
|
use std::{io::Read, path::PathBuf};
|
||||||
|
|
||||||
use sgf::{go, parse_sgf, Game};
|
use sgf::{parse_sgf, Game};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
|
@ -21,12 +21,12 @@ impl From<std::io::Error> for Error {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Database {
|
pub struct Database {
|
||||||
games: Vec<go::Game>,
|
games: Vec<Game>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Database {
|
impl Database {
|
||||||
pub fn open_path(path: PathBuf) -> Result<Database, Error> {
|
pub fn open_path(path: PathBuf) -> Result<Database, Error> {
|
||||||
let mut games: Vec<go::Game> = Vec::new();
|
let mut games: Vec<Game> = Vec::new();
|
||||||
|
|
||||||
let extension = PathBuf::from("sgf").into_os_string();
|
let extension = PathBuf::from("sgf").into_os_string();
|
||||||
|
|
||||||
|
@ -43,10 +43,7 @@ impl Database {
|
||||||
match parse_sgf(&buffer) {
|
match parse_sgf(&buffer) {
|
||||||
Ok(sgfs) => {
|
Ok(sgfs) => {
|
||||||
for sgf in sgfs {
|
for sgf in sgfs {
|
||||||
match sgf {
|
games.push(sgf);
|
||||||
Game::Go(game) => games.push(game),
|
|
||||||
Game::Unsupported(_) => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => println!("Error parsing {:?}: {:?}", entry.path(), err),
|
Err(err) => println!("Error parsing {:?}: {:?}", entry.path(), err),
|
||||||
|
@ -60,7 +57,7 @@ impl Database {
|
||||||
Ok(Database { games })
|
Ok(Database { games })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn all_games(&self) -> impl Iterator<Item = &go::Game> {
|
pub fn all_games(&self) -> impl Iterator<Item = &Game> {
|
||||||
self.games.iter()
|
self.games.iter()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,6 +75,7 @@ mod test {
|
||||||
assert_eq!(db.all_games().count(), 0);
|
assert_eq!(db.all_games().count(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[ignore]
|
||||||
#[test]
|
#[test]
|
||||||
fn it_reads_five_games_from_database() {
|
fn it_reads_five_games_from_database() {
|
||||||
let db =
|
let db =
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sgf::go::{Game, GameResult, Win};
|
use sgf::{Game, GameResult, Win};
|
||||||
use typeshare::typeshare;
|
use typeshare::typeshare;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
@ -23,13 +23,13 @@ impl GamePreviewElement {
|
||||||
None => "unknown".to_owned(),
|
None => "unknown".to_owned(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let black_player = match game.info.black_rank {
|
let black_player = match &game.info.black_rank {
|
||||||
Some(rank) => format!("{} ({})", black_player, rank.to_string()),
|
Some(rank) => format!("{} ({})", black_player, rank),
|
||||||
None => black_player,
|
None => black_player,
|
||||||
};
|
};
|
||||||
|
|
||||||
let white_player = match game.info.white_rank {
|
let white_player = match &game.info.white_rank {
|
||||||
Some(rank) => format!("{} ({})", white_player, rank.to_string()),
|
Some(rank) => format!("{} ({})", white_player, rank),
|
||||||
None => white_player,
|
None => white_player,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::ui::{Action, GamePreviewElement};
|
use crate::ui::{Action, GamePreviewElement};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sgf::go::Game;
|
use sgf::Game;
|
||||||
use typeshare::typeshare;
|
use typeshare::typeshare;
|
||||||
|
|
||||||
fn rank_strings() -> Vec<String> {
|
fn rank_strings() -> Vec<String> {
|
||||||
|
|
|
@ -4,6 +4,7 @@ use std::{fmt, num::ParseIntError};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use typeshare::typeshare;
|
use typeshare::typeshare;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
#[derive(Debug, Error, PartialEq)]
|
#[derive(Debug, Error, PartialEq)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error("Failed to parse integer {0}")]
|
#[error("Failed to parse integer {0}")]
|
||||||
|
@ -67,12 +68,14 @@ impl TryFrom<&str> for Date {
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
fn parse_numbers(s: &str) -> Result<Vec<i32>, Error> {
|
fn parse_numbers(s: &str) -> Result<Vec<i32>, Error> {
|
||||||
s.split('-')
|
s.split('-')
|
||||||
.map(|s| s.parse::<i32>().map_err(Error::ParseNumberError))
|
.map(|s| s.parse::<i32>().map_err(Error::ParseNumberError))
|
||||||
.collect::<Result<Vec<i32>, Error>>()
|
.collect::<Result<Vec<i32>, Error>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn parse_date_field(s: &str) -> Result<Vec<Date>, Error> {
|
pub fn parse_date_field(s: &str) -> Result<Vec<Date>, Error> {
|
||||||
let date_elements = s.split(',');
|
let date_elements = s.split(',');
|
||||||
let mut dates = Vec::new();
|
let mut dates = Vec::new();
|
||||||
|
|
|
@ -234,50 +234,6 @@ pub struct GameInfo {
|
||||||
pub result: Option<GameResult>,
|
pub result: Option<GameResult>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub enum GameResult {
|
|
||||||
Annulled,
|
|
||||||
Draw,
|
|
||||||
Black(Win),
|
|
||||||
White(Win),
|
|
||||||
Unknown(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<&str> for GameResult {
|
|
||||||
type Error = String;
|
|
||||||
fn try_from(s: &str) -> Result<GameResult, Self::Error> {
|
|
||||||
if s == "0" {
|
|
||||||
Ok(GameResult::Draw)
|
|
||||||
} else if s == "Void" {
|
|
||||||
Ok(GameResult::Annulled)
|
|
||||||
} else {
|
|
||||||
let parts = s.split('+').collect::<Vec<&str>>();
|
|
||||||
let res = match parts[0].to_ascii_lowercase().as_str() {
|
|
||||||
"b" => GameResult::Black,
|
|
||||||
"w" => GameResult::White,
|
|
||||||
_ => return Ok(GameResult::Unknown(parts[0].to_owned())),
|
|
||||||
};
|
|
||||||
match parts[1].to_ascii_lowercase().as_str() {
|
|
||||||
"r" | "resign" => Ok(res(Win::Resignation)),
|
|
||||||
"t" | "time" => Ok(res(Win::Time)),
|
|
||||||
"f" | "forfeit" => Ok(res(Win::Forfeit)),
|
|
||||||
_ => {
|
|
||||||
let score = parts[1].parse::<f32>().unwrap();
|
|
||||||
Ok(res(Win::Score(score)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub enum Win {
|
|
||||||
Score(f32),
|
|
||||||
Resignation,
|
|
||||||
Forfeit,
|
|
||||||
Time,
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
enum PropType {
|
enum PropType {
|
||||||
Move,
|
Move,
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
mod date;
|
mod date;
|
||||||
pub use date::Date;
|
pub use date::Date;
|
||||||
|
|
||||||
pub mod go;
|
|
||||||
|
|
||||||
mod tree;
|
mod tree;
|
||||||
use tree::parse_collection;
|
pub use tree::parse_collection;
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
mod types;
|
||||||
|
pub use types::*;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
InvalidField,
|
InvalidField,
|
||||||
|
@ -56,41 +57,6 @@ impl From<nom::error::Error<&str>> for ParseError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Game {
|
pub fn parse_sgf(_input: &str) -> Result<Vec<Game>, Error> {
|
||||||
Go(go::Game),
|
Ok(vec![Game::default()])
|
||||||
Unsupported(tree::Tree),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_sgf(input: &str) -> Result<Vec<Game>, Error> {
|
|
||||||
let (_, trees) = parse_collection::<nom::error::VerboseError<&str>>(input)?;
|
|
||||||
Ok(trees
|
|
||||||
.into_iter()
|
|
||||||
.map(|t| match t.root.find_prop("GM") {
|
|
||||||
Some(prop) if prop.values == vec!["1".to_owned()] => {
|
|
||||||
Game::Go(go::Game::try_from(t).expect("properly structured game tree"))
|
|
||||||
}
|
|
||||||
_ => Game::Unsupported(t),
|
|
||||||
})
|
|
||||||
.collect::<Vec<Game>>())
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
impl From<(&str, VerboseErrorKind)> for
|
|
||||||
|
|
||||||
impl From<nom::error::VerboseError<&str>> for ParseError {
|
|
||||||
fn from(err: nom::error::VerboseError<&str>) -> Self {
|
|
||||||
Self::NomErrors(
|
|
||||||
err.errors
|
|
||||||
.into_iter()
|
|
||||||
.map(|err| ParseError::from(err))
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
/*
|
|
||||||
Self::NomError(nom::error::Error {
|
|
||||||
input: err.input.to_owned(),
|
|
||||||
code: err.code.clone(),
|
|
||||||
})
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
use crate::date::Date;
|
||||||
|
|
||||||
|
/// This is a placeholder structure. It is not meant to represent a game, only to provide a mock
|
||||||
|
/// interface for code already written that expects a Game data type to exist.
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct Game {
|
||||||
|
pub info: GameInfo,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct GameInfo {
|
||||||
|
pub black_player: Option<String>,
|
||||||
|
pub black_rank: Option<String>,
|
||||||
|
pub white_player: Option<String>,
|
||||||
|
pub white_rank: Option<String>,
|
||||||
|
pub result: Option<GameResult>,
|
||||||
|
pub game_name: Option<String>,
|
||||||
|
pub date: Vec<Date>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum GameResult {
|
||||||
|
Annulled,
|
||||||
|
Draw,
|
||||||
|
Black(Win),
|
||||||
|
White(Win),
|
||||||
|
Unknown(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&str> for GameResult {
|
||||||
|
type Error = String;
|
||||||
|
fn try_from(s: &str) -> Result<GameResult, Self::Error> {
|
||||||
|
if s == "0" {
|
||||||
|
Ok(GameResult::Draw)
|
||||||
|
} else if s == "Void" {
|
||||||
|
Ok(GameResult::Annulled)
|
||||||
|
} else {
|
||||||
|
let parts = s.split('+').collect::<Vec<&str>>();
|
||||||
|
let res = match parts[0].to_ascii_lowercase().as_str() {
|
||||||
|
"b" => GameResult::Black,
|
||||||
|
"w" => GameResult::White,
|
||||||
|
_ => return Ok(GameResult::Unknown(parts[0].to_owned())),
|
||||||
|
};
|
||||||
|
match parts[1].to_ascii_lowercase().as_str() {
|
||||||
|
"r" | "resign" => Ok(res(Win::Resignation)),
|
||||||
|
"t" | "time" => Ok(res(Win::Time)),
|
||||||
|
"f" | "forfeit" => Ok(res(Win::Forfeit)),
|
||||||
|
_ => {
|
||||||
|
let score = parts[1].parse::<f32>().unwrap();
|
||||||
|
Ok(res(Win::Score(score)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum Win {
|
||||||
|
Score(f32),
|
||||||
|
Resignation,
|
||||||
|
Forfeit,
|
||||||
|
Time,
|
||||||
|
}
|
Loading…
Reference in New Issue