From a8d1be9536bce6d6be2cf1586c8bac049e820d31 Mon Sep 17 00:00:00 2001 From: fxqnlr Date: Sun, 8 Sep 2024 17:21:27 +0200 Subject: save files, real last modified check (doesn't work correctly) --- src/pathinfo.rs | 383 +++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 268 insertions(+), 115 deletions(-) (limited to 'src/pathinfo.rs') diff --git a/src/pathinfo.rs b/src/pathinfo.rs index be43b6e..641e7ef 100644 --- a/src/pathinfo.rs +++ b/src/pathinfo.rs @@ -1,31 +1,32 @@ use std::{ fmt::Display, - path::PathBuf, + fs::{create_dir_all, File}, + io::Read, + path::{Path, PathBuf}, }; use serde::{Deserialize, Serialize}; use crate::{ - backup::BackupId, + backup::{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 + last_modified: Option, + pub 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)?) + Self::handle_dir(config, &locations.0, &locations.1) } fn handle_dir( @@ -36,54 +37,129 @@ impl PathInfo { 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 last_modified = None; 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 root = format!("{location_root}/"); 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; + if handle.last_modified.is_some() { + // FIX: Check if new last modified is newer than old one + last_modified = handle.last_modified.clone(); }; children.push(handle); } Self { - modified, is_file: false, rel_location: rel_location.to_string(), location_root: location_root.clone(), - last_modified: "".to_string(), - children + last_modified, + children, } } else { - Self::from_file(rel_location, location_root.clone())? + Self::from_file(config, rel_location, location_root)? }) } - fn from_file(rel_location: &str, location_root: LocationRoot) -> Result { - println!("From file {rel_location}"); + fn from_file( + config: &Config, + rel_location: &str, + location_root: &LocationRoot, + ) -> Result { + let last_modified = Self::compare_to_last_modified(config, location_root, rel_location)?; - let modified = false; + println!("From file {rel_location} ({:?})", last_modified); Ok(Self { rel_location: rel_location.to_string(), - location_root, - modified, - last_modified: "".to_string(), + location_root: location_root.clone(), + last_modified, is_file: true, - children: Vec::new() + children: Vec::new(), }) } + pub fn compare_to_last_modified( + config: &Config, + location_root: &LocationRoot, + rel_location: &str, + ) -> Result> { + let Some(last_backup) = Backup::get_last(config)? else { + // First Backup + return Ok(None); + }; + + + let files = last_backup.files.clone(); + let last_file_opt = files.iter().find(|file| file.rel_location == rel_location && file.location_root == *location_root); + + let Some(last_file) = last_file_opt else { + // File didn't exist last Backup + println!("File didn't exist last Backup"); + return Ok(None); + }; + + let modified_backup = if let Some(modified_backup_id) = last_file.last_modified.clone() { + Backup::from_index(config, modified_backup_id)? + } else { + last_backup + }; + + let old_path = modified_backup.get_absolute_file_location(config, &last_file.rel_location); + let new_path = format!("{location_root}/{rel_location}"); + + let mut old = File::open(old_path)?; + let mut new = File::open(new_path)?; + + let old_len = old.metadata()?.len(); + let new_len = new.metadata()?.len(); + if old_len != new_len { + return Ok(None); + } + + let mut old_content = String::new(); + old.read_to_string(&mut old_content)?; + let mut new_content = String::new(); + new.read_to_string(&mut new_content)?; + if old_content != new_content { + return Ok(None); + } + + Ok(Some(modified_backup.id.clone())) + } + pub fn get_absolute_path(&self) -> PathBuf { Self::get_abs_path(&self.location_root.to_string(), &self.rel_location) } + pub fn save(&self, backup_root: &str) -> Result<()> { + if self.last_modified.is_some() { + return Ok(()); + } + println!("Save File {:?}", self.rel_location); + if !self.is_file { + for child in &self.children { + child.save(backup_root)?; + } + } else { + let new_path = format!("{}/{}", backup_root, self.rel_location); + // println!("New Path: {new_path}"); + // println!("Old Path: {:?}", self.get_absolute_path()); + let np = Path::new(&new_path); + if let Some(parent) = np.parent() { + create_dir_all(parent)?; + } + std::fs::copy(self.get_absolute_path(), new_path)?; + }; + + Ok(()) + } + fn get_abs_path(location_root: &str, rel_location: &str) -> PathBuf { let path = format!("{}/{}", location_root, rel_location); PathBuf::from(path) @@ -150,97 +226,174 @@ impl LocationRoot { } } -// #[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(()) -// } -// } +#[cfg(test)] +mod tests { + use std::{ + fs::{create_dir_all, remove_dir_all, File}, + io::Write, + }; + + use crate::{ + backup::Backup, + config::Config, + error::{Error, Result}, + packages::{pacman::Pacman, PackageManager}, + }; + + use super::LocationRoot; + use super::PathInfo; + + #[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_ok: Vec<(&str, LocationRoot)> = Vec::new(); + values_ok.push(("u:test", LocationRoot::User("test".to_string()))); + values_ok.push(("s:", LocationRoot::SystemSettings)); + values_ok.push(("r:", LocationRoot::Root)); + values_ok.push(( + "c:test", + LocationRoot::Custom("/usr/local/test".to_string()), + )); + + for value in values_ok { + println!("Testing {value:?}"); + assert_eq!(LocationRoot::from_op_str(value.0, &config)?, value.1); + println!("\x1B[FTesting {value:?} ✓"); + } + + let mut values_err: Vec<(&str, String)> = Vec::new(); + values_err.push(( + "c:rest", + Error::CustomDirectory("rest".to_string()).to_string(), + )); + values_err.push(("t:test/", Error::InvalidIndex("t".to_string()).to_string())); + values_err.push(( + "test:test/usr", + Error::InvalidIndex("test".to_string()).to_string(), + )); + values_err.push(("/usr/local/test", Error::NoIndex.to_string())); + values_err.push(("c/usr/local/test", Error::NoIndex.to_string())); + + for value in values_err { + println!("Testing {value:?}"); + assert_eq!( + LocationRoot::from_op_str(value.0, &config) + .err() + .unwrap() + .to_string(), + value.1 + ); + println!("\x1B[FTesting {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_ok: Vec<(&str, (String, LocationRoot))> = Vec::new(); + values_ok.push(( + "~/.config/nvim", + ( + ".config/nvim".to_string(), + LocationRoot::User("test".to_string()), + ), + )); + values_ok.push(( + "u:test/.config/nvim", + ( + ".config/nvim".to_string(), + LocationRoot::User("test".to_string()), + ), + )); + values_ok.push(( + "r:/.config/nvim", + (".config/nvim".to_string(), LocationRoot::Root), + )); + values_ok.push(( + "r:/.config/nvim", + (".config/nvim".to_string(), LocationRoot::Root), + )); + values_ok.push(( + "s:/.config/nvim", + (".config/nvim".to_string(), LocationRoot::SystemSettings), + )); + values_ok.push(( + "c:test/.config/nvim", + ( + ".config/nvim".to_string(), + LocationRoot::Custom("/usr/local/test".to_string()), + ), + )); + + for value in values_ok { + print!("Testing {value:?}"); + assert_eq!(PathInfo::parse_location(&value.0, &config)?, value.1); + println!("\x1B[FTesting {value:?} ✓"); + } + Ok(()) + } + + #[test] + fn compare_to_last_modified() -> color_eyre::Result<()> { + let mut config = Config::default(); + config.root = "./backup-test".to_string(); + config + .directories + .push("u:fx/code/proj/fxbaup/backup-test-dir".to_string()); + + create_dir_all("./backup-test-dir")?; + let mut f = File::create("./backup-test-dir/size.txt")?; + f.write_all("unmodified".as_bytes())?; + let mut f = File::create("./backup-test-dir/content.txt")?; + f.write_all("unmodified".as_bytes())?; + let mut f = File::create("./backup-test-dir/nothing.txt")?; + f.write_all("unmodified".as_bytes())?; + + let pacman = Pacman; + let pkgs = pacman.get_installed()?; + let backup = Backup::create(&config, pkgs)?; + backup.save(&config)?; + + let mut f = File::create("./backup-test-dir/size.txt")?; + f.write_all("modified".as_bytes())?; + let mut f = File::create("./backup-test-dir/content.txt")?; + f.write_all("unmodefied".as_bytes())?; + + let pi = PathInfo::from_path(&config, "u:fx/code/proj/fxbaup/backup-test-dir")?; + + let last_backup = Backup::get_last(&config)?.unwrap(); + for file in pi.children { + println!("test rel: {}", file.rel_location); + let res = if file.rel_location == "code/proj/fxbaup/backup-test-dir/nothing.txt" { + Some(last_backup.id.clone()) + } else { + None + }; + println!("Testing {file:?}"); + assert_eq!( + PathInfo::compare_to_last_modified( + &config, + &file.location_root, + &file.rel_location + )?, + res + ); + println!("\x1B[FTesting {file:?} ✓"); + } + + remove_dir_all("./backup-test-dir")?; + remove_dir_all("./backup-test")?; + Ok(()) + } +} -- cgit v1.2.3