use std::error::Error;
use std::io::prelude::*;
use std::str::FromStr;
use std::{collections::HashMap, fmt::Debug, fs::File};
#[derive(Clone)]
pub struct Configuration(HashMap<String, String>);
impl Configuration {
pub fn get(&self, key: &String) -> Option<&String> {
self.0.get(key)
}
pub fn get_parse<T, E>(&self, key: &String) -> Option<Result<T, E>>
where
T: FromStr<Err = E>,
E: Error,
{
match self.0.get(key)?.parse::<T>() {
Ok(v) => Some(Ok(v)),
Err(e) => Some(Err(e)),
}
}
pub fn set(&mut self, key: String, value: String) -> Option<String> {
self.0.insert(key, value)
}
}
pub fn parse_config(config: &str) -> Configuration {
Configuration(
config
.lines()
.filter(|line| !line.starts_with('#'))
.filter_map(|line| line.split_once('='))
.map(|(key, value)| (String::from(key.trim()), String::from(value.trim())))
.collect(),
)
}
pub fn parse_config_file(f: &mut File) -> Result<Configuration, std::io::Error> {
let mut data = "".to_owned();
f.read_to_string(&mut data)?;
Ok(parse_config(&data))
}
pub fn open_app_config(name: &str) -> Option<Result<Configuration, std::io::Error>> {
let cfg_file = dirs::config_dir()?.join(name).join("config");
let mut file = match File::open(cfg_file) {
Ok(f) => f,
Err(e) => return Some(Err(e)),
};
Some(parse_config_file(&mut file))
}
impl From<Configuration> for HashMap<String, String> {
fn from(value: Configuration) -> Self {
value.0
}
}
impl From<&str> for Configuration {
fn from(value: &str) -> Self {
parse_config(value)
}
}
impl TryFrom<&mut File> for Configuration {
type Error = std::io::Error;
fn try_from(value: &mut File) -> Result<Self, Self::Error> {
parse_config_file(value)
}
}
impl Debug for Configuration {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_map().entries(self.0.iter()).finish()?;
Ok(())
}
}