conditional Sync + Send if client supports it

This commit is contained in:
TotallyNot 2022-09-04 20:32:40 +02:00
parent da9c1b1563
commit 54345fef19
6 changed files with 37 additions and 27 deletions

View file

@ -1,2 +1,3 @@
[workspace]
resolver = "2"
members = [ "macros", "torn-api", "torn-key-pool" ]

View file

@ -1,6 +1,6 @@
[package]
name = "torn-api"
version = "0.2.0"
version = "0.2.1"
edition = "2021"
[features]

View file

@ -11,7 +11,7 @@ use serde::de::{DeserializeOwned, Error as DeError};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum Error {
pub enum ClientError {
#[error("api returned error '{reason}', code = '{code}'")]
Api { code: u8, reason: String },
@ -36,7 +36,7 @@ pub struct ApiResponse {
}
impl ApiResponse {
fn from_value(mut value: serde_json::Value) -> Result<Self, Error> {
fn from_value(mut value: serde_json::Value) -> Result<Self, ClientError> {
#[derive(serde::Deserialize)]
struct ApiErrorDto {
code: u8,
@ -46,7 +46,7 @@ impl ApiResponse {
match value.get_mut("error") {
Some(error) => {
let dto: ApiErrorDto = serde_json::from_value(error.take())?;
Err(Error::Api {
Err(ClientError::Api {
code: dto.code,
reason: dto.reason,
})
@ -82,15 +82,22 @@ pub trait ApiSelection {
fn category() -> &'static str;
}
pub trait ApiCategoryResponse {
pub trait ApiCategoryResponse: Send + Sync {
type Selection: ApiSelection;
fn from_response(response: ApiResponse) -> Self;
}
#[cfg(feature = "awc")]
#[async_trait(?Send)]
pub trait ApiClient {
async fn request(&self, url: String) -> Result<ApiResponse, Error>;
async fn request(&self, url: String) -> Result<ApiResponse, ClientError>;
}
#[cfg(not(feature = "awc"))]
#[async_trait]
pub trait ApiClient: Send + Sync {
async fn request(&self, url: String) -> Result<ApiResponse, ClientError>;
}
pub trait DirectApiClient: ApiClient {
@ -105,32 +112,32 @@ pub trait DirectApiClient: ApiClient {
pub trait BackedApiClient: ApiClient {}
#[cfg(feature = "reqwest")]
#[async_trait(?Send)]
#[cfg_attr(feature = "awc", async_trait(?Send))]
#[cfg_attr(not(feature = "awc"), async_trait)]
impl crate::ApiClient for reqwest::Client {
async fn request(&self, url: String) -> Result<ApiResponse, crate::Error> {
async fn request(&self, url: String) -> Result<ApiResponse, crate::ClientError> {
let value: serde_json::Value = self.get(url).send().await?.json().await?;
Ok(ApiResponse::from_value(value)?)
}
}
#[cfg(feature = "reqwest")]
#[async_trait(?Send)]
impl crate::DirectApiClient for reqwest::Client {}
#[cfg(feature = "awc")]
#[async_trait(?Send)]
impl crate::ApiClient for awc::Client {
async fn request(&self, url: String) -> Result<ApiResponse, crate::Error> {
async fn request(&self, url: String) -> Result<ApiResponse, crate::ClientError> {
let value: serde_json::Value = self.get(url).send().await?.json().await?;
Ok(ApiResponse::from_value(value)?)
}
}
#[cfg(feature = "awc")]
#[async_trait(?Send)]
impl crate::DirectApiClient for awc::Client {}
#[async_trait(?Send)]
#[cfg_attr(feature = "awc", async_trait(?Send))]
#[cfg_attr(not(feature = "awc"), async_trait)]
pub trait ApiRequestExecutor<'client> {
type Err: std::error::Error;
@ -171,12 +178,13 @@ where
}
}
#[async_trait(?Send)]
#[cfg_attr(feature = "awc", async_trait(?Send))]
#[cfg_attr(not(feature = "awc"), async_trait)]
impl<'client, C> ApiRequestExecutor<'client> for DirectExecutor<'client, C>
where
C: ApiClient,
{
type Err = Error;
type Err = ClientError;
async fn excute<A>(&self, request: ApiRequest<A>) -> Result<A, Self::Err>
where
@ -315,7 +323,7 @@ where
/// # Examples
///
/// ```no_run
/// use torn_api::{prelude::*, Error};
/// use torn_api::{prelude::*, ClientError};
/// use reqwest::Client;
/// # async {
///
@ -327,7 +335,7 @@ where
/// .await;
///
/// // invalid key
/// assert!(matches!(response, Err(Error::Api { code: 2, .. })));
/// assert!(matches!(response, Err(ClientError::Api { code: 2, .. })));
/// # };
/// ```
///
@ -394,7 +402,7 @@ pub(crate) mod tests {
awc::Client::default()
.torn_api(key)
.user(None)
.user()
.send()
.await
.unwrap();

View file

@ -1,6 +1,6 @@
[package]
name = "torn-key-pool"
version = "0.1.2"
version = "0.1.3"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View file

@ -11,13 +11,13 @@ use torn_api::prelude::*;
#[derive(Debug, Error)]
pub enum KeyPoolError<S>
where
S: std::error::Error + std::fmt::Debug,
S: Sync + Send + std::error::Error,
{
#[error("Key pool storage driver error: {0:?}")]
Storage(#[source] S),
#[error(transparent)]
Client(#[from] torn_api::Error),
Client(#[from] torn_api::ClientError),
}
#[derive(Debug, Clone, Copy)]
@ -27,14 +27,14 @@ pub enum KeyDomain {
Faction(i32),
}
pub trait ApiKey {
pub trait ApiKey: Sync + Send {
fn value(&self) -> &str;
}
#[async_trait(?Send)]
#[async_trait]
pub trait KeyPoolStorage {
type Key: ApiKey;
type Err: std::error::Error;
type Err: Sync + Send + std::error::Error;
async fn acquire_key(&self, domain: KeyDomain) -> Result<Self::Key, Self::Err>;
@ -66,11 +66,12 @@ where
}
}
#[async_trait(?Send)]
#[cfg_attr(feature = "awc", async_trait(?Send))]
#[cfg_attr(not(feature = "awc"), async_trait)]
impl<'client, C, S> ApiRequestExecutor<'client> for KeyPoolExecutor<'client, C, S>
where
C: ApiClient,
S: KeyPoolStorage + 'static,
S: KeyPoolStorage + Send + Sync + 'static,
{
type Err = KeyPoolError<S::Err>;
@ -88,7 +89,7 @@ where
let res = self.client.request(url).await;
match res {
Err(torn_api::Error::Api { code, .. }) => {
Err(torn_api::ClientError::Api { code, .. }) => {
if !self
.storage
.flag_key(key, code)

View file

@ -63,7 +63,7 @@ impl PgKeyPoolStorage {
}
}
#[async_trait(?Send)]
#[async_trait]
impl KeyPoolStorage for PgKeyPoolStorage {
type Key = PgKey;