Set up SGF reading and start on the game database #47
|
@ -71,7 +71,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "emseries"
|
||||
version = "0.5.1"
|
||||
version = "0.6.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"chrono-tz",
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -2,6 +2,7 @@ release:
|
|||
cargo build --release
|
||||
|
||||
dev:
|
||||
export CONFIG=.
|
||||
cargo watch -x 'run --bin kifu-gtk'
|
||||
|
||||
screenplay:
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
{"Me":{"name":"Savanni","rank":{"Kyu":10}},"DatabasePath":"../core/fixtures/five_games"}
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue