Set up SGF reading and start on the game database #47
|
@ -71,7 +71,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "emseries"
|
name = "emseries"
|
||||||
version = "0.5.1"
|
version = "0.6.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"chrono-tz",
|
"chrono-tz",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
types::{AppState, GameState, Player, Rank},
|
types::{AppState, GameState, Player, Rank},
|
||||||
ui::{home, playing_field, HomeView, PlayingFieldView},
|
ui::{home, playing_field, HomeView, PlayingFieldView},
|
||||||
Config,
|
Config, DatabasePath,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
@ -71,10 +71,14 @@ pub struct CoreApp {
|
||||||
|
|
||||||
impl CoreApp {
|
impl CoreApp {
|
||||||
pub fn new(config_path: std::path::PathBuf) -> Self {
|
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 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!("config: {:?}", config);
|
||||||
|
println!("games database: {:?}", state.read().unwrap().database.len());
|
||||||
|
|
||||||
Self { config, state }
|
Self { config, state }
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@ enum OptionNames {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
pub enum ConfigOption {
|
pub enum ConfigOption {
|
||||||
DatabasePath(DatabasePath),
|
DatabasePath(DatabasePath),
|
||||||
Me(Me),
|
Me(Me),
|
||||||
|
@ -96,14 +97,14 @@ impl Config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set(&mut self, val: ConfigOption) {
|
pub fn set(&mut self, val: ConfigOption) {
|
||||||
let _ = match val {
|
let _ = match val {
|
||||||
ConfigOption::DatabasePath(_) => self.values.insert(OptionNames::DatabasePath, val),
|
ConfigOption::DatabasePath(_) => self.values.insert(OptionNames::DatabasePath, val),
|
||||||
ConfigOption::Me(_) => self.values.insert(OptionNames::Me, 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
|
where
|
||||||
T: From<&'a Self>,
|
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);
|
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 {
|
impl From<&Config> for DatabasePath {
|
||||||
fn from(config: &Config) -> Self {
|
fn from(config: &Config) -> Self {
|
||||||
match config.values.get(&OptionNames::DatabasePath) {
|
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);
|
pub struct Me(Player);
|
||||||
|
|
||||||
impl From<&Config> for Option<Me> {
|
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 {
|
pub struct Database {
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
games: Vec<GameTree>,
|
games: Vec<GameTree>,
|
||||||
|
@ -49,6 +50,10 @@ impl Database {
|
||||||
Ok(Database { path, games })
|
Ok(Database { path, games })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.games.len()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn all_games(&self) -> impl Iterator<Item = &GameTree> {
|
pub fn all_games(&self) -> impl Iterator<Item = &GameTree> {
|
||||||
self.games.iter()
|
self.games.iter()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
api::PlayStoneRequest,
|
api::PlayStoneRequest,
|
||||||
board::{Board, Coordinate},
|
board::{Board, Coordinate},
|
||||||
|
config::DatabasePath,
|
||||||
|
database::Database,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::time::Duration;
|
use std::{path::PathBuf, time::Duration};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use typeshare::typeshare;
|
use typeshare::typeshare;
|
||||||
|
|
||||||
|
@ -43,12 +45,14 @@ impl Default for Size {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
pub game: Option<GameState>,
|
pub game: Option<GameState>,
|
||||||
|
pub database: Database,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppState {
|
impl AppState {
|
||||||
pub fn new() -> Self {
|
pub fn new(database_path: DatabasePath) -> Self {
|
||||||
Self {
|
Self {
|
||||||
game: Some(GameState::new()),
|
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 struct Player {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub rank: Option<Rank>,
|
pub rank: Option<Rank>,
|
||||||
|
|
|
@ -2,6 +2,7 @@ release:
|
||||||
cargo build --release
|
cargo build --release
|
||||||
|
|
||||||
dev:
|
dev:
|
||||||
|
export CONFIG=.
|
||||||
cargo watch -x 'run --bin kifu-gtk'
|
cargo watch -x 'run --bin kifu-gtk'
|
||||||
|
|
||||||
screenplay:
|
screenplay:
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
{"Me":{"name":"Savanni","rank":{"Kyu":10}},"DatabasePath":"../core/fixtures/five_games"}
|
|
@ -44,10 +44,17 @@ fn main() {
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let user_home = std::env::var("HOME").expect("the user's home directory isn't set");
|
let config_path = std::env::var("CONFIG")
|
||||||
let mut config_path = std::path::PathBuf::from(user_home);
|
.and_then(|config| Ok(std::path::PathBuf::from(config)))
|
||||||
config_path.push(".config");
|
.or({
|
||||||
config_path.push("kifu");
|
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);
|
let core = CoreApp::new(config_path);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue