Join replace_extra_tags with replace_name to create replace_name_with_tags.
Removed every unwrap() functions now return a Result. Used chatgpt to help me with the to_str() function.
This commit is contained in:
parent
184ff2b8f7
commit
3c083334a8
172
src/lib.rs
172
src/lib.rs
|
@ -1,123 +1,177 @@
|
||||||
use glob::glob;
|
//! The main functions that `rmv` uses.
|
||||||
|
|
||||||
|
use glob::{glob_with, MatchOptions};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
/// A struct representing a custom error for glob-related operations.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Error(String);
|
||||||
|
|
||||||
|
impl From<glob::GlobError> for Error {
|
||||||
|
/// Converts a `GlobError` into a `Error`.
|
||||||
|
fn from(e: glob::GlobError) -> Self {
|
||||||
|
Error(format!("Failed to read glob pattern: {}", e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<glob::PatternError> for Error {
|
||||||
|
/// Converts a `PatternError` into a `Error`.
|
||||||
|
fn from(e: glob::PatternError) -> Self {
|
||||||
|
Error(format!("Failed to parse glob pattern: {}", e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts an optional OsStr to a string slice (&str).
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `s` - An optional OsStr reference.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// Returns a Result containing a string slice (&str) if the conversion succeeds, otherwise an
|
||||||
|
/// Error is returned with a message indicating the reason for failure.
|
||||||
|
fn to_str(s: Option<&std::ffi::OsStr>) -> Result<&str, Error> {
|
||||||
|
s.ok_or_else(|| Error(String::from("Failed to convert OsStr to str")))
|
||||||
|
.and_then(|os_str| {
|
||||||
|
os_str
|
||||||
|
.to_str()
|
||||||
|
.ok_or_else(|| Error(String::from("Failed to convert OsStr to str")))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a vector of file names that match the given `wildcard` pattern in the directory specified by `path`.
|
/// Returns a vector of file names that match the given `wildcard` pattern in the directory specified by `path`.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `path` - A reference to a PathBuf representing the path to the directory.
|
/// * `path` - A reference to a `PathBuf` representing the path to the directory.
|
||||||
/// * `wildcard` - The wildcard pattern to match against file names.
|
/// * `wildcard` - The wildcard pattern to match against file names.
|
||||||
pub fn get_file_list(path: &PathBuf, wildcard: &str) -> Vec<String> {
|
#[inline]
|
||||||
|
pub fn get_file_list(path: &PathBuf, wildcard: &str) -> Result<Vec<String>, Error> {
|
||||||
let pattern = Path::new(path).join(wildcard);
|
let pattern = Path::new(path).join(wildcard);
|
||||||
let dir_entries: Vec<Result<PathBuf, _>> = glob(&pattern.to_string_lossy()).unwrap().collect();
|
let options = MatchOptions {
|
||||||
dir_entries
|
case_sensitive: true,
|
||||||
.into_iter()
|
require_literal_separator: false,
|
||||||
.filter_map(Result::ok)
|
require_literal_leading_dot: false,
|
||||||
.filter(|entry: &PathBuf| entry.is_file())
|
};
|
||||||
.map(|entry| entry.file_name().unwrap().to_string_lossy().to_string())
|
let mut file_names = vec![];
|
||||||
.collect()
|
for entry in glob_with(&pattern.to_string_lossy(), options)? {
|
||||||
}
|
let path = entry?;
|
||||||
|
if path.is_file() {
|
||||||
/// Replaces extra tags in the `new file_name` with corresponding values from the `original_file_name`.
|
if let Some(file_name) = path.file_name() {
|
||||||
///
|
if let Some(name) = file_name.to_str() {
|
||||||
/// Extra tags:
|
file_names.push(name.to_string());
|
||||||
/// * {full_name} - file name with extension
|
|
||||||
/// * {name} - file name without extension
|
|
||||||
/// * {ext} - extension
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `original_file_name` - The original name of the file.
|
|
||||||
/// * `new_file_name` - The new name of the file with tags to replace.
|
|
||||||
pub fn replace_extra_tags(original_file_name: &str, new_file_name: &str) -> String {
|
|
||||||
let path = Path::new(original_file_name);
|
|
||||||
let name = path.file_stem().unwrap().to_str().unwrap();
|
|
||||||
let extension = path.extension().unwrap().to_str().unwrap();
|
|
||||||
|
|
||||||
let re = Regex::new(r"(\{[^{}]+})").unwrap();
|
|
||||||
re.find_iter(new_file_name)
|
|
||||||
.map(|m| {
|
|
||||||
let tag = m.as_str();
|
|
||||||
match tag {
|
|
||||||
"{ext}" => new_file_name.replace(tag, extension),
|
|
||||||
"{full_name}" => new_file_name.replace(tag, original_file_name),
|
|
||||||
"{name}" => new_file_name.replace(tag, name),
|
|
||||||
_ => panic!("Invalid tag: {}", tag),
|
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
.fold(String::new(), |result, s| result + &s)
|
}
|
||||||
|
}
|
||||||
|
Ok(file_names)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Replaces parts of the file name that match a regular expression pattern with a replacement string.
|
/// Replaces parts of the file name that match a regular expression pattern with a replacement string.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `file_name` - The original name of the file.
|
/// * `original_file_name` - The original name of the file.
|
||||||
/// * `pattern` - A regular expression pattern to match against the file name.
|
/// * `pattern` - A regular expression pattern to match against the file name.
|
||||||
/// * `replacement` - The replacement text for the matched pattern.
|
/// * `replacement` - The replacement text for the matched pattern.
|
||||||
pub fn replace_name(file_name: &str, pattern: &str, replacement: &str) -> String {
|
#[inline]
|
||||||
let mut new_file_name = String::from(file_name);
|
pub fn replace_name_with_tags(
|
||||||
let re = Regex::new(pattern).unwrap();
|
original_file_name: &str,
|
||||||
for captures in re.captures_iter(file_name) {
|
pattern: &str,
|
||||||
let mut replaced_text = String::from(replacement);
|
replacement: &str,
|
||||||
|
) -> Result<String, Error> {
|
||||||
|
let path = Path::new(original_file_name);
|
||||||
|
let name = to_str(path.file_stem())?;
|
||||||
|
let extension = match path.extension() {
|
||||||
|
Some(ext) => to_str(Some(ext))?,
|
||||||
|
None => "",
|
||||||
|
};
|
||||||
|
|
||||||
|
let re = Regex::new(pattern).map_err(|e| Error(format!("Failed to create regex: {}", e)))?;
|
||||||
|
let mut new_file_name = String::from(replacement);
|
||||||
|
for captures in re.captures_iter(original_file_name) {
|
||||||
for (i, capture) in captures.iter().enumerate() {
|
for (i, capture) in captures.iter().enumerate() {
|
||||||
if let Some(capture) = capture {
|
if let Some(capture) = capture {
|
||||||
let capture_text = capture.as_str().to_owned();
|
let capture_text = capture.as_str().to_owned();
|
||||||
let replace_key = format!("{{{i}}}");
|
let replace_key = format!("{{{}}}", i);
|
||||||
replaced_text = replaced_text.replace(&replace_key, &capture_text);
|
new_file_name = new_file_name.replace(&replace_key, &capture_text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
new_file_name = replaced_text;
|
|
||||||
}
|
}
|
||||||
replace_extra_tags(file_name, &new_file_name)
|
|
||||||
|
let result = [
|
||||||
|
("{ext}", extension),
|
||||||
|
("{full_name}", original_file_name),
|
||||||
|
("{name}", name),
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.fold(new_file_name, |acc, (tag, val)| acc.replace(tag, val));
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn groups_and_ext_test() {
|
fn groups_and_ext_test() -> Result<(), Error> {
|
||||||
const INPUT: &str = "The Best Show - S01E16 - Bla Bla Bla HDTV-720p.mkv";
|
const INPUT: &str = "The Best Show - S01E16 - Bla Bla Bla HDTV-720p.mkv";
|
||||||
const RESULT: &str = "Season 1 - Episode 16 - The Best Show - Bla Bla Bla - 720p-HDTV.mkv";
|
const RESULT: &str = "Season 1 - Episode 16 - The Best Show - Bla Bla Bla - 720p-HDTV.mkv";
|
||||||
|
|
||||||
const PATTERN: &str = r"(.*) - S0?(\d+)E0?(\d+) - (.*) (HDTV|WEBDL)-(720p|1080p)";
|
const PATTERN: &str = r"(.*) - S0?(\d+)E0?(\d+) - (.*) (HDTV|WEBDL)-(720p|1080p)";
|
||||||
const REPLACEMENT: &str = r"Season {2} - Episode {3} - {1} - {4} - {6}-{5}.{ext}";
|
const REPLACEMENT: &str = r"Season {2} - Episode {3} - {1} - {4} - {6}-{5}.{ext}";
|
||||||
|
|
||||||
assert_eq!(replace_name(INPUT, PATTERN, REPLACEMENT), RESULT);
|
let output = replace_name_with_tags(INPUT, PATTERN, REPLACEMENT)?;
|
||||||
|
assert_eq!(output, RESULT);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn name_test() {
|
fn name_test() -> Result<(), Error> {
|
||||||
const INPUT: &str = "The Best Show - S01E16 - Bla Bla Bla HDTV-720p.mkv";
|
const INPUT: &str = "The Best Show - S01E16 - Bla Bla Bla HDTV-720p.mkv";
|
||||||
const RESULT: &str = "The Best Show - S01E16 - Bla Bla Bla HDTV-720p.crap";
|
const RESULT: &str = "The Best Show - S01E16 - Bla Bla Bla HDTV-720p.crap";
|
||||||
|
|
||||||
const PATTERN: &str = r".";
|
const PATTERN: &str = r".";
|
||||||
const REPLACEMENT: &str = r"{name}.crap";
|
const REPLACEMENT: &str = r"{name}.crap";
|
||||||
|
|
||||||
assert_eq!(replace_name(INPUT, PATTERN, REPLACEMENT), RESULT);
|
let output = replace_name_with_tags(INPUT, PATTERN, REPLACEMENT)?;
|
||||||
|
assert_eq!(output, RESULT);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn full_name_test() {
|
fn full_name_test() -> Result<(), Error> {
|
||||||
const INPUT: &str = "The Best Show - S01E16 - Bla Bla Bla HDTV-720p.mkv";
|
const INPUT: &str = "The Best Show - S01E16 - Bla Bla Bla HDTV-720p.mkv";
|
||||||
const RESULT: &str = "REVIEW - The Best Show - S01E16 - Bla Bla Bla HDTV-720p.mkv";
|
const RESULT: &str = "REVIEW - The Best Show - S01E16 - Bla Bla Bla HDTV-720p.mkv";
|
||||||
|
|
||||||
const PATTERN: &str = r".";
|
const PATTERN: &str = r".";
|
||||||
const REPLACEMENT: &str = r"REVIEW - {full_name}";
|
const REPLACEMENT: &str = r"REVIEW - {full_name}";
|
||||||
|
|
||||||
assert_eq!(replace_name(INPUT, PATTERN, REPLACEMENT), RESULT);
|
let output = replace_name_with_tags(INPUT, PATTERN, REPLACEMENT)?;
|
||||||
|
assert_eq!(output, RESULT);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn file_list_test() {
|
fn file_list_test() -> Result<(), Error> {
|
||||||
let directory = PathBuf::from(".");
|
let directory = PathBuf::from(".");
|
||||||
const WILDCARD: &str = "*.toml";
|
const WILDCARD: &str = "*.toml";
|
||||||
|
|
||||||
assert_eq!(get_file_list(&directory, WILDCARD), vec!["Cargo.toml"]);
|
let result = get_file_list(&directory, WILDCARD)?;
|
||||||
|
assert_eq!(result, vec!["Cargo.toml"]);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty_file_list_test() {
|
fn empty_file_list_test() -> Result<(), Error> {
|
||||||
let directory = PathBuf::from("src");
|
let directory = PathBuf::from("src");
|
||||||
const WILDCARD: &str = "*.exe";
|
const WILDCARD: &str = "*.exe";
|
||||||
|
|
||||||
assert_eq!(get_file_list(&directory, WILDCARD), Vec::<String>::new());
|
let result = get_file_list(&directory, WILDCARD)?;
|
||||||
|
assert_eq!(result, Vec::<String>::new());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
15
src/main.rs
15
src/main.rs
|
@ -7,7 +7,7 @@
|
||||||
//! More information on README.md
|
//! More information on README.md
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use rmv::{get_file_list, replace_name};
|
use rmv::{get_file_list, replace_name_with_tags};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
/// Command line parser with clap
|
/// Command line parser with clap
|
||||||
|
@ -51,8 +51,15 @@ fn main() {
|
||||||
let wildcard = &args.wildcard;
|
let wildcard = &args.wildcard;
|
||||||
|
|
||||||
let files = get_file_list(dir_path, wildcard);
|
let files = get_file_list(dir_path, wildcard);
|
||||||
for file_name in files {
|
match files {
|
||||||
let replaced_name = replace_name(&file_name, pattern, replacement);
|
Ok(file_names) => {
|
||||||
println!("mv \"{}\" \"{}\"", file_name, replaced_name);
|
for file_name in file_names {
|
||||||
|
match replace_name_with_tags(&file_name, pattern, replacement) {
|
||||||
|
Ok(replaced_name) => println!("mv \"{}\" \"{}\"", file_name, replaced_name),
|
||||||
|
Err(e) => println!("Error: {:?}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => println!("Error: {:?}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue