torn-api.rs/torn-key-pool/src/lib.rs
2024-04-04 15:59:10 +02:00

247 lines
5.5 KiB
Rust

#![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<S, C>
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<S, C> KeyPoolError<S, C>
where
S: std::error::Error + Clone,
C: std::error::Error,
{
#[inline(always)]
pub fn api_code(&self) -> Option<u8> {
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<D>(&self) -> KeySelector<Self, D>
where
D: KeyDomain,
{
KeySelector::Id(self.id())
}
}
pub trait KeyDomain: Clone + std::fmt::Debug + Send + Sync + 'static {
fn fallback(&self) -> Option<Self> {
None
}
}
#[derive(Debug, Clone)]
pub enum KeySelector<K, D>
where
K: ApiKey,
D: KeyDomain,
{
Key(String),
Id(K::IdType),
UserId(i32),
Has(Vec<D>),
OneOf(Vec<D>),
}
impl<K, D> KeySelector<K, D>
where
K: ApiKey,
D: KeyDomain,
{
pub(crate) fn fallback(&self) -> Option<Self> {
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<K, D>: Send + Sync
where
K: ApiKey,
D: KeyDomain,
{
fn into_selector(self) -> KeySelector<K, D>;
}
impl<K, D> IntoSelector<K, D> for D
where
K: ApiKey,
D: KeyDomain,
{
fn into_selector(self) -> KeySelector<K, D> {
KeySelector::Has(vec![self])
}
}
impl<K, D> IntoSelector<K, D> for KeySelector<K, D>
where
K: ApiKey,
D: KeyDomain,
{
fn into_selector(self) -> KeySelector<K, D> {
self
}
}
pub enum KeyAction<D>
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<S>(&self, selector: S) -> Result<Self::Key, Self::Error>
where
S: IntoSelector<Self::Key, Self::Domain>;
async fn acquire_many_keys<S>(
&self,
selector: S,
number: i64,
) -> Result<Vec<Self::Key>, Self::Error>
where
S: IntoSelector<Self::Key, Self::Domain>;
async fn flag_key(&self, key: Self::Key, code: u8) -> Result<bool, Self::Error>;
async fn store_key(
&self,
user_id: i32,
key: String,
domains: Vec<Self::Domain>,
) -> Result<Self::Key, Self::Error>;
async fn read_key<S>(&self, selector: S) -> Result<Option<Self::Key>, Self::Error>
where
S: IntoSelector<Self::Key, Self::Domain>;
async fn read_keys<S>(&self, selector: S) -> Result<Vec<Self::Key>, Self::Error>
where
S: IntoSelector<Self::Key, Self::Domain>;
async fn remove_key<S>(&self, selector: S) -> Result<Self::Key, Self::Error>
where
S: IntoSelector<Self::Key, Self::Domain>;
async fn add_domain_to_key<S>(
&self,
selector: S,
domain: Self::Domain,
) -> Result<Self::Key, Self::Error>
where
S: IntoSelector<Self::Key, Self::Domain>;
async fn remove_domain_from_key<S>(
&self,
selector: S,
domain: Self::Domain,
) -> Result<Self::Key, Self::Error>
where
S: IntoSelector<Self::Key, Self::Domain>;
async fn set_domains_for_key<S>(
&self,
selector: S,
domains: Vec<Self::Domain>,
) -> Result<Self::Key, Self::Error>
where
S: IntoSelector<Self::Key, Self::Domain>;
}
#[derive(Debug, Default)]
struct PoolOptions {
comment: Option<String>,
hooks_before: std::collections::HashMap<std::any::TypeId, Box<dyn std::any::Any + Send + Sync>>,
hooks_after: std::collections::HashMap<std::any::TypeId, Box<dyn std::any::Any + Send + Sync>>,
}
#[derive(Debug, Clone)]
pub struct KeyPoolExecutor<'a, C, S>
where
S: KeyPoolStorage,
{
storage: &'a S,
options: Arc<PoolOptions>,
selector: KeySelector<S::Key, S::Domain>,
_marker: std::marker::PhantomData<C>,
}
impl<'a, C, S> KeyPoolExecutor<'a, C, S>
where
S: KeyPoolStorage,
{
fn new(
storage: &'a S,
selector: KeySelector<S::Key, S::Domain>,
options: Arc<PoolOptions>,
) -> Self {
Self {
storage,
selector,
options,
_marker: std::marker::PhantomData,
}
}
}
#[cfg(all(test, feature = "postgres"))]
mod test {}