Skip to content

Commit

Permalink
Fixed Cli input to appropriately parse Option<T>
Browse files Browse the repository at this point in the history
  • Loading branch information
MinaMatta98 committed Feb 19, 2024
1 parent 73015d6 commit 3e76a39
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 64 deletions.
87 changes: 63 additions & 24 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
[![Rust Build](https://github.com/MinaMatta98/Bulk-Email-Cli-CLient/actions/workflows/rust.yml/badge.svg)](https://github.com/MinaMatta98/Bulk-Email-Cli-CLient/actions/workflows/rust.yml)

[![Test](https://github.com/MinaMatta98/Bulk-Email-Cli-CLient/actions/workflows/test.yml/badge.svg)](https://github.com/MinaMatta98/Bulk-Email-Cli-CLient/actions/workflows/test.yml)


[![GitHub version](https://img.shields.io/github/v/tag/MinaMatta98/Bulk-Email-Cli-CLient?label=Version)](https://github.com/MinaMatta98/Bulk-Email-Cli-CLient/releases)


Expand All @@ -15,40 +18,34 @@
An asynchronous [RUST](https://www.rust-lang.org/) based CLI bulk emailing client, built on top of [lettre-rs](https://github.com/lettre/lettre).


### Features
---
## Features

|Feature|Description|
|-------|-----------|
|Template Variable Substitution| Refer to the [Template Variable Substitution](#template-variable-substitution) section for modifying email-templates|
|Single Email Support| Refer to [Send a Single Email](#send-a-single-email)|
|Bulk Email Support| Support for asynchronous unordered sending of bulk emails detailed within a csv file as described within [Send Bulk Emails](#send-bulk-emails) |
|Attachment Support| Support for Inline/Standard Attachments |
|SMTP Transport| This tool uses the SMTP protocol to send emails over the network |
|TLS Encryption| This tool defaults to TLS encryption over network|

<br/>
<br/>
<br/>
<br/>

### Instructions:
---
## Instructions:

#### Installation:
### Installation:
Installation of this tool will require [RUST installation version](https://www.rust-lang.org/tools/install) of 1.70 or newer.

To install this package via cargo, run the following command from a shell of choice:

```bash
cargo install --locked --git https://github.com/MinaMatta98/Bulk-Email-Cli-CLient.git
```

<br/>
<br/>
<br/>
<br/>

#### Setting Environmental Variables
### Setting Environmental Variables
---
The following environmental variables must be set:

Expand All @@ -58,17 +55,15 @@ The following environmental variables must be set:
|SENDER_PASSWORD|Corrosponding password for the email account sending Email(s).Turn on Less-Secure-Apps to send emails: https://myaccount.google.com |
|SMTP_RELAY|The SMTP relay corrosponding to the SMTP Gateway of Choice. For example, Gmail uses smtp.gmail.com and AOL uses smtp.aol.com |

<br/>
<br/>
<br/>
<br/>

#### Send a Single Email
### Send a Single Email
---
Refer to [Environmental Variables](#environmental-variables) for setting up environmental variables.


##### Scenario 1:
#### Scenario 1
Send an email to `minamatta98@gmail.com` with the template saved within `./templates/email.html` relative to the current working directory (check with ```bash pwd```).

```bash
Expand All @@ -77,22 +72,21 @@ email-sender single-email --to-addr "minamatta98@gmail.com" --subject "Email Tes

<br/>

##### Scenario 2:
#### Scenario 2
Send an email to `minamatta98@gmail.com` with the template saved within `./templates/email.html` relative to the current working directory (check with ```bash pwd```).
This is to also send a pdf attachment within the example directory relative to the root directory of this repository.

```bash
email-sender single-email --to-addr "minamatta98@gmail.com" --subject "Email Testing CLI" --email-template "./templates/email.html" --attachment-1-path "./example/rust.pdf"
email-sender single-email --to-addr "minamatta98@gmail.com" --subject "Email Testing CLI" --email-template "./templates/email.html" attachments --attachment-1-path "./example/rust.pdf"
```
<br/>

##### Scenario 3:
#### Scenario 3
Send an email to `minamatta98@gmail.com` with the template saved within `./templates/email.html` relative to the current working directory (check with ```bash pwd```).
This is to also send an inline email attachment located within `./example/send_mail_7590.png` corrosponding to an inline content id of 2335.
This is to also send an inline email attachment located within `./example/send_mail_7590.png` corresponding to an inline content id of 2335.

```bash
email-sender single-email --to-addr "minamatta98@gmail.com" --subject "Email Testing CLI" --email-template "./templates/email.html" --attachment-1-path "./example/send_mail_7590.png" --attachment-1-inline-content-id 2335
```
email-sender single-email --to-addr "minamatta98@gmail.com" --subject "Email Testing CLI" --email-template "./templates/email.html" attachments --attachment-1-path "./example/send-mail-7590.svg" --attachment-1-inline-content-id 2335```
<br/>
Expand All @@ -107,13 +101,14 @@ email-sender single-email --to-addr "minamatta98@gmail.com" --subject "Email Tes
<br/>
<br/>
#### Send Bulk Emails
### Send Bulk Emails
---
All three scenarios are listed within their respective order within the following table.
This will corrospond to the following csv file:
This will correspond to the following csv file:
_Note that unfilled fields must still be comma seperated_
> _Note that unfilled fields must still be comma separated._
> _Also note that attachment and substitution headers can be omitted where not needed_
```csv
DeliveryAddress,Subject,RelativeEmailTemplatePath,Attachment1Path,Attachment1InlineContentId
Expand All @@ -128,3 +123,47 @@ To send bulk emails from a csv file saved within `./example/example.csv`, run th
email-sender bulk-email --csv-file "./example/example.csv"
```

<br/>
<br/>

### Template Variable Substitution
---
The following variables can be used within an email-template and encoded as `{{field name}}` to be replaced:

| Field Name | Description |Csv Header |
|----------------------|------------------------------------------------------------------------------------------------------| ----------|
| `name` | Replace instance of `{{name}}` within email-template to `--name` value |Name|
| `email` | Replace instance of `{{email}}` within email-template to `--email` value |Email|
| `date` | Replace instance of `{{date}}` within email-template to `--date` value |Date|
| `time` | Replace instance of `{{time}}` within email-template to `--time` value |Time|
| `order_number` | Replace instance of `{{order_number}}` within email-template to `--order-number` value |OrderNumber|
| `product_information`| Replace instance of `{{product_information}}` within email-template to `--product-information` value| ProductInformation |
| `location` | Replace instance of `{{location}}` within email-template to `--location` value |Location|
| `username` | Replace instance of `{{user_name}}` within email-template to `--user-name` value |Username|
| `confirmation_link` | Replace instance of `{{confirmation_link}}` within email-template to `--confirmation-link` value |ConfirmationLink|
| `password_reset_link`| Replace instance of `{{password_reset_link}}` within email-template to `--password-reset-link` value |PasswordResetLink|
| `discount_code` | Replace instance of `{{discount_code}}` within email-template to `--discount-code` value |DiscountCode|
| `temp_code` | Replace instance of `{{temp_code}}` within email-template to `--temp-code` value |TempCode|
| `course_details` | Replace instance of `{{course_details}}` within email-template to `--course-details` value |CourseDetails|

<br/>

#### Single Email Substitution Example
In this example, instances of `{{name}}` within the email-template will be replaced with Mina and instances of ``{{temp_code}}`` will be replaced with 123456

```bash
cargo run --release -- single-email --to-addr "minamatta98@gmail.com" --subject "Email Testing CLI" --email-template "./templates/test.html" attachments --attachment-1-path "./example/send-mail-7590.svg" --attachment-1-inline-content-id 2335 token --name "Mina" --temp-code 123456
```

<br/>

#### Bulk Email Substitution Example
The following example accomplishes the same function as the [single email substitution example](#single-email-substitution-example).

> _Note that variables are evaluated separately for each row entry and can be left blank if not needed._
> _Also note that field names for substitutions can be left empty if not needed. For example, the Name and TempCode header are not required if values are not passed._
```csv
DeliveryAddress,Subject,RelativeEmailTemplatePath,Attachment1Path,Attachment1InlineContentId,Name,TempCode
minamatta98@gmail.com,Email Template Tests,./templates/replacement_test.html,./example/send_mail_7590.png,2335,Mina,123456
```
6 changes: 3 additions & 3 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ macro_rules! commands_struct {

#[serde(flatten)]
#[command(subcommand)]
replacement_tokens: ReplacementTokens,
replacement_tokens: Option<ReplacementTokens>,
},
}
}
Expand Down Expand Up @@ -125,7 +125,7 @@ pub struct EmailInfo {
/// Optional Inline File Attachment
#[serde(flatten)]
#[command(subcommand)]
pub attachment: AttachmentCommand,
pub attachment: Option<AttachmentCommand>,
}

commands_struct!(10);
Expand All @@ -148,7 +148,7 @@ mod test {
attachment_1_path,
attachment_1_inline_content_id,
..
} = record.attachment;
} = record.attachment.unwrap();

assert!(attachment_1_path.is_some());
assert!(attachment_1_inline_content_id.is_some());
Expand Down
84 changes: 49 additions & 35 deletions src/email.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ macro_rules! email_struct {
subject: String,
email_body_location: PathBuf,
#[serde(flatten)]
attachment: AttachmentCommand,
attachment: Option<AttachmentCommand>,
}

impl Email {
Expand All @@ -63,7 +63,7 @@ macro_rules! email_struct {
delivery_address: String,
subject: String,
email_body_location: PathBuf,
attachment: AttachmentCommand,
attachment: Option<AttachmentCommand>,
) -> Self {
Email {
delivery_address,
Expand Down Expand Up @@ -129,46 +129,60 @@ macro_rules! email_struct {

let mut email = std::fs::read_to_string(email_body_location)?;

let mut multi_part: MultiPart;

#[allow(clippy::collapsible_match)]
let AttachmentCommand::Attachments {
if let Some(AttachmentCommand::Attachments {
$(
[<attachment_ $no _path>],
[<attachment_ $no _inline_content_id>],
)*
replacement_tokens
})
= attachment {

if let Some(replacement_tokens) = replacement_tokens {
replacement_tokens.replace_all_substrings(&mut email);
}

multi_part = MultiPart::related().singlepart(
SinglePart::builder()
.header(header::ContentType::TEXT_HTML)
.body(email),
);



$(
if let Some([<attachment_ $no _path>]) = [<attachment_ $no _path>] && &[<attachment_ $no _path>].display().to_string() != "" {
let attachment = if let Some([<attachment_ $no _inline_val>]) = [<attachment_ $no _inline_content_id>] && [<attachment_ $no _inline_val>] != String::new() {
Attachment::new_inline(
[<attachment_ $no _inline_val>].to_string().replace('"', "")
)
.body(
std::fs::read(&[< attachment_ $no _path>])?,
ContentType::parse(&mime_guess::from_path([< attachment_ $no _path>]).first().unwrap().to_string())?,
)

} else {
Attachment::new([< attachment_ $no _path>].file_name().unwrap().to_str().unwrap().to_string())
.body(
std::fs::read(&[< attachment_ $no _path>])?,
ContentType::parse(&mime_guess::from_path([< attachment_ $no _path>]).first().unwrap().to_string())?,
)

};
multi_part = multi_part.singlepart(attachment);
};
)*

} else {
multi_part = MultiPart::related().singlepart(
SinglePart::builder()
.header(header::ContentType::TEXT_HTML)
.body(email),
);
}
= attachment;

replacement_tokens.replace_all_substrings(&mut email);

let mut multi_part = MultiPart::related().singlepart(
SinglePart::builder()
.header(header::ContentType::TEXT_HTML)
.body(email),
);

$(
if let Some([<attachment_ $no _path>]) = [<attachment_ $no _path>] && &[<attachment_ $no _path>].display().to_string() != "" {
let attachment = if let Some([<attachment_ $no _inline_val>]) = [<attachment_ $no _inline_content_id>] && [<attachment_ $no _inline_val>] != String::new() {
Attachment::new_inline(
[<attachment_ $no _inline_val>].as_number().unwrap().to_string()
)
.body(
std::fs::read(&[< attachment_ $no _path>])?,
ContentType::parse(&mime_guess::from_path([< attachment_ $no _path>]).first().unwrap().to_string())?,
)

} else {
Attachment::new([< attachment_ $no _path>].file_name().unwrap().to_str().unwrap().to_string())
.body(
std::fs::read(&[< attachment_ $no _path>])?,
ContentType::parse(&mime_guess::from_path([< attachment_ $no _path>]).first().unwrap().to_string())?,
)

};
multi_part = multi_part.singlepart(attachment);
};
)*

let email = Message::builder()
.from(from_addr.parse()?)
Expand Down
4 changes: 2 additions & 2 deletions src/tokens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,9 +172,9 @@ mod test {

let AttachmentCommand::Attachments {
replacement_tokens, ..
} = record.attachment;
} = record.attachment.unwrap();

let ReplacementTokens::Token { replacement_tokens } = replacement_tokens;
let ReplacementTokens::Token { replacement_tokens } = replacement_tokens.unwrap();

replace_html_file_substrings(replacement_tokens)?;
}
Expand Down

0 comments on commit 3e76a39

Please sign in to comment.