roezio: first config draft; missing write
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
This commit is contained in:
parent
5dcccad90b
commit
0b5ed75e90
4 changed files with 184 additions and 0 deletions
|
@ -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"]
|
||||
|
|
177
src/config.rs
Normal file
177
src/config.rs
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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<S: Into<String>>(_val: S) -> Result<ConfigValue, Error> {
|
||||
Ok(ConfigValue::Bool(true))
|
||||
}
|
||||
|
||||
pub(crate) fn to_uint<S: Into<String>>(_val: S) -> Result<ConfigValue, Error> {
|
||||
Ok(ConfigValue::UInt(0u64))
|
||||
}
|
||||
|
||||
pub(crate) fn to_int<S: Into<String>>(_val: S) -> Result<ConfigValue, Error> {
|
||||
Ok(ConfigValue::Int(0i64))
|
||||
}
|
||||
|
||||
pub(crate) fn to_string<S: Into<String>>(_val: S) -> Result<ConfigValue, Error> {
|
||||
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<HashMap<&str, HashMap<&str, ConfigValue>>> =
|
||||
LazyCell::new(|| HashMap::new());
|
||||
|
||||
pub(crate) struct Config<'a> {
|
||||
filename: PathBuf,
|
||||
defaults: LazyCell<HashMap<&'a str, HashMap<&'a str, ConfigValue>>>,
|
||||
ini: Ini,
|
||||
}
|
||||
|
||||
impl Config<'static> {
|
||||
/// Create a new Config object
|
||||
pub(crate) fn new<P: Into<PathBuf>>(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<Jid>,
|
||||
) -> 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<Jid>,
|
||||
) -> Result<Option<ConfigValue>, 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<Jid>) -> Result<Option<bool>, 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<Jid>) -> Result<Option<u64>, 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<Jid>) -> Result<Option<i64>, 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<Jid>,
|
||||
) -> Result<Option<String>, Error> {
|
||||
match self.get(key, section)? {
|
||||
Some(ConfigValue::String(v)) => Ok(Some(v)),
|
||||
Some(v) => Err(Error::InvalidConfigValueType(v)),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,6 +13,8 @@
|
|||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#![feature(once_cell)]
|
||||
|
||||
mod args;
|
||||
mod config;
|
||||
mod error;
|
||||
mod logger;
|
||||
mod theming;
|
||||
|
|
Loading…
Reference in a new issue