use std::{
fmt::Display,
path::PathBuf,
};
use serde::{Deserialize, Serialize};
use crate::{
backup::BackupId,
config::Config,
error::{Error, Result},
};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PathInfo {
pub modified: bool,
pub is_file: bool,
rel_location: String,
location_root: LocationRoot,
last_modified: BackupId,
children: Vec<PathInfo>
}
impl PathInfo {
pub fn from_path(config: &Config, path: &str) -> Result<Self> {
let locations = Self::parse_location(path, config)?;
Ok(Self::handle_dir(config, &locations.0, &locations.1)?)
}
fn handle_dir(
config: &Config,
rel_location: &str,
location_root: &LocationRoot,
) -> Result<Self> {
println!("Handling {rel_location}");
let path = Self::get_abs_path(&location_root.to_string(), rel_location);
Ok(if path.is_dir() {
let mut modified = false;
let mut children: Vec<PathInfo> = Vec::new();
let paths = std::fs::read_dir(path).unwrap();
for path in paths {
let pathstr = path.unwrap().path().to_string_lossy().to_string();
let root = format!("{}/", location_root.to_string());
let Some(rl) = pathstr.split_once(&root) else {
panic!("HUH");
};
let handle = Self::handle_dir(config, rl.1, location_root)?;
if handle.modified {
modified = true;
};
children.push(handle);
}
Self {
modified,
is_file: false,
rel_location: rel_location.to_string(),
location_root: location_root.clone(),
last_modified: "".to_string(),
children
}
} else {
Self::from_file(rel_location, location_root.clone())?
})
}
fn from_file(rel_location: &str, location_root: LocationRoot) -> Result<Self> {
println!("From file {rel_location}");
let modified = false;
Ok(Self {
rel_location: rel_location.to_string(),
location_root,
modified,
last_modified: "".to_string(),
is_file: true,
children: Vec::new()
})
}
pub fn get_absolute_path(&self) -> PathBuf {
Self::get_abs_path(&self.location_root.to_string(), &self.rel_location)
}
fn get_abs_path(location_root: &str, rel_location: &str) -> PathBuf {
let path = format!("{}/{}", location_root, rel_location);
PathBuf::from(path)
}
fn parse_location(value: &str, config: &Config) -> Result<(String, LocationRoot)> {
let Some(split) = value.split_once('/') else {
return Err(Error::InvalidDirectory(value.to_string()));
};
if split.0.starts_with('~') {
if config.user.len() != 1 {
return Err(Error::MultiUser);
}
return Ok((
split.1.to_string(),
LocationRoot::User(config.user[0].clone()),
));
};
Ok((
split.1.to_string(),
LocationRoot::from_op_str(split.0, config)?,
))
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum LocationRoot {
User(String),
Custom(String),
SystemSettings,
Root,
}
impl Display for LocationRoot {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
LocationRoot::User(user) => write!(f, "/home/{user}"),
LocationRoot::Custom(loc) => write!(f, "{loc}"),
LocationRoot::SystemSettings => write!(f, "/etc"),
LocationRoot::Root => write!(f, "/"),
}
}
}
impl LocationRoot {
fn from_op_str(value: &str, config: &Config) -> Result<Self> {
let split_str = value.split_once(':');
let Some(split_op) = split_str else {
return Err(Error::NoIndex);
};
match split_op.0 {
"u" => Ok(Self::User(split_op.1.to_string())),
"s" => Ok(Self::SystemSettings),
"r" => Ok(Self::Root),
"c" => Ok(Self::Custom(
config
.custom_directories
.get(split_op.1)
.ok_or_else(|| Error::CustomDirectory(split_op.1.to_string()))?
.to_string(),
)),
_ => Err(Error::InvalidIndex(split_op.0.to_string())),
}
}
}
// #[cfg(test)]
// mod tests {
// use crate::{
// config::Config,
// error::{Error, Result},
// pathinfo::PathInfo,
// };
//
// use super::LocationRoot;
//
// #[test]
// fn from_op_str() -> Result<()> {
// let mut config = Config::default();
// config
// .custom_directories
// .insert("test".to_string(), "/usr/local/test".to_string());
//
// let mut values: Vec<(&str, Result<LocationRoot>)> = Vec::new();
// values.push(("u:test", Ok(LocationRoot::User("test".to_string()))));
// values.push(("s:", Ok(LocationRoot::SystemSettings)));
// values.push(("r:", Ok(LocationRoot::Root)));
// values.push((
// "c:test",
// Ok(LocationRoot::Custom("/usr/local/test".to_string())),
// ));
// values.push(("c:rest", Err(Error::CustomDirectory("rest".to_string()))));
// values.push(("t:test/", Err(Error::InvalidIndex("t".to_string()))));
// values.push((
// "test:test/usr",
// Err(Error::InvalidIndex("test".to_string())),
// ));
// values.push(("/usr/local/test", Err(Error::NoIndex)));
// values.push(("c/usr/local/test", Err(Error::NoIndex)));
//
// for value in values {
// print!("Testing {value:?}");
// assert_eq!(LocationRoot::from_op_str(value.0, &config), value.1);
// println!("\rTesting {value:?} ✓");
// }
//
// Ok(())
// }
//
// #[test]
// fn parse_location() -> Result<()> {
// let mut config = Config::default();
// config.user.push("test".to_string());
// config
// .custom_directories
// .insert("test".to_string(), "/usr/local/test".to_string());
//
// let mut values: Vec<(&str, Result<(String, LocationRoot)>)> = Vec::new();
// values.push((
// "~/.config/nvim",
// Ok((
// ".config/nvim".to_string(),
// LocationRoot::User("test".to_string()),
// )),
// ));
// values.push((
// "u:test/.config/nvim",
// Ok((
// ".config/nvim".to_string(),
// LocationRoot::User("test".to_string()),
// )),
// ));
// values.push((
// "r:/.config/nvim",
// Ok((".config/nvim".to_string(), LocationRoot::Root)),
// ));
// values.push((
// "r:/.config/nvim",
// Ok((".config/nvim".to_string(), LocationRoot::Root)),
// ));
// values.push((
// "s:/.config/nvim",
// Ok((".config/nvim".to_string(), LocationRoot::SystemSettings)),
// ));
// values.push((
// "c:test/.config/nvim",
// Ok((
// ".config/nvim".to_string(),
// LocationRoot::Custom("/usr/local/test".to_string()),
// )),
// ));
//
// for value in values {
// print!("Testing {value:?}");
// assert_eq!(PathInfo::parse_location(&value.0, &config), value.1);
// println!("\rTesting {value:?} ✓");
// }
// Ok(())
// }
// }