diff options
| author | Mel <einebeere@gmail.com> | 2021-10-02 19:55:01 +0200 |
|---|---|---|
| committer | Mel <einebeere@gmail.com> | 2021-10-02 19:55:01 +0200 |
| commit | 55da5ae8cf731c768d6cc580693068e64234a4af (patch) | |
| tree | 9024bf8c315f1c150a468db6791acbeec2907601 | |
| parent | c1eccadf76ca7625d8cff9a52480168aab876e62 (diff) | |
| download | ka-55da5ae8cf731c768d6cc580693068e64234a4af.tar.zst ka-55da5ae8cf731c768d6cc580693068e64234a4af.zip | |
Use filesystem abstraction throughout Ka.
| -rw-r--r-- | cli/src/main.rs | 13 | ||||
| -rw-r--r-- | src/actions/create.rs | 25 | ||||
| -rw-r--r-- | src/actions/shift.rs | 40 | ||||
| -rw-r--r-- | src/actions/update.rs | 53 | ||||
| -rw-r--r-- | src/files.rs | 111 | ||||
| -rw-r--r-- | src/filesystem.rs | 141 | ||||
| -rw-r--r-- | src/history.rs | 32 | ||||
| -rw-r--r-- | src/lib.rs | 4 |
8 files changed, 255 insertions, 164 deletions
diff --git a/cli/src/main.rs b/cli/src/main.rs index 751871c..be52e60 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,6 +1,9 @@ use std::env; -use ka::actions::{create, shift, update, ActionOptions}; +use ka::{ + actions::{create, shift, update, ActionOptions}, + filesystem::FsImpl, +}; fn main() { let args: Vec<String> = env::args().collect(); @@ -9,17 +12,19 @@ fn main() { let options = ActionOptions::from_path("./repo"); //let options = ActionOptions::from_pwd().expect("Could not get current path."); + let mut filesystem = FsImpl {}; + match command { "create" => { - create(options).expect("Failed executing Create action."); + create(options, &mut filesystem).expect("Failed executing Create action."); } "update" => { - update(options).expect("Failed executing Update action."); + update(options, &mut filesystem).expect("Failed executing Update action."); } "shift" => { let new_cursor: usize = args[2].as_str().parse().expect("Invalid cursor."); - shift(options, new_cursor).expect("Failed executing Shift actions."); + shift(options, &mut filesystem, new_cursor).expect("Failed executing Shift actions."); } _ => panic!("Unknown command: {}", command), } diff --git a/src/actions/create.rs b/src/actions/create.rs index 472cbe3..20dbff7 100644 --- a/src/actions/create.rs +++ b/src/actions/create.rs @@ -1,26 +1,23 @@ -use std::fs::{self, File}; - +use crate::{actions::update, files::Locations, filesystem::Fs, history::RepositoryHistory}; use anyhow::Result; -use crate::{actions::update, files::Locations, history::RepositoryHistory}; - use super::ActionOptions; -pub fn create(command_options: ActionOptions) -> Result<()> { +pub fn create(command_options: ActionOptions, fs: &mut impl Fs) -> Result<()> { let locations = Locations::from(&command_options); + // FIXME: Re-add old repository deletion. + // if locations.ka_path.exists() { + // fs::remove_dir_all(locations.ka_path.as_path())?; + // } - if locations.ka_path.exists() { - fs::remove_dir_all(locations.ka_path.as_path())?; - } - - fs::create_dir(&locations.ka_path)?; - fs::create_dir(&locations.ka_files_path)?; + // fs::create_dir(&locations.ka_path)?; + // fs::create_dir(&locations.ka_files_path)?; - let mut index_file = File::create(locations.get_repository_index())?; + let mut index_file = fs.create_file(&locations.get_repository_index_path())?; let empty_history = RepositoryHistory::default(); - empty_history.write_to_file(&mut index_file)?; + empty_history.write_to_file(fs, &mut index_file)?; - update(command_options)?; + update(command_options, fs)?; Ok(()) } diff --git a/src/actions/shift.rs b/src/actions/shift.rs index 94b8914..8f37874 100644 --- a/src/actions/shift.rs +++ b/src/actions/shift.rs @@ -1,29 +1,26 @@ -use std::{ - collections::HashSet, - fs::{self, OpenOptions}, - io::{Seek, Write}, -}; +use std::collections::HashSet; use anyhow::Result; use crate::{ files::{FileState, Locations}, + filesystem::Fs, history::{FileHistory, RepositoryHistory}, }; use super::ActionOptions; -pub fn shift(command_options: ActionOptions, new_cursor: usize) -> Result<()> { +pub fn shift(command_options: ActionOptions, fs: &mut impl Fs, new_cursor: usize) -> Result<()> { let locations = Locations::from(&command_options); - let repository_index_path = locations.get_repository_index(); - let mut repository_index_file = OpenOptions::new().write(true).open(repository_index_path)?; - let mut repository_history = RepositoryHistory::from_file(&mut repository_index_file)?; + let repository_index_path = locations.get_repository_index_path(); + let mut repository_index_file = fs.open_writable_file(&repository_index_path)?; + let mut repository_history = RepositoryHistory::from_file(fs, &mut repository_index_file)?; let old_cursor = repository_history.cursor; repository_history.cursor = new_cursor; - repository_history.write_to_file(&mut repository_index_file)?; + repository_history.write_to_file(fs, &mut repository_index_file)?; let changes_between_cursors = if old_cursor < new_cursor { old_cursor..new_cursor @@ -47,32 +44,27 @@ pub fn shift(command_options: ActionOptions, new_cursor: usize) -> Result<()> { for state in affected_files_by_shift? { match state { FileState::Tracked(tracked) => { - let mut history_file = tracked.load_history_file()?; + let mut history_file = tracked.load_history_file(fs)?; - let file_history = FileHistory::from_file(&mut history_file)?; + let file_history = FileHistory::from_file(fs, &mut history_file)?; if file_history.is_file_deleted(new_cursor) { - fs::remove_file(tracked.working_path)?; + fs.delete_file(&tracked.working_path)?; } else { let new_content = file_history.get_content(new_cursor); - let mut working_file = tracked.create_working_file()?; - - working_file.rewind()?; - working_file.set_len(0)?; - - working_file.write_all(&new_content)?; + let mut working_file = tracked.create_working_file(fs)?; + fs.write_to_file(&mut working_file, new_content)?; } } FileState::Deleted(deleted) => { - let mut history_file = deleted.load_history_file()?; + let mut history_file = deleted.load_history_file(fs)?; - let file_history = FileHistory::from_file(&mut history_file)?; + let file_history = FileHistory::from_file(fs, &mut history_file)?; if !file_history.is_file_deleted(new_cursor) { - let mut new_working_file = deleted.create_working_file(&locations)?; + let mut new_working_file = deleted.create_working_file(fs, &locations)?; let new_content = file_history.get_content(new_cursor); - - new_working_file.write_all(&new_content)?; + fs.write_to_file(&mut new_working_file, new_content)?; } } // TODO: What do we do with untracked files on a shift? Delete them? diff --git a/src/actions/update.rs b/src/actions/update.rs index a9bc0d2..8201862 100644 --- a/src/actions/update.rs +++ b/src/actions/update.rs @@ -1,43 +1,38 @@ -use std::{ - fs::{File, OpenOptions}, - io::Read, - time::SystemTime, -}; +use std::time::SystemTime; use anyhow::{Context, Result}; use crate::{ diff::ContentChange, files::{FileState, Locations}, + filesystem::Fs, history::{FileChange, FileChangeVariant, FileHistory, RepositoryChange, RepositoryHistory}, }; use super::ActionOptions; -pub fn update(command_options: ActionOptions) -> Result<()> { +pub fn update(command_options: ActionOptions, fs: &mut impl Fs) -> Result<()> { let timestamp = SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH)? .as_secs(); let locations = Locations::from(&command_options); - let repository_index_path = locations.get_repository_index(); - let mut repository_index_file = OpenOptions::new() - .read(true) - .write(true) - .open(repository_index_path)?; - let mut repository_history = RepositoryHistory::from_file(&mut repository_index_file)?; + let repository_index_path = locations.get_repository_index_path(); + let mut repository_index_file = fs.open_writable_file(&repository_index_path)?; + let mut repository_history = RepositoryHistory::from_file(fs, &mut repository_index_file)?; let entries = locations - .get_repository_files() + .get_repository_files(fs) .context("Could not traverse files.")?; let mut affected_files = Vec::new(); for state in entries { - let changed_file = get_new_history_for_file(repository_history.cursor, &state, &locations)?; + let changed_file = + get_new_history_for_file(fs, repository_history.cursor, &state, &locations)?; if let Some((mut history_file, new_file_history)) = changed_file { - new_file_history.write_to_file(&mut history_file)?; + new_file_history.write_to_file(fs, &mut history_file)?; affected_files.push(state.get_working_path(&locations)?); } } @@ -49,21 +44,22 @@ pub fn update(command_options: ActionOptions) -> Result<()> { }); repository_history.cursor += 1; - repository_history.write_to_file(&mut repository_index_file)?; + repository_history.write_to_file(fs, &mut repository_index_file)?; } Ok(()) } -fn get_new_history_for_file( +fn get_new_history_for_file<FS: Fs>( + fs: &mut FS, cursor: usize, file_state: &FileState, locations: &Locations, -) -> Result<Option<(File, FileHistory)>> { +) -> Result<Option<(FS::File, FileHistory)>> { match file_state { FileState::Deleted(deleted) => { - let mut history_file = deleted.load_history_file()?; - let file_history = FileHistory::from_file(&mut history_file)?; + let mut history_file = deleted.load_history_file(fs)?; + let file_history = FileHistory::from_file(fs, &mut history_file)?; if !file_history.is_file_deleted(cursor) { let mut new_history = file_history; new_history.add_change(FileChange { @@ -76,10 +72,9 @@ fn get_new_history_for_file( } } FileState::Untracked(untracked) => { - let mut file = untracked.load_file()?; + let mut file = untracked.load_file(fs)?; - let mut file_content = Vec::new(); - file.read_to_end(&mut file_content)?; + let file_content = fs.read_from_file(&mut file)?; let change = FileChange { change_index: cursor + 1, @@ -93,19 +88,17 @@ fn get_new_history_for_file( new_history.add_change(change); Ok(Some(( - untracked.create_history_file(locations)?, + untracked.create_history_file(fs, locations)?, new_history, ))) } FileState::Tracked(tracked) => { - let mut history_file = tracked.load_history_file()?; - let mut working_file = tracked.load_working_file()?; - - let file_history = FileHistory::from_file(&mut history_file)?; + let mut history_file = tracked.load_history_file(fs)?; + let mut working_file = tracked.load_working_file(fs)?; - let mut new_content = Vec::new(); - working_file.read_to_end(&mut new_content)?; + let file_history = FileHistory::from_file(fs, &mut history_file)?; + let new_content = fs.read_from_file(&mut working_file)?; let old_content = file_history.get_content(cursor); let changes = ContentChange::diff(&old_content, &new_content); diff --git a/src/files.rs b/src/files.rs index a2b1db4..aceb684 100644 --- a/src/files.rs +++ b/src/files.rs @@ -1,12 +1,11 @@ -use std::{ - fs::{self, DirEntry, File, OpenOptions}, - io, - path::{Path, PathBuf}, -}; +use std::path::{Path, PathBuf}; use anyhow::{Context, Error, Result}; -use crate::actions::ActionOptions; +use crate::{ + actions::ActionOptions, + filesystem::{Fs, FsEntry}, +}; pub struct Locations { pub repository_path: PathBuf, @@ -15,25 +14,26 @@ pub struct Locations { } impl Locations { - pub fn get_repository_index(&self) -> PathBuf { - return self.ka_path.join("index") + pub fn get_repository_index_path(&self) -> PathBuf { + return self.ka_path.join("index"); } - pub fn get_repository_files(&self) -> Result<Vec<FileState>, Error> { - let working_entries = fs::read_dir(&self.repository_path) + pub fn get_repository_files<FS: Fs>(&self, fs: &mut FS) -> Result<Vec<FileState>, Error> { + let working_entries = fs + .read_directory(&self.repository_path) .context("Failed reading working file entries.")? - .filter(|res| { - res.as_ref() - .map_or(true, |entry| entry.path() != self.ka_path) - }); - let history_entries = - fs::read_dir(&self.ka_files_path).context("Failed reading history file entries.")?; - - let working_files = Self::walk_directory(working_entries, &|entry| { + .into_iter() + .filter(|e| e.path() == self.ka_path) + .collect(); + let history_entries = fs + .read_directory(&self.ka_files_path) + .context("Failed reading history file entries.")?; + + let working_files = Self::walk_directory(fs, working_entries, &|entry| { FileState::from_working(self, &entry.path()).ok() })?; - let deleted_files = Self::walk_directory(history_entries, &|entry| { + let deleted_files = Self::walk_directory(fs, history_entries, &|entry| { let file_path = entry.path(); let file = FileState::from_history(self, &file_path).ok()?; match file { @@ -59,20 +59,19 @@ impl Locations { Ok(self.ka_files_path.join(raw_path)) } - fn walk_directory( - directory: impl Iterator<Item = io::Result<DirEntry>>, - filter_map: &dyn Fn(DirEntry) -> Option<FileState>, + fn walk_directory<FS: Fs>( + fs: &mut FS, + directory: Vec<FS::Entry>, + filter_map: &dyn Fn(&FS::Entry) -> Option<FileState>, ) -> Result<Vec<FileState>> { let mut entries = Vec::new(); - for res in directory { - let entry = res?; - let file_type = entry.file_type()?; - if file_type.is_dir() { - let nested_directory = fs::read_dir(entry.path())?; - let nested_files = Self::walk_directory(nested_directory, filter_map)?; + for entry in directory { + if entry.is_directory()? { + let nested_directory = fs.read_directory(&entry.path())?; + let nested_files = Self::walk_directory(fs, nested_directory, filter_map)?; entries.extend(nested_files); - } else if let Some(states) = filter_map(entry) { + } else if let Some(states) = filter_map(&entry) { entries.push(states); } } @@ -117,6 +116,8 @@ impl FileState { pub fn from_working(locations: &Locations, working_file_path: &Path) -> Result<Self> { let history_path = locations.history_from_working(working_file_path)?; + // FIXME: Path::exists wouldn't work with Fs abstraction. + // TODO: Think whether abstracting Path would be needed for Fs abstraction. Ok(if !history_path.exists() { FileState::Untracked(FileUntracked { path: working_file_path.to_path_buf(), @@ -143,17 +144,17 @@ pub struct FileDeleted { } impl FileDeleted { - pub fn load_history_file(&self) -> io::Result<File> { - OpenOptions::new() - .write(true) - .read(true) - .open(&self.history_path) + pub fn load_history_file<FS: Fs>(&self, fs: &mut FS) -> Result<FS::File> { + fs.open_writable_file(&self.history_path) } - pub fn create_working_file(&self, locations: &Locations) -> Result<File> { - Ok(File::create( - locations.working_from_history(&self.history_path)?, - )?) + pub fn create_working_file<FS: Fs>( + &self, + fs: &mut FS, + locations: &Locations, + ) -> Result<FS::File> { + let working_path = locations.working_from_history(&self.history_path)?; + fs.create_file(&working_path) } } @@ -162,20 +163,17 @@ pub struct FileUntracked { } impl FileUntracked { - pub fn load_file(&self) -> io::Result<File> { - OpenOptions::new().read(true).open(&self.path) + pub fn load_file<FS: Fs>(&self, fs: &mut FS) -> Result<FS::File> { + fs.open_readable_file(&self.path) } - pub fn create_history_file(&self, locations: &Locations) -> Result<File> { + pub fn create_history_file<FS: Fs>( + &self, + fs: &mut FS, + locations: &Locations, + ) -> Result<FS::File> { let history_path = locations.history_from_working(&self.path)?; - - if let Some(parent_path) = history_path.parent() { - if !parent_path.exists() { - fs::create_dir_all(parent_path)?; - } - } - - Ok(File::create(history_path)?) + Ok(fs.create_file(&history_path)?) } } @@ -185,18 +183,15 @@ pub struct FileTracked { } impl FileTracked { - pub fn load_history_file(&self) -> io::Result<File> { - OpenOptions::new() - .write(true) - .read(true) - .open(&self.history_path) + pub fn load_history_file<FS: Fs>(&self, fs: &mut FS) -> Result<FS::File> { + fs.open_writable_file(&self.history_path) } - pub fn load_working_file(&self) -> io::Result<File> { - File::open(&self.working_path) + pub fn load_working_file<FS: Fs>(&self, fs: &mut FS) -> Result<FS::File> { + fs.open_readable_file(&self.working_path) } - pub fn create_working_file(&self) -> io::Result<File> { - File::create(&self.working_path) + pub fn create_working_file<FS: Fs>(&self, fs: &mut FS) -> Result<FS::File> { + fs.create_file(&self.working_path) } } diff --git a/src/filesystem.rs b/src/filesystem.rs index ab5bf66..d8c96a1 100644 --- a/src/filesystem.rs +++ b/src/filesystem.rs @@ -1,27 +1,45 @@ use anyhow::{Context, Result}; use std::{ - fs::{File, OpenOptions}, - io::{Read, Seek, Write}, - path::Path, + fs::{self, DirEntry, File, OpenOptions}, + io::{self, Read, Seek, Write}, + path::{Path, PathBuf}, }; pub trait Fs { type File; + type Entry: FsEntry; fn create_file(&mut self, path: &Path) -> Result<Self::File>; + fn delete_file(&mut self, path: &Path) -> Result<()>; fn open_readable_file(&mut self, path: &Path) -> Result<Self::File>; fn open_writable_file(&mut self, path: &Path) -> Result<Self::File>; + fn read_directory(&mut self, path: &Path) -> Result<Vec<Self::Entry>>; + fn write_to_file(&mut self, file: &mut Self::File, buffer: Vec<u8>) -> Result<()>; fn read_from_file(&mut self, file: &mut Self::File) -> Result<Vec<u8>>; + + fn path_exists(&mut self, path: &Path) -> bool; +} + +pub trait FsEntry { + fn path(&self) -> PathBuf; + fn is_directory(&self) -> Result<bool>; } pub struct FsImpl {} impl Fs for FsImpl { type File = File; + type Entry = DirEntry; fn create_file(&mut self, path: &Path) -> Result<Self::File> { + if let Some(parent_path) = path.parent() { + if !parent_path.exists() { + fs::create_dir_all(parent_path)?; + } + } + OpenOptions::new() .create(true) .read(true) @@ -30,6 +48,11 @@ impl Fs for FsImpl { .with_context(|| format!("Failed creating '{}'.", path.display())) } + fn delete_file(&mut self, path: &Path) -> Result<()> { + fs::remove_file(path)?; + Ok(()) + } + fn open_readable_file(&mut self, path: &Path) -> Result<Self::File> { File::open(path) .with_context(|| format!("Failed opening '{}' for reading.", path.display())) @@ -48,6 +71,11 @@ impl Fs for FsImpl { }) } + fn read_directory(&mut self, path: &Path) -> Result<Vec<Self::Entry>> { + let result: io::Result<_> = fs::read_dir(path)?.collect(); + result.with_context(|| format!("Failed reading directory {}", path.display())) + } + fn write_to_file(&mut self, file: &mut Self::File, buffer: Vec<u8>) -> Result<()> { file.rewind()?; file.set_len(0)?; @@ -60,14 +88,31 @@ impl Fs for FsImpl { file.read_to_end(&mut buffer)?; Ok(buffer) } + + fn path_exists(&mut self, path: &Path) -> bool { + path.exists() + } } +impl FsEntry for DirEntry { + fn path(&self) -> PathBuf { + self.path() + } + + fn is_directory(&self) -> Result<bool> { + let file_type = self.file_type()?; + Ok(file_type.is_dir()) + } +} + +// TODO: This will be used for tests. Write them. +#[allow(dead_code)] #[cfg(test)] pub mod mock { - use std::path::{Path, PathBuf}; use anyhow::{Error, Result}; + use std::path::{Path, PathBuf}; - use super::Fs; + use super::{Fs, FsEntry}; pub struct FsMock { expected_calls: Vec<ExpectedCall>, @@ -85,10 +130,11 @@ pub mod mock { impl Fs for FsMock { type File = FileMock; + type Entry = EntryMock; fn create_file(&mut self, path: &Path) -> Result<Self::File> { let call = ReceivedCall { - affected_file: path.to_path_buf(), + affected_path: path.to_path_buf(), variant: ReceivedCallVariant::FileCreated, }; @@ -100,9 +146,20 @@ pub mod mock { }) } + fn delete_file(&mut self, path: &Path) -> Result<()> { + let call = ReceivedCall { + affected_path: path.to_path_buf(), + variant: ReceivedCallVariant::FileCreated, + }; + + self.received_calls.push(call); + + Ok(()) + } + fn open_readable_file(&mut self, path: &Path) -> Result<Self::File> { let call = ReceivedCall { - affected_file: path.to_path_buf(), + affected_path: path.to_path_buf(), variant: ReceivedCallVariant::ReadableFileOpened, }; @@ -116,7 +173,7 @@ pub mod mock { fn open_writable_file(&mut self, path: &Path) -> Result<Self::File> { let call = ReceivedCall { - affected_file: path.to_path_buf(), + affected_path: path.to_path_buf(), variant: ReceivedCallVariant::WritableFileOpened, }; @@ -128,9 +185,28 @@ pub mod mock { }) } + fn read_directory(&mut self, path: &Path) -> Result<Vec<Self::Entry>> { + let call = ReceivedCall { + affected_path: path.to_path_buf(), + variant: ReceivedCallVariant::DirectoryRead, + }; + + self.received_calls.push(call); + + let expected_call_option = self.expected_calls.get(self.received_calls.len() - 1); + + if let Some(expected_call) = expected_call_option { + if let ExpectedCallVariant::ReadDirectory { entries } = &expected_call.variant { + return Ok(entries.to_vec()); + } + } + + Err(Error::msg("No content to read.")) + } + fn write_to_file(&mut self, file: &mut Self::File, buffer: Vec<u8>) -> Result<()> { let call = ReceivedCall { - affected_file: file.path.clone(), + affected_path: file.path.clone(), variant: ReceivedCallVariant::FileWritten { written_content: buffer, }, @@ -145,7 +221,7 @@ pub mod mock { fn read_from_file(&mut self, file: &mut Self::File) -> Result<Vec<u8>> { let call = ReceivedCall { - affected_file: file.path.clone(), + affected_path: file.path.clone(), variant: ReceivedCallVariant::FileRead, }; @@ -161,6 +237,41 @@ pub mod mock { Err(Error::msg("No content to read.")) } + + fn path_exists(&mut self, path: &Path) -> bool { + let call = ReceivedCall { + affected_path: path.to_path_buf(), + variant: ReceivedCallVariant::DoesPathExist, + }; + + self.received_calls.push(call); + + let expected_call_option = self.expected_calls.get(self.received_calls.len() - 1); + + if let Some(expected_call) = expected_call_option { + if let ExpectedCallVariant::PathExists(answer) = &expected_call.variant { + return *answer; + } + } + + panic!("No mocked return value given for call."); + } + } + + #[derive(Clone)] + pub struct EntryMock { + path: PathBuf, + is_directory: bool, + } + + impl FsEntry for EntryMock { + fn path(&self) -> PathBuf { + self.path.clone() + } + + fn is_directory(&self) -> Result<bool> { + Ok(self.is_directory) + } } pub struct FileMock { @@ -170,28 +281,34 @@ pub mod mock { // TODO: Do we need ways to return errors? pub struct ExpectedCall { - pub affected_file: PathBuf, + pub affected_path: PathBuf, pub variant: ExpectedCallVariant, } pub enum ExpectedCallVariant { CreateFile, + DeleteFile, OpenReadableFile, OpenWritableFile, + ReadDirectory { entries: Vec<EntryMock> }, ReadFile { read_content: Vec<u8> }, WriteToFile, + PathExists(bool), } pub struct ReceivedCall { - pub affected_file: PathBuf, + pub affected_path: PathBuf, pub variant: ReceivedCallVariant, } pub enum ReceivedCallVariant { FileCreated, + FileDeleted, ReadableFileOpened, WritableFileOpened, + DirectoryRead, FileRead, FileWritten { written_content: Vec<u8> }, + DoesPathExist, } } diff --git a/src/history.rs b/src/history.rs index 72640b6..4f23790 100644 --- a/src/history.rs +++ b/src/history.rs @@ -1,14 +1,10 @@ -use std::{ - fs::File, - io::{Read, Seek, Write}, - path::PathBuf, -}; +use std::path::PathBuf; use serde::{Deserialize, Serialize}; use anyhow::{Context, Result}; -use crate::diff::ContentChange; +use crate::{diff::ContentChange, filesystem::Fs}; #[derive(Serialize, Deserialize, Debug)] pub struct RepositoryHistory { @@ -17,20 +13,18 @@ pub struct RepositoryHistory { } impl RepositoryHistory { - pub fn from_file(file: &mut File) -> Result<RepositoryHistory> { - let mut buffer = Vec::new(); - file.read_to_end(&mut buffer) + pub fn from_file<FS: Fs>(fs: &mut FS, file: &mut FS::File) -> Result<RepositoryHistory> { + let buffer = fs + .read_from_file(file) .context("Failed reading repository history.")?; let repository_history = serde_json::from_slice::<RepositoryHistory>(&buffer); repository_history.context("Corrupted repository history.") } - pub fn write_to_file(&self, file: &mut File) -> anyhow::Result<()> { + pub fn write_to_file<FS: Fs>(&self, fs: &mut FS, file: &mut FS::File) -> anyhow::Result<()> { let encoded: Vec<u8> = serde_json::to_vec(self)?; - file.rewind()?; - file.set_len(0)?; - file.write_all(encoded.as_ref())?; + fs.write_to_file(file, encoded)?; Ok(()) } @@ -64,20 +58,18 @@ pub struct FileHistory { } impl FileHistory { - pub fn from_file(file: &mut File) -> Result<FileHistory> { - let mut buffer = Vec::new(); - file.read_to_end(&mut buffer) + pub fn from_file<FS: Fs>(fs: &mut FS, file: &mut FS::File) -> Result<FileHistory> { + let buffer = fs + .read_from_file(file) .context("Failed reading file history.")?; let file_history = serde_json::from_slice::<FileHistory>(&buffer); file_history.context("Corrupted file history.") } - pub fn write_to_file(&self, file: &mut File) -> anyhow::Result<()> { + pub fn write_to_file<FS: Fs>(&self, fs: &mut FS, file: &mut FS::File) -> Result<()> { let encoded: Vec<u8> = serde_json::to_vec(self)?; - file.rewind()?; - file.set_len(0)?; - file.write_all(encoded.as_ref())?; + fs.write_to_file(file, encoded)?; Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index 3c489c5..34937ba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ pub mod actions; +pub mod filesystem; -mod history; mod diff; mod files; -mod filesystem; +mod history; |
