1#![forbid(unsafe_code)]
2
3pub mod api;
4
5mod error;
6use error::ApiError;
7pub use error::{Error, Result};
8use reqwest::{
9 header::{self, HeaderMap},
10 Method, RequestBuilder, Response, StatusCode, Url,
11};
12use serde::de::DeserializeOwned;
13
14pub struct Client {
15 http: reqwest::Client,
16 token: Option<String>,
17 base_url: Url,
18}
19
20impl Client {
21 pub fn new(token: String, base_url: Url) -> Self {
22 let mut headers = HeaderMap::new();
23 headers.insert(header::AUTHORIZATION, format!("Bearer {token}").try_into().expect("the token is not a valid header value"));
24
25 Self {
26 http: reqwest::Client::builder().default_headers(headers).build().expect("the client was not constructed"),
27 token: Some(token),
28 base_url,
29 }
30 }
31
32 pub fn new_unauth(base_url: Url) -> Self {
33 Self {
34 http: reqwest::Client::new(),
35 token: None,
36 base_url,
37 }
38 }
39
40 pub fn token(&self) -> Option<&str> {
41 self.token.as_deref()
42 }
43
44 pub async fn execute(&self, request: RequestBuilder) -> Result<Response> {
45 let response = request.send().await?;
46 match response.status() {
47 StatusCode::UNAUTHORIZED => Err(Error::Api(ApiError::Unauthorized)),
48 StatusCode::NOT_FOUND => Err(Error::Api(ApiError::NotFound)),
49 StatusCode::FORBIDDEN => Err(Error::Api(ApiError::Forbidden(response.json().await?))),
50 _ => Ok(response),
51 }
52 }
53
54 pub async fn get<T: DeserializeOwned>(&self, url: &str) -> Result<T> {
55 Ok(self
56 .execute(
57 self.http.request(
58 Method::GET,
59 Url::options()
60 .base_url(Some(&self.base_url))
61 .parse(url)
62 .expect("invalid url"),
63 ),
64 )
65 .await?
66 .json()
67 .await?)
68 }
69
70 pub async fn delete<T: DeserializeOwned>(&self, url: &str) -> Result<T> {
71 Ok(self
72 .execute(
73 self.http.request(
74 Method::DELETE,
75 Url::options()
76 .base_url(Some(&self.base_url))
77 .parse(url)
78 .expect("invalid url"),
79 ),
80 )
81 .await?
82 .json()
83 .await?)
84 }
85}
86