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, }) } }