about summary refs log tree commit diff
path: root/src/files.rs
diff options
context:
space:
mode:
authorMel <einebeere@gmail.com>2021-09-20 22:25:55 +0200
committerMel <einebeere@gmail.com>2021-09-20 22:25:55 +0200
commitc25d22681f9bc6ddba512895182af0ce0252731b (patch)
treeeadcdec7d0b4d26d59e77169bd89b8db13e22fb6 /src/files.rs
parent8bcbf51cf4f7924600eae39db67ff653b48ad95d (diff)
downloadka-c25d22681f9bc6ddba512895182af0ce0252731b.tar.zst
ka-c25d22681f9bc6ddba512895182af0ce0252731b.zip
Split out common logic
Diffstat (limited to 'src/files.rs')
-rw-r--r--src/files.rs182
1 files changed, 182 insertions, 0 deletions
diff --git a/src/files.rs b/src/files.rs
new file mode 100644
index 0000000..2512fa5
--- /dev/null
+++ b/src/files.rs
@@ -0,0 +1,182 @@
+use std::{
+    fs::{self, DirEntry, File, OpenOptions},
+    io,
+    path::{Path, PathBuf},
+};
+
+use crate::actions::ActionOptions;
+
+pub struct Locations {
+    pub repository_path: PathBuf,
+    pub ka_path: PathBuf,
+    pub ka_files_path: PathBuf,
+}
+
+impl Locations {
+    pub fn get_repository_paths(&self) -> Result<Vec<RepositoryPaths>, ()> {
+        let repository_entries = fs::read_dir(&self.repository_path).map_err(|_| ())?;
+        let history_entries = fs::read_dir(&self.ka_files_path).map_err(|_| ())?;
+
+        let tracked_paths = repository_entries
+            .map(Result::unwrap)
+            .filter(|entry| entry.path() != self.ka_path)
+            .flat_map(Self::flatten_directories)
+            .map(|entry| {
+                let path = entry.path();
+                RepositoryPaths::from_tracked(&self, &path)
+            });
+
+        let deleted_paths = history_entries
+            .map(Result::unwrap)
+            .flat_map(Self::flatten_directories)
+            .filter_map(|entry| {
+                let path = entry.path();
+                let file = RepositoryPaths::from_history(&self, &path);
+                match file {
+                    RepositoryPaths::Deleted { .. } => Some(file),
+                    RepositoryPaths::Tracked { .. } => None,
+                    _ => unreachable!(),
+                }
+            });
+
+        let all_paths = tracked_paths.chain(deleted_paths);
+
+        Ok(all_paths.collect())
+    }
+
+    pub fn tracked_from_history(&self, history_file_path: &Path) -> PathBuf {
+        self.repository_path
+            .join(history_file_path.strip_prefix(&self.ka_files_path).unwrap())
+    }
+
+    pub fn history_from_tracked(&self, tracked_file_path: &Path) -> PathBuf {
+        self.ka_files_path.join(
+            tracked_file_path
+                .strip_prefix(&self.repository_path)
+                .unwrap(),
+        )
+    }
+
+    fn flatten_directories(entry: DirEntry) -> Vec<DirEntry> {
+        let file_type = entry.file_type().expect("Could not read a file type.");
+        if file_type.is_dir() {
+            fs::read_dir(entry.path())
+                .expect("Could not read nested directory.")
+                .map(Result::unwrap)
+                .flat_map(Self::flatten_directories)
+                .collect()
+        } else {
+            vec![entry]
+        }
+    }
+}
+
+impl From<&ActionOptions> for Locations {
+    fn from(options: &ActionOptions) -> Self {
+        let ka_path = options.repository_path.join(".ka");
+        let ka_files_path = ka_path.join("files");
+
+        Self {
+            repository_path: options.repository_path.clone(),
+            ka_path,
+            ka_files_path,
+        }
+    }
+}
+
+pub enum RepositoryPaths {
+    Deleted(FileDeleted),
+    Untracked(FileUntracked),
+    Tracked(FileTracked),
+}
+
+impl RepositoryPaths {
+    pub fn from_history(locations: &Locations, history_file_path: &Path) -> Self {
+        let tracked_path = locations.tracked_from_history(history_file_path);
+        if !tracked_path.exists() {
+            RepositoryPaths::Deleted(FileDeleted {
+                history_path: history_file_path.to_path_buf(),
+            })
+        } else {
+            RepositoryPaths::Tracked(FileTracked {
+                history_path: history_file_path.to_path_buf(),
+                tracked_path,
+            })
+        }
+    }
+
+    pub fn from_tracked(locations: &Locations, tracked_file_path: &Path) -> Self {
+        let history_path = locations.history_from_tracked(tracked_file_path);
+        if !history_path.exists() {
+            RepositoryPaths::Untracked(FileUntracked {
+                path: tracked_file_path.to_path_buf(),
+            })
+        } else {
+            RepositoryPaths::Tracked(FileTracked {
+                history_path,
+                tracked_path: tracked_file_path.to_path_buf(),
+            })
+        }
+    }
+}
+
+pub struct FileDeleted {
+    history_path: PathBuf,
+}
+
+impl FileDeleted {
+    pub fn load_history_file(&self) -> io::Result<File> {
+        OpenOptions::new()
+            .write(true)
+            .read(true)
+            .open(&self.history_path)
+    }
+
+    pub fn create_tracked_file(&self, locations: &Locations) -> io::Result<File> {
+        File::create(locations.tracked_from_history(&self.history_path))
+    }
+}
+
+pub struct FileUntracked {
+    path: PathBuf,
+}
+
+impl FileUntracked {
+    pub fn load_file(&self) -> io::Result<File> {
+        OpenOptions::new().read(true).open(&self.path)
+    }
+
+    pub fn create_history_file(&self, locations: &Locations) -> io::Result<File> {
+        let history_path = locations.history_from_tracked(&self.path);
+
+        history_path.parent().map(|dir_path| {
+            if !dir_path.exists() {
+                fs::create_dir_all(dir_path).unwrap();
+            }
+        });
+
+        File::create(history_path)
+    }
+}
+
+pub struct FileTracked {
+    history_path: PathBuf,
+    tracked_path: PathBuf,
+}
+
+impl FileTracked {
+    pub fn load_history_file(&self) -> io::Result<File> {
+        OpenOptions::new()
+            .write(true)
+            .read(true)
+            .open(&self.history_path)
+    }
+
+    pub fn load_tracked_file(&self) -> io::Result<File> {
+        File::open(&self.tracked_path)
+    }
+
+    pub fn create_tracked_file(&self) -> io::Result<File> {
+        File::create(&self.tracked_path)
+    }
+}