use reqwest::{Client, Response};
use serde::{de::DeserializeOwned, Serialize};
use crate::error::Error;
use crate::emails::Emails;
use crate::batch::Batch;
use crate::templates::Templates;
use crate::domains::Domains;
use crate::webhooks::Webhooks;
use crate::analytics::Analytics;
const DEFAULT_BASE_URL: &str = "https://api.sentd.io";
const VERSION: &str = env!("CARGO_PKG_VERSION");
#[derive(Clone)]
pub struct Sentd {
api_key: String,
base_url: String,
client: Client,
}
impl Sentd {
pub fn new(api_key: impl Into<String>) -> Self {
Self::with_base_url(api_key, DEFAULT_BASE_URL)
}
pub fn with_base_url(api_key: impl Into<String>, base_url: impl Into<String>) -> Self {
Self {
api_key: api_key.into(),
base_url: base_url.into(),
client: Client::new(),
}
}
pub fn emails(&self) -> Emails {
Emails::new(self.clone())
}
pub fn batch(&self) -> Batch {
Batch::new(self.clone())
}
pub fn templates(&self) -> Templates {
Templates::new(self.clone())
}
pub fn domains(&self) -> Domains {
Domains::new(self.clone())
}
pub fn webhooks(&self) -> Webhooks {
Webhooks::new(self.clone())
}
pub fn analytics(&self) -> Analytics {
Analytics::new(self.clone())
}
pub(crate) async fn get<T: DeserializeOwned>(&self, path: &str) -> Result<T, Error> {
let response = self.client
.get(format!("{}{}", self.base_url, path))
.header("Authorization", format!("Bearer {}", self.api_key))
.header("User-Agent", format!("sentd-rust/{}", VERSION))
.send()
.await?;
self.handle_response(response).await
}
pub(crate) async fn post<T: DeserializeOwned, B: Serialize>(&self, path: &str, body: &B) -> Result<T, Error> {
let response = self.client
.post(format!("{}{}", self.base_url, path))
.header("Authorization", format!("Bearer {}", self.api_key))
.header("Content-Type", "application/json")
.header("User-Agent", format!("sentd-rust/{}", VERSION))
.json(body)
.send()
.await?;
self.handle_response(response).await
}
pub(crate) async fn put<T: DeserializeOwned, B: Serialize>(&self, path: &str, body: &B) -> Result<T, Error> {
let response = self.client
.put(format!("{}{}", self.base_url, path))
.header("Authorization", format!("Bearer {}", self.api_key))
.header("Content-Type", "application/json")
.header("User-Agent", format!("sentd-rust/{}", VERSION))
.json(body)
.send()
.await?;
self.handle_response(response).await
}
pub(crate) async fn patch<T: DeserializeOwned, B: Serialize>(&self, path: &str, body: &B) -> Result<T, Error> {
let response = self.client
.patch(format!("{}{}", self.base_url, path))
.header("Authorization", format!("Bearer {}", self.api_key))
.header("Content-Type", "application/json")
.header("User-Agent", format!("sentd-rust/{}", VERSION))
.json(body)
.send()
.await?;
self.handle_response(response).await
}
pub(crate) async fn delete<T: DeserializeOwned>(&self, path: &str) -> Result<T, Error> {
let response = self.client
.delete(format!("{}{}", self.base_url, path))
.header("Authorization", format!("Bearer {}", self.api_key))
.header("User-Agent", format!("sentd-rust/{}", VERSION))
.send()
.await?;
self.handle_response(response).await
}
async fn handle_response<T: DeserializeOwned>(&self, response: Response) -> Result<T, Error> {
let status = response.status().as_u16();
if status >= 400 {
let body: serde_json::Value = response.json().await.unwrap_or_default();
let message = body["error"]
.as_str()
.or(body["message"].as_str())
.unwrap_or("Unknown error")
.to_string();
return Err(Error::from_status(status, message));
}
let data = response.json().await?;
Ok(data)
}
}