From d9531c69ad726992e72c0aaaefaaa48f5b789585 Mon Sep 17 00:00:00 2001 From: savanni Date: Thu, 20 Jul 2023 03:57:19 +0000 Subject: [PATCH] Create a configuration system for the Kifu (#46) This is a nice, typesafe configuration system. Co-authored-by: Savanni D'Gerinel Reviewed-on: https://git.luminescent-dreams.com/savanni/tools/pulls/46 --- kifu/core/Cargo.lock | 1 + kifu/core/Cargo.toml | 1 + kifu/core/src/api.rs | 7 +- kifu/core/src/config.rs | 139 ++++++++++++++++++++++++++++++++++++++++ kifu/core/src/lib.rs | 3 + kifu/core/src/types.rs | 2 +- kifu/gtk/Cargo.lock | 1 + kifu/gtk/src/main.rs | 7 +- 8 files changed, 157 insertions(+), 4 deletions(-) create mode 100644 kifu/core/src/config.rs diff --git a/kifu/core/Cargo.lock b/kifu/core/Cargo.lock index 75c0dcb..91de0a2 100644 --- a/kifu/core/Cargo.lock +++ b/kifu/core/Cargo.lock @@ -163,6 +163,7 @@ version = "0.1.0" dependencies = [ "grid", "serde", + "serde_json", "thiserror", "typeshare", ] diff --git a/kifu/core/Cargo.toml b/kifu/core/Cargo.toml index 12b2b0d..e78c7bc 100644 --- a/kifu/core/Cargo.toml +++ b/kifu/core/Cargo.toml @@ -8,5 +8,6 @@ edition = "2021" [dependencies] grid = { version = "0.9" } serde = { version = "1", features = [ "derive" ] } +serde_json = { version = "1" } thiserror = { version = "1" } typeshare = { version = "1" } diff --git a/kifu/core/src/api.rs b/kifu/core/src/api.rs index 7e4e0d6..6777816 100644 --- a/kifu/core/src/api.rs +++ b/kifu/core/src/api.rs @@ -1,6 +1,7 @@ use crate::{ types::{AppState, GameState, Player, Rank}, ui::{new_game, playing_field, NewGameView, PlayingFieldView}, + Config, }; use serde::{Deserialize, Serialize}; use std::sync::{Arc, RwLock}; @@ -64,14 +65,16 @@ pub enum CoreResponse { #[derive(Clone, Debug)] pub struct CoreApp { + config: Config, state: Arc>, } impl CoreApp { - pub fn new() -> Self { + pub fn new(config_path: std::path::PathBuf) -> Self { + let config = Config::from_path(config_path).expect("configuration to open"); let state = Arc::new(RwLock::new(AppState::new())); - Self { state } + Self { config, state } } pub async fn dispatch(&self, request: CoreRequest) -> CoreResponse { diff --git a/kifu/core/src/config.rs b/kifu/core/src/config.rs new file mode 100644 index 0000000..5a7b26b --- /dev/null +++ b/kifu/core/src/config.rs @@ -0,0 +1,139 @@ +use crate::types::Player; +use serde::{Deserialize, Serialize}; +use std::{ + collections::HashMap, + fs::File, + io::{ErrorKind, Read}, + path::PathBuf, +}; +use thiserror::Error; + +/* +pub trait ConfigOption { + type Value; +} + +pub struct DatabasePath(PathBuf); + +impl ConfigOption for DatabasePath { + type Value = PathBuf; +} + +impl ConfigOption for Player { + type Value = Player; +} + +pub trait Config { + // fn set_option(option: ConfigOption); + fn get_option(name: Name) -> C +} +*/ + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +enum OptionNames { + DatabasePath, + Me, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum ConfigOption { + DatabasePath(DatabasePath), + Me(Me), +} + +#[derive(Debug, Error)] +pub enum ConfigReadError { + #[error("Cannot read the configuration file: {0}")] + CannotRead(std::io::Error), + #[error("Cannot open the configuration file for reading: {0}")] + CannotOpen(std::io::Error), + #[error("Invalid json data found in the configurationfile: {0}")] + InvalidJSON(serde_json::Error), +} + +#[derive(Clone, Debug)] +pub struct Config { + config_path: PathBuf, + values: HashMap, +} + +impl Config { + pub fn new(config_path: PathBuf) -> Self { + Self { + config_path, + values: HashMap::new(), + } + } + + pub fn from_path(config_path: PathBuf) -> Result { + let mut settings = config_path.clone(); + settings.push("config"); + + match File::open(settings) { + Ok(mut file) => { + let mut buf = String::new(); + file.read_to_string(&mut buf) + .map_err(|err| ConfigReadError::CannotRead(err))?; + let values = serde_json::from_str(buf.as_ref()) + .map_err(|err| ConfigReadError::InvalidJSON(err))?; + Ok(Self { + config_path, + values, + }) + } + Err(io_err) => { + match io_err.kind() { + ErrorKind::NotFound => { + /* create the path and an empty file */ + Ok(Self { + config_path, + values: HashMap::new(), + }) + } + _ => Err(ConfigReadError::CannotOpen(io_err)), + } + } + } + } + + 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 + where + T: From<&'a Self>, + { + self.into() + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct DatabasePath(PathBuf); + +impl From<&Config> for DatabasePath { + fn from(config: &Config) -> Self { + match config.values.get(&OptionNames::DatabasePath) { + Some(ConfigOption::DatabasePath(path)) => path.clone(), + _ => DatabasePath(config.config_path.clone()), + } + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Me(Player); + +impl From<&Config> for Option { + fn from(config: &Config) -> Self { + config + .values + .get(&OptionNames::Me) + .and_then(|val| match val { + ConfigOption::Me(me) => Some(me.clone()), + _ => None, + }) + } +} diff --git a/kifu/core/src/lib.rs b/kifu/core/src/lib.rs index fe0d321..b5b56da 100644 --- a/kifu/core/src/lib.rs +++ b/kifu/core/src/lib.rs @@ -9,3 +9,6 @@ pub mod ui; mod board; pub use board::*; + +mod config; +pub use config::*; diff --git a/kifu/core/src/types.rs b/kifu/core/src/types.rs index 3249ea4..1b3ce32 100644 --- a/kifu/core/src/types.rs +++ b/kifu/core/src/types.rs @@ -91,7 +91,7 @@ impl From for String { } } -#[derive(Debug)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct Player { pub name: String, pub rank: Option, diff --git a/kifu/gtk/Cargo.lock b/kifu/gtk/Cargo.lock index 41fd545..660eee1 100644 --- a/kifu/gtk/Cargo.lock +++ b/kifu/gtk/Cargo.lock @@ -787,6 +787,7 @@ version = "0.1.0" dependencies = [ "grid", "serde", + "serde_json", "thiserror", "typeshare", ] diff --git a/kifu/gtk/src/main.rs b/kifu/gtk/src/main.rs index 3e34e3e..3570cf7 100644 --- a/kifu/gtk/src/main.rs +++ b/kifu/gtk/src/main.rs @@ -44,7 +44,12 @@ fn main() { .unwrap(), ); - let core = CoreApp::new(); + 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 core = CoreApp::new(config_path); let core_handle = runtime.spawn({ let core = core.clone();