#![doc(html_root_url = "https://docs.rs/dbl-rs/0.4.0")]
#![deny(rust_2018_idioms)]
use reqwest::header::AUTHORIZATION;
use reqwest::{Client as ReqwestClient, Response};
use reqwest::{Method, StatusCode};
use url::Url;
macro_rules! api {
($e:expr) => {
concat!("https://top.gg/api", $e)
};
($e:expr, $($rest:tt)*) => {
format!(api!($e), $($rest)*)
};
}
mod error;
pub mod types;
pub mod widget;
pub use error::Error;
use types::*;
#[derive(Clone)]
pub struct Client {
client: ReqwestClient,
token: String,
}
impl Client {
pub fn new(token: String) -> Result<Self, Error> {
let client = ReqwestClient::builder().build().map_err(error::from)?;
Ok(Client { client, token })
}
pub fn new_with_client(client: ReqwestClient, token: String) -> Self {
Client { client, token }
}
pub async fn get<T>(&self, bot: T) -> Result<Bot, Error>
where
T: Into<BotId>,
{
let url = api!("/bots/{}", bot.into());
get(self, url).await
}
pub async fn search(&self, filter: &Filter) -> Result<Listing, Error> {
let url = Url::parse_with_params(api!("/bots"), &filter.0).map_err(Error::Url)?;
get(self, url.to_string()).await
}
pub async fn stats<T>(&self, bot: T) -> Result<Stats, Error>
where
T: Into<BotId>,
{
let url = api!("/bots/{}/stats", bot.into());
get(self, url).await
}
pub async fn update_stats<T>(&self, bot: T, stats: ShardStats) -> Result<(), Error>
where
T: Into<BotId>,
{
let url = api!("/bots/{}/stats", bot.into());
post(self, url, Some(stats)).await
}
pub async fn votes<T>(&self, bot: T) -> Result<Vec<User>, Error>
where
T: Into<BotId>,
{
let url = api!("/bots/{}/votes", bot.into());
get(self, url).await
}
pub async fn has_voted<T, U>(&self, bot: T, user: U) -> Result<bool, Error>
where
T: Into<BotId>,
U: Into<UserId>,
{
let bot = bot.into();
let user = user.into();
let url = api!("/bots/{}/check?userId={}", bot, user);
let v: UserVoted = get(self, url).await?;
Ok(v.voted > 0)
}
pub async fn user<T>(&self, user: T) -> Result<DetailedUser, Error>
where
T: Into<UserId>,
{
let url = api!("/users/{}", user.into());
get(self, url).await
}
}
async fn request<T>(
client: &Client,
method: Method,
url: String,
data: Option<T>,
) -> Result<Response, Error>
where
T: serde::Serialize + Sized,
{
let mut req = client
.client
.request(method, &url)
.header(AUTHORIZATION, &*client.token);
if let Some(data) = data {
req = req.json(&data);
}
let resp = match req.send().await {
Ok(resp) => resp,
Err(e) => return Err(error::from(e)),
};
match resp.status() {
StatusCode::TOO_MANY_REQUESTS => {
let rl = match resp.json::<Ratelimit>().await {
Ok(rl) => rl,
Err(e) => return Err(error::from(e)),
};
Err(error::ratelimit(rl.retry_after))
}
_ => resp.error_for_status().map_err(error::from),
}
}
async fn get<T>(client: &Client, url: String) -> Result<T, Error>
where
T: serde::de::DeserializeOwned + Sized,
{
let resp = request(client, Method::GET, url, None::<()>).await?;
match resp.json().await {
Ok(data) => Ok(data),
Err(e) => Err(error::from(e)),
}
}
async fn post<T>(client: &Client, url: String, data: Option<T>) -> Result<(), Error>
where
T: serde::Serialize + Sized,
{
request(client, Method::POST, url, data).await?;
Ok(())
}