diff --git a/Cargo.toml b/Cargo.toml index 6c28926d..b4ce60df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,8 @@ lazy_static = "1" enum-set = "0.0" clap = { version = "3.2.17", features = ["derive"] } directories = "4.0.1" +configparser = "3.0.1" +jid = "0.9.4" [lib] crate-type = ["cdylib"] diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 00000000..feaf51ca --- /dev/null +++ b/src/config.rs @@ -0,0 +1,177 @@ +// Copyright (C) 2018-2099 The crate authors. +// +// This program is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::error::Error; + +use std::cell::LazyCell; +use std::collections::HashMap; +use std::fmt; +use std::path::PathBuf; + +use configparser::ini::Ini; +use jid::Jid; + +#[derive(Clone, Debug, Hash, Eq, PartialEq)] +pub(crate) enum ConfigValue { + Bool(bool), + UInt(u64), + Int(i64), + String(String), +} + +impl ConfigValue { + pub(crate) fn to_bool>(_val: S) -> Result { + Ok(ConfigValue::Bool(true)) + } + + pub(crate) fn to_uint>(_val: S) -> Result { + Ok(ConfigValue::UInt(0u64)) + } + + pub(crate) fn to_int>(_val: S) -> Result { + Ok(ConfigValue::Int(0i64)) + } + + pub(crate) fn to_string>(_val: S) -> Result { + Ok(ConfigValue::String(String::new())) + } +} + +impl fmt::Display for ConfigValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self { + ConfigValue::Bool(b) => b.to_string(), + ConfigValue::UInt(i) => i.to_string(), + ConfigValue::Int(i) => i.to_string(), + ConfigValue::String(s) => s.to_string(), + } + ) + } +} + +const DEFAULT_CONFIG: LazyCell>> = + LazyCell::new(|| HashMap::new()); + +pub(crate) struct Config<'a> { + filename: PathBuf, + defaults: LazyCell>>, + ini: Ini, +} + +impl Config<'static> { + /// Create a new Config object + pub(crate) fn new>(filename: P) -> Self { + // TODO: read ini file. + + Config { + filename: filename.into(), + defaults: DEFAULT_CONFIG, + ini: Ini::new(), + } + } + + /// Sets a key/value pair in memory and updates the config file. + pub(crate) fn set( + &mut self, + key: &str, + value: ConfigValue, + section: Option, + ) -> Result<(), Error> { + let section: String = section + .map(|jid| jid.to_string()) + .unwrap_or(String::from("default")); + self.write(key.clone(), value.clone(), section.as_str())?; + let _ = self.ini.set(section.as_str(), key, Some(value.to_string())); + Ok(()) + } + + fn write(&self, _key: &str, _value: ConfigValue, _section: &str) -> Result<(), Error> { + // TODO: Copy the file and parse it manually. If the key exists, edit it in place during + // parsing. If it doesn't, add it at the end of the section. Replace the original file + // atomically. + Ok(()) + } + + /// Fetches a value + pub(crate) fn get( + &self, + key: &str, + section: Option, + ) -> Result, Error> { + let section: String = section + .map(|jid| jid.to_string()) + .unwrap_or(String::from("default")); + let value = self.ini.get(§ion, key); + let default = self + .defaults + .get(section.as_str()) + .map(|section| section.get(key)); + + Ok(match (default, value) { + // Default exists, value exists + (Some(Some(def)), Some(val)) => Some(match def { + ConfigValue::Bool(_) => ConfigValue::to_bool(val.clone())?, + ConfigValue::UInt(_) => ConfigValue::to_uint(val.clone())?, + ConfigValue::Int(_) => ConfigValue::to_int(val.clone())?, + ConfigValue::String(_) => ConfigValue::to_string(val.clone())?, + }), + // Default exists, value doesn't + (Some(Some(def)), _) => Some(def.clone()), + // Default doesn't exist, value does + // XXX: How do we know what type to return? + (_, Some(val)) => Some(ConfigValue::to_string(val)?), + _ => None, + }) + } + + pub(crate) fn getbool(&self, key: &str, section: Option) -> Result, Error> { + match self.get(key, section)? { + Some(ConfigValue::Bool(v)) => Ok(Some(v)), + Some(v) => Err(Error::InvalidConfigValueType(v)), + None => Ok(None), + } + } + + pub(crate) fn getuint(&self, key: &str, section: Option) -> Result, Error> { + match self.get(key, section)? { + Some(ConfigValue::UInt(v)) => Ok(Some(v)), + Some(v) => Err(Error::InvalidConfigValueType(v)), + None => Ok(None), + } + } + + pub(crate) fn getint(&self, key: &str, section: Option) -> Result, Error> { + match self.get(key, section)? { + Some(ConfigValue::Int(v)) => Ok(Some(v)), + Some(v) => Err(Error::InvalidConfigValueType(v)), + None => Ok(None), + } + } + + pub(crate) fn getstring( + &self, + key: &str, + section: Option, + ) -> Result, Error> { + match self.get(key, section)? { + Some(ConfigValue::String(v)) => Ok(Some(v)), + Some(v) => Err(Error::InvalidConfigValueType(v)), + None => Ok(None), + } + } +} diff --git a/src/error.rs b/src/error.rs index c1a0cb88..4d9028a3 100644 --- a/src/error.rs +++ b/src/error.rs @@ -13,6 +13,8 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +use crate::config::ConfigValue; + use std::error::Error as StdError; use std::fmt; use std::io; @@ -21,6 +23,7 @@ use std::io; pub(crate) enum Error { IOError(io::Error), UnableToCreateConfigDir, + InvalidConfigValueType(ConfigValue), } impl fmt::Display for Error { @@ -28,6 +31,7 @@ impl fmt::Display for Error { match self { Error::IOError(e) => write!(f, "io error: {}", e), Error::UnableToCreateConfigDir => write!(f, "Unable to create config dir"), + Error::InvalidConfigValueType(err) => write!(f, "Invalid ConfigValue type: {}", err), } } } diff --git a/src/lib.rs b/src/lib.rs index 72007500..91ed653d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ #![feature(once_cell)] mod args; +mod config; mod error; mod logger; mod theming;