From 3e1cb020d5449849b37874f91cadfa4a9c878747 Mon Sep 17 00:00:00 2001 From: fxqnlr Date: Fri, 6 Sep 2024 10:56:30 +0200 Subject: initial commit, can save index, no modification check --- src/pathinfo.rs | 246 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 246 insertions(+) create mode 100644 src/pathinfo.rs (limited to 'src/pathinfo.rs') diff --git a/src/pathinfo.rs b/src/pathinfo.rs new file mode 100644 index 0000000..b0c3be4 --- /dev/null +++ b/src/pathinfo.rs @@ -0,0 +1,246 @@ +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 +} + +impl PathInfo { + pub fn from_path(config: &Config, path: &str) -> Result { + 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 { + 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 = 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 { + 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 { + 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)> = 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(()) + } +} -- cgit v1.2.3