use reqwest::Client;
use serde_json::json;
use crate::error::{Result, SmsDevError};
use crate::models::{
BalanceResponse, CancelRequest, CancelResponse, DlrRequest, DlrResponse,
InboxMessage, InboxRequest, ReportRequest, ReportResponse, SendSmsRequest,
SendSmsResponse,
};
const BASE_URL: &str = "https://api.smsdev.com.br/v1";
#[derive(Debug, Clone)]
pub struct SmsDev {
api_key: String,
http: Client,
base_url: String,
}
impl SmsDev {
pub fn new(api_key: impl Into<String>) -> Self {
Self {
api_key: api_key.into(),
http: Client::new(),
base_url: BASE_URL.to_string(),
}
}
#[doc(hidden)]
pub fn with_base_url(mut self, url: impl Into<String>) -> Self {
self.base_url = url.into();
self
}
fn endpoint(&self, path: &str) -> String {
format!("{}/{}", self.base_url, path)
}
async fn post_json<T: serde::de::DeserializeOwned>(
&self,
path: &str,
body: &serde_json::Value,
) -> Result<T> {
let url = self.endpoint(path);
let response = self
.http
.post(&url)
.json(body)
.send()
.await?
.error_for_status()?;
let parsed = response.json::<T>().await?;
Ok(parsed)
}
pub async fn send_sms(&self, messages: Vec<SendSmsRequest>) -> Result<Vec<SendSmsResponse>> {
let url = self.endpoint("send");
let body = serde_json::to_value(&messages)?;
let response = self
.http
.post(&url)
.json(&body)
.send()
.await?
.error_for_status()?;
let results: Vec<SendSmsResponse> = response.json().await?;
Ok(results)
}
pub async fn send_one(&self, message: SendSmsRequest) -> Result<SendSmsResponse> {
let mut results = self.send_sms(vec![message]).await?;
results
.pop()
.ok_or_else(|| SmsDevError::UnexpectedResponse("Empty response array".into()))
}
pub async fn cancel(&self, ids: Vec<u64>) -> Result<Vec<CancelResponse>> {
let req = CancelRequest::new(&self.api_key, ids);
let body = json!(req);
self.post_json::<Vec<CancelResponse>>("cancel", &body).await
}
pub async fn inbox(&self, req: InboxRequest) -> Result<Vec<InboxMessage>> {
let mut body = json!({
"key": req.key,
"status": req.status as u8,
});
if let Some(df) = req.date_from {
body["date_from"] = json!(df);
}
if let Some(dt) = req.date_to {
body["date_to"] = json!(dt);
}
if let Some(ids) = req.id {
body["id"] = json!(ids);
}
self.post_json::<Vec<InboxMessage>>("inbox", &body).await
}
pub async fn dlr(&self, ids: Vec<u64>) -> Result<Vec<DlrResponse>> {
let req = DlrRequest::new(&self.api_key, ids);
let body = json!(req);
let url = self.endpoint("dlr");
let raw = self
.http
.post(&url)
.json(&body)
.send()
.await?
.error_for_status()?
.text()
.await?;
if let Ok(vec) = serde_json::from_str::<Vec<DlrResponse>>(&raw) {
Ok(vec)
} else if let Ok(single) = serde_json::from_str::<DlrResponse>(&raw) {
Ok(vec![single])
} else {
Err(SmsDevError::UnexpectedResponse(raw))
}
}
pub async fn balance(&self) -> Result<BalanceResponse> {
let body = json!({ "key": self.api_key });
self.post_json::<BalanceResponse>("balance", &body).await
}
pub async fn report(&self, req: ReportRequest) -> Result<ReportResponse> {
let mut body = json!({ "key": req.key });
if let Some(df) = req.date_from {
body["date_from"] = json!(df);
}
if let Some(dt) = req.date_to {
body["date_to"] = json!(dt);
}
self.post_json::<ReportResponse>("report/total", &body).await
}
}