make it work with weird non-UTF characters

This commit is contained in:
Ward Wouts 2025-04-29 15:18:45 +02:00
parent 2936e58e00
commit 14942ed2b0
2 changed files with 51 additions and 22 deletions

2
Cargo.lock generated
View file

@ -206,7 +206,7 @@ checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
[[package]] [[package]]
name = "mvw" name = "mvw"
version = "0.1.4" version = "0.1.5"
dependencies = [ dependencies = [
"clap", "clap",
"dialoguer", "dialoguer",

View file

@ -7,11 +7,25 @@ use rand::distributions::{Alphanumeric, DistString};
const LOCK_FILE: &str = ".mvwrap"; const LOCK_FILE: &str = ".mvwrap";
enum DirListEntry {
Path(PathBuf),
Text(String)
}
impl ToString for DirListEntry {
fn to_string(&self) -> String {
match self {
DirListEntry::Text(line) => line.clone(),
DirListEntry::Path(path) => path.display().to_string(),
}
}
}
pub struct DirList { pub struct DirList {
safe_source: bool, safe_source: bool,
noop: bool, noop: bool,
verbose: bool, verbose: bool,
entries: Vec<String>, entries: Vec<DirListEntry>,
} }
impl DirList { impl DirList {
@ -24,14 +38,15 @@ impl DirList {
.collect() .collect()
}; };
let mut path_list : Vec<String> = vec![]; let mut path_list : Vec<DirListEntry> = vec![];
for path in paths { for path in paths {
let path_string = path.display().to_string(); let path_string = path.display().to_string();
if path_string != format!("./{}", LOCK_FILE) { if path_string != format!("./{}", LOCK_FILE) {
path_list.push(path_string[2..].to_string()); path_list.push(DirListEntry::Path(path));
} }
} }
path_list.sort(); // This sort is probably not /really/ needed, but just nice
//path_list.sort();
Self { Self {
safe_source: true, safe_source: true,
@ -45,7 +60,7 @@ impl DirList {
let file = File::open(src_file).expect("no such file"); let file = File::open(src_file).expect("no such file");
let buf = BufReader::new(file); let buf = BufReader::new(file);
let lines = buf.lines() let lines = buf.lines()
.map(|l| l.expect("Could not parse line")) .map(|l| DirListEntry::Text(l.expect("Could not parse line")))
.collect(); .collect();
Self { Self {
@ -61,7 +76,7 @@ impl DirList {
safe_source: false, safe_source: false,
noop: false, noop: false,
verbose: false, verbose: false,
entries: list.to_vec(), entries: list.iter().map(|l| DirListEntry::Text(l.to_string())).collect(),
} }
} }
@ -77,7 +92,10 @@ impl DirList {
let mut file = File::create(target_file).expect("no such file"); let mut file = File::create(target_file).expect("no such file");
for entry in &self.entries { for entry in &self.entries {
writeln!(file, "{}", entry).expect("Unable to write to file"); match entry {
DirListEntry::Text(line) => writeln!(file, "{}", line).expect("Unable to write to file"),
DirListEntry::Path(path) => writeln!(file, "{}", path.display().to_string()).expect("Unable to write to file"),
}
} }
} }
@ -91,17 +109,27 @@ impl DirList {
} }
for i in 0..src_len { for i in 0..src_len {
if self.entries[i] != target_list.entries[i] { if self.entries[i].to_string() != target_list.entries[i].to_string() {
// is the destination already in the source list? // is the destination already in the source list?
// if so, an intermediate file is needed // if so, an intermediate file is needed
if self.entries.iter().any(|j| j==&target_list.entries[i]) { if self.entries.iter().any(|j| j.to_string() == target_list.entries[i].to_string()) {
let unique = Self::get_unique_entry(target_list); let unique = Self::get_unique_entry(target_list);
intermediate_files.insert(unique.clone(), target_list.entries[i].clone()); intermediate_files.insert(unique.clone(), target_list.entries[i].to_string().clone());
if ! self.noop { fs::rename(&self.entries[i], &unique).expect("failed to rename file"); } if ! self.noop {
if self.verbose { println!("Moving {} -> {}", self.entries[i], unique); } match &self.entries[i] {
DirListEntry::Text(name) => fs::rename(name.to_string(), &unique).expect("failed to rename file"),
DirListEntry::Path(path) => fs::rename(path.as_path(), &unique).expect("failed to rename file"),
}
}
if self.verbose { println!("Moving {} -> {}", self.entries[i].to_string(), unique); }
} else { } else {
if ! self.noop { fs::rename(&self.entries[i], &target_list.entries[i]).expect("failed to rename file"); } if ! self.noop {
if self.verbose { println!("Moving {} -> {}", self.entries[i], target_list.entries[i]); } match &self.entries[i] {
DirListEntry::Text(name) => fs::rename(name.to_string(), &target_list.entries[i].to_string()).expect("failed to rename file"),
DirListEntry::Path(path) => fs::rename(path.as_path(), &target_list.entries[i].to_string()).expect("failed to rename file"),
}
}
if self.verbose { println!("Moving {} -> {}", self.entries[i].to_string(), target_list.entries[i].to_string()); }
} }
} }
} }
@ -116,7 +144,7 @@ impl DirList {
let mut string = Alphanumeric.sample_string(&mut rand::thread_rng(), 16); 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 // Generate unique name that does not exist in current dir and is not in target_list
while target_list.entries.iter().any(|j| j==&string) || Path::new(&string).is_file() { while target_list.entries.iter().any(|j| j.to_string() == string) || Path::new(&string).is_file() {
string = Alphanumeric.sample_string(&mut rand::thread_rng(), 16); string = Alphanumeric.sample_string(&mut rand::thread_rng(), 16);
} }
string string
@ -130,12 +158,12 @@ impl DirList {
} }
// Make sure destination names are not empty // Make sure destination names are not empty
if target_list.entries.iter().any(|i| i.is_empty()) { if target_list.entries.iter().any(|i| i.to_string().is_empty()) {
return Err("ERROR: You can't move to empty names.".to_string()); return Err("ERROR: You can't move to empty names.".to_string());
} }
// Make sure all destination files are unique in target_list // Make sure all destination files are unique in target_list
let unique_entries = target_list.entries.iter().map(|x| (x, x)).collect::<HashMap<_, _>>(); let unique_entries = target_list.entries.iter().map(|x| (x.to_string(), x.to_string())).collect::<HashMap<_, _>>();
if target_list.entries.len() != unique_entries.len() { if target_list.entries.len() != unique_entries.len() {
let doubles = self.show_doubles(target_list); let doubles = self.show_doubles(target_list);
let error = format!("ERROR: You're trying to move multiple files to the same name.\n{}", doubles); let error = format!("ERROR: You're trying to move multiple files to the same name.\n{}", doubles);
@ -148,15 +176,16 @@ impl DirList {
let mut paths = HashMap::new(); let mut paths = HashMap::new();
let mut doubles = String::new(); let mut doubles = String::new();
for (i, _) in target_list.entries.iter().enumerate() { for (linenr, _) in target_list.entries.iter().enumerate() {
paths.entry(&target_list.entries[i]).or_insert(Vec::new()); let path_name = target_list.entries[linenr].to_string().clone();
paths.get_mut(&target_list.entries[i]).unwrap().push(i); paths.entry(target_list.entries[linenr].to_string()).or_insert(Vec::new());
paths.get_mut(&path_name).unwrap().push(linenr);
} }
for (path, lines) in paths.iter() { for (path, lines) in paths.iter() {
if lines.len() > 1 { if lines.len() > 1 {
for i in lines.iter() { for i in lines.iter() {
doubles = format!("{doubles}\n[line: {}] {} -> {}", i+1, self.entries[*i], path); doubles = format!("{doubles}\n[line: {}] {} -> {}", i+1, self.entries[*i].to_string(), path);
} }
doubles = format!("{doubles}\n"); doubles = format!("{doubles}\n");
} }