use serde::{Deserialize, Deserializer, Serialize};
#[derive(Debug, Clone, Deserialize)]
pub struct ApiEnvelope<T> {
pub http_code: u16,
pub response_code: String,
#[serde(default)]
pub response_msg: Option<String>,
#[serde(default = "Option::default")]
pub data: Option<T>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct Paginated<T> {
#[serde(default)]
pub total: Option<u64>,
#[serde(default)]
pub per_page: Option<u32>,
#[serde(default)]
pub current_page: Option<u32>,
#[serde(default)]
pub last_page: Option<u32>,
#[serde(default)]
pub next_page_url: Option<String>,
#[serde(default)]
pub prev_page_url: Option<String>,
#[serde(default)]
pub from: Option<u64>,
#[serde(default)]
pub to: Option<u64>,
#[serde(default = "Vec::new")]
pub data: Vec<T>,
}
pub(crate) fn deser_schedule<'de, D>(d: D) -> Result<Option<i64>, D::Error>
where
D: Deserializer<'de>,
{
use serde::de::Error;
let v = serde_json::Value::deserialize(d)?;
match v {
serde_json::Value::Null => Ok(None),
serde_json::Value::String(s) if s.is_empty() => Ok(None),
serde_json::Value::String(s) => s.parse::<i64>().map(Some).map_err(Error::custom),
serde_json::Value::Number(n) => Ok(n.as_i64()),
other => Err(Error::custom(format!("invalid schedule field: {other}"))),
}
}
pub(crate) fn deser_str_or_f64<'de, D>(d: D) -> Result<Option<f64>, D::Error>
where
D: Deserializer<'de>,
{
use serde::de::Error;
let v = serde_json::Value::deserialize(d)?;
match v {
serde_json::Value::Null => Ok(None),
serde_json::Value::String(s) if s.is_empty() => Ok(None),
serde_json::Value::String(s) => s.parse::<f64>().map(Some).map_err(Error::custom),
serde_json::Value::Number(n) => Ok(n.as_f64()),
other => Err(Error::custom(format!("expected float-ish, got: {other}"))),
}
}
#[derive(Debug, Clone, Default, Deserialize)]
pub struct AccountData {
#[serde(default)]
pub user_id: Option<u64>,
#[serde(default)]
pub username: Option<String>,
#[serde(default)]
pub user_email: Option<String>,
#[serde(default)]
pub user_phone: Option<String>,
#[serde(default)]
pub user_first_name: Option<String>,
#[serde(default)]
pub user_last_name: Option<String>,
#[serde(default)]
pub active: Option<u8>,
#[serde(default)]
pub banned: Option<u8>,
#[serde(default)]
pub date_sign_up: Option<i64>,
#[serde(default, deserialize_with = "deser_str_or_f64")]
pub balance: Option<f64>,
#[serde(default)]
pub account_name: Option<String>,
#[serde(default)]
pub account_billing_email: Option<String>,
#[serde(default)]
pub country: Option<String>,
#[serde(default)]
pub default_country_sms: Option<String>,
#[serde(default)]
pub timezone: Option<String>,
#[serde(default)]
pub on_trial: Option<u8>,
#[serde(default, rename = "_currency")]
pub currency: Option<Currency>,
}
impl AccountData {
pub fn email(&self) -> Option<&str> {
self.user_email.as_deref()
}
}
#[derive(Debug, Clone, Serialize, Default)]
pub struct SmsMessage {
#[serde(skip_serializing_if = "Option::is_none")]
pub from: Option<String>,
pub body: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub to: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub source: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub schedule: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none", rename = "custom_string")]
pub custom_string: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "list_id")]
pub list_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub country: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "from_email")]
pub from_email: Option<String>,
}
impl SmsMessage {
pub fn new(to: impl Into<String>, body: impl Into<String>) -> Self {
Self {
to: Some(to.into()),
body: body.into(),
source: Some("rust".into()),
..Default::default()
}
}
pub fn from(mut self, from: impl Into<String>) -> Self {
self.from = Some(from.into());
self
}
pub fn schedule(mut self, ts: i64) -> Self {
self.schedule = Some(ts);
self
}
pub fn custom_string(mut self, s: impl Into<String>) -> Self {
self.custom_string = Some(s.into());
self
}
}
#[derive(Debug, Clone, Serialize)]
pub struct SmsMessageCollection {
pub messages: Vec<SmsMessage>,
}
impl SmsMessageCollection {
pub fn new(messages: Vec<SmsMessage>) -> Self {
Self { messages }
}
}
#[derive(Debug, Clone, Default, Deserialize)]
pub struct SmsSendData {
#[serde(default, deserialize_with = "deser_str_or_f64")]
pub total_price: Option<f64>,
#[serde(default)]
pub total_count: Option<u32>,
#[serde(default)]
pub queued_count: Option<u32>,
#[serde(default)]
pub blocked_count: Option<u32>,
#[serde(default)]
pub messages: Vec<SmsSendMessageResult>,
#[serde(default, rename = "_currency")]
pub currency: Option<Currency>,
}
#[derive(Debug, Clone, Default, Deserialize)]
pub struct Currency {
#[serde(default)]
pub currency_name_short: Option<String>,
#[serde(default)]
pub currency_name_long: Option<String>,
#[serde(default)]
pub currency_prefix_d: Option<String>,
#[serde(default)]
pub currency_prefix_c: Option<String>,
}
#[derive(Debug, Clone, Default, Deserialize)]
pub struct SmsSendMessageResult {
#[serde(default)]
pub direction: Option<String>,
#[serde(default)]
pub date: Option<i64>,
#[serde(default)]
pub to: Option<String>,
#[serde(default)]
pub body: Option<String>,
#[serde(default)]
pub from: Option<String>,
#[serde(default, deserialize_with = "deser_schedule")]
pub schedule: Option<i64>,
#[serde(default)]
pub message_id: Option<String>,
#[serde(default)]
pub message_parts: Option<u32>,
#[serde(default, deserialize_with = "deser_str_or_f64")]
pub message_price: Option<f64>,
#[serde(default)]
pub custom_string: Option<String>,
#[serde(default)]
pub user_id: Option<u64>,
#[serde(default)]
pub subaccount_id: Option<u64>,
#[serde(default)]
pub country: Option<String>,
#[serde(default)]
pub carrier: Option<String>,
#[serde(default)]
pub status: Option<String>,
#[serde(default)]
pub status_code: Option<String>,
#[serde(default)]
pub status_text: Option<String>,
#[serde(default)]
pub error_code: Option<String>,
#[serde(default)]
pub error_text: Option<String>,
#[serde(default)]
pub is_shared_system_number: Option<bool>,
}
#[derive(Debug, Clone, Default, Deserialize)]
pub struct SmsHistoryItem {
#[serde(default)]
pub direction: Option<String>,
#[serde(default)]
pub date: Option<i64>,
#[serde(default)]
pub to: Option<String>,
#[serde(default)]
pub body: Option<String>,
#[serde(default)]
pub from: Option<String>,
#[serde(default, deserialize_with = "deser_schedule")]
pub schedule: Option<i64>,
#[serde(default)]
pub message_id: Option<String>,
#[serde(default)]
pub message_parts: Option<u32>,
#[serde(default, deserialize_with = "deser_str_or_f64")]
pub message_price: Option<f64>,
#[serde(default)]
pub custom_string: Option<String>,
#[serde(default)]
pub user_id: Option<u64>,
#[serde(default)]
pub subaccount_id: Option<u64>,
#[serde(default)]
pub country: Option<String>,
#[serde(default)]
pub carrier: Option<String>,
#[serde(default)]
pub status: Option<String>,
#[serde(default)]
pub status_code: Option<String>,
#[serde(default)]
pub status_text: Option<String>,
#[serde(default)]
pub error_code: Option<String>,
#[serde(default)]
pub error_text: Option<String>,
}
#[derive(Debug, Clone, Default, Deserialize)]
pub struct SmsReceiptItem {
#[serde(default)]
pub message_id: Option<String>,
#[serde(default)]
pub status: Option<String>,
#[serde(default)]
pub status_code: Option<String>,
#[serde(default)]
pub status_text: Option<String>,
#[serde(default)]
pub timestamp: Option<i64>,
#[serde(default)]
pub originator: Option<String>,
}
#[derive(Debug, Clone, Default, Deserialize)]
pub struct SmsInboundItem {
#[serde(default)]
pub message_id: Option<String>,
#[serde(default)]
pub from: Option<String>,
#[serde(default)]
pub to: Option<String>,
#[serde(default)]
pub body: Option<String>,
#[serde(default)]
pub timestamp: Option<i64>,
#[serde(default)]
pub custom_string: Option<String>,
#[serde(default)]
pub original_message_id: Option<String>,
#[serde(default)]
pub original_body: Option<String>,
#[serde(default)]
pub originator: Option<String>,
}
#[derive(Debug, Clone, Serialize, Default)]
pub struct MmsMessage {
#[serde(skip_serializing_if = "Option::is_none")]
pub from: Option<String>,
pub body: String,
pub subject: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub to: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub source: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub schedule: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none", rename = "custom_string")]
pub custom_string: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "list_id")]
pub list_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub country: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "from_email")]
pub from_email: Option<String>,
}
impl MmsMessage {
pub fn new(
to: impl Into<String>,
subject: impl Into<String>,
body: impl Into<String>,
) -> Self {
Self {
to: Some(to.into()),
subject: subject.into(),
body: body.into(),
source: Some("rust".into()),
..Default::default()
}
}
}
#[derive(Debug, Clone, Serialize)]
pub struct MmsMessageCollection {
pub media_file: String,
pub messages: Vec<MmsMessage>,
}
impl MmsMessageCollection {
pub fn new(media_file: impl Into<String>, messages: Vec<MmsMessage>) -> Self {
Self {
media_file: media_file.into(),
messages,
}
}
}
#[derive(Debug, Clone, Serialize, Default)]
pub struct VoiceMessage {
#[serde(skip_serializing_if = "Option::is_none")]
pub to: Option<String>,
pub body: String,
pub voice: String,
pub lang: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub country: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub source: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "custom_string")]
pub custom_string: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "list_id")]
pub list_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub schedule: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none", rename = "require_input")]
pub require_input: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none", rename = "machine_detection")]
pub machine_detection: Option<u8>,
}
impl VoiceMessage {
pub fn new(to: impl Into<String>, body: impl Into<String>) -> Self {
Self {
to: Some(to.into()),
body: body.into(),
voice: "female".into(),
lang: "en-us".into(),
source: Some("rust".into()),
..Default::default()
}
}
pub fn voice(mut self, v: impl Into<String>) -> Self {
self.voice = v.into();
self
}
pub fn lang(mut self, v: impl Into<String>) -> Self {
self.lang = v.into();
self
}
}
#[derive(Debug, Clone, Serialize)]
pub struct VoiceMessageCollection {
pub messages: Vec<VoiceMessage>,
}
impl VoiceMessageCollection {
pub fn new(messages: Vec<VoiceMessage>) -> Self {
Self { messages }
}
}
#[derive(Debug, Clone, Serialize)]
pub struct EmailRecipient {
pub email: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
}
#[derive(Debug, Clone, Serialize)]
pub struct EmailFrom {
pub email_address_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
}
#[derive(Debug, Clone, Serialize)]
pub struct Email {
pub to: Vec<EmailRecipient>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cc: Option<Vec<EmailRecipient>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub bcc: Option<Vec<EmailRecipient>>,
pub from: EmailFrom,
#[serde(skip_serializing_if = "Option::is_none")]
pub subject: Option<String>,
pub body: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub attachments: Option<Vec<serde_json::Value>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub schedule: Option<i64>,
}
#[derive(Debug, Clone, Default, Deserialize)]
pub struct InboundSmsWebhook {
#[serde(default)]
pub message_id: Option<String>,
#[serde(default)]
pub from: Option<String>,
#[serde(default)]
pub to: Option<String>,
#[serde(default)]
pub body: Option<String>,
#[serde(default)]
pub timestamp: Option<i64>,
#[serde(default)]
pub custom_string: Option<String>,
#[serde(default)]
pub original_message_id: Option<String>,
#[serde(default)]
pub original_body: Option<String>,
#[serde(default)]
pub originator: Option<String>,
}
#[derive(Debug, Clone, Default, Deserialize)]
pub struct DeliveryReceiptWebhook {
#[serde(default)]
pub message_id: Option<String>,
#[serde(default)]
pub status: Option<String>,
#[serde(default)]
pub status_code: Option<String>,
#[serde(default)]
pub status_text: Option<String>,
#[serde(default)]
pub error_code: Option<String>,
#[serde(default)]
pub error_text: Option<String>,
#[serde(default)]
pub timestamp: Option<i64>,
#[serde(default)]
pub custom_string: Option<String>,
#[serde(default)]
pub originator: Option<String>,
}