Skip to content

Commit

Permalink
If move command fail, partial moves are undo.
Browse files Browse the repository at this point in the history
  • Loading branch information
svenslaggare committed Aug 14, 2023
1 parent 1e9742a commit 33c9318
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 12 deletions.
12 changes: 9 additions & 3 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,17 @@ impl App {
let destination = self.get_path(destination)?;

self.note_metadata_storage()?;
self.create_and_execute_commands(self.create_move_commands(

let result = self.create_and_execute_commands(self.create_move_commands(
source,
destination,
force
)?)?;
)?);

if let Err(err) = result {
self.command_interpreter.reset()?;
return Err(err);
}
}
InputCommand::Remove { path } => {
let path = self.get_path(path)?;
Expand Down Expand Up @@ -371,7 +377,7 @@ impl App {
)
}

fn clear_cache(&mut self) {
pub fn clear_cache(&mut self) {
self.note_metadata_storage = None;
}

Expand Down
64 changes: 63 additions & 1 deletion src/app_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use crate::app::{App, AppError, InputCommand};
use crate::command::{Command, CommandError, CommandInterpreter};
use crate::config::{Config, FileConfig};


#[test]
fn test_add() {
use tempfile::TempDir;
Expand Down Expand Up @@ -385,6 +384,69 @@ print(np.square(np.arange(0, 15)))
assert_eq!(2, repository.reflog("HEAD").unwrap().len());
}

#[test]
fn test_add_and_move_dir_to_existing1() {
use tempfile::TempDir;

let temp_repository_dir = TempDir::new().unwrap();
let config = Config::from_env(FileConfig::new(&temp_repository_dir.path().to_path_buf()));
let repository = git2::Repository::init(&config.repository).unwrap();

let note1_path = Path::new("2023/07/test1");
let note1_content = "Test1".to_owned();

let note2_path = Path::new("2023/07/test2");
let note2_content = "Test2".to_owned();

let note3_path = Path::new("2024/07/test2");
let note3_content = "Test3".to_owned();

let mut app = App::new(config).unwrap();

app.create_and_execute_commands(vec![
Command::AddNoteWithContent {
path: note1_path.to_path_buf(),
tags: vec![],
content: note1_content.clone()
},
Command::AddNoteWithContent {
path: note2_path.to_path_buf(),
tags: vec![],
content: note2_content.clone()
},
Command::AddNoteWithContent {
path: note3_path.to_path_buf(),
tags: vec![],
content: note3_content.clone()
}
]).unwrap();
assert_eq!(note1_content, app.note_metadata_storage().unwrap().get_content(note1_path).unwrap());
assert_eq!(note2_content, app.note_metadata_storage().unwrap().get_content(note2_path).unwrap());
assert_eq!(note3_content, app.note_metadata_storage().unwrap().get_content(note3_path).unwrap());
assert_eq!(1, repository.reflog("HEAD").unwrap().len());

let note_id1 = app.note_metadata_storage().unwrap().get_id(note1_path).unwrap();
let note_id2 = app.note_metadata_storage().unwrap().get_id(note2_path).unwrap();
let note_id3 = app.note_metadata_storage().unwrap().get_id(note3_path).unwrap();

let err = app.run(InputCommand::Move { source: Path::new("2023").to_owned(), destination: Path::new("2024").to_owned(), force: false }).err().unwrap();
assert_eq!(1, repository.reflog("HEAD").unwrap().len());
if let AppError::Command(CommandError::NoteExistsAtDestination(err_path)) = err {
app.clear_cache();

assert_eq!(note3_path, err_path);
assert_eq!(note_id1, app.note_metadata_storage().unwrap().get_id(note1_path).unwrap());
assert_eq!(note_id2, app.note_metadata_storage().unwrap().get_id(note2_path).unwrap());
assert_eq!(note_id3, app.note_metadata_storage().unwrap().get_id(note3_path).unwrap());

assert_eq!(note1_content, app.note_metadata_storage().unwrap().get_content(note1_path).unwrap());
assert_eq!(note2_content, app.note_metadata_storage().unwrap().get_content(note2_path).unwrap());
assert_eq!(note3_content, app.note_metadata_storage().unwrap().get_content(note3_path).unwrap());
} else {
assert!(false, "Expected 'NoteAtDestination' error");
}
}

#[test]
fn test_add_and_remove() {
use tempfile::TempDir;
Expand Down
50 changes: 42 additions & 8 deletions src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ pub struct CommandInterpreter {
snippet_runner_manager: SnippetRunnerManger,

index: Option<git2::Index>,
commit_message_lines: OrderedSet<String>
commit_message_lines: OrderedSet<String>,
changed_files: Vec<PathBuf>
}

impl CommandInterpreter {
Expand All @@ -91,7 +92,8 @@ impl CommandInterpreter {
snippet_runner_manager,

index: None,
commit_message_lines: OrderedSet::new()
commit_message_lines: OrderedSet::new(),
changed_files: Vec::new()
}
)
}
Expand Down Expand Up @@ -145,9 +147,7 @@ impl CommandInterpreter {

(self.launch_editor)(&self.config, &abs_content_path).map_err(|err| FailedToEditNote(err.to_string()))?;

let index = self.index()?;
index.add_path(&relative_content_path)?;
index.write()?;
self.edited_file(relative_content_path)?;

self.change_note_tags(&id, clear_tags, add_tags)?;
let changed = self.try_change_last_updated(&id)?;
Expand All @@ -162,9 +162,7 @@ impl CommandInterpreter {

std::fs::write(&abs_content_path, content).map_err(|err| FailedToEditNote(err.to_string()))?;

let index = self.index()?;
index.add_path(&relative_content_path)?;
index.write()?;
self.edited_file(relative_content_path)?;

self.change_note_tags(&id, clear_tags, add_tags)?;
self.try_change_last_updated(&id)?;
Expand Down Expand Up @@ -302,6 +300,7 @@ impl CommandInterpreter {

self.index = None;
self.note_metadata_storage = None;
self.changed_files.clear();
}
}
}
Expand All @@ -316,6 +315,28 @@ impl CommandInterpreter {
Ok(())
}

pub fn reset(&mut self) -> CommandResult<()> {
let repository = self.repository.borrow_mut();
let head = repository.head()?;
let head_commit = head.peel(git2::ObjectType::Commit)?;

let mut checkout_builder = git2::build::CheckoutBuilder::new();
for path in &self.changed_files {
checkout_builder.path(path.to_str().unwrap());
}

repository.reset(
&head_commit,
git2::ResetType::Hard,
Some(&mut checkout_builder)
)?;

self.changed_files.clear();
self.commit_message_lines.clear();

Ok(())
}

fn add_note(&mut self,
id: NoteId, relative_path: &Path,
path: PathBuf, mut tags: Vec<String>) -> CommandResult<()> {
Expand Down Expand Up @@ -364,10 +385,19 @@ impl CommandInterpreter {
index.write()?;

self.commit_message_lines.insert(format!("Deleted note '{}'.", real_path));
self.changed_files.push(relative_metadata_path);

Ok(())
}

fn edited_file(&mut self, path: PathBuf) -> CommandResult<()> {
let index = self.index()?;
index.add_path(&path)?;
index.write()?;
self.changed_files.push(path);
Ok(())
}

fn try_change_last_updated(&mut self, id: &NoteId) -> CommandResult<bool> {
if self.has_git_changes()? {
let (relative_metadata_path, abs_metadata_path) = self.get_note_metadata_path(&id);
Expand All @@ -378,6 +408,8 @@ impl CommandInterpreter {
let index = self.index()?;
index.add_path(&relative_metadata_path)?;
index.write()?;

self.changed_files.push(relative_metadata_path);
Ok(true)
} else {
Ok(false)
Expand Down Expand Up @@ -414,6 +446,8 @@ impl CommandInterpreter {
let index = self.index()?;
index.add_path(&relative_metadata_path)?;
index.write()?;

self.changed_files.push(relative_metadata_path);
}

Ok(())
Expand Down

0 comments on commit 33c9318

Please sign in to comment.