#![forbid(unsafe_code)]
pub mod api;
mod error;
use error::ApiError;
pub use error::{Error, Result};
use reqwest::{
header::{self, HeaderMap},
Method, RequestBuilder, Response, StatusCode, Url,
};
use serde::de::DeserializeOwned;
pub struct Client {
http: reqwest::Client,
token: Option<String>,
base_url: Url,
}
impl Client {
pub fn new(token: String, base_url: Url) -> Self {
let mut headers = HeaderMap::new();
headers.insert(header::AUTHORIZATION, format!("Bearer {token}").try_into().expect("the token is not a valid header value"));
Self {
http: reqwest::Client::builder().default_headers(headers).build().expect("the client was not constructed"),
token: Some(token),
base_url,
}
}
pub fn new_unauth(base_url: Url) -> Self {
Self {
http: reqwest::Client::new(),
token: None,
base_url,
}
}
pub fn token(&self) -> Option<&str> {
self.token.as_deref()
}
pub async fn execute(&self, request: RequestBuilder) -> Result<Response> {
let response = request.send().await?;
match response.status() {
StatusCode::UNAUTHORIZED => Err(Error::Api(ApiError::Unauthorized)),
StatusCode::NOT_FOUND => Err(Error::Api(ApiError::NotFound)),
StatusCode::FORBIDDEN => Err(Error::Api(ApiError::Forbidden(response.json().await?))),
_ => Ok(response),
}
}
pub async fn get<T: DeserializeOwned>(&self, url: &str) -> Result<T> {
Ok(self
.execute(
self.http.request(
Method::GET,
Url::options()
.base_url(Some(&self.base_url))
.parse(url)
.expect("invalid url"),
),
)
.await?
.json()
.await?)
}
pub async fn delete<T: DeserializeOwned>(&self, url: &str) -> Result<T> {
Ok(self
.execute(
self.http.request(
Method::DELETE,
Url::options()
.base_url(Some(&self.base_url))
.parse(url)
.expect("invalid url"),
),
)
.await?
.json()
.await?)
}
}