summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorfxqnlr <[email protected]>2024-09-08 17:21:27 +0200
committerfxqnlr <[email protected]>2024-09-08 17:21:27 +0200
commita8d1be9536bce6d6be2cf1586c8bac049e820d31 (patch)
tree05c5c78c11f4506ba2eaa8a0751d3d896cfb81e9
parent695556c3441f5ffd40c35387a5b45e4459684c2c (diff)
downloadarbs-a8d1be9536bce6d6be2cf1586c8bac049e820d31.tar
arbs-a8d1be9536bce6d6be2cf1586c8bac049e820d31.tar.gz
arbs-a8d1be9536bce6d6be2cf1586c8bac049e820d31.zip
save files, real last modified check (doesn't work correctly)
-rw-r--r--.gitignore1
-rw-r--r--src/backup.rs126
-rw-r--r--src/config.rs6
-rw-r--r--src/error.rs7
-rw-r--r--src/main.rs15
-rw-r--r--src/packages.rs5
-rw-r--r--src/packages/pacman.rs14
-rw-r--r--src/packages/portage.rs13
-rw-r--r--src/pathinfo.rs383
9 files changed, 388 insertions, 182 deletions
diff --git a/.gitignore b/.gitignore
index ea8c4bf..e39d245 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
1/target 1/target
2/backup-test*
diff --git a/src/backup.rs b/src/backup.rs
index 8cc94f1..a643cb2 100644
--- a/src/backup.rs
+++ b/src/backup.rs
@@ -5,7 +5,6 @@ use std::{
5 time::{SystemTime, UNIX_EPOCH}, 5 time::{SystemTime, UNIX_EPOCH},
6}; 6};
7 7
8use gethostname::gethostname;
9use serde::{Deserialize, Serialize}; 8use serde::{Deserialize, Serialize};
10use uuid::Uuid; 9use uuid::Uuid;
11 10
@@ -20,10 +19,11 @@ pub type BackupId = String;
20 19
21#[derive(Debug, Serialize, Deserialize)] 20#[derive(Debug, Serialize, Deserialize)]
22pub struct Backup { 21pub struct Backup {
23 id: String, 22 pub id: String,
24 timestamp: u64, 23 timestamp: u64,
25 packages: Vec<Package>, 24 packages: Vec<Package>,
26 files: Vec<PathInfo>, 25 pub files: Vec<PathInfo>,
26 device: String,
27} 27}
28 28
29impl Backup { 29impl Backup {
@@ -33,63 +33,82 @@ impl Backup {
33 files.push(PathInfo::from_path(config, dir)?); 33 files.push(PathInfo::from_path(config, dir)?);
34 } 34 }
35 Ok(Self { 35 Ok(Self {
36 // UUID not really needed, maybe a shorter hash 36 // TODO: UUID not really needed, maybe a shorter hash
37 id: Uuid::new_v4().to_string(), 37 id: Uuid::new_v4().to_string(),
38 timestamp: Self::get_timestamp(), 38 timestamp: Self::get_timestamp(),
39 packages, 39 packages,
40 files, 40 files,
41 device: config.device.clone(),
41 }) 42 })
42 } 43 }
43 44
44 pub fn save(&self, config: &Config) -> Result<()> { 45 pub fn save(&self, config: &Config) -> Result<()> {
45 let rel_location = format!( 46 println!("Save Backup {:?}", self.get_location(config));
46 "{}_{}", 47 // println!("{self:#?}");
47 gethostname() 48 self.get_location(config).append_to_root(config)?;
48 .into_string()
49 .map_err(|_| Error::InvalidOsString)?,
50 Self::get_timestamp()
51 );
52
53 let bl = BackupLocation {
54 id: self.id.to_string(),
55 rel_location,
56 };
57
58 Self::append_to_root_index(config, bl.clone())?;
59 49
60 let backup_root = format!("{}/{}", config.root, bl.rel_location); 50 let backup_root = self.get_location(config).get_absolute_dir(config);
61 create_dir_all(&backup_root).unwrap(); 51 create_dir_all(&backup_root).unwrap();
62 let path = format!("{}/index.json", backup_root); 52 let path = format!("{}/index.json", backup_root);
63 let mut f = File::create(path).unwrap(); 53 let mut f = File::create(path).unwrap();
64 f.write_all(&serde_json::to_vec(self).unwrap()).unwrap(); 54 f.write_all(&serde_json::to_vec(self).unwrap()).unwrap();
65 55
56 for path in &self.files {
57 path.save(&backup_root)?;
58 }
59
66 Ok(()) 60 Ok(())
67 } 61 }
68 62
69 pub fn get_index(config: &Config, id: Option<BackupId>) -> Result<Self> { 63 pub fn get_last(config: &Config) -> Result<Option<Self>> {
70 let backup_index_root = format!("{}/index.json", config.root); 64 let backup_index_root = format!("{}/index.json", config.root);
71 let list: Vec<BackupLocation> = Self::get_json_content(&backup_index_root)?; 65 let list: Vec<BackupLocation> = match Self::get_json_content(&backup_index_root) {
72 println!("{list:#?}"); 66 Ok(list) => list,
73 67 Err(err) => {
74 let index_loc = if let Some(id) = id { 68 if err.to_string() == "io: No such file or directory (os error 2)" {
75 list.iter() 69 return Ok(None);
76 .find(|bl| bl.id == id) 70 };
77 .ok_or(Error::BackupNotFound)? 71 return Err(err);
78 .rel_location 72 }
79 .clone()
80 } else {
81 list.last()
82 .ok_or(Error::BackupNotFound)?
83 .rel_location
84 .clone()
85 }; 73 };
86 74
75 Ok(Some(Self::from_index(
76 config,
77 list.last().ok_or(Error::BackupNotFound)?.id.clone(),
78 )?))
79 }
80
81 pub fn from_index(config: &Config, id: BackupId) -> Result<Self> {
82 let backup_index_root = format!("{}/index.json", config.root);
83 let list: Vec<BackupLocation> = Self::get_json_content(&backup_index_root)?;
84 let index_loc = list
85 .iter()
86 .find(|bl| bl.id == id)
87 .ok_or(Error::BackupNotFound)?
88 .rel_location
89 .clone();
90
87 let path = format!("{}/{index_loc}/index.json", config.root); 91 let path = format!("{}/{index_loc}/index.json", config.root);
88 let index_file: Self = Self::get_json_content(&path)?; 92 let index_file: Self = Self::get_json_content(&path)?;
89 93
90 Ok(index_file) 94 Ok(index_file)
91 } 95 }
92 96
97 pub fn get_location(&self, config: &Config) -> BackupLocation {
98 let rel_location = format!("{}_{}", config.device, self.timestamp);
99
100 BackupLocation {
101 id: self.id.to_string(),
102 rel_location,
103 }
104 }
105
106 pub fn get_absolute_file_location(&self, config: &Config, rel_location: &str) -> String {
107 let loc = self.get_location(config).get_absolute_dir(config);
108
109 format!("{}/{}", loc, rel_location)
110 }
111
93 fn get_json_content<T: for<'a> Deserialize<'a>>(path: &str) -> Result<T> { 112 fn get_json_content<T: for<'a> Deserialize<'a>>(path: &str) -> Result<T> {
94 let mut file = File::open(path)?; 113 let mut file = File::open(path)?;
95 let mut content = String::new(); 114 let mut content = String::new();
@@ -97,7 +116,26 @@ impl Backup {
97 Ok(serde_json::from_str(&content)?) 116 Ok(serde_json::from_str(&content)?)
98 } 117 }
99 118
100 fn append_to_root_index(config: &Config, new_backup: BackupLocation) -> Result<()> { 119 fn get_timestamp() -> u64 {
120 SystemTime::now()
121 .duration_since(UNIX_EPOCH)
122 .unwrap()
123 .as_secs()
124 }
125}
126
127#[derive(Debug, Clone, Serialize, Deserialize)]
128pub struct BackupLocation {
129 id: BackupId,
130 rel_location: String,
131}
132
133impl BackupLocation {
134 pub fn get_absolute_dir(&self, config: &Config) -> String {
135 format!("{}/{}", config.root, self.rel_location)
136 }
137
138 pub fn append_to_root(&self, config: &Config) -> Result<()> {
101 let backup_index_root = format!("{}/index.json", config.root); 139 let backup_index_root = format!("{}/index.json", config.root);
102 let path = PathBuf::from(&backup_index_root); 140 let path = PathBuf::from(&backup_index_root);
103 if path.exists() { 141 if path.exists() {
@@ -107,27 +145,15 @@ impl Backup {
107 let mut loc: Vec<BackupLocation> = serde_json::from_str(&content)?; 145 let mut loc: Vec<BackupLocation> = serde_json::from_str(&content)?;
108 146
109 let mut f = File::create(path)?; 147 let mut f = File::create(path)?;
110 loc.push(new_backup); 148 loc.push(self.clone());
111 149
112 f.write_all(&serde_json::to_vec(&loc)?)?; 150 f.write_all(&serde_json::to_vec(&loc)?)?;
113 } else { 151 } else {
152 create_dir_all(&config.root).unwrap();
114 let mut f = File::create(backup_index_root)?; 153 let mut f = File::create(backup_index_root)?;
115 f.write_all(&serde_json::to_vec(&vec![new_backup])?)?; 154 f.write_all(&serde_json::to_vec(&vec![self])?)?;
116 }; 155 };
117 156
118 Ok(()) 157 Ok(())
119 } 158 }
120
121 fn get_timestamp() -> u64 {
122 SystemTime::now()
123 .duration_since(UNIX_EPOCH)
124 .unwrap()
125 .as_secs()
126 }
127}
128
129#[derive(Debug, Clone, Serialize, Deserialize)]
130struct BackupLocation {
131 id: BackupId,
132 rel_location: String,
133} 159}
diff --git a/src/config.rs b/src/config.rs
index 625118a..439c17c 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -7,7 +7,8 @@ pub struct Config {
7 pub root: String, 7 pub root: String,
8 pub user: Vec<String>, 8 pub user: Vec<String>,
9 pub directories: Vec<String>, 9 pub directories: Vec<String>,
10 pub custom_directories: Map<String, String> 10 pub custom_directories: Map<String, String>,
11 pub device: String,
11} 12}
12 13
13impl Default for Config { 14impl Default for Config {
@@ -17,6 +18,9 @@ impl Default for Config {
17 user: vec![], 18 user: vec![],
18 directories: vec![], 19 directories: vec![],
19 custom_directories: Map::new(), 20 custom_directories: Map::new(),
21 device: gethostname::gethostname()
22 .into_string()
23 .expect("invalid hostname string"),
20 } 24 }
21 } 25 }
22} 26}
diff --git a/src/error.rs b/src/error.rs
index 6afa3d0..c43c1fc 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -17,12 +17,13 @@ pub enum Error {
17 #[error("Only exactly one user allowed in config")] 17 #[error("Only exactly one user allowed in config")]
18 MultiUser, 18 MultiUser,
19 19
20 #[error("OsString couldn't be converted to string")]
21 InvalidOsString,
22
23 #[error("Requested backup not found")] 20 #[error("Requested backup not found")]
24 BackupNotFound, 21 BackupNotFound,
25 22
23 // Packages
24 #[error("Unknown Package Manger Output")]
25 UnknownOutput,
26
26 #[error("json: {source}")] 27 #[error("json: {source}")]
27 SerdeJson { 28 SerdeJson {
28 #[from] 29 #[from]
diff --git a/src/main.rs b/src/main.rs
index d5ccb75..acb728f 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -16,18 +16,23 @@ fn main() -> color_eyre::Result<()> {
16 cfg.directories.push("~/.config/nvim".to_string()); 16 cfg.directories.push("~/.config/nvim".to_string());
17 cfg.directories.push("~/.config/hypr".to_string()); 17 cfg.directories.push("~/.config/hypr".to_string());
18 cfg.root = "./backup".to_string(); 18 cfg.root = "./backup".to_string();
19 // cfg.root = "./backup-test".to_string();
20 // cfg.directories.push("u:/code/proj/fxbaup/backup-test-dir".to_string());
19 21
20 let pacman = Pacman; 22 let pacman = Pacman;
21 let pkgs = pacman.get_installed(); 23 let pkgs = pacman.get_installed()?;
22 24
23 let backup = Backup::create(&cfg, pkgs)?; 25 let backup = Backup::create(&cfg, pkgs)?;
24 println!("{backup:#?}"); 26 // println!("{backup:#?}");
25 27
26 // backup.save(&cfg)?; 28 backup.save(&cfg)?;
27 29
28 let index = Backup::get_index(&cfg, None)?; 30 // PathInfo::compare_to_last_modified(&cfg, &LocationRoot::User("fx".to_string()), "code/proj/fxbaub/backup-test-dir/size.txt")?;
31 // PathInfo::compare_to_last_modified(&cfg, &LocationRoot::User("fx".to_string()), "code/proj/fxbaub/backup-test-dir/content.txt")?;
29 32
30 println!("{index:#?}"); 33 // let index = Backup::get_index(&cfg, None)?;
34
35 // println!("{index:#?}");
31 36
32 // let fi = FileInfo::new("~/.config/nvim", &cfg)?; 37 // let fi = FileInfo::new("~/.config/nvim", &cfg)?;
33 // println!("{:?}", fi.get_absolute_path()); 38 // println!("{:?}", fi.get_absolute_path());
diff --git a/src/packages.rs b/src/packages.rs
index 9f765d6..7ac1736 100644
--- a/src/packages.rs
+++ b/src/packages.rs
@@ -1,6 +1,9 @@
1use serde::{Deserialize, Serialize}; 1use serde::{Deserialize, Serialize};
2 2
3use crate::error::Result;
4
3pub mod pacman; 5pub mod pacman;
6pub mod portage;
4 7
5#[derive(Debug, Serialize, Deserialize)] 8#[derive(Debug, Serialize, Deserialize)]
6pub struct Package { 9pub struct Package {
@@ -10,7 +13,7 @@ pub struct Package {
10} 13}
11 14
12pub trait PackageManager { 15pub trait PackageManager {
13 fn get_installed(&self) -> Vec<Package>; 16 fn get_installed(&self) -> Result<Vec<Package>>;
14 17
15 fn install(&self, pkgs: Vec<Package>); 18 fn install(&self, pkgs: Vec<Package>);
16} 19}
diff --git a/src/packages/pacman.rs b/src/packages/pacman.rs
index 0a9e1ff..b5be4c0 100644
--- a/src/packages/pacman.rs
+++ b/src/packages/pacman.rs
@@ -1,13 +1,13 @@
1use std::process::Command; 1use std::process::Command;
2 2
3use crate::packages::Package; 3use super::{Package, PackageManager};
4 4
5use super::PackageManager; 5use crate::error::{Error, Result};
6 6
7pub struct Pacman; 7pub struct Pacman;
8 8
9impl PackageManager for Pacman { 9impl PackageManager for Pacman {
10 fn get_installed(&self) -> Vec<super::Package> { 10 fn get_installed(&self) -> Result<Vec<super::Package>> {
11 let pm_pkgs = Command::new("pacman").args(["-Q"]).output().unwrap(); 11 let pm_pkgs = Command::new("pacman").args(["-Q"]).output().unwrap();
12 let pm_e_pkgs = Command::new("pacman") 12 let pm_e_pkgs = Command::new("pacman")
13 .args(["-Q", "--explicit"]) 13 .args(["-Q", "--explicit"])
@@ -25,7 +25,7 @@ impl PackageManager for Pacman {
25 }; 25 };
26 let split: Vec<&str> = pkg.split_whitespace().collect(); 26 let split: Vec<&str> = pkg.split_whitespace().collect();
27 if split.len() != 2 { 27 if split.len() != 2 {
28 panic!("Unknown Pacman Output"); 28 return Err(Error::UnknownOutput);
29 }; 29 };
30 30
31 let explicit = pm_e_pkgs_out.contains(pkg); 31 let explicit = pm_e_pkgs_out.contains(pkg);
@@ -33,14 +33,14 @@ impl PackageManager for Pacman {
33 pkgs.push(Package { 33 pkgs.push(Package {
34 id: split[0].to_string(), 34 id: split[0].to_string(),
35 version: split[1].to_string(), 35 version: split[1].to_string(),
36 explicit 36 explicit,
37 }) 37 })
38 } 38 }
39 39
40 pkgs 40 Ok(pkgs)
41 } 41 }
42 42
43 fn install(&self, pkgs: Vec<Package>) { 43 fn install(&self, _pkgs: Vec<super::Package>) {
44 todo!(); 44 todo!();
45 } 45 }
46} 46}
diff --git a/src/packages/portage.rs b/src/packages/portage.rs
new file mode 100644
index 0000000..6b9e508
--- /dev/null
+++ b/src/packages/portage.rs
@@ -0,0 +1,13 @@
1use super::PackageManager;
2
3pub struct Portage;
4
5impl PackageManager for Portage {
6 fn get_installed(&self) -> crate::error::Result<Vec<super::Package>> {
7 todo!()
8 }
9
10 fn install(&self, pkgs: Vec<super::Package>) {
11 todo!()
12 }
13}
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 @@
1use std::{ 1use std::{
2 fmt::Display, 2 fmt::Display,
3 path::PathBuf, 3 fs::{create_dir_all, File},
4 io::Read,
5 path::{Path, PathBuf},
4}; 6};
5 7
6use serde::{Deserialize, Serialize}; 8use serde::{Deserialize, Serialize};
7 9
8use crate::{ 10use crate::{
9 backup::BackupId, 11 backup::{Backup, BackupId},
10 config::Config, 12 config::Config,
11 error::{Error, Result}, 13 error::{Error, Result},
12}; 14};
13 15
14#[derive(Debug, Clone, Serialize, Deserialize)] 16#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct PathInfo { 17pub struct PathInfo {
16 pub modified: bool,
17 pub is_file: bool, 18 pub is_file: bool,
18 rel_location: String, 19 rel_location: String,
19 location_root: LocationRoot, 20 location_root: LocationRoot,
20 last_modified: BackupId, 21 last_modified: Option<BackupId>,
21 children: Vec<PathInfo> 22 pub children: Vec<PathInfo>,
22} 23}
23 24
24impl PathInfo { 25impl PathInfo {
25 pub fn from_path(config: &Config, path: &str) -> Result<Self> { 26 pub fn from_path(config: &Config, path: &str) -> Result<Self> {
26 let locations = Self::parse_location(path, config)?; 27 let locations = Self::parse_location(path, config)?;
27 28
28 Ok(Self::handle_dir(config, &locations.0, &locations.1)?) 29 Self::handle_dir(config, &locations.0, &locations.1)
29 } 30 }
30 31
31 fn handle_dir( 32 fn handle_dir(
@@ -36,54 +37,129 @@ impl PathInfo {
36 println!("Handling {rel_location}"); 37 println!("Handling {rel_location}");
37 let path = Self::get_abs_path(&location_root.to_string(), rel_location); 38 let path = Self::get_abs_path(&location_root.to_string(), rel_location);
38 Ok(if path.is_dir() { 39 Ok(if path.is_dir() {
39 let mut modified = false; 40 let mut last_modified = None;
40 let mut children: Vec<PathInfo> = Vec::new(); 41 let mut children: Vec<PathInfo> = Vec::new();
41 42
42 let paths = std::fs::read_dir(path).unwrap(); 43 let paths = std::fs::read_dir(path).unwrap();
43 for path in paths { 44 for path in paths {
44 let pathstr = path.unwrap().path().to_string_lossy().to_string(); 45 let pathstr = path.unwrap().path().to_string_lossy().to_string();
45 let root = format!("{}/", location_root.to_string()); 46 let root = format!("{location_root}/");
46 let Some(rl) = pathstr.split_once(&root) else { 47 let Some(rl) = pathstr.split_once(&root) else {
47 panic!("HUH"); 48 panic!("HUH");
48 }; 49 };
49 let handle = Self::handle_dir(config, rl.1, location_root)?; 50 let handle = Self::handle_dir(config, rl.1, location_root)?;
50 if handle.modified { 51 if handle.last_modified.is_some() {
51 modified = true; 52 // FIX: Check if new last modified is newer than old one
53 last_modified = handle.last_modified.clone();
52 }; 54 };
53 children.push(handle); 55 children.push(handle);
54 } 56 }
55 Self { 57 Self {
56 modified,
57 is_file: false, 58 is_file: false,
58 rel_location: rel_location.to_string(), 59 rel_location: rel_location.to_string(),
59 location_root: location_root.clone(), 60 location_root: location_root.clone(),
60 last_modified: "".to_string(), 61 last_modified,
61 children 62 children,
62 } 63 }
63 } else { 64 } else {
64 Self::from_file(rel_location, location_root.clone())? 65 Self::from_file(config, rel_location, location_root)?
65 }) 66 })
66 } 67 }
67 68
68 fn from_file(rel_location: &str, location_root: LocationRoot) -> Result<Self> { 69 fn from_file(
69 println!("From file {rel_location}"); 70 config: &Config,
71 rel_location: &str,
72 location_root: &LocationRoot,
73 ) -> Result<Self> {
74 let last_modified = Self::compare_to_last_modified(config, location_root, rel_location)?;
70 75
71 let modified = false; 76 println!("From file {rel_location} ({:?})", last_modified);
72 77
73 Ok(Self { 78 Ok(Self {
74 rel_location: rel_location.to_string(), 79 rel_location: rel_location.to_string(),
75 location_root, 80 location_root: location_root.clone(),
76 modified, 81 last_modified,
77 last_modified: "".to_string(),
78 is_file: true, 82 is_file: true,
79 children: Vec::new() 83 children: Vec::new(),
80 }) 84 })
81 } 85 }
82 86
87 pub fn compare_to_last_modified(
88 config: &Config,
89 location_root: &LocationRoot,
90 rel_location: &str,
91 ) -> Result<Option<String>> {
92 let Some(last_backup) = Backup::get_last(config)? else {
93 // First Backup
94 return Ok(None);
95 };
96
97
98 let files = last_backup.files.clone();
99 let last_file_opt = files.iter().find(|file| file.rel_location == rel_location && file.location_root == *location_root);
100
101 let Some(last_file) = last_file_opt else {
102 // File didn't exist last Backup
103 println!("File didn't exist last Backup");
104 return Ok(None);
105 };
106
107 let modified_backup = if let Some(modified_backup_id) = last_file.last_modified.clone() {
108 Backup::from_index(config, modified_backup_id)?
109 } else {
110 last_backup
111 };
112
113 let old_path = modified_backup.get_absolute_file_location(config, &last_file.rel_location);
114 let new_path = format!("{location_root}/{rel_location}");
115
116 let mut old = File::open(old_path)?;
117 let mut new = File::open(new_path)?;
118
119 let old_len = old.metadata()?.len();
120 let new_len = new.metadata()?.len();
121 if old_len != new_len {
122 return Ok(None);
123 }
124
125 let mut old_content = String::new();
126 old.read_to_string(&mut old_content)?;
127 let mut new_content = String::new();
128 new.read_to_string(&mut new_content)?;
129 if old_content != new_content {
130 return Ok(None);
131 }
132
133 Ok(Some(modified_backup.id.clone()))
134 }
135
83 pub fn get_absolute_path(&self) -> PathBuf { 136 pub fn get_absolute_path(&self) -> PathBuf {
84 Self::get_abs_path(&self.location_root.to_string(), &self.rel_location) 137 Self::get_abs_path(&self.location_root.to_string(), &self.rel_location)
85 } 138 }
86 139
140 pub fn save(&self, backup_root: &str) -> Result<()> {
141 if self.last_modified.is_some() {
142 return Ok(());
143 }
144 println!("Save File {:?}", self.rel_location);
145 if !self.is_file {
146 for child in &self.children {
147 child.save(backup_root)?;
148 }
149 } else {
150 let new_path = format!("{}/{}", backup_root, self.rel_location);
151 // println!("New Path: {new_path}");
152 // println!("Old Path: {:?}", self.get_absolute_path());
153 let np = Path::new(&new_path);
154 if let Some(parent) = np.parent() {
155 create_dir_all(parent)?;
156 }
157 std::fs::copy(self.get_absolute_path(), new_path)?;
158 };
159
160 Ok(())
161 }
162
87 fn get_abs_path(location_root: &str, rel_location: &str) -> PathBuf { 163 fn get_abs_path(location_root: &str, rel_location: &str) -> PathBuf {
88 let path = format!("{}/{}", location_root, rel_location); 164 let path = format!("{}/{}", location_root, rel_location);
89 PathBuf::from(path) 165 PathBuf::from(path)
@@ -150,97 +226,174 @@ impl LocationRoot {
150 } 226 }
151} 227}
152 228
153// #[cfg(test)] 229#[cfg(test)]
154// mod tests { 230mod tests {
155// use crate::{ 231 use std::{
156// config::Config, 232 fs::{create_dir_all, remove_dir_all, File},
157// error::{Error, Result}, 233 io::Write,
158// pathinfo::PathInfo, 234 };
159// }; 235
160// 236 use crate::{
161// use super::LocationRoot; 237 backup::Backup,
162// 238 config::Config,
163// #[test] 239 error::{Error, Result},
164// fn from_op_str() -> Result<()> { 240 packages::{pacman::Pacman, PackageManager},
165// let mut config = Config::default(); 241 };
166// config 242
167// .custom_directories 243 use super::LocationRoot;
168// .insert("test".to_string(), "/usr/local/test".to_string()); 244 use super::PathInfo;
169// 245
170// let mut values: Vec<(&str, Result<LocationRoot>)> = Vec::new(); 246 #[test]
171// values.push(("u:test", Ok(LocationRoot::User("test".to_string())))); 247 fn from_op_str() -> Result<()> {
172// values.push(("s:", Ok(LocationRoot::SystemSettings))); 248 let mut config = Config::default();
173// values.push(("r:", Ok(LocationRoot::Root))); 249 config
174// values.push(( 250 .custom_directories
175// "c:test", 251 .insert("test".to_string(), "/usr/local/test".to_string());
176// Ok(LocationRoot::Custom("/usr/local/test".to_string())), 252
177// )); 253 let mut values_ok: Vec<(&str, LocationRoot)> = Vec::new();
178// values.push(("c:rest", Err(Error::CustomDirectory("rest".to_string())))); 254 values_ok.push(("u:test", LocationRoot::User("test".to_string())));
179// values.push(("t:test/", Err(Error::InvalidIndex("t".to_string())))); 255 values_ok.push(("s:", LocationRoot::SystemSettings));
180// values.push(( 256 values_ok.push(("r:", LocationRoot::Root));
181// "test:test/usr", 257 values_ok.push((
182// Err(Error::InvalidIndex("test".to_string())), 258 "c:test",
183// )); 259 LocationRoot::Custom("/usr/local/test".to_string()),
184// values.push(("/usr/local/test", Err(Error::NoIndex))); 260 ));
185// values.push(("c/usr/local/test", Err(Error::NoIndex))); 261
186// 262 for value in values_ok {
187// for value in values { 263 println!("Testing {value:?}");
188// print!("Testing {value:?}"); 264 assert_eq!(LocationRoot::from_op_str(value.0, &config)?, value.1);
189// assert_eq!(LocationRoot::from_op_str(value.0, &config), value.1); 265 println!("\x1B[FTesting {value:?} ✓");
190// println!("\rTesting {value:?} ✓"); 266 }
191// } 267
192// 268 let mut values_err: Vec<(&str, String)> = Vec::new();
193// Ok(()) 269 values_err.push((
194// } 270 "c:rest",
195// 271 Error::CustomDirectory("rest".to_string()).to_string(),
196// #[test] 272 ));
197// fn parse_location() -> Result<()> { 273 values_err.push(("t:test/", Error::InvalidIndex("t".to_string()).to_string()));
198// let mut config = Config::default(); 274 values_err.push((
199// config.user.push("test".to_string()); 275 "test:test/usr",
200// config 276 Error::InvalidIndex("test".to_string()).to_string(),
201// .custom_directories 277 ));
202// .insert("test".to_string(), "/usr/local/test".to_string()); 278 values_err.push(("/usr/local/test", Error::NoIndex.to_string()));
203// 279 values_err.push(("c/usr/local/test", Error::NoIndex.to_string()));
204// let mut values: Vec<(&str, Result<(String, LocationRoot)>)> = Vec::new(); 280
205// values.push(( 281 for value in values_err {
206// "~/.config/nvim", 282 println!("Testing {value:?}");
207// Ok(( 283 assert_eq!(
208// ".config/nvim".to_string(), 284 LocationRoot::from_op_str(value.0, &config)
209// LocationRoot::User("test".to_string()), 285 .err()
210// )), 286 .unwrap()
211// )); 287 .to_string(),
212// values.push(( 288 value.1
213// "u:test/.config/nvim", 289 );
214// Ok(( 290 println!("\x1B[FTesting {value:?} ✓");
215// ".config/nvim".to_string(), 291 }
216// LocationRoot::User("test".to_string()), 292
217// )), 293 Ok(())
218// )); 294 }
219// values.push(( 295
220// "r:/.config/nvim", 296 #[test]
221// Ok((".config/nvim".to_string(), LocationRoot::Root)), 297 fn parse_location() -> Result<()> {
222// )); 298 let mut config = Config::default();
223// values.push(( 299 config.user.push("test".to_string());
224// "r:/.config/nvim", 300 config
225// Ok((".config/nvim".to_string(), LocationRoot::Root)), 301 .custom_directories
226// )); 302 .insert("test".to_string(), "/usr/local/test".to_string());
227// values.push(( 303
228// "s:/.config/nvim", 304 let mut values_ok: Vec<(&str, (String, LocationRoot))> = Vec::new();
229// Ok((".config/nvim".to_string(), LocationRoot::SystemSettings)), 305 values_ok.push((
230// )); 306 "~/.config/nvim",
231// values.push(( 307 (
232// "c:test/.config/nvim", 308 ".config/nvim".to_string(),
233// Ok(( 309 LocationRoot::User("test".to_string()),
234// ".config/nvim".to_string(), 310 ),
235// LocationRoot::Custom("/usr/local/test".to_string()), 311 ));
236// )), 312 values_ok.push((
237// )); 313 "u:test/.config/nvim",
238// 314 (
239// for value in values { 315 ".config/nvim".to_string(),
240// print!("Testing {value:?}"); 316 LocationRoot::User("test".to_string()),
241// assert_eq!(PathInfo::parse_location(&value.0, &config), value.1); 317 ),
242// println!("\rTesting {value:?} ✓"); 318 ));
243// } 319 values_ok.push((
244// Ok(()) 320 "r:/.config/nvim",
245// } 321 (".config/nvim".to_string(), LocationRoot::Root),
246// } 322 ));
323 values_ok.push((
324 "r:/.config/nvim",
325 (".config/nvim".to_string(), LocationRoot::Root),
326 ));
327 values_ok.push((
328 "s:/.config/nvim",
329 (".config/nvim".to_string(), LocationRoot::SystemSettings),
330 ));
331 values_ok.push((
332 "c:test/.config/nvim",
333 (
334 ".config/nvim".to_string(),
335 LocationRoot::Custom("/usr/local/test".to_string()),
336 ),
337 ));
338
339 for value in values_ok {
340 print!("Testing {value:?}");
341 assert_eq!(PathInfo::parse_location(&value.0, &config)?, value.1);
342 println!("\x1B[FTesting {value:?} ✓");
343 }
344 Ok(())
345 }
346
347 #[test]
348 fn compare_to_last_modified() -> color_eyre::Result<()> {
349 let mut config = Config::default();
350 config.root = "./backup-test".to_string();
351 config
352 .directories
353 .push("u:fx/code/proj/fxbaup/backup-test-dir".to_string());
354
355 create_dir_all("./backup-test-dir")?;
356 let mut f = File::create("./backup-test-dir/size.txt")?;
357 f.write_all("unmodified".as_bytes())?;
358 let mut f = File::create("./backup-test-dir/content.txt")?;
359 f.write_all("unmodified".as_bytes())?;
360 let mut f = File::create("./backup-test-dir/nothing.txt")?;
361 f.write_all("unmodified".as_bytes())?;
362
363 let pacman = Pacman;
364 let pkgs = pacman.get_installed()?;
365 let backup = Backup::create(&config, pkgs)?;
366 backup.save(&config)?;
367
368 let mut f = File::create("./backup-test-dir/size.txt")?;
369 f.write_all("modified".as_bytes())?;
370 let mut f = File::create("./backup-test-dir/content.txt")?;
371 f.write_all("unmodefied".as_bytes())?;
372
373 let pi = PathInfo::from_path(&config, "u:fx/code/proj/fxbaup/backup-test-dir")?;
374
375 let last_backup = Backup::get_last(&config)?.unwrap();
376 for file in pi.children {
377 println!("test rel: {}", file.rel_location);
378 let res = if file.rel_location == "code/proj/fxbaup/backup-test-dir/nothing.txt" {
379 Some(last_backup.id.clone())
380 } else {
381 None
382 };
383 println!("Testing {file:?}");
384 assert_eq!(
385 PathInfo::compare_to_last_modified(
386 &config,
387 &file.location_root,
388 &file.rel_location
389 )?,
390 res
391 );
392 println!("\x1B[FTesting {file:?} ✓");
393 }
394
395 remove_dir_all("./backup-test-dir")?;
396 remove_dir_all("./backup-test")?;
397 Ok(())
398 }
399}