From 8bcbf51cf4f7924600eae39db67ff653b48ad95d Mon Sep 17 00:00:00 2001 From: Mel Date: Mon, 20 Sep 2021 17:36:40 +0200 Subject: Extract CLI --- src/lib.rs | 257 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 279 ------------------------------------------------------------ 2 files changed, 257 insertions(+), 279 deletions(-) create mode 100644 src/lib.rs delete mode 100644 src/main.rs (limited to 'src') diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..3a05db5 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,257 @@ +use difference::{Changeset, Difference}; +use serde::{Deserialize, Serialize}; +use std::{ + fs::{self, DirEntry, File, OpenOptions}, + io::{self, Read, Seek, Write}, + path::{Path, PathBuf}, + vec, +}; + +pub fn create_command() -> io::Result<()> { + let repository_directory = Path::new("./repository"); + + let ka_directory = repository_directory.join(".ka"); + let ka_files_directory = ka_directory.join("files"); + + if ka_directory.exists() { + fs::remove_dir_all(ka_directory.as_path())?; + } + + fs::create_dir(ka_directory)?; + fs::create_dir(ka_files_directory)?; + + update_command()?; + + Ok(()) +} + +pub fn update_command() -> io::Result<()> { + let repository_directory = Path::new("./repository"); + + let ka_directory = repository_directory.join(".ka"); + + let entries: Vec = fs::read_dir(repository_directory)? + .map(Result::unwrap) + .filter(|entry| entry.path() != ka_directory) + .flat_map(flatten_directories) + .collect(); + + for element in entries.iter() { + update_file(element.path())?; + } + + Ok(()) +} + +pub fn shift_command(path: &str, new_cursor: usize) { + let repository_directory = Path::new("./repository"); + + let ka_directory = repository_directory.join(".ka"); + let ka_files_directory = ka_directory.join("files"); + + let file_path = Path::new(path); + + let mut tracked_file = File::create(file_path).expect("Could not find file."); + + let history_file_path = + ka_files_directory.join(file_path.strip_prefix(repository_directory).unwrap()); + let mut history_file = OpenOptions::new() + .write(true) + .read(true) + .open(history_file_path) + .expect("Could not find corresponding history file"); + + let mut current_history = + read_history_from_file(&mut history_file).expect("Failed reading history from file."); + let state_at_cursor = get_state_from_history_for_cursor(¤t_history, new_cursor); + + println!("{}", state_at_cursor); + println!("{:?}", current_history); + + tracked_file + .write_all(state_at_cursor.as_bytes()) + .expect("Failed writing new state"); + + current_history.cursor = new_cursor; + write_history_to_file(&mut history_file, ¤t_history) + .expect("Could not write new cursor to history file."); +} + +fn flatten_directories(entry: DirEntry) -> Vec { + 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(flatten_directories) + .collect() + } else { + vec![entry] + } +} + +fn update_file(file_path: PathBuf) -> io::Result<()> { + let repository_directory = Path::new("./repository"); + + let ka_directory = repository_directory.join(".ka"); + let ka_files_directory = ka_directory.join("files"); + + let mut tracked_file = File::open(file_path.as_path()).expect("Could not find tracked file."); + + let history_file_path = + ka_files_directory.join(file_path.strip_prefix(repository_directory).unwrap()); + + let (mut history_file, current_file_history) = if history_file_path.exists() { + let mut history_file = OpenOptions::new() + .read(true) + .write(true) + .open(history_file_path) + .expect("Could not open history file."); + + let file_history = read_history_from_file(&mut history_file).unwrap(); + + history_file + .set_len(0) + .expect("Failed truncating old history file."); + + history_file + .rewind() + .expect("Failed rewinding history file."); + + (history_file, file_history) + } else { + history_file_path.parent().map(|dir_path| { + if !dir_path.exists() { + fs::create_dir_all(dir_path).unwrap(); + } + }); + + let file = File::create(history_file_path).expect("Could not create history file."); + + let empty_file_history = FileHistory { + cursor: 0, + changes: Vec::new(), + }; + + (file, empty_file_history) + }; + + let mut new_content = String::new(); + tracked_file + .read_to_string(&mut new_content) + .expect("Could not read tracked file."); + + let old_content = + get_state_from_history_for_cursor(¤t_file_history, current_file_history.cursor); + + let change_set = Changeset::new(&old_content, &new_content, ""); + + let mut changes = Vec::new(); + let mut at: usize = 0; + for diff in change_set.diffs.iter() { + match diff { + Difference::Add(new_content) => { + let change = TextChange::Inserted(TextInserted { + at, + new_content: new_content.to_owned(), + }); + at += new_content.len(); + changes.push(change); + } + Difference::Rem(removed_content) => { + changes.push(TextChange::Deleted(TextDeleted { + at, + upto: removed_content.len(), + })); + } + Difference::Same(same_content) => { + at += same_content.len(); + } + } + } + + let mut file_changes = current_file_history.changes; + println!("{:?}", file_changes); + file_changes.push(FileChange::Updated(FileUpdated { changes })); + + let new_file_history = FileHistory { + changes: file_changes, + cursor: current_file_history.cursor + 1, + }; + + write_history_to_file(&mut history_file, &new_file_history) + .expect("Failed writing new history."); + + Ok(()) +} + +fn write_history_to_file(file: &mut File, history: &FileHistory) -> io::Result<()> { + let encoded: Vec = serde_json::to_vec(history).unwrap(); + file.write_all(encoded.as_ref())?; + Ok(()) +} + +fn read_history_from_file(file: &mut File) -> Result { + let mut buffer = Vec::new(); + file.read_to_end(&mut buffer) + .expect("Could not read file history,"); + + let file_history = serde_json::from_slice::(&buffer); + Ok(file_history.expect("Corrupted file history.")) +} + +fn get_state_from_history_for_cursor(history: &FileHistory, cursor: usize) -> String { + let mut buffer = String::new(); + for file_change in history.changes.iter().take(cursor) { + if let FileChange::Updated(ref updated) = file_change { + for change in updated.changes.iter() { + match change { + TextChange::Deleted(deletion) => { + buffer.replace_range(deletion.at..deletion.upto, ""); + } + TextChange::Inserted(insertion) => { + buffer.insert_str(insertion.at, insertion.new_content.as_str()); + } + } + } + } else { + buffer = String::new(); + } + } + buffer +} + +#[derive(Serialize, Deserialize, Debug)] +struct FileHistory { + cursor: usize, + changes: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +enum FileChange { + Updated(FileUpdated), + Deleted, +} + +#[derive(Serialize, Deserialize, Debug)] +struct FileUpdated { + changes: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +enum TextChange { + Inserted(TextInserted), + Deleted(TextDeleted), +} + +#[derive(Serialize, Deserialize, Debug)] +struct TextInserted { + at: usize, + new_content: String, +} + +#[derive(Serialize, Deserialize, Debug)] +struct TextDeleted { + at: usize, + upto: usize, +} diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index fcbb4d0..0000000 --- a/src/main.rs +++ /dev/null @@ -1,279 +0,0 @@ -use difference::{Changeset, Difference}; -use serde::{Deserialize, Serialize}; -use std::{ - env, - fs::{self, DirEntry, File, OpenOptions}, - io::{self, Read, Seek, Write}, - path::{Path, PathBuf}, - vec, -}; - -fn main() { - let args: Vec = env::args().collect(); - let command = args[1].as_str(); - match command { - "create" => { - create_command().unwrap(); - } - "update" => { - update_command().unwrap(); - } - "shift" => { - let file_path = args[2].as_str(); - - let new_cursor: usize = args[3].as_str().parse().expect("Invalid cursor."); - - shift_command(file_path, new_cursor); - } - _ => println!("Unknown command: {}", command), - } -} - -fn create_command() -> io::Result<()> { - let repository_directory = Path::new("./repository"); - - let ka_directory = repository_directory.join(".ka"); - let ka_files_directory = ka_directory.join("files"); - - if ka_directory.exists() { - fs::remove_dir_all(ka_directory.as_path())?; - } - - fs::create_dir(ka_directory)?; - fs::create_dir(ka_files_directory)?; - - update_command()?; - - Ok(()) -} - -fn update_command() -> io::Result<()> { - let repository_directory = Path::new("./repository"); - - let ka_directory = repository_directory.join(".ka"); - - let entries: Vec = fs::read_dir(repository_directory)? - .map(Result::unwrap) - .filter(|entry| entry.path() != ka_directory) - .flat_map(flatten_directories) - .collect(); - - for element in entries.iter() { - update_file(element.path())?; - } - - Ok(()) -} - -fn shift_command(path: &str, new_cursor: usize) { - let repository_directory = Path::new("./repository"); - - let ka_directory = repository_directory.join(".ka"); - let ka_files_directory = ka_directory.join("files"); - - let file_path = Path::new(path); - - let mut tracked_file = File::create(file_path).expect("Could not find file."); - - let history_file_path = - ka_files_directory.join(file_path.strip_prefix(repository_directory).unwrap()); - let mut history_file = OpenOptions::new() - .write(true) - .read(true) - .open(history_file_path) - .expect("Could not find corresponding history file"); - - let mut current_history = - read_history_from_file(&mut history_file).expect("Failed reading history from file."); - let state_at_cursor = get_state_from_history_for_cursor(¤t_history, new_cursor); - - println!("{}", state_at_cursor); - println!("{:?}", current_history); - - tracked_file - .write_all(state_at_cursor.as_bytes()) - .expect("Failed writing new state"); - - current_history.cursor = new_cursor; - write_history_to_file(&mut history_file, ¤t_history) - .expect("Could not write new cursor to history file."); -} - -fn flatten_directories(entry: DirEntry) -> Vec { - 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(flatten_directories) - .collect() - } else { - vec![entry] - } -} - -fn update_file(file_path: PathBuf) -> io::Result<()> { - let repository_directory = Path::new("./repository"); - - let ka_directory = repository_directory.join(".ka"); - let ka_files_directory = ka_directory.join("files"); - - let mut tracked_file = File::open(file_path.as_path()).expect("Could not find tracked file."); - - let history_file_path = - ka_files_directory.join(file_path.strip_prefix(repository_directory).unwrap()); - - let (mut history_file, current_file_history) = if history_file_path.exists() { - let mut history_file = OpenOptions::new() - .read(true) - .write(true) - .open(history_file_path) - .expect("Could not open history file."); - - let file_history = read_history_from_file(&mut history_file).unwrap(); - - history_file - .set_len(0) - .expect("Failed truncating old history file."); - - history_file - .rewind() - .expect("Failed rewinding history file."); - - (history_file, file_history) - } else { - history_file_path.parent().map(|dir_path| { - if !dir_path.exists() { - fs::create_dir_all(dir_path).unwrap(); - } - }); - - let file = File::create(history_file_path).expect("Could not create history file."); - - let empty_file_history = FileHistory { - cursor: 0, - changes: Vec::new(), - }; - - (file, empty_file_history) - }; - - let mut new_content = String::new(); - tracked_file - .read_to_string(&mut new_content) - .expect("Could not read tracked file."); - - let old_content = - get_state_from_history_for_cursor(¤t_file_history, current_file_history.cursor); - - let change_set = Changeset::new(&old_content, &new_content, ""); - - let mut changes = Vec::new(); - let mut at: usize = 0; - for diff in change_set.diffs.iter() { - match diff { - Difference::Add(new_content) => { - let change = TextChange::Inserted(TextInserted { - at, - new_content: new_content.to_owned(), - }); - at += new_content.len(); - changes.push(change); - } - Difference::Rem(removed_content) => { - changes.push(TextChange::Deleted(TextDeleted { - at, - upto: removed_content.len(), - })); - } - Difference::Same(same_content) => { - at += same_content.len(); - } - } - } - - let mut file_changes = current_file_history.changes; - println!("{:?}", file_changes); - file_changes.push(FileChange::Updated(FileUpdated { changes })); - - let new_file_history = FileHistory { - changes: file_changes, - cursor: current_file_history.cursor + 1, - }; - - write_history_to_file(&mut history_file, &new_file_history) - .expect("Failed writing new history."); - - Ok(()) -} - -fn write_history_to_file(file: &mut File, history: &FileHistory) -> io::Result<()> { - let encoded: Vec = serde_json::to_vec(history).unwrap(); - file.write_all(encoded.as_ref())?; - Ok(()) -} - -fn read_history_from_file(file: &mut File) -> Result { - let mut buffer = Vec::new(); - file.read_to_end(&mut buffer) - .expect("Could not read file history,"); - - let file_history = serde_json::from_slice::(&buffer); - Ok(file_history.expect("Corrupted file history.")) -} - -fn get_state_from_history_for_cursor(history: &FileHistory, cursor: usize) -> String { - let mut buffer = String::new(); - for file_change in history.changes.iter().take(cursor) { - if let FileChange::Updated(ref updated) = file_change { - for change in updated.changes.iter() { - match change { - TextChange::Deleted(deletion) => { - buffer.replace_range(deletion.at..deletion.upto, ""); - } - TextChange::Inserted(insertion) => { - buffer.insert_str(insertion.at, insertion.new_content.as_str()); - } - } - } - } else { - buffer = String::new(); - } - } - buffer -} - -#[derive(Serialize, Deserialize, Debug)] -struct FileHistory { - cursor: usize, - changes: Vec, -} - -#[derive(Serialize, Deserialize, Debug)] -enum FileChange { - Updated(FileUpdated), - Deleted, -} - -#[derive(Serialize, Deserialize, Debug)] -struct FileUpdated { - changes: Vec, -} - -#[derive(Serialize, Deserialize, Debug)] -enum TextChange { - Inserted(TextInserted), - Deleted(TextDeleted), -} - -#[derive(Serialize, Deserialize, Debug)] -struct TextInserted { - at: usize, - new_content: String, -} - -#[derive(Serialize, Deserialize, Debug)] -struct TextDeleted { - at: usize, - upto: usize, -} -- cgit 1.4.1