new: Do argparsing using clap-rs instead of handwritten parser

This commit is contained in:
Sayantan Santra 2024-03-06 02:56:48 -06:00
parent b62bffd340
commit 67dcd9e378
Signed by: SinTan1729
GPG key ID: EB3E68BFBA25C85F
4 changed files with 139 additions and 93 deletions

94
Cargo.lock generated
View file

@ -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"

View file

@ -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

View file

@ -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<String>) -> (Vec<String>, 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 <filename(s)> [-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<String>, HashMap<String, bool>) {
let matches = command!()
.name("movie-rename")
.author("Sayantan Santra <sayantan.santra@gmail.com>")
.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<String> = matches
.get_many::<String>("entries")
.expect("No entries provided!")
.cloned()
.collect();
(entries, settings)
}

View file

@ -9,11 +9,11 @@ mod structs;
#[tokio::main]
async fn main() {
// Read arguments from commandline
let args: Vec<String> = 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!");