diff --git a/Cargo.lock b/Cargo.lock index 8d2b328..f4635a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "anyhow" +version = "1.0.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" + [[package]] name = "autocfg" version = "1.1.0" @@ -293,6 +299,7 @@ checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" name = "listmonk-sync" version = "0.1.0" dependencies = [ + "anyhow", "csv", "itertools", "log", diff --git a/Cargo.toml b/Cargo.toml index af9a371..3000147 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ license = "Apache-2.0" edition = "2021" [dependencies] +anyhow = "1.0.71" csv = "1.2.1" itertools = "0.10.5" log = "0.4.17" diff --git a/src/main.rs b/src/main.rs index f1e9b21..09b07a8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +use anyhow::Context; use itertools::Itertools; use log::{error, info, LevelFilter}; use reqwest::{ @@ -29,6 +30,13 @@ struct Config { listmonk_overwrite: bool, } +#[derive(Debug)] +struct Credentials { + square_api_token: String, + listmonk_username: String, + listmonk_password: String, +} + #[derive(Deserialize, Debug)] struct SquareCustomer { email_address: Option, @@ -81,8 +89,20 @@ async fn main() -> Result<(), Box> { env!("CARGO_PKG_VERSION") ); - let config_file = File::open("config.json")?; - let config: Config = serde_json::from_reader(config_file)?; + let config_file = + File::open("config.json").with_context(|| "Failed to open config.json! Does it exist?")?; + let config: Config = serde_json::from_reader(config_file).with_context(|| { + "Failed to deserialize config. Does it have syntax errors or missing fields?" + })?; + + let creds = Credentials { + square_api_token: env::var("SQUARE_API_TOKEN") + .with_context(|| "Missing the SQUARE_API_TOKEN environment variable!")?, + listmonk_username: env::var("LISTMONK_USER") + .with_context(|| "Missing the LISTMONK_USER environment variable!")?, + listmonk_password: env::var("LISTMONK_PASSWORD") + .with_context(|| "Missing the LISTMONK_PASSWORD environment variable!")?, + }; let forever = task::spawn(async move { let mut interval = time::interval(Duration::from_secs(config.run_every)); @@ -91,7 +111,7 @@ async fn main() -> Result<(), Box> { loop { interval.tick().await; info!("Syncing..."); - if let Err(err) = run(&config).await { + if let Err(err) = run(&config, &creds).await { error!("Failed to sync! {:?}", err); } else { info!("Sync complete.") @@ -102,8 +122,8 @@ async fn main() -> Result<(), Box> { Ok(forever.await?) } -async fn run(config: &Config) -> Result<(), Box> { - let subscribers: Vec = get_square_customers() +async fn run(config: &Config, creds: &Credentials) -> Result<(), Box> { + let subscribers: Vec = get_square_customers(creds) .await? .into_iter() .filter(|c| c.email_address.is_some()) @@ -150,13 +170,13 @@ async fn run(config: &Config) -> Result<(), Box> { let csv = generate_import_csv(&subs)?; info!("Uploading {} listmonk subscribers ({})", subs.len(), mode.0); - upload_subscribers(import, csv, &config.listmonk_domain).await?; + upload_subscribers(import, csv, &config.listmonk_domain, creds).await?; } Ok(()) } -async fn get_square_customers() -> Result, Box> { +async fn get_square_customers(creds: &Credentials) -> Result, Box> { let mut cursor = String::new(); let mut customers = vec![]; let mut idx: u32 = 0; @@ -167,7 +187,7 @@ async fn get_square_customers() -> Result, Box> { .get("https://connect.squareup.com/v2/customers") .header("Square-Version", SQUARE_VERSION) .query(&[("cursor", cursor)]) - .bearer_auth(env::var("SQUARE_API_TOKEN")?) + .bearer_auth(&creds.square_api_token) .send() .await? .json::() @@ -202,6 +222,7 @@ async fn upload_subscribers( import: ListmonkImport, csv: String, domain: &str, + creds: &Credentials, ) -> Result> { let form = Form::new() .text("params", serde_json::to_string(&import)?) @@ -214,10 +235,9 @@ async fn upload_subscribers( Ok(Client::new() .post(format!("https://{domain}/api/import/subscribers")) .multipart(form) - .basic_auth( - env::var("LISTMONK_USER")?, - Some(env::var("LISTMONK_PASSWORD")?), - ) + .basic_auth(&creds.listmonk_username, Some(&creds.listmonk_password)) .send() - .await?) + .await? + .error_for_status() + .with_context(|| "Failed to upload subscribers to listmonk")?) }