aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMel <einebeere@gmail.com>2021-10-02 19:55:01 +0200
committerMel <einebeere@gmail.com>2021-10-02 19:55:01 +0200
commit55da5ae8cf731c768d6cc580693068e64234a4af (patch)
tree9024bf8c315f1c150a468db6791acbeec2907601 /src
parentc1eccadf76ca7625d8cff9a52480168aab876e62 (diff)
downloadka-55da5ae8cf731c768d6cc580693068e64234a4af.tar.zst
ka-55da5ae8cf731c768d6cc580693068e64234a4af.zip
Use filesystem abstraction throughout Ka.
Diffstat (limited to 'src')
-rw-r--r--src/actions/create.rs25
-rw-r--r--src/actions/shift.rs40
-rw-r--r--src/actions/update.rs53
-rw-r--r--src/files.rs109
-rw-r--r--src/filesystem.rs141
-rw-r--r--src/history.rs32
-rw-r--r--src/lib.rs4
7 files changed, 245 insertions, 159 deletions
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.")?;
+ .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(working_entries, &|entry| {
+ 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;