use std::{
collections::HashMap,
path::{Path, PathBuf},
};
#[derive(Debug, Default, serde::Serialize, serde::Deserialize)]
pub(crate) struct Config {
pub(crate) root: PathBuf,
pub(crate) dots_dir: PathBuf,
pub(crate) backup_dir: PathBuf,
pub(crate) remote: String,
pub(crate) symlink: bool,
pub(crate) sources: Vec<PathBuf>,
pub(crate) git_repos: Vec<GitRepo>,
pub(crate) systemd_services: Vec<String>,
}
#[derive(Debug, Default, serde::Serialize, serde::Deserialize)]
pub(crate) struct GitRepo {
pub(crate) id: String,
pub(crate) remote: String,
pub(crate) path: PathBuf,
}
#[derive(Debug, thiserror::Error)]
pub(crate) enum ConfigError {
#[error("`{0}`: Path does not exist")]
PathResolutionError(String),
#[error(transparent)]
IoError(#[from] std::io::Error),
#[error(transparent)]
SerdeError(#[from] serde_yaml::Error),
}
pub(crate) fn read(path: &Path) -> Result<Config, ConfigError> {
let path = match path.resolve_path() {
Ok(pb) => pb,
Err(err) => return Err(err),
};
let file = match std::fs::read_to_string(path) {
Ok(file) => file,
Err(err) => return Err(ConfigError::IoError(err)),
};
match serde_yaml::from_str(&file) {
Ok(config) => Ok(config),
Err(err) => Err(ConfigError::SerdeError(err)),
}
}
pub(crate) trait ResolvePath {
fn resolve_path(&self) -> Result<PathBuf, ConfigError>;
}
impl ResolvePath for Path {
fn resolve_path(&self) -> Result<PathBuf, ConfigError> {
self.to_path_buf().resolve_path()
}
}
impl ResolvePath for PathBuf {
fn resolve_path(&self) -> Result<PathBuf, ConfigError> {
if !self.as_path().exists() {
match Path::new(env!("HOME")).join(self.as_path()).canonicalize() {
Ok(pb) => return Ok(pb),
Err(_) => {
return Err(ConfigError::PathResolutionError(
self.to_string_lossy().to_string(),
))
}
}
}
Ok(self.to_path_buf())
}
}
impl Config {
pub(crate) fn resolve_paths(mut self) -> Config {
match self.root.resolve_path() {
Ok(path) => {
log::debug!("{} resolved", self.root.to_string_lossy());
self.root = path;
}
Err(err) => log::error!("{}: {}", self.root.to_string_lossy(), err),
}
self.dots_dir = self.root.join(self.dots_dir);
match self.dots_dir.resolve_path() {
Ok(path) => {
log::debug!("{} resolved", self.dots_dir.to_string_lossy());
self.dots_dir = path;
}
Err(err) => log::error!("{}: {}", self.dots_dir.to_string_lossy(), err),
}
self.backup_dir = self.root.join(self.backup_dir);
match self.backup_dir.resolve_path() {
Ok(path) => {
log::debug!("{} resolved", self.backup_dir.to_string_lossy());
self.backup_dir = path;
}
Err(err) => log::error!("{}: {}", self.backup_dir.to_string_lossy(), err),
}
self.sources = self
.sources
.into_iter()
.map(|s| match s.resolve_path() {
Ok(path) => {
log::debug!("{} resolved", path.to_string_lossy());
path
}
Err(err) => {
log::error!("{}: {}", s.to_string_lossy(), err);
Path::new("").to_path_buf()
}
})
.filter(|s| s.ne(Path::new("")))
.collect();
self.git_repos = self
.git_repos
.into_iter()
.map(|mut gr| match gr.path.resolve_path() {
Ok(path) => {
log::debug!("{} resolved", path.to_string_lossy());
gr.path = path;
gr
}
Err(err) => {
log::error!("{}: {}", gr.path.to_string_lossy(), err);
gr.path = Path::new("").to_path_buf();
gr
}
})
.filter(|gr| gr.path.ne(Path::new("")))
.collect();
self
}
pub(crate) fn get_dst_from_src(
&self,
src: &Path,
) -> Result<PathBuf, std::path::StripPrefixError> {
Ok(self.dots_dir.join(src.strip_prefix("/")?))
}
pub(crate) fn get_sources_as_hashmap(&self) -> HashMap<&str, PathBuf> {
let mut all_parts: Vec<&str> = Vec::new();
for pb in &self.sources {
let _: Vec<&str> = pb
.to_str()
.unwrap_or("")
.split('/')
.map(|part| {
all_parts.push(part);
part
})
.collect();
}
all_parts.sort();
all_parts.dedup_by(|a, b| a.eq(&b));
let mut hash_map: HashMap<&str, PathBuf> = HashMap::new();
for part in &all_parts {
if *part != "" {
hash_map.insert(
part,
self.sources
.iter()
.find(|s| s.to_str().unwrap_or("").contains(part))
.unwrap()
.to_path_buf(),
);
}
}
log::debug!("Sources as HashMap: {:#?}", &hash_map);
hash_map
}
}