From 90b0f830e07b45d65c98d818f288424a59100787 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Thu, 20 Jul 2023 00:55:24 -0400 Subject: [PATCH] Start building an interface to the database --- kifu/core/Cargo.lock | 50 +++++++++++++++++++++++++ kifu/core/Cargo.toml | 3 +- kifu/core/src/database.rs | 77 +++++++++++++++++++++++++++++++++++++++ kifu/core/src/lib.rs | 10 +++-- 4 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 kifu/core/src/database.rs diff --git a/kifu/core/Cargo.lock b/kifu/core/Cargo.lock index 91de0a2..43e9276 100644 --- a/kifu/core/Cargo.lock +++ b/kifu/core/Cargo.lock @@ -45,6 +45,7 @@ dependencies = [ "js-sys", "num-integer", "num-traits", + "time", "wasm-bindgen", "winapi", ] @@ -109,6 +110,15 @@ dependencies = [ "syn 2.0.12", ] +[[package]] +name = "go-sgf" +version = "0.1.0" +dependencies = [ + "chrono", + "nom", + "thiserror", +] + [[package]] name = "grid" version = "0.9.0" @@ -161,6 +171,7 @@ dependencies = [ name = "kifu-core" version = "0.1.0" dependencies = [ + "go-sgf", "grid", "serde", "serde_json", @@ -192,12 +203,34 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "no-std-compat" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "num-integer" version = "0.1.45" @@ -335,6 +368,17 @@ dependencies = [ "syn 2.0.12", ] +[[package]] +name = "time" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +dependencies = [ + "libc", + "wasi", + "winapi", +] + [[package]] name = "typeshare" version = "1.0.1" @@ -369,6 +413,12 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + [[package]] name = "wasm-bindgen" version = "0.2.84" diff --git a/kifu/core/Cargo.toml b/kifu/core/Cargo.toml index e78c7bc..df1da7b 100644 --- a/kifu/core/Cargo.toml +++ b/kifu/core/Cargo.toml @@ -6,8 +6,9 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +go-sgf = { path = "../../go-sgf" } grid = { version = "0.9" } -serde = { version = "1", features = [ "derive" ] } serde_json = { version = "1" } +serde = { version = "1", features = [ "derive" ] } thiserror = { version = "1" } typeshare = { version = "1" } diff --git a/kifu/core/src/database.rs b/kifu/core/src/database.rs new file mode 100644 index 0000000..76ca20c --- /dev/null +++ b/kifu/core/src/database.rs @@ -0,0 +1,77 @@ +use std::{ffi::OsStr, io::Read, os::unix::ffi::OsStrExt, path::PathBuf}; + +use go_sgf::go::{parse_sgf, GameTree, GameType}; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum Error { + #[error("Database permission denied")] + PermissionDenied, + #[error("An IO error occurred: {0}")] + IOError(std::io::Error), +} + +impl From for Error { + fn from(err: std::io::Error) -> Self { + Error::IOError(err) + } +} + +pub struct Database { + path: PathBuf, + games: Vec, +} + +impl Database { + pub fn open_path(path: PathBuf) -> Result { + let mut games: Vec = Vec::new(); + + let extension = PathBuf::from("sgf").into_os_string(); + + let path_iter = std::fs::read_dir(path.clone())?; + for entry in path_iter { + match entry { + Ok(entry) => { + if entry.path().extension() == Some(&extension) { + let mut buffer = String::new(); + std::fs::File::open(entry.path()) + .unwrap() + .read_to_string(&mut buffer) + .unwrap(); + let sgf = parse_sgf(&buffer).unwrap(); + games.extend(sgf); + } + } + Err(err) => println!("failed entry: {:?}", err), + } + } + + Ok(Database { path, games }) + } + + pub fn all_games(&self) -> impl Iterator { + self.games.iter() + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn it_reads_empty_database() { + let db = Database::open_path(PathBuf::from("fixtures/empty_database/")) + .expect("database to open"); + assert_eq!(db.all_games().count(), 0); + } + + #[test] + fn it_reads_five_games_from_database() { + let db = + Database::open_path(PathBuf::from("fixtures/five_games/")).expect("database to open"); + assert_eq!(db.all_games().count(), 5); + for game in db.all_games() { + assert_eq!(game.game_type, GameType::Go); + } + } +} diff --git a/kifu/core/src/lib.rs b/kifu/core/src/lib.rs index b5b56da..1e85131 100644 --- a/kifu/core/src/lib.rs +++ b/kifu/core/src/lib.rs @@ -3,12 +3,14 @@ pub use api::{ CoreApp, CoreRequest, CoreResponse, CreateGameRequest, HotseatPlayerRequest, PlayerInfoRequest, }; -mod types; -pub use types::{BoardError, Color, Rank, Size}; -pub mod ui; - mod board; pub use board::*; mod config; pub use config::*; + +mod database; + +mod types; +pub use types::{BoardError, Color, Rank, Size}; +pub mod ui;