use std::{sync::Arc, time::Duration};
use composure::rest::{self, DISCORD_API, DISCORD_HEADERS, RawResponse, Transport, TransportError};
#[derive(Debug, Clone, Default)]
pub struct HttpTransport {
client: reqwest::Client,
api_base: Option<Arc<str>>,
}
impl HttpTransport {
pub fn new() -> Self {
Self::default()
}
pub fn with_client(client: reqwest::Client) -> Self {
Self {
client,
api_base: None,
}
}
pub fn with_api_base(mut self, base: impl Into<String>) -> Self {
let base = base.into();
self.api_base = Some(base.trim_end_matches('/').into());
self
}
pub fn client(&self) -> &reqwest::Client {
&self.client
}
}
impl Transport for HttpTransport {
async fn send(&self, request: &rest::Request) -> Result<RawResponse, TransportError> {
let method = match request.method {
rest::Method::Get => reqwest::Method::GET,
rest::Method::Post => reqwest::Method::POST,
rest::Method::Put => reqwest::Method::PUT,
rest::Method::Patch => reqwest::Method::PATCH,
rest::Method::Delete => reqwest::Method::DELETE,
};
let url = match (&self.api_base, request.url.strip_prefix(DISCORD_API)) {
(Some(base), Some(path)) => format!("{base}{path}"),
_ => request.url.clone(),
};
let mut builder = self.client.request(method, url);
for (name, value) in &request.headers {
builder = builder.header(*name, value.as_ref());
}
if let Some(body) = &request.body {
builder = builder.body(body.clone());
}
let response = builder.send().await.map_err(TransportError::new)?;
let status = response.status().as_u16();
let received = response.headers();
let headers = DISCORD_HEADERS
.iter()
.filter_map(|&name| {
let value = received.get(name)?.to_str().ok()?;
Some((name.to_string(), value.to_string()))
})
.collect();
let body = response
.bytes()
.await
.map_err(TransportError::new)?
.to_vec();
Ok(RawResponse {
status,
headers,
body,
})
}
async fn sleep(&self, duration: Duration) {
tokio::time::sleep(duration).await
}
}