Set up SGF reading and start on the game database #47

Merged
savanni merged 20 commits from kifu/sgf into main 2023-07-26 13:54:28 +00:00
8 changed files with 95 additions and 14 deletions
Showing only changes of commit 744511a552 - Show all commits

2
emseries/Cargo.lock generated
View File

@ -71,7 +71,7 @@ dependencies = [
[[package]]
name = "emseries"
version = "0.5.1"
version = "0.6.0"
dependencies = [
"chrono",
"chrono-tz",

View File

@ -1,7 +1,7 @@
use crate::{
types::{AppState, GameState, Player, Rank},
ui::{home, playing_field, HomeView, PlayingFieldView},
Config,
Config, DatabasePath,
};
use serde::{Deserialize, Serialize};
use std::sync::{Arc, RwLock};
@ -71,10 +71,14 @@ pub struct CoreApp {
impl CoreApp {
pub fn new(config_path: std::path::PathBuf) -> Self {
println!("config_path: {:?}", config_path);
let config = Config::from_path(config_path).expect("configuration to open");
let state = Arc::new(RwLock::new(AppState::new()));
let db_path: DatabasePath = config.get();
let state = Arc::new(RwLock::new(AppState::new(db_path)));
println!("config: {:?}", config);
println!("games database: {:?}", state.read().unwrap().database.len());
Self { config, state }
}

View File

@ -36,6 +36,7 @@ enum OptionNames {
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ConfigOption {
DatabasePath(DatabasePath),
Me(Me),
@ -96,14 +97,14 @@ impl Config {
}
}
fn set(&mut self, val: ConfigOption) {
pub fn set(&mut self, val: ConfigOption) {
let _ = match val {
ConfigOption::DatabasePath(_) => self.values.insert(OptionNames::DatabasePath, val),
ConfigOption::Me(_) => self.values.insert(OptionNames::Me, val),
};
}
fn get<'a, T>(&'a self) -> T
pub fn get<'a, T>(&'a self) -> T
where
T: From<&'a Self>,
{
@ -111,9 +112,16 @@ impl Config {
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct DatabasePath(PathBuf);
impl std::ops::Deref for DatabasePath {
type Target = PathBuf;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<&Config> for DatabasePath {
fn from(config: &Config) -> Self {
match config.values.get(&OptionNames::DatabasePath) {
@ -123,7 +131,7 @@ impl From<&Config> for DatabasePath {
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Me(Player);
impl From<&Config> for Option<Me> {
@ -137,3 +145,54 @@ impl From<&Config> for Option<Me> {
})
}
}
impl std::ops::Deref for Me {
type Target = Player;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::types::Rank;
use cool_asserts::assert_matches;
#[test]
fn it_can_set_and_get_options() {
let mut config = Config::new(PathBuf::from("."));
config.set(ConfigOption::DatabasePath(DatabasePath(PathBuf::from(
"fixtures/five_games",
))));
config.set(ConfigOption::Me(Me(Player {
name: "Savanni".to_owned(),
rank: Some(Rank::Kyu(10)),
})));
}
#[test]
fn it_can_serialize_and_deserialize() {
let mut config = Config::new(PathBuf::from("."));
config.set(ConfigOption::DatabasePath(DatabasePath(PathBuf::from(
"fixtures/five_games",
))));
config.set(ConfigOption::Me(Me(Player {
name: "Savanni".to_owned(),
rank: Some(Rank::Kyu(10)),
})));
let s = serde_json::to_string(&config.values).unwrap();
println!("{}", s);
let values: HashMap<OptionNames, ConfigOption> = serde_json::from_str(s.as_ref()).unwrap();
println!("options: {:?}", values);
assert_matches!(values.get(&OptionNames::DatabasePath),
Some(ConfigOption::DatabasePath(db_path)) =>
assert_eq!(*db_path, config.get())
);
assert_matches!(values.get(&OptionNames::Me), Some(ConfigOption::Me(val)) =>
assert_eq!(Some(val.clone()), config.get())
);
}
}

View File

@ -17,6 +17,7 @@ impl From<std::io::Error> for Error {
}
}
#[derive(Debug)]
pub struct Database {
path: PathBuf,
games: Vec<GameTree>,
@ -49,6 +50,10 @@ impl Database {
Ok(Database { path, games })
}
pub fn len(&self) -> usize {
self.games.len()
}
pub fn all_games(&self) -> impl Iterator<Item = &GameTree> {
self.games.iter()
}

View File

@ -1,9 +1,11 @@
use crate::{
api::PlayStoneRequest,
board::{Board, Coordinate},
config::DatabasePath,
database::Database,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use std::{path::PathBuf, time::Duration};
use thiserror::Error;
use typeshare::typeshare;
@ -43,12 +45,14 @@ impl Default for Size {
#[derive(Debug)]
pub struct AppState {
pub game: Option<GameState>,
pub database: Database,
}
impl AppState {
pub fn new() -> Self {
pub fn new(database_path: DatabasePath) -> Self {
Self {
game: Some(GameState::new()),
database: Database::open_path(database_path.to_path_buf()).unwrap(),
}
}
@ -91,7 +95,7 @@ impl From<Rank> for String {
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Player {
pub name: String,
pub rank: Option<Rank>,

View File

@ -2,6 +2,7 @@ release:
cargo build --release
dev:
export CONFIG=.
cargo watch -x 'run --bin kifu-gtk'
screenplay:

1
kifu/gtk/config Normal file
View File

@ -0,0 +1 @@
{"Me":{"name":"Savanni","rank":{"Kyu":10}},"DatabasePath":"../core/fixtures/five_games"}

View File

@ -44,10 +44,17 @@ fn main() {
.unwrap(),
);
let user_home = std::env::var("HOME").expect("the user's home directory isn't set");
let mut config_path = std::path::PathBuf::from(user_home);
config_path.push(".config");
config_path.push("kifu");
let config_path = std::env::var("CONFIG")
.and_then(|config| Ok(std::path::PathBuf::from(config)))
.or({
std::env::var("HOME").and_then(|base| {
let mut config_path = std::path::PathBuf::from(base);
config_path.push(".config");
config_path.push("kifu");
Ok(config_path)
})
})
.expect("no config path could be found");
let core = CoreApp::new(config_path);