From 7cda127fb70a007daaf2066c0a5ae53f923965df Mon Sep 17 00:00:00 2001 From: quambene Date: Wed, 3 Apr 2024 20:18:45 +0200 Subject: [PATCH 1/7] Update dependency --- Cargo.lock | 122 +++++++++++++++++++++++++++++++---------------------- Cargo.toml | 2 +- 2 files changed, 73 insertions(+), 51 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cdade9d..092af1d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -72,12 +72,17 @@ dependencies = [ ] [[package]] -name = "ansi_term" -version = "0.12.1" +name = "anstream" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" dependencies = [ - "winapi", + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", ] [[package]] @@ -86,6 +91,34 @@ 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 = "anyhow" version = "1.0.81" @@ -187,17 +220,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - [[package]] name = "autocfg" version = "1.2.0" @@ -434,19 +456,37 @@ dependencies = [ [[package]] name = "clap" -version = "2.34.0" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" dependencies = [ - "ansi_term", - "atty", - "bitflags 1.3.2", + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", "strsim", - "textwrap", - "unicode-width", - "vec_map", ] +[[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 = "comfy-table" version = "7.1.0" @@ -1000,15 +1040,6 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - [[package]] name = "hermit-abi" version = "0.3.9" @@ -1541,7 +1572,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.9", + "hermit-abi", "libc", ] @@ -2866,9 +2897,9 @@ dependencies = [ [[package]] name = "strsim" -version = "0.8.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" @@ -2973,15 +3004,6 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - [[package]] name = "thiserror" version = "1.0.58" @@ -3229,6 +3251,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 = "uuid" version = "0.8.2" @@ -3253,12 +3281,6 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - [[package]] name = "version_check" version = "0.9.4" diff --git a/Cargo.toml b/Cargo.toml index 9c34d62..d270953 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ serde = { version = "1.0", features = ["derive"] } serde_yaml = "0.9.34" tokio = "1.37" csv = "1.3" -clap = "2.33" +clap = "4.5.4" chrono = "0.4" polars = { version = "0.32", features = ["dtype-u8"] } connectorx = { version = "0.3.2", features = ["src_postgres", "dst_arrow2"] } From e5024070e74c10e06fbd2d530a3a15e69921886d Mon Sep 17 00:00:00 2001 From: quambene Date: Wed, 3 Apr 2024 20:20:51 +0200 Subject: [PATCH 2/7] Update clap --- src/arg.rs | 4 ++-- src/cmd/connect.rs | 6 +++--- src/cmd/init.rs | 2 +- src/cmd/query.rs | 16 ++++++++-------- src/cmd/read.rs | 8 ++++---- src/cmd/send.rs | 14 +++++++------- src/cmd/send_bulk.rs | 18 +++++++++--------- src/cmd/simple_query.rs | 6 +++--- src/email_builder/message.rs | 20 ++++++++++---------- src/email_builder/receiver.rs | 10 +++++----- src/email_provider/aws.rs | 2 +- src/email_transmission/client.rs | 2 +- src/main.rs | 2 +- 13 files changed, 55 insertions(+), 55 deletions(-) diff --git a/src/arg.rs b/src/arg.rs index 94445a4..bc3c2e6 100644 --- a/src/arg.rs +++ b/src/arg.rs @@ -40,8 +40,8 @@ pub mod val { pub const AWS: &str = "aws"; } -pub fn value<'a>(name: &str, matches: &'a ArgMatches<'a>) -> Result<&'a str, anyhow::Error> { - match matches.value_of(name) { +pub fn value<'a>(name: &str, matches: &'a ArgMatches) -> Result<&'a str, anyhow::Error> { + match matches.get_one::<&str>(name) { Some(query) => Ok(query), None => Err(anyhow!("Missing value for argument '{}'", name)), } diff --git a/src/cmd/connect.rs b/src/cmd/connect.rs index 9938bf5..9b92a30 100644 --- a/src/cmd/connect.rs +++ b/src/cmd/connect.rs @@ -8,12 +8,12 @@ use anyhow::{anyhow, Result}; use clap::ArgMatches; pub fn connect(matches: &ArgMatches) -> Result<(), anyhow::Error> { - if matches.is_present(arg::VERBOSE) { + if matches.contains_id(arg::VERBOSE) { println!("matches: {:#?}", matches); } - if matches.is_present(cmd::CONNECT) { - match matches.value_of(cmd::CONNECT) { + if matches.contains_id(cmd::CONNECT) { + match matches.get_one::<&str>(cmd::CONNECT) { Some(connection) => match connection.to_lowercase().as_str() { val::SMTP => { let _client = SmtpClient::new()?; diff --git a/src/cmd/init.rs b/src/cmd/init.rs index 8c9bd91..0dc8656 100644 --- a/src/cmd/init.rs +++ b/src/cmd/init.rs @@ -4,7 +4,7 @@ use clap::ArgMatches; use std::{env, io}; pub fn init(matches: &ArgMatches) -> Result<(), anyhow::Error> { - if matches.is_present(arg::VERBOSE) { + if matches.contains_id(arg::VERBOSE) { println!("matches: {:#?}", matches); } diff --git a/src/cmd/query.rs b/src/cmd/query.rs index 6e99f94..0c99fe0 100644 --- a/src/cmd/query.rs +++ b/src/cmd/query.rs @@ -8,29 +8,29 @@ use clap::ArgMatches; use std::path::Path; pub fn query(matches: &ArgMatches) -> Result<(), anyhow::Error> { - if matches.is_present(arg::VERBOSE) { + if matches.contains_id(arg::VERBOSE) { println!("matches: {:#?}", matches); } - if matches.is_present(cmd::QUERY) { - match matches.value_of(cmd::QUERY) { + if matches.contains_id(cmd::QUERY) { + match matches.get_one::<&str>(cmd::QUERY) { Some(query) => { let now = Utc::now(); let conn_vars = ConnVars::from_env()?; - let ssh_tunnel = matches.value_of(arg::SSH_TUNNEL); + let ssh_tunnel = matches.get_one::<&str>(arg::SSH_TUNNEL).map(|arg| *arg); let connection = DbConnection::new(&conn_vars, ssh_tunnel)?; let mut df_query = sources::query_postgres(&connection, query)?; - if matches.is_present(arg::DISPLAY) { + if matches.contains_id(arg::DISPLAY) { println!("Display query result: {}", df_query); } - if matches.is_present(arg::SAVE) { + if matches.contains_id(arg::SAVE) { let save_dir = Path::new(arg::value(arg::SAVE_DIR, matches)?); // If argument 'FILE_TYPE' is not present the default value 'csv' will be used - match matches.value_of(arg::FILE_TYPE) { - Some(file_type) => match file_type { + match matches.get_one::<&str>(arg::FILE_TYPE) { + Some(file_type) => match *file_type { "csv" => { sources::write_csv(&mut df_query, save_dir, now)?; } diff --git a/src/cmd/read.rs b/src/cmd/read.rs index 47725d7..827fb31 100644 --- a/src/cmd/read.rs +++ b/src/cmd/read.rs @@ -4,17 +4,17 @@ use clap::ArgMatches; use std::path::PathBuf; pub fn read(matches: &ArgMatches) -> Result<(), anyhow::Error> { - if matches.is_present(arg::VERBOSE) { + if matches.contains_id(arg::VERBOSE) { println!("matches: {:#?}", matches); } - if matches.is_present(cmd::READ) { - match matches.value_of(cmd::READ) { + if matches.contains_id(cmd::READ) { + match matches.get_one::<&str>(cmd::READ) { Some(csv_file) => { let path = PathBuf::from(csv_file); let csv = sources::read_csv(&path)?; - if matches.is_present(arg::DISPLAY) { + if matches.contains_id(arg::DISPLAY) { println!("Display csv file: {}", csv); } diff --git a/src/cmd/send.rs b/src/cmd/send.rs index ac4cdef..f7b51c4 100644 --- a/src/cmd/send.rs +++ b/src/cmd/send.rs @@ -11,22 +11,22 @@ use clap::ArgMatches; use std::{io, path::Path, time::SystemTime}; pub fn send(matches: &ArgMatches) -> Result<(), anyhow::Error> { - if matches.is_present(arg::VERBOSE) { + if matches.contains_id(arg::VERBOSE) { println!("matches: {:#?}", matches); } let now = SystemTime::now(); - let dry_run = matches.is_present(arg::DRY_RUN); - let is_archived = matches.is_present(arg::ARCHIVE); + let dry_run = matches.contains_id(arg::DRY_RUN); + let is_archived = matches.contains_id(arg::ARCHIVE); let archive_dir = Path::new(arg::value(arg::ARCHIVE_DIR, matches)?); let sender = Sender(arg::value(arg::SENDER, matches)?); let receiver = Receiver(arg::value(arg::RECEIVER, matches)?); let message = Message::from_args(matches)?; - let attachment = matches.value_of(arg::ATTACHMENT).map(Path::new); + let attachment = matches.get_one::<&str>(arg::ATTACHMENT).map(Path::new); let mime_format = MimeFormat::new(sender, receiver, &message, attachment, now)?; let email = Email::new(sender, receiver, &message, &mime_format)?; - if matches.is_present(arg::DISPLAY) { + if matches.contains_id(arg::DISPLAY) { println!("Display email: {:#?}", email); } @@ -40,7 +40,7 @@ pub fn send(matches: &ArgMatches) -> Result<(), anyhow::Error> { println!("Sending email to 1 receiver ..."); - if matches.is_present(arg::ASSUME_YES) { + if matches.contains_id(arg::ASSUME_YES) { let sent_email = client.send(&email)?; sent_email.display_status(); @@ -62,7 +62,7 @@ pub fn send(matches: &ArgMatches) -> Result<(), anyhow::Error> { } } - if matches.is_present(arg::DRY_RUN) { + if matches.contains_id(arg::DRY_RUN) { println!("Email sent (dry run)"); } else { println!("Email sent"); diff --git a/src/cmd/send_bulk.rs b/src/cmd/send_bulk.rs index 8146951..a97ffb3 100644 --- a/src/cmd/send_bulk.rs +++ b/src/cmd/send_bulk.rs @@ -11,21 +11,21 @@ use clap::ArgMatches; use std::{io, path::Path}; pub fn send_bulk(matches: &ArgMatches) -> Result<(), anyhow::Error> { - if matches.is_present(arg::VERBOSE) { + if matches.contains_id(arg::VERBOSE) { println!("matches: {:#?}", matches); } - let dry_run = matches.is_present(arg::DRY_RUN); - let is_archived = matches.is_present(arg::ARCHIVE); + let dry_run = matches.contains_id(arg::DRY_RUN); + let is_archived = matches.contains_id(arg::ARCHIVE); let archive_dir = Path::new(arg::value(arg::ARCHIVE_DIR, matches)?); let sender = Sender(arg::value(arg::SENDER, matches)?); let receivers = BulkReceiver::from_args(matches)?; let message = Message::from_args(matches)?; - let attachment = matches.value_of(arg::ATTACHMENT).map(Path::new); + let attachment = matches.get_one::<&str>(arg::ATTACHMENT).map(Path::new); - let bulk_email = if matches.is_present(arg::PERSONALIZE) { - if let Some(personalized_columns) = matches.values_of(arg::PERSONALIZE) { - let personalized_columns = personalized_columns.collect::>(); + let bulk_email = if matches.contains_id(arg::PERSONALIZE) { + if let Some(personalized_columns) = matches.get_many::<&str>(arg::PERSONALIZE) { + let personalized_columns = personalized_columns.map(|arg| *arg).collect::>(); BulkEmail::new( sender, &receivers, @@ -42,7 +42,7 @@ pub fn send_bulk(matches: &ArgMatches) -> Result<(), anyhow::Error> { let client = Client::from_args(matches)?; let eml_formatter = EmlFormatter::new(archive_dir)?; - if matches.is_present(arg::DISPLAY) { + if matches.contains_id(arg::DISPLAY) { println!("Display emails: {:#?}", bulk_email); } @@ -50,7 +50,7 @@ pub fn send_bulk(matches: &ArgMatches) -> Result<(), anyhow::Error> { println!("Dry run: {}", format_green("activated")); } - if matches.is_present(arg::ASSUME_YES) { + if matches.contains_id(arg::ASSUME_YES) { process_emails( &client, &eml_formatter, diff --git a/src/cmd/simple_query.rs b/src/cmd/simple_query.rs index c9426cc..209def8 100644 --- a/src/cmd/simple_query.rs +++ b/src/cmd/simple_query.rs @@ -4,13 +4,13 @@ use clap::ArgMatches; use postgres::{Client, NoTls, SimpleQueryMessage}; pub fn simple_query(matches: &ArgMatches) -> Result<(), anyhow::Error> { - if matches.is_present(arg::VERBOSE) { + if matches.contains_id(arg::VERBOSE) { println!("matches: {:#?}", matches); } - if matches.is_present(cmd::SIMPLE_QUERY) { + if matches.contains_id(cmd::SIMPLE_QUERY) { let conn_vars = ConnVars::from_env()?; - let simple_query = match matches.value_of(cmd::SIMPLE_QUERY) { + let simple_query = match matches.get_one::<&str>(cmd::SIMPLE_QUERY) { Some(query) => query, None => { return Err(anyhow!( diff --git a/src/email_builder/message.rs b/src/email_builder/message.rs index 35a1e2d..a7c8c05 100644 --- a/src/email_builder/message.rs +++ b/src/email_builder/message.rs @@ -41,13 +41,13 @@ impl Message { } pub fn from_args(matches: &ArgMatches) -> Result { - if matches.is_present(arg::SUBJECT) && matches.is_present(arg::CONTENT) { + if matches.contains_id(arg::SUBJECT) && matches.contains_id(arg::CONTENT) { match ( - matches.value_of(arg::SUBJECT), - matches.value_of(arg::CONTENT), + matches.get_one::<&str>(arg::SUBJECT), + matches.get_one::<&str>(arg::CONTENT), ) { (Some(subject), Some(content)) => { - let message = Message::new(subject, Some(content), None); + let message = Message::new(*subject, Some(content), None); Ok(message) } (Some(_), None) => Err(anyhow!("Missing value for argument '{}'", arg::CONTENT)), @@ -58,22 +58,22 @@ impl Message { arg::CONTENT )), } - } else if matches.is_present(arg::MESSAGE_FILE) { + } else if matches.contains_id(arg::MESSAGE_FILE) { let message_file = arg::value(arg::MESSAGE_FILE, matches)?; let message_path = Path::new(message_file); let message = Message::read_yaml(message_path)?; - if matches.is_present(arg::DISPLAY) { + if matches.contains_id(arg::DISPLAY) { println!("Display message file: {:#?}", message); } Ok(message) - } else if matches.is_present(arg::SUBJECT) - && (matches.is_present(arg::TEXT_FILE) || matches.is_present(arg::HTML_FILE)) + } else if matches.contains_id(arg::SUBJECT) + && (matches.contains_id(arg::TEXT_FILE) || matches.contains_id(arg::HTML_FILE)) { let subject = arg::value(arg::SUBJECT, matches)?; - let text_path = matches.value_of(arg::TEXT_FILE).map(Path::new); - let html_path = matches.value_of(arg::HTML_FILE).map(Path::new); + let text_path = matches.get_one::<&str>(arg::TEXT_FILE).map(Path::new); + let html_path = matches.get_one::<&str>(arg::HTML_FILE).map(Path::new); let text = if let Some(path) = text_path { Some(utils::read_file(path)?) } else { diff --git a/src/email_builder/receiver.rs b/src/email_builder/receiver.rs index 9b79173..71235e7 100644 --- a/src/email_builder/receiver.rs +++ b/src/email_builder/receiver.rs @@ -36,17 +36,17 @@ impl BulkReceiver { pub fn from_args(matches: &ArgMatches) -> Result { let column_name = arg::value(arg::RECEIVER_COLUMN, matches)?; - let receiver_query = matches.value_of(arg::RECEIVER_QUERY); - let receiver_path = matches.value_of(arg::RECEIVER_FILE).map(Path::new); + let receiver_query = matches.get_one::<&str>(arg::RECEIVER_QUERY); + let receiver_path = matches.get_one::<&str>(arg::RECEIVER_FILE).map(Path::new); match (receiver_query, receiver_path) { (Some(query), None) => { let conn_vars = ConnVars::from_env()?; - let ssh_tunnel = matches.value_of(arg::SSH_TUNNEL); + let ssh_tunnel = matches.get_one::<&str>(arg::SSH_TUNNEL).map(|arg| *arg); let connection = DbConnection::new(&conn_vars, ssh_tunnel)?; let df_receiver = sources::query_postgres(&connection, query)?; - if matches.is_present(arg::DISPLAY) { + if matches.contains_id(arg::DISPLAY) { println!("Display query result: {}", df_receiver); } @@ -58,7 +58,7 @@ impl BulkReceiver { (None, Some(path)) => { let df_receiver = sources::read_csv(path)?; - if matches.is_present(arg::DISPLAY) { + if matches.contains_id(arg::DISPLAY) { println!("Display csv file: {}", df_receiver); } diff --git a/src/email_provider/aws.rs b/src/email_provider/aws.rs index 1643c42..ce1dc59 100644 --- a/src/email_provider/aws.rs +++ b/src/email_provider/aws.rs @@ -28,7 +28,7 @@ impl AwsSesClient { let region_name = region.name().to_string(); // Check if AWS access keys are set in environment - if matches.is_present(arg::DRY_RUN) { + if matches.contains_id(arg::DRY_RUN) { AwsSesClient::get_credentials(&provider).context( "Missing environment variable 'AWS_ACCESS_KEY_ID' and/or 'AWS_SECRET_ACCESS_KEY'", )?; diff --git a/src/email_transmission/client.rs b/src/email_transmission/client.rs index 547fe3d..81cbfee 100644 --- a/src/email_transmission/client.rs +++ b/src/email_transmission/client.rs @@ -42,7 +42,7 @@ impl<'a> Client<'a> { } pub fn from_args(matches: &ArgMatches) -> Result { - if matches.is_present(arg::DRY_RUN) { + if matches.contains_id(arg::DRY_RUN) { let client = MockClient; return Ok(Client::new(TransmissionType::Dry, Box::new(client))); } diff --git a/src/main.rs b/src/main.rs index f5683cb..9ff63f7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ fn main() -> Result<(), anyhow::Error> { let app = app(); let matches = app.get_matches(); - if matches.is_present(arg::VERBOSE) { + if matches.contains_id(arg::VERBOSE) { println!("matches: {:#?}", matches); } From ff410e75a94a7596e83d8e8c29d9758d53d80dc7 Mon Sep 17 00:00:00 2001 From: quambene Date: Wed, 3 Apr 2024 23:43:48 +0200 Subject: [PATCH 3/7] Update clap II --- Cargo.toml | 2 +- src/cmd/query.rs | 2 +- src/cmd/send_bulk.rs | 2 +- src/email_builder/receiver.rs | 2 +- src/lib.rs | 324 +++++++++++++++++++--------------- src/main.rs | 16 +- 6 files changed, 197 insertions(+), 151 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d270953..49da4a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ serde = { version = "1.0", features = ["derive"] } serde_yaml = "0.9.34" tokio = "1.37" csv = "1.3" -clap = "4.5.4" +clap = { version = "4.5.4", features = ["cargo"] } chrono = "0.4" polars = { version = "0.32", features = ["dtype-u8"] } connectorx = { version = "0.3.2", features = ["src_postgres", "dst_arrow2"] } diff --git a/src/cmd/query.rs b/src/cmd/query.rs index 0c99fe0..40e617b 100644 --- a/src/cmd/query.rs +++ b/src/cmd/query.rs @@ -17,7 +17,7 @@ pub fn query(matches: &ArgMatches) -> Result<(), anyhow::Error> { Some(query) => { let now = Utc::now(); let conn_vars = ConnVars::from_env()?; - let ssh_tunnel = matches.get_one::<&str>(arg::SSH_TUNNEL).map(|arg| *arg); + let ssh_tunnel = matches.get_one::<&str>(arg::SSH_TUNNEL).copied(); let connection = DbConnection::new(&conn_vars, ssh_tunnel)?; let mut df_query = sources::query_postgres(&connection, query)?; diff --git a/src/cmd/send_bulk.rs b/src/cmd/send_bulk.rs index a97ffb3..3542dc5 100644 --- a/src/cmd/send_bulk.rs +++ b/src/cmd/send_bulk.rs @@ -25,7 +25,7 @@ pub fn send_bulk(matches: &ArgMatches) -> Result<(), anyhow::Error> { let bulk_email = if matches.contains_id(arg::PERSONALIZE) { if let Some(personalized_columns) = matches.get_many::<&str>(arg::PERSONALIZE) { - let personalized_columns = personalized_columns.map(|arg| *arg).collect::>(); + let personalized_columns = personalized_columns.copied().collect::>(); BulkEmail::new( sender, &receivers, diff --git a/src/email_builder/receiver.rs b/src/email_builder/receiver.rs index 71235e7..2520d3b 100644 --- a/src/email_builder/receiver.rs +++ b/src/email_builder/receiver.rs @@ -42,7 +42,7 @@ impl BulkReceiver { match (receiver_query, receiver_path) { (Some(query), None) => { let conn_vars = ConnVars::from_env()?; - let ssh_tunnel = matches.get_one::<&str>(arg::SSH_TUNNEL).map(|arg| *arg); + let ssh_tunnel = matches.get_one::<&str>(arg::SSH_TUNNEL).copied(); let connection = DbConnection::new(&conn_vars, ssh_tunnel)?; let df_receiver = sources::query_postgres(&connection, query)?; diff --git a/src/lib.rs b/src/lib.rs index 0b09d2a..4a655a1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,314 +14,360 @@ mod sources; mod utils; use arg::val; -use clap::{crate_name, crate_version, App, Arg, SubCommand}; +use clap::{crate_name, crate_version, Arg, Command}; /// Create the CLI app to get the matches. -pub fn app() -> App<'static, 'static> { - App::new(crate_name!()) +pub fn app() -> Command { + Command::new(crate_name!()) .version(crate_version!()) .arg( - clap::Arg::with_name(arg::VERBOSE) + clap::Arg::new(arg::VERBOSE) .long(arg::VERBOSE) - .takes_value(false) + .required(false) .help("Shows what is going on"), ) .subcommand( - SubCommand::with_name(cmd::INIT) + Command::new(cmd::INIT) .about("Create template files in current directory") - .args(&[Arg::with_name(arg::VERBOSE) + .args(&[Arg::new(arg::VERBOSE) .long(arg::VERBOSE) - .takes_value(false) + .required(false) .help("Shows what is going on for subcommand")]), ) .subcommand( - SubCommand::with_name(cmd::CONNECT) + Command::new(cmd::CONNECT) .about("Check connection to SMTP server or email provider") .args(&[ - Arg::with_name(cmd::CONNECT) - .takes_value(true) - .possible_values(&[val::SMTP, val::AWS]) + Arg::new(cmd::CONNECT) + .required(true) + .value_parser([val::SMTP, val::AWS]) .default_value(val::SMTP) .help("Check connection to SMTP server."), - Arg::with_name(arg::VERBOSE) + Arg::new(arg::VERBOSE) .long(arg::VERBOSE) - .takes_value(false) + .required(false) .help("Shows what is going on for subcommand"), ]), ) .subcommand( - SubCommand::with_name(cmd::QUERY) + Command::new(cmd::QUERY) .about("Query database and display results in terminal (select statements only)") .args(&[ - Arg::with_name(cmd::QUERY) + Arg::new(cmd::QUERY) .index(1) + .num_args(1) .required(true) - .takes_value(true) .help("Takes a sql query"), - Arg::with_name(arg::SSH_TUNNEL) + Arg::new(arg::SSH_TUNNEL) .long(arg::SSH_TUNNEL) .value_name("port") - .takes_value(true) + .num_args(1) + .required(true) .help("Connect to db through ssh tunnel"), - Arg::with_name(arg::SAVE) + Arg::new(arg::SAVE) .long(arg::SAVE) - .takes_value(false) + .num_args(1) + .required(false) .help("Save query result"), - Arg::with_name(arg::SAVE_DIR) + Arg::new(arg::SAVE_DIR) .long(arg::SAVE_DIR) - .takes_value(true) + .num_args(1) .default_value("./saved_queries") .help("Specifies the output directory for saved query"), - Arg::with_name(arg::FILE_TYPE) + Arg::new(arg::FILE_TYPE) .long(arg::FILE_TYPE) - .takes_value(true) + .num_args(1) + .required(true) .default_value("csv") - .possible_values(&["csv", "jpg", "png"]) + .value_parser(["csv", "jpg", "png"]) .help("Specifies the file type for saved query"), - Arg::with_name(arg::IMAGE_COLUMN) + Arg::new(arg::IMAGE_COLUMN) .long(arg::IMAGE_COLUMN) - .required_ifs(&[(arg::FILE_TYPE, "jpg"), (arg::FILE_TYPE, "png")]) - .takes_value(true) + .requires_ifs([(arg::FILE_TYPE, "jpg"), (arg::FILE_TYPE, "png")]) + .num_args(1) + .required(true) .help("Specifies the column in which to look for images"), - Arg::with_name(arg::IMAGE_NAME) + Arg::new(arg::IMAGE_NAME) .long(arg::IMAGE_NAME) - .required_ifs(&[(arg::FILE_TYPE, "jpg"), (arg::FILE_TYPE, "png")]) - .takes_value(true) + .requires_ifs([(arg::FILE_TYPE, "jpg"), (arg::FILE_TYPE, "png")]) + .num_args(1) + .required(true) .help("Specifies the column used for the image name"), - Arg::with_name(arg::DISPLAY) + Arg::new(arg::DISPLAY) .long(arg::DISPLAY) - .takes_value(false) + .num_args(1) + .required(false) .help("Print query result to terminal"), - Arg::with_name(arg::VERBOSE) + Arg::new(arg::VERBOSE) .long(arg::VERBOSE) - .takes_value(false) + .num_args(1) + .required(false) .help("Shows what is going on for subcommand"), ]), ) .subcommand( - SubCommand::with_name(cmd::SIMPLE_QUERY) + Command::new(cmd::SIMPLE_QUERY) .about("Simple query using the simple query protocol") .args(&[ - Arg::with_name(cmd::SIMPLE_QUERY) + Arg::new(cmd::SIMPLE_QUERY) .index(1) .required(true) - .takes_value(true) .help("Takes a sql query"), - Arg::with_name(arg::VERBOSE) + Arg::new(arg::VERBOSE) .long(arg::VERBOSE) - .takes_value(false) + .num_args(0) + .required(false) .help("Shows what is going on for subcommand"), ]), ) .subcommand( - SubCommand::with_name(cmd::READ) + Command::new(cmd::READ) .about("Read csv file and display results in terminal") .args(&[ - Arg::with_name(cmd::READ).required(true).takes_value(true), - Arg::with_name(arg::VERBOSE) + Arg::new(cmd::READ).num_args(1).required(true), + Arg::new(arg::VERBOSE) .long(arg::VERBOSE) - .takes_value(false) + .num_args(0) + .required(false) .help("Shows what is going on for subcommand"), - Arg::with_name(arg::DISPLAY) + Arg::new(arg::DISPLAY) .long(arg::DISPLAY) - .takes_value(false) + .num_args(0) + .required(false) .help("Display csv file in terminal"), ]), ) .subcommand( - SubCommand::with_name(cmd::SEND) + Command::new(cmd::SEND) .about("Send email to single recipient") .args(&[ - Arg::with_name(arg::SENDER) + Arg::new(arg::SENDER) .index(1) + .num_args(1) .required(true) - .takes_value(true) - .requires_all(&[arg::RECEIVER]) + .requires_all([arg::RECEIVER]) .help("Email address of the sender"), - Arg::with_name(arg::RECEIVER) + Arg::new(arg::RECEIVER) .index(2) + .num_args(1) .required(true) - .takes_value(true) - .requires_all(&[arg::SENDER]) + .requires_all([arg::SENDER]) .help("Email address of the receiver"), - Arg::with_name(arg::SUBJECT) + Arg::new(arg::SUBJECT) .long(arg::SUBJECT) - .takes_value(true) - .required_unless_one(&[arg::MESSAGE_FILE]) + .num_args(1) + .required(true) + .required_unless_present(arg::MESSAGE_FILE) .help("Subject of the email"), - Arg::with_name(arg::CONTENT) + Arg::new(arg::CONTENT) .long(arg::CONTENT) - .takes_value(true) + .num_args(1) + .required(false) .requires(arg::SUBJECT) - .required_unless_one(&[arg::MESSAGE_FILE, arg::TEXT_FILE, arg::HTML_FILE]) - .conflicts_with_all(&[arg::MESSAGE_FILE, arg::TEXT_FILE, arg::HTML_FILE]) + .required_unless_present_all([ + arg::MESSAGE_FILE, + arg::TEXT_FILE, + arg::HTML_FILE, + ]) + .conflicts_with_all([arg::MESSAGE_FILE, arg::TEXT_FILE, arg::HTML_FILE]) .help("Content of the email"), - Arg::with_name(arg::MESSAGE_FILE) + Arg::new(arg::MESSAGE_FILE) .long(arg::MESSAGE_FILE) - .takes_value(true) - .required_unless_one(&[ + .num_args(1) + .required(false) + .required_unless_present_all([ arg::SUBJECT, arg::CONTENT, arg::TEXT_FILE, arg::HTML_FILE, ]) - .conflicts_with_all(&[arg::CONTENT, arg::TEXT_FILE, arg::HTML_FILE]) + .conflicts_with_all([arg::CONTENT, arg::TEXT_FILE, arg::HTML_FILE]) .help("Path of the message file"), - Arg::with_name(arg::TEXT_FILE) + Arg::new(arg::TEXT_FILE) .long(arg::TEXT_FILE) - .takes_value(true) + .num_args(1) + .required(false) .requires(arg::SUBJECT) - .conflicts_with_all(&[arg::CONTENT, arg::MESSAGE_FILE]) + .conflicts_with_all([arg::CONTENT, arg::MESSAGE_FILE]) .help("Path of text file"), - Arg::with_name(arg::HTML_FILE) + Arg::new(arg::HTML_FILE) .long(arg::HTML_FILE) - .takes_value(true) + .num_args(1) + .required(false) .requires(arg::SUBJECT) - .conflicts_with_all(&[arg::CONTENT, arg::MESSAGE_FILE]) + .conflicts_with_all([arg::CONTENT, arg::MESSAGE_FILE]) .help("Path of html file"), - Arg::with_name(arg::ATTACHMENT) + Arg::new(arg::ATTACHMENT) .long(arg::ATTACHMENT) - .takes_value(true) + .num_args(1) + .required(false) .help("Path of attachment"), - Arg::with_name(arg::ARCHIVE) + Arg::new(arg::ARCHIVE) .long(arg::ARCHIVE) - .takes_value(false) + .num_args(0) + .required(false) .help("Archive sent emails"), - Arg::with_name(arg::ARCHIVE_DIR) + Arg::new(arg::ARCHIVE_DIR) .long(arg::ARCHIVE_DIR) - .takes_value(true) + .num_args(1) + .required(false) .default_value("./sent_emails") .help("Path of sent emails"), - Arg::with_name(arg::DISPLAY) + Arg::new(arg::DISPLAY) .long(arg::DISPLAY) - .takes_value(false) + .num_args(0) + .required(false) .help("Display email in terminal"), - Arg::with_name(arg::DRY_RUN) + Arg::new(arg::DRY_RUN) .long(arg::DRY_RUN) - .takes_value(false) + .num_args(0) + .required(false) .help("Prepare email but do not send email"), - Arg::with_name(arg::ASSUME_YES) + Arg::new(arg::ASSUME_YES) .long(arg::ASSUME_YES) - .takes_value(false) + .num_args(0) + .required(false) .help("Send email without confirmation"), - Arg::with_name(arg::CONNECTION) + Arg::new(arg::CONNECTION) .long(arg::CONNECTION) - .takes_value(true) - .possible_values(&[val::SMTP, val::AWS]) + .num_args(1) + .required(false) + .value_parser([val::SMTP, val::AWS]) .default_value(val::SMTP) .help("Send emails via SMTP or AWS API"), - Arg::with_name(arg::VERBOSE) + Arg::new(arg::VERBOSE) .long(arg::VERBOSE) - .takes_value(false) + .num_args(0) + .required(false) .help("Shows what is going on for subcommand"), ]), ) .subcommand( - SubCommand::with_name(cmd::SEND_BULK) + Command::new(cmd::SEND_BULK) .about("Send email to multiple recipients") .args(&[ - Arg::with_name(arg::SENDER) + Arg::new(arg::SENDER) .index(1) + .num_args(1) .required(true) - .takes_value(true) .help("Email address of the sender"), - Arg::with_name(arg::RECEIVER_FILE) + Arg::new(arg::RECEIVER_FILE) .long(arg::RECEIVER_FILE) - .required_unless(arg::RECEIVER_QUERY) - .takes_value(true) + .num_args(1) + .required(false) + .required_unless_present(arg::RECEIVER_QUERY) .help( "Email addresses of multiple receivers fetched from provided csv file", ), - Arg::with_name(arg::RECEIVER_QUERY) + Arg::new(arg::RECEIVER_QUERY) .long(arg::RECEIVER_QUERY) - .required_unless(arg::RECEIVER_FILE) - .takes_value(true) + .num_args(1) + .required(false) + .required_unless_present(arg::RECEIVER_FILE) .help("Email addresses of multiple receivers fetched from provided query"), - Arg::with_name(arg::SUBJECT) + Arg::new(arg::SUBJECT) .long(arg::SUBJECT) - .takes_value(true) - .required_unless_one(&[arg::MESSAGE_FILE]) + .num_args(1) + .required(false) + .required_unless_present(arg::MESSAGE_FILE) .help("Subject of the email"), - Arg::with_name(arg::CONTENT) + Arg::new(arg::CONTENT) .long(arg::CONTENT) - .takes_value(true) + .num_args(1) + .required(false) .requires(arg::SUBJECT) - .required_unless_one(&[arg::MESSAGE_FILE, arg::TEXT_FILE, arg::HTML_FILE]) - .conflicts_with_all(&[arg::MESSAGE_FILE, arg::TEXT_FILE, arg::HTML_FILE]) + .required_unless_present_any([ + arg::MESSAGE_FILE, + arg::TEXT_FILE, + arg::HTML_FILE, + ]) + .conflicts_with_all([arg::MESSAGE_FILE, arg::TEXT_FILE, arg::HTML_FILE]) .help("Content of the email"), - Arg::with_name(arg::MESSAGE_FILE) + Arg::new(arg::MESSAGE_FILE) .long(arg::MESSAGE_FILE) - .takes_value(true) - .required_unless_one(&[ + .num_args(1) + .required(false) + .required_unless_present_any([ arg::SUBJECT, arg::CONTENT, arg::TEXT_FILE, arg::HTML_FILE, ]) - .conflicts_with_all(&[arg::CONTENT, arg::TEXT_FILE, arg::HTML_FILE]) + .conflicts_with_all([arg::CONTENT, arg::TEXT_FILE, arg::HTML_FILE]) .help("Path of the message file"), - Arg::with_name(arg::TEXT_FILE) + Arg::new(arg::TEXT_FILE) .long(arg::TEXT_FILE) - .takes_value(true) + .num_args(1) + .required(false) .requires(arg::SUBJECT) - .conflicts_with_all(&[arg::CONTENT, arg::MESSAGE_FILE]) + .conflicts_with_all([arg::CONTENT, arg::MESSAGE_FILE]) .help("Path of text file"), - Arg::with_name(arg::HTML_FILE) + Arg::new(arg::HTML_FILE) .long(arg::HTML_FILE) - .takes_value(true) + .num_args(1) + .required(false) .requires(arg::SUBJECT) - .conflicts_with_all(&[arg::CONTENT, arg::MESSAGE_FILE]) + .conflicts_with_all([arg::CONTENT, arg::MESSAGE_FILE]) .help("Path of html file"), - Arg::with_name(arg::ATTACHMENT) + Arg::new(arg::ATTACHMENT) .long(arg::ATTACHMENT) - .takes_value(true) + .num_args(1) + .required(false) .help("Path of attachment"), - Arg::with_name(arg::ARCHIVE) + Arg::new(arg::ARCHIVE) .long(arg::ARCHIVE) - .takes_value(false) + .num_args(0) + .required(false) .help("Archive sent emails"), - Arg::with_name(arg::ARCHIVE_DIR) + Arg::new(arg::ARCHIVE_DIR) .long(arg::ARCHIVE_DIR) - .takes_value(true) + .num_args(1) + .required(false) .default_value("./sent_emails") .help("Path of sent emails"), - Arg::with_name(arg::RECEIVER_COLUMN) + Arg::new(arg::RECEIVER_COLUMN) .long(arg::RECEIVER_COLUMN) - .takes_value(true) + .num_args(1) + .required(false) .default_value(val::EMAIL) .help("Specifies the column in which to look for email addresses"), - Arg::with_name(arg::PERSONALIZE) + Arg::new(arg::PERSONALIZE) .long(arg::PERSONALIZE) - .takes_value(true) - .multiple(true) - .max_values(100) + .num_args(0..100) + .required(false) .help("Personalizes email for variables defined in the message template"), - Arg::with_name(arg::DISPLAY) + Arg::new(arg::DISPLAY) .long(arg::DISPLAY) - .takes_value(false) + .num_args(0) + .required(false) .help("Print emails to terminal"), - Arg::with_name(arg::DRY_RUN) + Arg::new(arg::DRY_RUN) .long(arg::DRY_RUN) - .takes_value(false) + .num_args(0) + .required(false) .help("Prepare emails but do not send emails"), - Arg::with_name(arg::ASSUME_YES) + Arg::new(arg::ASSUME_YES) .long(arg::ASSUME_YES) - .takes_value(false) + .num_args(0) + .required(false) .help("Send emails without confirmation"), - Arg::with_name(arg::SSH_TUNNEL) + Arg::new(arg::SSH_TUNNEL) .long(arg::SSH_TUNNEL) .value_name("port") - .takes_value(true) + .num_args(1) + .required(false) .help("Query db through ssh tunnel"), - Arg::with_name(arg::CONNECTION) + Arg::new(arg::CONNECTION) .long(arg::CONNECTION) - .takes_value(true) - .possible_values(&[val::SMTP, val::AWS]) + .num_args(1) + .required(false) + .value_parser([val::SMTP, val::AWS]) .default_value(val::SMTP) .help("Send emails via SMTP or AWS API"), - Arg::with_name(arg::VERBOSE) + Arg::new(arg::VERBOSE) .long(arg::VERBOSE) - .takes_value(false) + .num_args(0) + .required(false) .help("Shows what is going on for subcommand"), ]), ) diff --git a/src/main.rs b/src/main.rs index 9ff63f7..609f636 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,13 +10,13 @@ fn main() -> Result<(), anyhow::Error> { } match matches.subcommand() { - (cmd::INIT, Some(matches)) => cmd::init(matches), - (cmd::CONNECT, Some(matches)) => cmd::connect(matches), - (cmd::QUERY, Some(matches)) => cmd::query(matches), - (cmd::SIMPLE_QUERY, Some(matches)) => cmd::simple_query(matches), - (cmd::READ, Some(matches)) => cmd::read(matches), - (cmd::SEND, Some(matches)) => cmd::send(matches), - (cmd::SEND_BULK, Some(matches)) => cmd::send_bulk(matches), - (_, _) => Err(anyhow!("Subcommand not found")), + Some((cmd::INIT, matches)) => cmd::init(matches), + Some((cmd::CONNECT, matches)) => cmd::connect(matches), + Some((cmd::QUERY, matches)) => cmd::query(matches), + Some((cmd::SIMPLE_QUERY, matches)) => cmd::simple_query(matches), + Some((cmd::READ, matches)) => cmd::read(matches), + Some((cmd::SEND, matches)) => cmd::send(matches), + Some((cmd::SEND_BULK, matches)) => cmd::send_bulk(matches), + _ => Err(anyhow!("Subcommand not found")), } } From 2b77593f0872f319e44cc5f0787687adfff98d4b Mon Sep 17 00:00:00 2001 From: quambene Date: Thu, 4 Apr 2024 00:43:47 +0200 Subject: [PATCH 4/7] Fix args --- src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4a655a1..690d523 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -156,7 +156,7 @@ pub fn app() -> Command { Arg::new(arg::SUBJECT) .long(arg::SUBJECT) .num_args(1) - .required(true) + .required(false) .required_unless_present(arg::MESSAGE_FILE) .help("Subject of the email"), Arg::new(arg::CONTENT) @@ -164,7 +164,7 @@ pub fn app() -> Command { .num_args(1) .required(false) .requires(arg::SUBJECT) - .required_unless_present_all([ + .required_unless_present_any([ arg::MESSAGE_FILE, arg::TEXT_FILE, arg::HTML_FILE, @@ -175,7 +175,7 @@ pub fn app() -> Command { .long(arg::MESSAGE_FILE) .num_args(1) .required(false) - .required_unless_present_all([ + .required_unless_present_any([ arg::SUBJECT, arg::CONTENT, arg::TEXT_FILE, From 1d4fd6405be73ae5271ce8a9ceca3fbccdc6cd90 Mon Sep 17 00:00:00 2001 From: quambene Date: Thu, 4 Apr 2024 00:46:07 +0200 Subject: [PATCH 5/7] Fix tests --- src/arg.rs | 2 +- src/cmd/connect.rs | 2 +- src/cmd/query.rs | 10 ++++++---- src/cmd/read.rs | 2 +- src/cmd/send.rs | 2 +- src/cmd/send_bulk.rs | 8 +++++--- src/cmd/simple_query.rs | 2 +- src/email_builder/message.rs | 10 +++++----- src/email_builder/receiver.rs | 6 +++--- 9 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/arg.rs b/src/arg.rs index bc3c2e6..db3195b 100644 --- a/src/arg.rs +++ b/src/arg.rs @@ -41,7 +41,7 @@ pub mod val { } pub fn value<'a>(name: &str, matches: &'a ArgMatches) -> Result<&'a str, anyhow::Error> { - match matches.get_one::<&str>(name) { + match matches.get_one::(name) { Some(query) => Ok(query), None => Err(anyhow!("Missing value for argument '{}'", name)), } diff --git a/src/cmd/connect.rs b/src/cmd/connect.rs index 9b92a30..db0de80 100644 --- a/src/cmd/connect.rs +++ b/src/cmd/connect.rs @@ -13,7 +13,7 @@ pub fn connect(matches: &ArgMatches) -> Result<(), anyhow::Error> { } if matches.contains_id(cmd::CONNECT) { - match matches.get_one::<&str>(cmd::CONNECT) { + match matches.get_one::(cmd::CONNECT) { Some(connection) => match connection.to_lowercase().as_str() { val::SMTP => { let _client = SmtpClient::new()?; diff --git a/src/cmd/query.rs b/src/cmd/query.rs index 40e617b..a52c539 100644 --- a/src/cmd/query.rs +++ b/src/cmd/query.rs @@ -13,11 +13,13 @@ pub fn query(matches: &ArgMatches) -> Result<(), anyhow::Error> { } if matches.contains_id(cmd::QUERY) { - match matches.get_one::<&str>(cmd::QUERY) { + match matches.get_one::(cmd::QUERY) { Some(query) => { let now = Utc::now(); let conn_vars = ConnVars::from_env()?; - let ssh_tunnel = matches.get_one::<&str>(arg::SSH_TUNNEL).copied(); + let ssh_tunnel = matches + .get_one::(arg::SSH_TUNNEL) + .map(|arg| arg.as_ref()); let connection = DbConnection::new(&conn_vars, ssh_tunnel)?; let mut df_query = sources::query_postgres(&connection, query)?; @@ -29,8 +31,8 @@ pub fn query(matches: &ArgMatches) -> Result<(), anyhow::Error> { let save_dir = Path::new(arg::value(arg::SAVE_DIR, matches)?); // If argument 'FILE_TYPE' is not present the default value 'csv' will be used - match matches.get_one::<&str>(arg::FILE_TYPE) { - Some(file_type) => match *file_type { + match matches.get_one::(arg::FILE_TYPE) { + Some(file_type) => match file_type.as_ref() { "csv" => { sources::write_csv(&mut df_query, save_dir, now)?; } diff --git a/src/cmd/read.rs b/src/cmd/read.rs index 827fb31..0713a96 100644 --- a/src/cmd/read.rs +++ b/src/cmd/read.rs @@ -9,7 +9,7 @@ pub fn read(matches: &ArgMatches) -> Result<(), anyhow::Error> { } if matches.contains_id(cmd::READ) { - match matches.get_one::<&str>(cmd::READ) { + match matches.get_one::(cmd::READ) { Some(csv_file) => { let path = PathBuf::from(csv_file); let csv = sources::read_csv(&path)?; diff --git a/src/cmd/send.rs b/src/cmd/send.rs index f7b51c4..323ebd4 100644 --- a/src/cmd/send.rs +++ b/src/cmd/send.rs @@ -22,7 +22,7 @@ pub fn send(matches: &ArgMatches) -> Result<(), anyhow::Error> { let sender = Sender(arg::value(arg::SENDER, matches)?); let receiver = Receiver(arg::value(arg::RECEIVER, matches)?); let message = Message::from_args(matches)?; - let attachment = matches.get_one::<&str>(arg::ATTACHMENT).map(Path::new); + let attachment = matches.get_one::(arg::ATTACHMENT).map(Path::new); let mime_format = MimeFormat::new(sender, receiver, &message, attachment, now)?; let email = Email::new(sender, receiver, &message, &mime_format)?; diff --git a/src/cmd/send_bulk.rs b/src/cmd/send_bulk.rs index 3542dc5..42953fd 100644 --- a/src/cmd/send_bulk.rs +++ b/src/cmd/send_bulk.rs @@ -21,11 +21,13 @@ pub fn send_bulk(matches: &ArgMatches) -> Result<(), anyhow::Error> { let sender = Sender(arg::value(arg::SENDER, matches)?); let receivers = BulkReceiver::from_args(matches)?; let message = Message::from_args(matches)?; - let attachment = matches.get_one::<&str>(arg::ATTACHMENT).map(Path::new); + let attachment = matches.get_one::(arg::ATTACHMENT).map(Path::new); let bulk_email = if matches.contains_id(arg::PERSONALIZE) { - if let Some(personalized_columns) = matches.get_many::<&str>(arg::PERSONALIZE) { - let personalized_columns = personalized_columns.copied().collect::>(); + if let Some(personalized_columns) = matches.get_many::(arg::PERSONALIZE) { + let personalized_columns = personalized_columns + .map(|arg| arg.as_ref()) + .collect::>(); BulkEmail::new( sender, &receivers, diff --git a/src/cmd/simple_query.rs b/src/cmd/simple_query.rs index 209def8..0a1b6b4 100644 --- a/src/cmd/simple_query.rs +++ b/src/cmd/simple_query.rs @@ -10,7 +10,7 @@ pub fn simple_query(matches: &ArgMatches) -> Result<(), anyhow::Error> { if matches.contains_id(cmd::SIMPLE_QUERY) { let conn_vars = ConnVars::from_env()?; - let simple_query = match matches.get_one::<&str>(cmd::SIMPLE_QUERY) { + let simple_query = match matches.get_one::(cmd::SIMPLE_QUERY) { Some(query) => query, None => { return Err(anyhow!( diff --git a/src/email_builder/message.rs b/src/email_builder/message.rs index a7c8c05..2fce0bc 100644 --- a/src/email_builder/message.rs +++ b/src/email_builder/message.rs @@ -43,11 +43,11 @@ impl Message { pub fn from_args(matches: &ArgMatches) -> Result { if matches.contains_id(arg::SUBJECT) && matches.contains_id(arg::CONTENT) { match ( - matches.get_one::<&str>(arg::SUBJECT), - matches.get_one::<&str>(arg::CONTENT), + matches.get_one::(arg::SUBJECT), + matches.get_one::(arg::CONTENT), ) { (Some(subject), Some(content)) => { - let message = Message::new(*subject, Some(content), None); + let message = Message::new(subject, Some(content), None); Ok(message) } (Some(_), None) => Err(anyhow!("Missing value for argument '{}'", arg::CONTENT)), @@ -72,8 +72,8 @@ impl Message { && (matches.contains_id(arg::TEXT_FILE) || matches.contains_id(arg::HTML_FILE)) { let subject = arg::value(arg::SUBJECT, matches)?; - let text_path = matches.get_one::<&str>(arg::TEXT_FILE).map(Path::new); - let html_path = matches.get_one::<&str>(arg::HTML_FILE).map(Path::new); + let text_path = matches.get_one::(arg::TEXT_FILE).map(Path::new); + let html_path = matches.get_one::(arg::HTML_FILE).map(Path::new); let text = if let Some(path) = text_path { Some(utils::read_file(path)?) } else { diff --git a/src/email_builder/receiver.rs b/src/email_builder/receiver.rs index 2520d3b..0dabe68 100644 --- a/src/email_builder/receiver.rs +++ b/src/email_builder/receiver.rs @@ -36,13 +36,13 @@ impl BulkReceiver { pub fn from_args(matches: &ArgMatches) -> Result { let column_name = arg::value(arg::RECEIVER_COLUMN, matches)?; - let receiver_query = matches.get_one::<&str>(arg::RECEIVER_QUERY); - let receiver_path = matches.get_one::<&str>(arg::RECEIVER_FILE).map(Path::new); + let receiver_query = matches.get_one::(arg::RECEIVER_QUERY); + let receiver_path = matches.get_one::(arg::RECEIVER_FILE).map(Path::new); match (receiver_query, receiver_path) { (Some(query), None) => { let conn_vars = ConnVars::from_env()?; - let ssh_tunnel = matches.get_one::<&str>(arg::SSH_TUNNEL).copied(); + let ssh_tunnel = matches.get_one::(arg::SSH_TUNNEL).map(|arg| arg.as_ref()); let connection = DbConnection::new(&conn_vars, ssh_tunnel)?; let df_receiver = sources::query_postgres(&connection, query)?; From 8f8b7f10c247209b45bc1e204f85c0bf6b6d894c Mon Sep 17 00:00:00 2001 From: quambene Date: Thu, 4 Apr 2024 01:14:20 +0200 Subject: [PATCH 6/7] Add tests --- src/lib.rs | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 690d523..c87d6eb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -372,3 +372,62 @@ pub fn app() -> Command { ]), ) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_send_args_subject_content() { + let args = vec![ + "pigeon", + "send", + "albert@einstein.com", + "marie@curie.com", + "--subject", + "Test subject", + "--content", + "This is a test message (plaintext).", + ]; + let app = app(); + let matches = app.get_matches_from(args); + let subcommand_matches = matches.subcommand_matches("send"); + assert!(subcommand_matches.is_some()); + } + + #[test] + fn test_send_args_text_file_html_file() { + let args = vec![ + "pigeon", + "send", + "albert@einstein.com", + "marie@curie.com", + "--subject", + "Test subject", + "--text-file", + "./test_data/message.txt", + "--html-file", + "./test_data/message.html", + ]; + let app = app(); + let matches = app.get_matches_from(args); + let subcommand_matches = matches.subcommand_matches("send"); + assert!(subcommand_matches.is_some()); + } + + #[test] + fn test_send_args_message_file() { + let args = vec![ + "pigeon", + "send", + "albert@einstein.com", + "marie@curie.com", + "--message-file", + "./test_data/message.yaml", + ]; + let app = app(); + let matches = app.get_matches_from(args); + let subcommand_matches = matches.subcommand_matches("send"); + assert!(subcommand_matches.is_some()); + } +} From cf61e19bbcefbfbc5df66fa9049158919bf576d8 Mon Sep 17 00:00:00 2001 From: quambene Date: Thu, 4 Apr 2024 01:47:52 +0200 Subject: [PATCH 7/7] Fix tests --- src/lib.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c87d6eb..642f547 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,11 +62,11 @@ pub fn app() -> Command { .long(arg::SSH_TUNNEL) .value_name("port") .num_args(1) - .required(true) + .required(false) .help("Connect to db through ssh tunnel"), Arg::new(arg::SAVE) .long(arg::SAVE) - .num_args(1) + .num_args(0) .required(false) .help("Save query result"), Arg::new(arg::SAVE_DIR) @@ -77,25 +77,25 @@ pub fn app() -> Command { Arg::new(arg::FILE_TYPE) .long(arg::FILE_TYPE) .num_args(1) - .required(true) + .required(false) .default_value("csv") .value_parser(["csv", "jpg", "png"]) .help("Specifies the file type for saved query"), Arg::new(arg::IMAGE_COLUMN) .long(arg::IMAGE_COLUMN) - .requires_ifs([(arg::FILE_TYPE, "jpg"), (arg::FILE_TYPE, "png")]) .num_args(1) - .required(true) + .required(false) + .required_if_eq_any([(arg::FILE_TYPE, "jpg"), (arg::FILE_TYPE, "png")]) .help("Specifies the column in which to look for images"), Arg::new(arg::IMAGE_NAME) .long(arg::IMAGE_NAME) - .requires_ifs([(arg::FILE_TYPE, "jpg"), (arg::FILE_TYPE, "png")]) .num_args(1) - .required(true) + .required(false) + .required_if_eq_any([(arg::FILE_TYPE, "jpg"), (arg::FILE_TYPE, "png")]) .help("Specifies the column used for the image name"), Arg::new(arg::DISPLAY) .long(arg::DISPLAY) - .num_args(1) + .num_args(0) .required(false) .help("Print query result to terminal"), Arg::new(arg::VERBOSE)