use std::path::{Path, PathBuf};
use std::{fs, io};
use serde::{Deserialize, Serialize};
pub trait BaseConfig: Sized + Default + Serialize + for<'a> Deserialize<'a> {
const PACKAGE: &'static str;
fn path() -> Option<PathBuf> {
dirs::config_dir()
.map(|p| p.join(Self::PACKAGE))
.map(|p| p.join("config.toml"))
}
fn load() -> io::Result<Self> {
Self::path()
.ok_or_else(|| {
io::Error::new(io::ErrorKind::Other, "unable to define configuration path")
})
.and_then(Self::load_path)
}
fn load_path<P>(path: P) -> io::Result<Self>
where
P: AsRef<Path>,
{
let path = path.as_ref();
if !path.exists() {
let config = Self::default();
path.parent()
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::Other,
"unable to fetch parent dir of config file",
)
})
.and_then(fs::create_dir_all)
.and_then(|_| {
toml::to_string_pretty(&config)
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))
})
.and_then(|contents| fs::write(path, contents))
.unwrap_or_else(|e| eprintln!("failed to serialize config file: {}", e));
return Ok(config);
}
let contents = fs::read_to_string(path)?;
toml::from_str(&contents).map_err(|e| io::Error::new(io::ErrorKind::Other, e))
}
}