use std::num::NonZeroU32;
use governor::{
clock::DefaultClock,
middleware::NoOpMiddleware,
state::{InMemoryState, NotKeyed},
Quota,
RateLimiter,
};
pub use racal::reqwest::{ApiClient, ApiError};
use reqwest::{header::HeaderMap, Client, RequestBuilder};
use crate::query::{Authentication, NoAuthentication};
type NormalRateLimiter =
RateLimiter<NotKeyed, InMemoryState, DefaultClock, NoOpMiddleware>;
#[must_use]
fn http_rate_limiter() -> NormalRateLimiter {
RateLimiter::direct(
Quota::per_minute(NonZeroU32::try_from(12).unwrap())
.allow_burst(NonZeroU32::try_from(5).unwrap()),
)
}
pub struct UnauthenticatedNeos {
user_agent: String,
http: Client,
rate_limiter: NormalRateLimiter,
}
#[async_trait::async_trait]
impl ApiClient<NoAuthentication> for UnauthenticatedNeos {
fn state(&self) -> &NoAuthentication { &NoAuthentication {} }
fn client(&self) -> &reqwest::Client { &self.http }
async fn before_request(
&self, req: RequestBuilder,
) -> Result<RequestBuilder, racal::reqwest::ApiError> {
self.rate_limiter.until_ready().await;
Ok(req)
}
}
pub struct AuthenticatedNeos {
user_agent: String,
http: Client,
rate_limiter: NormalRateLimiter,
auth: Authentication,
}
#[async_trait::async_trait]
impl ApiClient<Authentication> for AuthenticatedNeos {
fn state(&self) -> &Authentication { &self.auth }
fn client(&self) -> &reqwest::Client { &self.http }
async fn before_request(
&self, req: RequestBuilder,
) -> Result<RequestBuilder, racal::reqwest::ApiError> {
self.rate_limiter.until_ready().await;
Ok(req)
}
}
impl AuthenticatedNeos {
fn http_client(
user_agent: &str, auth: &Authentication,
) -> Result<Client, ApiError> {
use serde::ser::Error;
let builder = Client::builder();
let mut headers = HeaderMap::new();
headers.insert(
"Authorization",
("neos ".to_owned() + auth.user_id.as_ref() + ":" + &auth.token)
.parse()
.map_err(|_| {
serde_json::Error::custom("Couldn't turn auth into a header")
})?,
);
Ok(builder.user_agent(user_agent).default_headers(headers).build()?)
}
pub fn downgrade(self) -> Result<UnauthenticatedNeos, ApiError> {
Ok(UnauthenticatedNeos {
http: UnauthenticatedNeos::http_client(&self.user_agent)?,
rate_limiter: self.rate_limiter,
user_agent: self.user_agent,
})
}
pub fn new(
user_agent: String, auth: impl Into<Authentication> + Send,
) -> Result<Self, ApiError> {
let auth = auth.into();
Ok(Self {
http: Self::http_client(&user_agent, &auth)?,
rate_limiter: http_rate_limiter(),
user_agent,
auth,
})
}
}
impl UnauthenticatedNeos {
fn http_client(user_agent: &str) -> Result<Client, ApiError> {
Ok(Client::builder().user_agent(user_agent).build()?)
}
pub fn upgrade(
self, auth: impl Into<Authentication> + Send,
) -> Result<AuthenticatedNeos, ApiError> {
let auth = auth.into();
Ok(AuthenticatedNeos {
http: AuthenticatedNeos::http_client(&self.user_agent, &auth)?,
rate_limiter: self.rate_limiter,
user_agent: self.user_agent,
auth,
})
}
pub fn new(user_agent: String) -> Result<Self, ApiError> {
Ok(Self {
http: Self::http_client(&user_agent)?,
rate_limiter: http_rate_limiter(),
user_agent,
})
}
}