From 67dcd9e3788d3302cad4eeea91e10ea075734e15 Mon Sep 17 00:00:00 2001 From: SinTan1729 Date: Wed, 6 Mar 2024 02:56:48 -0600 Subject: [PATCH] new: Do argparsing using clap-rs instead of handwritten parser --- Cargo.lock | 94 +++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/functions.rs | 111 +++++++++++++++-------------------------------- src/main.rs | 26 ++++------- 4 files changed, 139 insertions(+), 93 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aeec25d..86ce8be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,54 @@ dependencies = [ "memchr", ] +[[package]] +name = "anstream" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + [[package]] name = "async-trait" version = "0.1.77" @@ -104,6 +152,39 @@ dependencies = [ "serde", ] +[[package]] +name = "clap" +version = "4.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_lex" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "core-foundation" version = "0.9.4" @@ -468,6 +549,7 @@ dependencies = [ name = "movie-rename" version = "2.2.2" dependencies = [ + "clap", "inquire", "load_file", "tmdb-api", @@ -845,6 +927,12 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "strsim" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" + [[package]] name = "syn" version = "2.0.52" @@ -1074,6 +1162,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "want" version = "0.3.1" diff --git a/Cargo.toml b/Cargo.toml index 37fb57f..6ca35da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ tmdb-api = "0.5.0" inquire = "0.6.2" load_file = "1.0.1" tokio = { version = "1.32.0", features = ["macros", "rt-multi-thread"] } +clap = { version = "4.5.1", features = ["cargo"] } [profile.release] strip = true diff --git a/src/functions.rs b/src/functions.rs index a173fa1..2246e64 100644 --- a/src/functions.rs +++ b/src/functions.rs @@ -1,8 +1,9 @@ +use clap::{arg, command, ArgAction}; use inquire::{ ui::{Color, IndexPrefix, RenderConfig, Styled}, InquireError, Select, }; -use std::{collections::HashMap, fs, path::Path, process::exit}; +use std::{collections::HashMap, fs, path::Path}; use tmdb_api::{ movie::{credits::MovieCredits, search::MovieSearch}, prelude::Command, @@ -12,9 +13,6 @@ use torrent_name_parser::Metadata; use crate::structs::{get_long_lang, Language, MovieEntry}; -// Get the version from Cargo.toml -const VERSION: &str = env!("CARGO_PKG_VERSION"); - // Function to process movie entries pub async fn process_file( filename: &String, @@ -209,80 +207,41 @@ pub async fn process_file( } // Function to process the passed arguments -pub fn process_args(mut args: Vec) -> (Vec, HashMap<&'static str, bool>) { - // Remove the entry corresponding to the running process - args.remove(0); - let mut entries = Vec::new(); - let mut settings = HashMap::from([("dry_run", false), ("directory", false), ("lucky", false)]); - for arg in args { - match arg.as_str() { - "--help" | "-h" => { - println!(" The expected syntax is:"); - println!( - " movie-rename [-n|--dry-run] [-d|--directory] [-l|--i-feel-lucky] [-v|--version]" - ); - println!( - " There needs to be a config file named config in the $XDG_CONFIG_HOME/movie-rename/ directory." - ); - println!(" It should consist of two lines. The first line should have your TMDb API key."); - println!( - " The second line should have a pattern, that will be used for the rename." - ); - println!(" In the pattern, the variables need to be enclosed in {{}}, the supported variables are `title`, `year` and `director`."); - println!( - " Default pattern is `{{title}} ({{year}}) - {{director}}`. Extension is always kept." - ); - println!(" Passing --directory or -d assumes that the arguments are directory names, which contain exactly one movie and optionally subtitles."); - println!(" Passing --dry-run or -n does a dry tun and only prints out the new names, without actually doing anything."); - println!( - " You can join the short flags -d, -n and -l together (e.g. -dn or -dln)." - ); - println!(" Passing --version or -v shows version and exits."); - println!(" Pass --help to get this again."); - exit(0); - } - "--version" | "-v" => { - println!("movie-rename {VERSION}"); - exit(0); - } - "--dry-run" => { - println!("Doing a dry run..."); - settings.entry("dry_run").and_modify(|x| *x = true); - } - "--i-feel-lucky" => { - println!("Choosing the first option automatically..."); - settings.entry("lucky").and_modify(|x| *x = true); - } - "--directory" => { - println!("Running in directory mode..."); - settings.entry("directory").and_modify(|x| *x = true); - } - other => { - if other.starts_with('-') { - if !other.starts_with("--") && other.len() < 5 { - // Process short args - if other.contains('l') { - println!("Choosing the first option automatically..."); - settings.entry("lucky").and_modify(|x| *x = true); - } - if other.contains('d') { - println!("Running in directory mode..."); - settings.entry("directory").and_modify(|x| *x = true); - } - if other.contains('n') { - println!("Doing a dry run..."); - settings.entry("dry_run").and_modify(|x| *x = true); - } - } else { - eprintln!("Unknown argument passed: {other}"); - exit(1); - } - } else { - entries.push(arg); - } - } +pub fn process_args() -> (Vec, HashMap) { + let matches = command!() + .name("movie-rename") + .author("Sayantan Santra ") + .about("A simple tool to rename movies, written in Rust.") + .arg(arg!(-d --directory "Run in directory mode").action(ArgAction::SetTrue)) + .arg(arg!(-n --"dry-run" "Do a dry run").action(ArgAction::SetTrue)) + .arg(arg!(-l --"i-feel-lucky" "Always choose the first option").action(ArgAction::SetTrue)) + .arg( + arg!([entries] "The files/directories to be processed") + .trailing_var_arg(true) + .num_args(1..) + .required(true), + ) + // Use -v instead of -V for version + .disable_version_flag(true) + .arg(arg!(-v --version "Print version").action(ArgAction::Version)) + .arg_required_else_help(true) + .get_matches(); + + // Generate the settings HashMap from read flags + let mut settings = HashMap::new(); + for id in matches.ids().map(|x| x.as_str()) { + if id != "entries" { + settings.insert(id.to_string(), matches.get_flag(id)); } } + + // Every unmatched argument should be treated as a file entry + let entries: Vec = matches + .get_many::("entries") + .expect("No entries provided!") + .cloned() + .collect(); + (entries, settings) } diff --git a/src/main.rs b/src/main.rs index f21dfde..2f28fd9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,11 +9,11 @@ mod structs; #[tokio::main] async fn main() { - // Read arguments from commandline - let args: Vec = env::args().collect(); - // Process the passed arguments - let (entries, settings) = process_args(args); + let (entries, settings) = process_args(); + let flag_dry_run = *settings.get("dry-run").unwrap_or(&false); + let flag_directory = *settings.get("directory").unwrap_or(&false); + let flag_lucky = *settings.get("i-feel-lucky").unwrap_or(&false); // Try to read config file, or display error let mut config_file = env::var("XDG_CONFIG_HOME").unwrap_or(String::from("$HOME")); @@ -43,20 +43,12 @@ async fn main() { // Iterate over entries for entry in entries { // Check if the file/directory exists on disk and run necessary commands - match settings["directory"] { + match flag_directory { // Normal file false => { if Path::new(entry.as_str()).is_file() { // Process the filename for movie entries - process_file( - &entry, - &tmdb, - pattern, - settings["dry_run"], - settings["lucky"], - None, - ) - .await; + process_file(&entry, &tmdb, pattern, flag_dry_run, flag_lucky, None).await; } else { eprintln!("The file {entry} wasn't found on disk, skipping..."); continue; @@ -77,8 +69,8 @@ async fn main() { &filename, &tmdb, pattern, - settings["dry_run"], - settings["lucky"], + flag_dry_run, + flag_lucky, Some(&movie_list), ) .await; @@ -109,7 +101,7 @@ async fn main() { ); } else { println!("[directory] '{entry_clean}' -> '{name}'",); - if !settings["dry_run"] { + if !flag_dry_run { if !Path::new(name.as_str()).is_dir() { fs::rename(entry, name) .expect("Unable to rename directory!");