diff --git a/emseries/Cargo.lock b/emseries/Cargo.lock index 4435e02..cc38833 100644 --- a/emseries/Cargo.lock +++ b/emseries/Cargo.lock @@ -71,7 +71,7 @@ dependencies = [ [[package]] name = "emseries" -version = "0.5.1" +version = "0.6.0" dependencies = [ "chrono", "chrono-tz", diff --git a/kifu/core/src/api.rs b/kifu/core/src/api.rs index ec3987d..3af2fe0 100644 --- a/kifu/core/src/api.rs +++ b/kifu/core/src/api.rs @@ -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 } } diff --git a/kifu/core/src/config.rs b/kifu/core/src/config.rs index 5a7b26b..c2807e4 100644 --- a/kifu/core/src/config.rs +++ b/kifu/core/src/config.rs @@ -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 { @@ -137,3 +145,54 @@ impl From<&Config> for Option { }) } } + +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 = 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()) + ); + } +} diff --git a/kifu/core/src/database.rs b/kifu/core/src/database.rs index d83c3a2..6797e29 100644 --- a/kifu/core/src/database.rs +++ b/kifu/core/src/database.rs @@ -17,6 +17,7 @@ impl From for Error { } } +#[derive(Debug)] pub struct Database { path: PathBuf, games: Vec, @@ -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 { self.games.iter() } diff --git a/kifu/core/src/types.rs b/kifu/core/src/types.rs index 1b3ce32..7ee8eba 100644 --- a/kifu/core/src/types.rs +++ b/kifu/core/src/types.rs @@ -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, + 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 for String { } } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Player { pub name: String, pub rank: Option, diff --git a/kifu/gtk/Makefile b/kifu/gtk/Makefile index be968cd..3692242 100644 --- a/kifu/gtk/Makefile +++ b/kifu/gtk/Makefile @@ -2,6 +2,7 @@ release: cargo build --release dev: + export CONFIG=. cargo watch -x 'run --bin kifu-gtk' screenplay: diff --git a/kifu/gtk/config b/kifu/gtk/config new file mode 100644 index 0000000..6ad4a86 --- /dev/null +++ b/kifu/gtk/config @@ -0,0 +1 @@ +{"Me":{"name":"Savanni","rank":{"Kyu":10}},"DatabasePath":"../core/fixtures/five_games"} diff --git a/kifu/gtk/src/main.rs b/kifu/gtk/src/main.rs index c29f15c..95cda24 100644 --- a/kifu/gtk/src/main.rs +++ b/kifu/gtk/src/main.rs @@ -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);