use std::{fs, io};
use serde::{Deserialize, Serialize};
use toml_edit::{DocumentMut, Item, Table};
use crate::config_dir;
pub mod common;
#[derive(Debug, thiserror::Error)]
pub enum ConfigError {
#[error("IoError: {0}")]
Io(#[from] std::io::Error),
#[error("No home directory was found for the current user")]
NoHomeDir,
#[error("TomlError: {0}")]
TomlEdit(#[from] toml_edit::TomlError),
#[error("TomlError: {0}")]
Toml(#[from] toml::de::Error),
}
pub trait Config: Sized + Serialize + for<'de> Deserialize<'de> + Default {
const CONFIG_FILE_NAME: &'static str;
fn load() -> Result<Self, ConfigError> {
let mut file = config_dir().join(Self::CONFIG_FILE_NAME);
file.add_extension("toml");
let config = match std::fs::read_to_string(&file) {
Ok(toml) => toml::from_str(&toml)?,
Err(err) if matches![err.kind(), io::ErrorKind::NotFound] => {
let config = Self::default();
config.save()?;
config
}
Err(err) => return Err(err.into()),
};
Ok(config)
}
fn save(&self) -> Result<(), ConfigError> {
let config_dir = config_dir();
let mut file = config_dir.join(Self::CONFIG_FILE_NAME);
file.add_extension("toml");
let config_doc = toml_edit::ser::to_document(self).unwrap();
let mut disk_doc: DocumentMut = if file.exists() {
fs::read_to_string(&file)?.parse()?
} else {
DocumentMut::new()
};
write_needed_config_values(disk_doc.as_table_mut(), config_doc.as_table());
fs::create_dir_all(config_dir)?;
fs::write(file, disk_doc.to_string())?;
Ok(())
}
}
fn write_needed_config_values(disk: &mut Table, current_table: &Table) {
for (key, value) in current_table {
match value {
Item::Table(table) => {
let disk_table = disk
.entry(key)
.or_insert(Item::Table(Table::new()))
.as_table_mut()
.unwrap();
write_needed_config_values(disk_table, table);
}
_ => {
disk.insert(key, value.clone());
}
}
}
}