#![warn(clippy::all, clippy::perf, clippy::style, clippy::suspicious)] #[cfg(feature = "postgres")] pub mod postgres; // pub mod local; pub mod send; use std::sync::Arc; use async_trait::async_trait; use thiserror::Error; use torn_api::ResponseError; #[derive(Debug, Error)] pub enum KeyPoolError where S: std::error::Error + Clone, C: std::error::Error, { #[error("Key pool storage driver error: {0:?}")] Storage(#[source] S), #[error(transparent)] Client(#[from] C), #[error(transparent)] Response(ResponseError), } impl KeyPoolError where S: std::error::Error + Clone, C: std::error::Error, { #[inline(always)] pub fn api_code(&self) -> Option { match self { Self::Response(why) => why.api_code(), _ => None, } } } pub trait ApiKey: Sync + Send + std::fmt::Debug + Clone { type IdType: PartialEq + Eq + std::hash::Hash + Send + Sync + std::fmt::Debug + Clone; fn value(&self) -> &str; fn id(&self) -> Self::IdType; fn selector(&self) -> KeySelector where D: KeyDomain, { KeySelector::Id(self.id()) } } pub trait KeyDomain: Clone + std::fmt::Debug + Send + Sync + 'static { fn fallback(&self) -> Option { None } } #[derive(Debug, Clone)] pub enum KeySelector where K: ApiKey, D: KeyDomain, { Key(String), Id(K::IdType), UserId(i32), Has(Vec), OneOf(Vec), } impl KeySelector where K: ApiKey, D: KeyDomain, { pub(crate) fn fallback(&self) -> Option { match self { Self::Key(_) | Self::UserId(_) | Self::Id(_) => None, Self::Has(domains) => { let fallbacks: Vec<_> = domains.iter().filter_map(|d| d.fallback()).collect(); if fallbacks.is_empty() { None } else { Some(Self::Has(fallbacks)) } } Self::OneOf(domains) => { let fallbacks: Vec<_> = domains.iter().filter_map(|d| d.fallback()).collect(); if fallbacks.is_empty() { None } else { Some(Self::OneOf(fallbacks)) } } } } } pub trait IntoSelector: Send + Sync where K: ApiKey, D: KeyDomain, { fn into_selector(self) -> KeySelector; } impl IntoSelector for D where K: ApiKey, D: KeyDomain, { fn into_selector(self) -> KeySelector { KeySelector::Has(vec![self]) } } impl IntoSelector for KeySelector where K: ApiKey, D: KeyDomain, { fn into_selector(self) -> KeySelector { self } } pub enum KeyAction where D: KeyDomain, { Delete, RemoveDomain(D), Timeout(chrono::Duration), } #[async_trait] pub trait KeyPoolStorage { type Key: ApiKey; type Domain: KeyDomain; type Error: std::error::Error + Sync + Send + Clone; async fn acquire_key(&self, selector: S) -> Result where S: IntoSelector; async fn acquire_many_keys( &self, selector: S, number: i64, ) -> Result, Self::Error> where S: IntoSelector; async fn flag_key(&self, key: Self::Key, code: u8) -> Result; async fn store_key( &self, user_id: i32, key: String, domains: Vec, ) -> Result; async fn read_key(&self, selector: S) -> Result, Self::Error> where S: IntoSelector; async fn read_keys(&self, selector: S) -> Result, Self::Error> where S: IntoSelector; async fn remove_key(&self, selector: S) -> Result where S: IntoSelector; async fn add_domain_to_key( &self, selector: S, domain: Self::Domain, ) -> Result where S: IntoSelector; async fn remove_domain_from_key( &self, selector: S, domain: Self::Domain, ) -> Result where S: IntoSelector; async fn set_domains_for_key( &self, selector: S, domains: Vec, ) -> Result where S: IntoSelector; } #[derive(Debug, Default)] struct PoolOptions { comment: Option, hooks_before: std::collections::HashMap>, hooks_after: std::collections::HashMap>, } #[derive(Debug, Clone)] pub struct KeyPoolExecutor<'a, C, S> where S: KeyPoolStorage, { storage: &'a S, options: Arc, selector: KeySelector, _marker: std::marker::PhantomData, } impl<'a, C, S> KeyPoolExecutor<'a, C, S> where S: KeyPoolStorage, { fn new( storage: &'a S, selector: KeySelector, options: Arc, ) -> Self { Self { storage, selector, options, _marker: std::marker::PhantomData, } } } #[cfg(all(test, feature = "postgres"))] mod test {}