use std::{borrow::Cow, fmt, sync::Arc, time::Duration};
pub trait Transport: Send + Sync {
fn send(
&self,
request: &Request,
) -> impl Future<Output = Result<RawResponse, TransportError>> + Send;
fn sleep(&self, duration: Duration) -> impl Future<Output = ()> + Send;
}
impl<T: Transport + ?Sized> Transport for &T {
async fn send(&self, request: &Request) -> Result<RawResponse, TransportError> {
(**self).send(request).await
}
async fn sleep(&self, duration: Duration) {
(**self).sleep(duration).await
}
}
impl<T: Transport + ?Sized> Transport for Arc<T> {
async fn send(&self, request: &Request) -> Result<RawResponse, TransportError> {
(**self).send(request).await
}
async fn sleep(&self, duration: Duration) {
(**self).sleep(duration).await
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Method {
Get,
Post,
Put,
Patch,
Delete,
}
impl Method {
pub fn as_str(self) -> &'static str {
match self {
Method::Get => "GET",
Method::Post => "POST",
Method::Put => "PUT",
Method::Patch => "PATCH",
Method::Delete => "DELETE",
}
}
}
impl fmt::Display for Method {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Request {
pub method: Method,
pub url: String,
pub headers: Vec<(&'static str, Cow<'static, str>)>,
pub body: Option<Vec<u8>>,
}
impl Request {
pub fn header(&self, name: &str) -> Option<&str> {
self.headers
.iter()
.find(|(key, _)| key.eq_ignore_ascii_case(name))
.map(|(_, value)| value.as_ref())
}
}
pub const DISCORD_HEADERS: &[&str] = &[
"content-type",
"retry-after",
"x-ratelimit-bucket",
"x-ratelimit-global",
"x-ratelimit-limit",
"x-ratelimit-remaining",
"x-ratelimit-reset",
"x-ratelimit-reset-after",
"x-ratelimit-scope",
];
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct RawResponse {
pub status: u16,
pub headers: Vec<(String, String)>,
pub body: Vec<u8>,
}
impl RawResponse {
pub fn header(&self, name: &str) -> Option<&str> {
self.headers
.iter()
.find(|(key, _)| key.eq_ignore_ascii_case(name))
.map(|(_, value)| value.as_str())
}
pub fn is_success(&self) -> bool {
(200..300).contains(&self.status)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TransportError(pub String);
impl TransportError {
pub fn new(error: impl fmt::Display) -> Self {
Self(error.to_string())
}
}
impl fmt::Display for TransportError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
impl std::error::Error for TransportError {}