pub mod request;
use crate::{
endpoints,
error::{ChunkingText, Deserializing, InvalidHeaderValue, InvalidUrl, Result, TokenMissing},
model::*,
};
use request::SearchBots;
use reqwest::{
header::{HeaderValue, AUTHORIZATION},
Client as HttpClient, Url,
};
use serde::de::DeserializeOwned;
use snafu::{OptionExt, ResultExt};
use std::sync::Arc;
#[derive(Debug)]
struct ClientRef {
http: Arc<HttpClient>,
token: Option<String>,
}
#[derive(Clone, Debug)]
pub struct Client(Arc<ClientRef>);
impl Client {
pub fn new(http_client: impl Into<Arc<HttpClient>>, token: impl Into<Option<String>>) -> Self {
Self(Arc::new(ClientRef {
http: http_client.into(),
token: token.into(),
}))
}
pub async fn get_bot(&self, user_id: u64) -> Result<Bot> {
let url = url(endpoints::bot(user_id))?;
self.get(url).await
}
pub fn get_bots(&self) -> SearchBots<'_> {
SearchBots::new(self)
}
pub async fn get_bot_stats(&self, user_id: u64) -> Result<BotStats> {
let url = url(endpoints::bot_stats(user_id))?;
self.get(url).await
}
pub async fn get_bot_vote_check(&self, bot_id: u64, user_id: u64) -> Result<bool> {
let token = self.0.token.as_ref().context(TokenMissing)?;
let path = endpoints::bot_vote_check(bot_id, user_id);
let user_id = user_id.to_string();
let params = [("userId", user_id.as_ref())];
let url = url_params(path, ¶ms)?;
let header_value = auth(token)?;
let text = self
.0
.http
.get(url)
.header(AUTHORIZATION, header_value)
.send()
.await?
.text()
.await
.context(ChunkingText)?;
let body = deser::<ResponseUserVoted>(text)?;
Ok(body.voted == 1)
}
pub async fn get_bot_votes(&self, bot_id: u64) -> Result<BotVotes> {
let url = url(endpoints::bot_votes(bot_id))?;
self.get(url).await
}
pub async fn get_user(&self, user_id: u64) -> Result<User> {
let url = url(endpoints::user(user_id))?;
self.get(url).await
}
pub async fn post_stats<'a>(&'a self, bot_id: u64, stats: &'a ShardStats) -> Result<()> {
let token = self.0.token.as_ref().context(TokenMissing)?;
let url = url(endpoints::bot_stats(bot_id))?;
let header_value = auth(token)?;
self.0
.http
.post(url)
.header(AUTHORIZATION, header_value)
.json(stats)
.send()
.await?;
Ok(())
}
async fn get<T: DeserializeOwned>(&self, url: Url) -> Result<T> {
let text = self
.0
.http
.get(url)
.send()
.await?
.text()
.await
.context(ChunkingText)?;
deser(text)
}
}
impl From<(Arc<HttpClient>, Option<String>)> for Client {
fn from((client, token): (Arc<HttpClient>, Option<String>)) -> Self {
Self::new(client, token)
}
}
fn auth(token: &str) -> Result<HeaderValue> {
HeaderValue::from_str(token).with_context(|| InvalidHeaderValue {
name: AUTHORIZATION,
value: token.to_owned(),
})
}
fn deser<T: DeserializeOwned>(text: String) -> Result<T> {
serde_json::from_str(&text).with_context(|| Deserializing { text })
}
fn url(uri: String) -> Result<Url> {
Url::parse(&uri).with_context(|| InvalidUrl { uri })
}
fn url_params<'a>(uri: String, params: &[(&'a str, &'a str)]) -> Result<Url> {
Url::parse_with_params(&uri, params.iter()).with_context(|| InvalidUrl { uri })
}