mvw/src/dirlist.rs

167 lines
5.8 KiB
Rust
Raw Normal View History

2024-11-26 22:17:15 +01:00
use std::fs;
use std::fs::File;
use std::path::{Path, PathBuf};
use std::io::{ErrorKind, Write, BufRead, BufReader};
2024-11-26 22:17:15 +01:00
use std::collections::HashMap;
use rand::distributions::{Alphanumeric, DistString};
const LOCK_FILE: &str = ".mvwrap";
2024-11-26 22:17:15 +01:00
pub struct DirList {
safe_source: bool,
noop: bool,
verbose: bool,
entries: Vec<String>,
2024-11-26 22:17:15 +01:00
}
impl DirList {
pub fn from_current_dir() -> Self {
let paths : Vec<PathBuf> = match fs::read_dir(".") {
Err(e) if e.kind() == ErrorKind::NotFound => Vec::new(),
Err(e) => panic!("Unexpected Error! {:?}", e),
Ok(entries) => entries.filter_map(|e| e.ok())
.map(|e| e.path())
.collect()
};
let mut path_list : Vec<String> = vec![];
for path in paths {
let path_string = path.display().to_string();
if path_string != format!("./{}", LOCK_FILE) {
2024-11-26 22:17:15 +01:00
path_list.push(path_string[2..].to_string());
}
}
path_list.sort();
Self {
safe_source: true,
noop: false,
verbose: false,
2024-11-26 22:17:15 +01:00
entries: path_list,
}
}
pub fn from_file(src_file: &String) -> Self {
let file = File::open(src_file).expect("no such file");
let buf = BufReader::new(file);
let lines = buf.lines()
.map(|l| l.expect("Could not parse line"))
.collect();
Self {
safe_source: true,
noop: false,
verbose: false,
2024-11-26 22:17:15 +01:00
entries: lines,
}
}
pub fn from_list(list: &Vec<String>) -> Self {
Self {
safe_source: false,
noop: false,
verbose: false,
2024-11-26 22:17:15 +01:00
entries: list.to_vec(),
}
}
pub fn set_noop(&mut self) {
self.noop = true;
}
pub fn set_verbose(&mut self) {
self.verbose = true;
}
pub fn to_file(&self, target_file: &String) {
let mut file = File::create(target_file).expect("no such file");
for entry in &self.entries {
writeln!(file, "{}", entry).expect("Unable to write to file");
}
}
2024-11-26 22:17:15 +01:00
pub fn move_to(&mut self, target_list: &DirList) -> Result<(), String> {
let src_len = self.entries.len();
let mut intermediate_files: HashMap<String, String> = HashMap::new();
self.run_basic_checks(&target_list)?;
if ! self.safe_source {
println!("Implement check if target files already exist");
}
2024-11-26 22:17:15 +01:00
for i in 0..src_len {
if self.entries[i] != target_list.entries[i] {
// is the destination already in the source list?
// if so, an intermediate file is needed
if self.entries.iter().any(|j| j==&target_list.entries[i]) {
let unique = Self::get_unique_entry(&target_list);
2024-11-26 22:17:15 +01:00
intermediate_files.insert(unique.clone(), target_list.entries[i].clone());
if ! self.noop { fs::rename(&self.entries[i], &unique).expect("failed to rename file"); }
if self.verbose { println!("Moving {} -> {}", self.entries[i], unique); }
2024-11-26 22:17:15 +01:00
} else {
if ! self.noop { fs::rename(&self.entries[i], &target_list.entries[i]).expect("failed to rename file"); }
if self.verbose { println!("Moving {} -> {}", self.entries[i], target_list.entries[i]); }
2024-11-26 22:17:15 +01:00
}
}
}
for (src, dst) in intermediate_files.iter() {
if ! self.noop { fs::rename(src, dst).expect("failed to rename file"); }
if self.verbose { println!("Moving {} -> {}", src, dst); }
2024-11-26 22:17:15 +01:00
}
Ok(())
}
fn get_unique_entry(target_list: &DirList) -> String {
2024-11-26 22:17:15 +01:00
let mut string = Alphanumeric.sample_string(&mut rand::thread_rng(), 16);
// Generate unique name that does not exist in current dir and is not in target_list
2024-11-26 22:17:15 +01:00
while target_list.entries.iter().any(|j| j==&string) || Path::new(&string).is_file() {
string = Alphanumeric.sample_string(&mut rand::thread_rng(), 16);
}
string
}
fn run_basic_checks(&self, target_list: &DirList) -> Result<(), String> {
2024-11-26 22:17:15 +01:00
// Make sure there are an equal number of sources and destinations
if self.entries.len() != target_list.entries.len() {
return Err("ERROR: Source and target list don't have the same number of
lines.".to_string());
}
// Make sure destination names are not empty
if target_list.entries.iter().any(|i| i.is_empty()) {
return Err("ERROR: You can't move to empty names.".to_string());
}
// Make sure all destination files are unique in target_list
let unique_entries = target_list.entries.iter().map(|x| (x, x)).collect::<HashMap<_, _>>();
if target_list.entries.len() != unique_entries.len() {
2024-11-26 22:17:15 +01:00
let doubles = self.show_doubles(&target_list);
let error = format!("ERROR: You're trying to move multiple files to the same name.\n{}", doubles);
return Err(error);
}
Ok(())
}
fn show_doubles(&self, target_list: &DirList) -> String {
let mut paths = HashMap::new();
let mut doubles = String::new();
for (i, _) in target_list.entries.iter().enumerate() {
paths.entry(&target_list.entries[i]).or_insert(Vec::new());
paths.get_mut(&target_list.entries[i]).unwrap().push(i);
}
for (path, lines) in paths.iter() {
if lines.len() > 1 {
for i in lines.iter() {
doubles = format!("{doubles}\n[line: {}] {} -> {}", i+1, self.entries[*i], path);
}
doubles = format!("{doubles}\n");
}
}
doubles
}
}