use crate::{
fsio::list_current_dir,
param::{ExecMode, Param},
};
use colored::Colorize;
use rust_i18n::t;
use std::{collections::HashMap, fs, path::PathBuf};
use unicode_display_width::width;
pub(crate) fn execute(param: &Param) {
let paths = list_current_dir(param);
match param.get_exec_mode() {
ExecMode::DryRun => print_replace_result(paths, param),
ExecMode::Exec => exec_replace(paths, param),
}
}
fn get_filename(path: &PathBuf) -> Option<String> {
path.file_name()
.and_then(|name| name.to_str())
.map(|s| s.to_string())
}
fn print_replace_result(paths: Vec<PathBuf>, param: &Param) {
if paths.is_empty() {
println!("{}", t!("result.no.found").red());
return;
}
let str_from = t!("label.from");
let str_to = t!("label.to");
let str_status = t!("label.status");
let str_from_len = width(&str_from) as usize;
let str_to_len = width(&str_to) as usize;
let str_status_len = width(&str_status) as usize;
let mut output_vec = Vec::new();
let from_regex = param.get_from_regex();
let mut existed = HashMap::new();
let mut conflect_count = 0;
paths.iter().for_each(|x| {
let from_target = x.file_name()
.map(|name| name.to_string_lossy().to_string())
.unwrap_or_default();
let from_str = get_filename(x).unwrap_or_default();
let to_result = from_regex
.replace_all(from_str.as_str(), ¶m.get_to_pattern())
.to_string();
if existed.contains_key(&to_result) {
output_vec.push((from_target, to_result, t!("status.dup").to_string(), false));
conflect_count += 1;
} else {
existed.insert(to_result.clone(), 0);
output_vec.push((from_target, to_result, t!("status.ok").to_string(), true));
}
});
let max_left_len = output_vec
.iter()
.fold(0, |acc, x| acc.max(width(&x.0) as usize))
.max(str_from_len);
let max_right_len = output_vec
.iter()
.fold(0, |acc, x| acc.max(width(&x.1) as usize))
.max(str_to_len);
let max_status_len = output_vec
.iter()
.fold(0, |acc, x| acc.max(width(&x.2) as usize))
.max(str_status_len);
let separator = format!("*{}*{}*{}*",
"-".repeat(max_left_len + 2),
"-".repeat(max_right_len + 2),
"-".repeat(max_status_len + 2));
println!("{}", separator);
println!("| {} | {} | {} |",
fill(&str_from.to_string(), max_left_len).green(),
fill(&str_to.to_string(), max_right_len).green(),
fill(&str_status.to_string(), max_status_len).green(),
);
println!("{}", separator);
output_vec.iter().for_each(|x| {
let status = fill(&x.2, max_status_len);
println!(
"| {} | {} | {} |",
fill(&x.0, max_left_len),
fill(&x.1, max_right_len),
if x.3 { status.green() } else { status.red() },
)
});
println!("{}", separator);
if conflect_count == 0 {
println!("{}", t!("result.dryrun.suc").green());
} else {
println!(
"{}",
t!("result.dryrun.fail",conflect_count=> conflect_count).red()
);
}
}
fn fill(source: &String, max: usize) -> String {
let display_width = width(source) as usize;
let padding_needed = max - display_width;
format!("{}{}", source, " ".repeat(padding_needed))
}
fn exec_replace(paths: Vec<PathBuf>, param: &Param) {
if paths.is_empty() {
println!("{}", t!("result.no.found").red());
return;
}
let from_regex = param.get_from_regex();
let mut existed = HashMap::new();
let mut ready_vec = Vec::new();
let mut conflect_count = 0;
paths.iter().for_each(|x| {
let from_str = get_filename(x).unwrap_or_default();
let to_result = from_regex
.replace_all(from_str.as_str(), ¶m.get_to_pattern())
.to_string();
let to_file = x.parent()
.map(|p| p.join(&to_result))
.unwrap_or_else(|| PathBuf::from(&to_result));
if existed.contains_key(&to_result) {
conflect_count += 1;
} else {
existed.insert(to_result.clone(), 0);
ready_vec.push((x.clone(), to_file));
}
});
if conflect_count == 0 {
for (from_path, to_path) in ready_vec {
let file1 = get_filename(&from_path).unwrap_or_default();
let file2 = get_filename(&to_path).unwrap_or_default();
println!("{}", t!("result.exec.log", file1 => file1, file2 => file2));
if let Err(e) = fs::rename(from_path, to_path) {
eprintln!("{}", t!("result.exec.fail", error => e.to_string()).red());
}
}
} else {
print_replace_result(paths, param);
}
}