use config::{Config, RunMode};
use dumpfile;
use error::*;
use fileutils::{cleanup_paths, create_backup, get_paths};
use solver;
use solver::{Operation, Operations, RenameMap};
use std::fs;
use std::path::{Path, PathBuf};
use std::sync::Arc;
pub struct Renamer {
config: Arc<Config>,
}
impl Renamer {
pub fn new(config: &Arc<Config>) -> Result<Renamer> {
Ok(Renamer {
config: config.clone(),
})
}
pub fn process(&self) -> Result<Operations> {
let operations = match self.config.mode {
RunMode::Simple(_) | RunMode::Recursive { .. } => {
let mut input_paths = get_paths(&self.config.mode);
cleanup_paths(&mut input_paths, self.config.dirs);
let rename_map = self.get_rename_map(&input_paths)?;
solver::solve_rename_order(&rename_map)?
}
RunMode::FromFile { ref path, undo } => {
let operations = dumpfile::read_from_file(&PathBuf::from(path))?;
if undo {
solver::revert_operations(&operations)?
} else {
operations
}
}
};
if self.config.dump {
dumpfile::dump_to_file(&operations)?;
}
Ok(operations)
}
pub fn batch_rename(&self, operations: Operations) -> Result<()> {
for operation in operations {
self.rename(&operation)?;
}
Ok(())
}
fn replace_match(&self, path: &PathBuf) -> PathBuf {
let expression = &self.config.expression;
let replacement = &self.config.replacement;
let file_name = path.file_name().unwrap().to_str().unwrap();
let parent = path.parent();
let target_name = expression.replacen(file_name, self.config.limit, &replacement[..]);
match parent {
None => PathBuf::from(target_name.to_string()),
Some(path) => path.join(Path::new(&target_name.into_owned())),
}
}
fn get_rename_map(&self, paths: &[PathBuf]) -> Result<RenameMap> {
let printer = &self.config.printer;
let colors = &printer.colors;
let mut rename_map = RenameMap::new();
let mut error_string = String::new();
for path in paths {
let target = self.replace_match(&path);
if target != *path {
if let Some(old_path) = rename_map.insert(target.clone(), path.clone()) {
error_string.push_str(
&colors
.error
.paint(format!(
"\n{0}->{2}\n{1}->{2}\n",
old_path.display(),
path.display(),
target.display()
))
.to_string(),
);
}
}
}
if error_string.is_empty() {
Ok(rename_map)
} else {
Err(Error {
kind: ErrorKind::SameFilename,
value: Some(error_string),
})
}
}
fn rename(&self, operation: &Operation) -> Result<()> {
let printer = &self.config.printer;
let colors = &printer.colors;
if self.config.force {
if self.config.backup {
match create_backup(&operation.source) {
Ok(backup) => printer.print(&format!(
"{} Backup created - {}",
colors.info.paint("Info: "),
colors.source.paint(format!(
"{} -> {}",
operation.source.display(),
backup.display()
))
)),
Err(err) => {
return Err(err);
}
}
}
if let Err(err) = fs::rename(&operation.source, &operation.target) {
return Err(Error {
kind: ErrorKind::Rename,
value: Some(format!(
"{} -> {}\n{}",
operation.source.display(),
operation.target.display(),
err
)),
});
} else {
printer.print_operation(&operation.source, &operation.target);
}
} else {
printer.print_operation(&operation.source, &operation.target);
}
Ok(())
}
}
#[cfg(test)]
mod test {
extern crate tempfile;
use super::*;
use config::RunMode;
use output::Printer;
use regex::Regex;
use std::fs;
use std::path::Path;
use std::process;
use std::sync::Arc;
#[test]
fn renamer() {
let tempdir = tempfile::tempdir().expect("Error creating temp directory");
println!("Running test in '{:?}'", tempdir);
let temp_path = tempdir.path().to_str().unwrap();
let mock_dir = format!("{}/mock_dir", temp_path);
let mock_files: Vec<String> = vec![
format!("{}/test_file_1.txt", temp_path),
format!("{}/test_file_2.txt", temp_path),
format!("{}/test_file_1.txt", mock_dir),
format!("{}/test_file_2.txt", mock_dir),
];
fs::create_dir(&mock_dir).expect("Error creating mock directory...");
for file in &mock_files {
fs::File::create(&file).expect("Error creating mock file...");
}
let mock_config = Arc::new(Config {
expression: Regex::new("test").unwrap(),
replacement: "passed".to_string(),
force: true,
backup: true,
dirs: false,
dump: false,
mode: RunMode::Simple(mock_files),
printer: Printer::color(),
limit: 1,
});
let renamer = match Renamer::new(&mock_config) {
Ok(renamer) => renamer,
Err(err) => {
mock_config.printer.print_error(&err);
process::exit(1);
}
};
let operations = match renamer.process() {
Ok(operations) => operations,
Err(err) => {
mock_config.printer.print_error(&err);
process::exit(1);
}
};
if let Err(err) = renamer.batch_rename(operations) {
mock_config.printer.print_error(&err);
process::exit(1);
}
assert!(Path::new(&format!("{}/passed_file_1.txt", temp_path)).exists());
assert!(Path::new(&format!("{}/passed_file_2.txt", temp_path)).exists());
assert!(Path::new(&format!("{}/passed_file_1.txt", mock_dir)).exists());
assert!(Path::new(&format!("{}/passed_file_2.txt", mock_dir)).exists());
assert!(Path::new(&format!("{}/test_file_1.txt.bk", temp_path)).exists());
assert!(Path::new(&format!("{}/test_file_2.txt.bk", temp_path)).exists());
assert!(Path::new(&format!("{}/test_file_1.txt.bk", mock_dir)).exists());
assert!(Path::new(&format!("{}/test_file_2.txt.bk", mock_dir)).exists());
}
#[test]
fn replace_limit() {
let tempdir = tempfile::tempdir().expect("Error creating temp directory");
println!("Running test in '{:?}'", tempdir);
let temp_path = tempdir.path().to_str().unwrap();
let mock_files: Vec<String> = vec![format!("{}/replace_all_aaaaa.txt", temp_path)];
for file in &mock_files {
fs::File::create(&file).expect("Error creating mock file...");
}
let mock_config = Arc::new(Config {
expression: Regex::new("a").unwrap(),
replacement: "b".to_string(),
force: true,
backup: false,
dirs: false,
dump: false,
mode: RunMode::Simple(mock_files),
printer: Printer::color(),
limit: 0,
});
let renamer = match Renamer::new(&mock_config) {
Ok(renamer) => renamer,
Err(err) => {
mock_config.printer.print_error(&err);
process::exit(1);
}
};
let operations = match renamer.process() {
Ok(operations) => operations,
Err(err) => {
mock_config.printer.print_error(&err);
process::exit(1);
}
};
if let Err(err) = renamer.batch_rename(operations) {
mock_config.printer.print_error(&err);
process::exit(1);
}
assert!(Path::new(&format!("{}/replbce_bll_bbbbb.txt", temp_path)).exists());
}
}