Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactorings2 #16

Merged
merged 4 commits into from
Sep 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@
name = "muren"
version = "0.1.1"
edition = "2021"
license = "MIT"
authors = ["Jan Pipek <[email protected]"]
readme = "README.md"
description = "Command-line utility for filename manipulations."
homepage = "https://github.com/janpipek/muren"
keywords = ["cli", "rename", "path"]
categories = ["command-line-utilities"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

Expand Down
38 changes: 23 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,31 @@ Command-line utility for filename manipulations.
Usage: muren [OPTIONS] [COMMAND]

Commands:
set-ext Change extension
prefix Prefix with string
replace Replace parts of the name
normalize Convert names to reasonable ASCII.
fix-ext Fix extension according to the file contents.
remove Remove part of a name from all files.
change-case Change case of all files.
help Print this message or the help of the given subcommand(s)
set-ext Change extension
prefix Prefix with string
replace Replace parts of the name
normalize Convert names to reasonable ASCII.
fix-ext Fix extension according to the file contents.
remove Remove part of a name from all files.
help Print this message or the help of the given subcommand(s)

Options:
-d, --dry Dry run
-u, --unchanged Show unchanged files
-y, --yes Automatically confirm all actions
-h, --help Print help
-V, --version Print version
-d, --dry Dry run
-y, --yes Automatically confirm all actions
-h, --help Print help
-V, --version Print version
```

## Why not [rnr](https://github.com/ismaelgv/rnr)?
## Installation

Because this project scratches my particular itch and allows me to try/learn rust.
Once you have [`cargo`](https://doc.rust-lang.org/cargo/getting-started/installation.html) on your system:

```
cargo install muren
```

## Alternatives

- [rnr](https://github.com/ismaelgv/rnr)

Why am I implementing this then? Because this project scratches my particular itch and allows me to try/learn rust.
136 changes: 73 additions & 63 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ extern crate unidecode;
use unidecode::unidecode;

pub struct RenameIntent {
path: PathBuf,
old_name: PathBuf,
new_name: PathBuf,
}

impl RenameIntent {
/// Is the new name different from the old one?
fn is_changed(&self) -> bool {
self.path != self.new_name
self.old_name != self.new_name
}
}

Expand All @@ -26,11 +27,11 @@ impl Display for RenameIntent {
write!(
f,
"{0} → {1}",
self.path.to_string_lossy().red(),
self.old_name.to_string_lossy().red(),
self.new_name.to_string_lossy().green()
)
} else {
write!(f, "{0} =", self.path.to_string_lossy(),)
write!(f, "{0} =", self.old_name.to_string_lossy(),)
}
}
}
Expand Down Expand Up @@ -62,6 +63,7 @@ fn confirm_intents(intents: &Vec<RenameIntent>) -> bool {
input.trim().to_lowercase() == "y"
}

/// Print all renames
fn print_intents(intents: &Vec<RenameIntent>, show_unchanged: bool) {
for intent in intents {
if intent.is_changed() || show_unchanged {
Expand All @@ -70,71 +72,79 @@ fn print_intents(intents: &Vec<RenameIntent>, show_unchanged: bool) {
}
}

/// Find a new name for a single file
fn suggest_rename(path: &PathBuf, command: &RenameCommand) -> RenameIntent {
RenameIntent {
old_name: path.clone(),
new_name: match &command {
RenameCommand::SetExtension(extension) => {
let mut new_name = path.clone();
new_name.set_extension(extension);
new_name
}
RenameCommand::Remove(pattern) => {
let new_name = path.to_string_lossy().replace(pattern, "");
PathBuf::from(new_name)
}
RenameCommand::Prefix(prefix) => {
let mut new_name = prefix.clone();
new_name.push_str(path.to_string_lossy().to_string().as_str());
PathBuf::from(new_name)
}
RenameCommand::Normalize => {
let path_str = path.to_string_lossy().to_string();
let new_name = unidecode(&path_str).replace(' ', "_"); //#.to_lowercase();
PathBuf::from(new_name)
}
RenameCommand::FixExtension(append) => {
let possible_extensions = find_extensions_from_content(path);
let mut new_name = path.clone();
if !has_correct_extension(path, &possible_extensions) {
let mut new_extension = possible_extensions[0].clone();
if *append {
let old_extension = new_name.extension();
if old_extension.is_some() {
new_extension.insert(0, '.');
new_extension.insert_str(0, old_extension.unwrap().to_str().unwrap())
}
}
new_name.set_extension(new_extension);
};
new_name
}
RenameCommand::Replace(pattern, replacement, is_regex) => {
let path_str = path.to_string_lossy().to_string();
let new_name = if *is_regex {
let re = Regex::new(pattern).unwrap();
re.replace_all(&path_str, replacement).to_string()
} else {
path_str.replace(pattern, replacement)
};
PathBuf::from(new_name)
}
RenameCommand::ChangeCase(upper) => {
let path_str = path.to_string_lossy().to_string();
let new_name = match upper {
true => path_str.to_uppercase(),
false => path_str.to_lowercase(),
};
PathBuf::from(new_name)
}
}}
}

fn suggest_renames(files: &[PathBuf], command: &RenameCommand) -> Vec<RenameIntent> {
files
.iter()
.map(|path| RenameIntent{
path: path.clone(),
new_name: match &command {
RenameCommand::SetExtension(extension) => {
let mut new_name = path.clone();
new_name.set_extension(extension);
new_name
}
RenameCommand::Remove(pattern) => {
let new_name = path.to_string_lossy().replace(pattern, "");
PathBuf::from(new_name)
}
RenameCommand::ChangeCase(upper) => {
let path_str = path.to_string_lossy().to_string();
let new_name = match upper {
true => path_str.to_uppercase(),
false => path_str.to_lowercase(),
};
PathBuf::from(new_name)
}
RenameCommand::Prefix(prefix) => {
let mut new_name = prefix.clone();
new_name.push_str(path.to_string_lossy().to_string().as_str());
PathBuf::from(new_name)
}
RenameCommand::Normalize => {
let path_str = path.to_string_lossy().to_string();
let new_name = unidecode(&path_str).replace(' ', "_"); //#.to_lowercase();
PathBuf::from(new_name)
}
RenameCommand::FixExtension(append) => {
let possible_extensions = find_extensions_from_content(path);
let mut new_name = path.clone();
if !has_correct_extension(path, &possible_extensions) {
let mut new_extension = possible_extensions[0].clone();
if *append {
let old_extension = new_name.extension();
if old_extension.is_some() {
new_extension.insert(0, '.');
new_extension.insert_str(0, old_extension.unwrap().to_str().unwrap())
}
}
new_name.set_extension(new_extension);
};
new_name
}
RenameCommand::Replace(pattern, replacement, is_regex) => {
let path_str = path.to_string_lossy().to_string();
let new_name = if *is_regex {
let re = Regex::new(pattern).unwrap();
re.replace_all(&path_str, replacement).to_string()
} else {
path_str.replace(pattern, replacement)
};
PathBuf::from(new_name)
}
}}
)
.map(|path| suggest_rename(
path,
command,
))
.collect()
}

fn infer_mimetype(path: &Path, mime_type: bool) -> Option<String> {
// TODO: Do something on windows :see_no_evil:
let mut cmd = process::Command::new("file");
let cmd_with_args = cmd.arg(path).arg("--brief");
let cmd_with_args = if mime_type {
Expand Down Expand Up @@ -254,7 +264,7 @@ fn process_command(
if confirmed {
for intent in intents {
if intent.is_changed() {
let renamed = try_rename(&intent.path, &intent.new_name);
let renamed = try_rename(&intent.old_name, &intent.new_name);
renamed_count += renamed as i32;
}
if show_unchanged {
Expand Down
Loading