Works as an MVP
This commit is contained in:
parent
a0014cf247
commit
3ba9097489
3 changed files with 329 additions and 20 deletions
151
src/main.rs
151
src/main.rs
|
|
@ -1,13 +1,24 @@
|
|||
use std::fs;
|
||||
use std::fs::File;
|
||||
use std::path::PathBuf;
|
||||
use std::path::Path;
|
||||
use std::io::ErrorKind;
|
||||
use std::io::Write;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::io::{ErrorKind, Write, BufRead, BufReader};
|
||||
use std::process;
|
||||
use std::process::Command;
|
||||
use std::collections::HashMap;
|
||||
use tempfile::NamedTempFile;
|
||||
use dialoguer::Confirm;
|
||||
use rand::distributions::{Alphanumeric, DistString};
|
||||
|
||||
const LOCK_FILE: &str = ".mvwrap";
|
||||
|
||||
struct Cleanup;
|
||||
|
||||
impl Drop for Cleanup {
|
||||
fn drop(&mut self) {
|
||||
unlock_dir();
|
||||
}
|
||||
}
|
||||
|
||||
fn read_current_dir() -> Vec<String> {
|
||||
let paths : Vec<PathBuf> = match fs::read_dir(".") {
|
||||
Err(e) if e.kind() == ErrorKind::NotFound => Vec::new(),
|
||||
|
|
@ -47,7 +58,7 @@ fn unlock_dir() {
|
|||
}
|
||||
}
|
||||
|
||||
fn create_temp_file(paths: Vec<String>) -> String {
|
||||
fn create_temp_file(paths: &Vec<String>) -> String {
|
||||
let mut tmpfile = match NamedTempFile::new(){
|
||||
Ok(file) => file,
|
||||
Err(e) => panic!("Could not create tempfile: {}", e),
|
||||
|
|
@ -67,35 +78,135 @@ fn create_temp_file(paths: Vec<String>) -> String {
|
|||
filepath
|
||||
}
|
||||
|
||||
fn remove_temp_file(tmpfile: String) {
|
||||
if Path::new(&tmpfile).is_file() {
|
||||
let _result = match fs::remove_file(&tmpfile){
|
||||
fn remove_temp_file(tmpfile: &String) {
|
||||
if Path::new(tmpfile).is_file() {
|
||||
let _result = match fs::remove_file(tmpfile){
|
||||
Ok(result) => result,
|
||||
Err(_) => panic!("Could not remove tempfile: {}", &tmpfile),
|
||||
Err(_) => panic!("Could not remove tempfile: {}", tmpfile),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
lock_dir();
|
||||
fn edit_temp_file(tmpfile: &String) {
|
||||
let _output = Command::new("vi")
|
||||
.arg(tmpfile)
|
||||
.status()
|
||||
.expect("failed to execute editor process");
|
||||
|
||||
}
|
||||
|
||||
fn read_temp_file(tmpfile: &String) -> Vec<String> {
|
||||
let file = File::open(tmpfile).expect("no such file");
|
||||
let buf = BufReader::new(file);
|
||||
buf.lines()
|
||||
.map(|l| l.expect("Could not parse line"))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn unique_length(dst_paths: &Vec<String>) -> usize {
|
||||
let map = dst_paths.into_iter().map(|x| (x, x)).collect::<HashMap<_, _>>();
|
||||
map.len()
|
||||
}
|
||||
|
||||
fn unique_filename(dst_paths: &Vec<String>) -> String {
|
||||
let mut string = Alphanumeric.sample_string(&mut rand::thread_rng(), 16);
|
||||
|
||||
// Generate unique name that does not exist in dir and is not in dst_paths
|
||||
while dst_paths.iter().any(|j| j==&string) || Path::new(&string).is_file() {
|
||||
string = Alphanumeric.sample_string(&mut rand::thread_rng(), 16);
|
||||
}
|
||||
string
|
||||
}
|
||||
|
||||
fn move_safely(src_paths: &Vec<String>, dst_paths: &Vec<String>) {
|
||||
let src_len = src_paths.len();
|
||||
let mut intermediate_files: HashMap<String, String> = HashMap::new();
|
||||
|
||||
for i in 0..src_len {
|
||||
if src_paths[i] != dst_paths[i] {
|
||||
// is the destination already in the source list?
|
||||
// if so, an intermediate file is needed
|
||||
if src_paths.iter().any(|j| j==&dst_paths[i]) {
|
||||
let unique = unique_filename(&dst_paths);
|
||||
intermediate_files.insert(unique.clone(), dst_paths[i].clone());
|
||||
fs::rename(&src_paths[i], &unique).expect("failed to rename file");
|
||||
println!("Moving {} -> {}", src_paths[i], unique);
|
||||
} else {
|
||||
fs::rename(&src_paths[i], &dst_paths[i]).expect("failed to rename file");
|
||||
println!("Moving {} -> {}", src_paths[i], dst_paths[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (src, dst) in intermediate_files.iter() {
|
||||
fs::rename(&src, &dst).expect("failed to rename file");
|
||||
println!("Moving {} -> {}", src, dst);
|
||||
}
|
||||
}
|
||||
|
||||
fn run_checks(src_paths: &Vec<String>, dst_paths: &Vec<String>) -> bool {
|
||||
// Make sure there are an equal number of sources and destinations
|
||||
if src_paths.len() != dst_paths.len() {
|
||||
println!("ERROR: Source and target list don't have the same number of lines.");
|
||||
return true
|
||||
}
|
||||
|
||||
// Make sure destination names are not empty
|
||||
if dst_paths.iter().any(|i| i=="") {
|
||||
println!("ERROR: You can't move to empty names.");
|
||||
return true
|
||||
}
|
||||
|
||||
// Make sure all destination files are unique
|
||||
let dst_paths_length_unique = unique_length(&dst_paths);
|
||||
if dst_paths.len() != dst_paths_length_unique {
|
||||
println!("Clashing destination names!");
|
||||
// TODO: show which ones!
|
||||
return true
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Place lockfile to not have multiple mvw processes running in the same dir at the same time
|
||||
lock_dir();
|
||||
let _cleanup = Cleanup; // Cleanup LOCK_FILE on panic
|
||||
|
||||
// read directory contents into vector of Strings
|
||||
let paths = read_current_dir();
|
||||
|
||||
// create named tempfile and fill with paths
|
||||
let temp_file = create_temp_file(paths);
|
||||
let temp_file = create_temp_file(&paths);
|
||||
|
||||
// edit tempfile
|
||||
//display_temp_file(&temp_file);
|
||||
|
||||
// read and process tempfile
|
||||
println!("In file: {}", temp_file);
|
||||
edit_temp_file(&temp_file);
|
||||
|
||||
let contents = fs::read_to_string(temp_file.clone())
|
||||
.expect("Should have been able to read the file");
|
||||
let mut new_paths = read_temp_file(&temp_file);
|
||||
|
||||
println!("With text:\n{contents}");
|
||||
println!("----- END ----");
|
||||
// Compare length, if not equal offer to try again (or panic for now)
|
||||
while run_checks(&paths, &new_paths) {
|
||||
|
||||
remove_temp_file(temp_file);
|
||||
let confirmation = Confirm::new()
|
||||
.with_prompt("Continue editing?")
|
||||
.interact()
|
||||
.unwrap();
|
||||
|
||||
if ! confirmation {
|
||||
unlock_dir();
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
edit_temp_file(&temp_file);
|
||||
new_paths = read_temp_file(&temp_file);
|
||||
}
|
||||
|
||||
// (also don't overwrite existing files that are not in the input list!)
|
||||
|
||||
|
||||
move_safely(&paths, &new_paths);
|
||||
|
||||
//display_temp_file(&temp_file);
|
||||
|
||||
remove_temp_file(&temp_file);
|
||||
unlock_dir();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue