From 0929a3f918a2023f01173a6a5a6126d21edd5615 Mon Sep 17 00:00:00 2001 From: Lol3rrr Date: Tue, 31 Oct 2023 13:35:09 +0100 Subject: [PATCH] Added support for customizing storage backend --- Cargo.lock | 113 ++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/lib.rs | 62 +++++++++++++++++++++ src/main.rs | 49 ++++++----------- src/storage.rs | 16 ++++++ src/storage/replicated.rs | 16 ++---- 6 files changed, 214 insertions(+), 43 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 77a9690..bf814ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -52,6 +52,54 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "anstyle-parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +dependencies = [ + "anstyle", + "windows-sys", +] + [[package]] name = "arc-swap" version = "1.6.0" @@ -255,6 +303,52 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "clap" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "command_attr" version = "0.4.2" @@ -572,6 +666,7 @@ dependencies = [ "arc-swap", "aws-creds 0.36.0", "chrono", + "clap", "prometheus", "reqwest", "rust-s3", @@ -624,6 +719,12 @@ version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "hex" version = "0.4.3" @@ -1524,6 +1625,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "subtle" version = "2.5.0" @@ -1892,6 +1999,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "uwl" version = "0.6.0" diff --git a/Cargo.toml b/Cargo.toml index 930d4ec..17709d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,3 +19,4 @@ rust_xlsxwriter = { version = "0.44" } prometheus = { version = "0.13.3" } aws-creds = { version = "0.36.0", default_features = false, features = ["http-credentials", "rustls-tls"]} rust-s3 = { version = "0.33.0", default_features = false, features = [ "tokio", "tokio-rustls-tls", "no-verify-ssl"] } +clap = { version = "4.4.7", features = ["derive"] } diff --git a/src/lib.rs b/src/lib.rs index 69bf9cf..8f43c89 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,3 +12,65 @@ pub use storage::*; mod excelstats; pub use excelstats::ExcelStats; + +pub fn parse_storage(args: &str) -> Result, &'static str> { + args.split("->") + .filter_map(|arg| match arg { + "file" => { + let store_path = + std::env::var("STORE_PATH").unwrap_or_else(|_| "data.json".to_string()); + + Some(Box::new(storage::FileStorage::new(store_path)) as Box) + } + "s3" => { + let s3_bucket = std::env::var("S3_BUCKET").expect("Missing `S3_BUCKET`"); + let s3_access_key = + std::env::var("S3_ACCESS_KEY").expect("Missing `S3_ACCESS_KEY`"); + let s3_secret_key = + std::env::var("S3_SECRET_KEY").expect("Missing `S3_SECRET_KEY`"); + let s3_endpoint = std::env::var("S3_ENDPOINT").expect("Missing `S3_ENDPOINT`"); + + Some(Box::new(S3Storage::new( + s3::Bucket::new( + &s3_bucket, + s3::Region::Custom { + region: "default".to_string(), + endpoint: s3_endpoint.to_string(), + }, + s3::creds::Credentials::new( + Some(&s3_access_key), + Some(&s3_secret_key), + None, + None, + None, + ) + .unwrap(), + ) + .unwrap() + .with_path_style(), + ))) + } + other => { + tracing::error!("Unknown Storage {:?}", other); + + None + } + }) + .reduce( + |acc: Box, elem: Box| { + Box::new(storage::Replicated::new(acc, elem)) as Box + }, + ) + .ok_or("") +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_single() { + std::env::set_var("S3", ""); + parse_storage("s3").unwrap(); + } +} diff --git a/src/main.rs b/src/main.rs index 5f88391..b6b11ea 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,10 +6,7 @@ use std::env; use std::sync::Arc; use std::time::{Duration, SystemTime, UNIX_EPOCH}; -use gold_pass_bot::{ - ClanTag, ExcelStats, FileStorage, RaidMember, RaidWeekendStats, Replicated, S3Storage, Season, - Storage, StorageBackend, -}; +use gold_pass_bot::{ClanTag, ExcelStats, RaidMember, RaidWeekendStats, Season, Storage}; use serenity::async_trait; use serenity::framework::standard::macros::{command, group}; use serenity::framework::standard::{CommandResult, StandardFramework}; @@ -41,13 +38,22 @@ async fn main() { .with(tracing_subscriber::fmt::layer()); tracing::subscriber::set_global_default(layers).unwrap(); - let store_path = std::env::var("STORE_PATH").unwrap_or_else(|_| "data.json".to_string()); - let api_path = std::env::var("API_PATH").unwrap_or_else(|_| "api.key".to_string()); + let args = clap::Command::new("Gold-Pass-Bot") + .arg( + clap::Arg::new("storage") + .long("storage") + .value_names(["storage-target"]), + ) + .get_matches(); + tracing::debug!("Args: {:#?}", args); + + let mut storage_backend = args + .get_one::("storage") + .map(|arg: &String| gold_pass_bot::parse_storage(&arg)) + .unwrap() + .unwrap(); - let s3_bucket = std::env::var("S3_BUCKET").unwrap(); - let s3_access_key = std::env::var("S3_ACCESS_KEY").unwrap(); - let s3_secret_key = std::env::var("S3_SECRET_KEY").unwrap(); - let s3_endpoint = std::env::var("S3_ENDPOINT").unwrap(); + let api_path = std::env::var("API_PATH").unwrap_or_else(|_| "api.key".to_string()); #[cfg(not(debug_assertions))] let prefix = "!"; @@ -67,29 +73,6 @@ async fn main() { .await .expect("Error creating client"); - let mut storage_backend: Box = Box::new(Replicated::new( - FileStorage::new(store_path.clone()), - S3Storage::new( - s3::Bucket::new( - &s3_bucket, - s3::Region::Custom { - region: "default".to_string(), - endpoint: s3_endpoint.to_string(), - }, - s3::creds::Credentials::new( - Some(&s3_access_key), - Some(&s3_secret_key), - None, - None, - None, - ) - .unwrap(), - ) - .unwrap() - .with_path_style(), - ), - )); - let elapsed = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() diff --git a/src/storage.rs b/src/storage.rs index 0ccef1c..2fb59b7 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -26,6 +26,22 @@ pub trait StorageBackend: Send { fn load(&mut self) -> Pin, ()>> + Send + 'static>>; } +impl StorageBackend for Box +where + S: StorageBackend, +{ + fn write( + &mut self, + content: Vec, + ) -> Pin> + Send + 'static>> { + S::write(self.as_mut(), content) + } + + fn load(&mut self) -> Pin, ()>> + Send + 'static>> { + S::load(self.as_mut()) + } +} + #[derive(Debug, Clone, Deserialize, Serialize)] pub struct Storage { clans: HashMap>, diff --git a/src/storage/replicated.rs b/src/storage/replicated.rs index 654c5e1..98a8fe7 100644 --- a/src/storage/replicated.rs +++ b/src/storage/replicated.rs @@ -1,21 +1,17 @@ use crate::StorageBackend; -pub struct Replicated { - primary: P, - secondary: S, +pub struct Replicated { + primary: Box, + secondary: Box, } -impl Replicated { - pub fn new(primary: P, secondary: S) -> Self { +impl Replicated { + pub fn new(primary: Box, secondary: Box) -> Self { Self { primary, secondary } } } -impl StorageBackend for Replicated -where - P: StorageBackend, - S: StorageBackend, -{ +impl StorageBackend for Replicated { #[tracing::instrument(skip(self, content))] fn write( &mut self,