1use serde::{Deserialize, Deserializer, Serialize};
7
8#[derive(Debug, Clone, Deserialize)]
13pub struct ApiEnvelope<T> {
14 pub http_code: u16,
16 pub response_code: String,
18 #[serde(default)]
20 pub response_msg: Option<String>,
21 #[serde(default = "Option::default")]
23 pub data: Option<T>,
24}
25
26#[derive(Debug, Clone, Deserialize)]
28pub struct Paginated<T> {
29 #[serde(default)]
31 pub total: Option<u64>,
32 #[serde(default)]
34 pub per_page: Option<u32>,
35 #[serde(default)]
37 pub current_page: Option<u32>,
38 #[serde(default)]
40 pub last_page: Option<u32>,
41 #[serde(default)]
43 pub next_page_url: Option<String>,
44 #[serde(default)]
46 pub prev_page_url: Option<String>,
47 #[serde(default)]
49 pub from: Option<u64>,
50 #[serde(default)]
52 pub to: Option<u64>,
53 #[serde(default = "Vec::new")]
55 pub data: Vec<T>,
56}
57
58pub(crate) fn deser_schedule<'de, D>(d: D) -> Result<Option<i64>, D::Error>
63where
64 D: Deserializer<'de>,
65{
66 use serde::de::Error;
67 let v = serde_json::Value::deserialize(d)?;
68 match v {
69 serde_json::Value::Null => Ok(None),
70 serde_json::Value::String(s) if s.is_empty() => Ok(None),
71 serde_json::Value::String(s) => s.parse::<i64>().map(Some).map_err(Error::custom),
72 serde_json::Value::Number(n) => Ok(n.as_i64()),
73 other => Err(Error::custom(format!("invalid schedule field: {other}"))),
74 }
75}
76
77pub(crate) fn deser_str_or_f64<'de, D>(d: D) -> Result<Option<f64>, D::Error>
79where
80 D: Deserializer<'de>,
81{
82 use serde::de::Error;
83 let v = serde_json::Value::deserialize(d)?;
84 match v {
85 serde_json::Value::Null => Ok(None),
86 serde_json::Value::String(s) if s.is_empty() => Ok(None),
87 serde_json::Value::String(s) => s.parse::<f64>().map(Some).map_err(Error::custom),
88 serde_json::Value::Number(n) => Ok(n.as_f64()),
89 other => Err(Error::custom(format!("expected float-ish, got: {other}"))),
90 }
91}
92
93#[derive(Debug, Clone, Default, Deserialize)]
101pub struct AccountData {
102 #[serde(default)]
104 pub user_id: Option<u64>,
105 #[serde(default)]
107 pub username: Option<String>,
108 #[serde(default)]
110 pub user_email: Option<String>,
111 #[serde(default)]
113 pub user_phone: Option<String>,
114 #[serde(default)]
116 pub user_first_name: Option<String>,
117 #[serde(default)]
119 pub user_last_name: Option<String>,
120 #[serde(default)]
122 pub active: Option<u8>,
123 #[serde(default)]
125 pub banned: Option<u8>,
126 #[serde(default)]
128 pub date_sign_up: Option<i64>,
129 #[serde(default, deserialize_with = "deser_str_or_f64")]
132 pub balance: Option<f64>,
133 #[serde(default)]
135 pub account_name: Option<String>,
136 #[serde(default)]
138 pub account_billing_email: Option<String>,
139 #[serde(default)]
141 pub country: Option<String>,
142 #[serde(default)]
144 pub default_country_sms: Option<String>,
145 #[serde(default)]
147 pub timezone: Option<String>,
148 #[serde(default)]
150 pub on_trial: Option<u8>,
151 #[serde(default, rename = "_currency")]
153 pub currency: Option<Currency>,
154}
155
156impl AccountData {
157 pub fn email(&self) -> Option<&str> {
159 self.user_email.as_deref()
160 }
161}
162
163#[derive(Debug, Clone, Serialize, Default)]
170pub struct SmsMessage {
171 #[serde(skip_serializing_if = "Option::is_none")]
174 pub from: Option<String>,
175 pub body: String,
177 #[serde(skip_serializing_if = "Option::is_none")]
179 pub to: Option<String>,
180 #[serde(skip_serializing_if = "Option::is_none")]
182 pub source: Option<String>,
183 #[serde(skip_serializing_if = "Option::is_none")]
185 pub schedule: Option<i64>,
186 #[serde(skip_serializing_if = "Option::is_none", rename = "custom_string")]
188 pub custom_string: Option<String>,
189 #[serde(skip_serializing_if = "Option::is_none", rename = "list_id")]
191 pub list_id: Option<i64>,
192 #[serde(skip_serializing_if = "Option::is_none")]
194 pub country: Option<String>,
195 #[serde(skip_serializing_if = "Option::is_none", rename = "from_email")]
197 pub from_email: Option<String>,
198}
199
200impl SmsMessage {
201 pub fn new(to: impl Into<String>, body: impl Into<String>) -> Self {
203 Self {
204 to: Some(to.into()),
205 body: body.into(),
206 source: Some("rust".into()),
207 ..Default::default()
208 }
209 }
210
211 pub fn from(mut self, from: impl Into<String>) -> Self {
213 self.from = Some(from.into());
214 self
215 }
216
217 pub fn schedule(mut self, ts: i64) -> Self {
219 self.schedule = Some(ts);
220 self
221 }
222
223 pub fn custom_string(mut self, s: impl Into<String>) -> Self {
225 self.custom_string = Some(s.into());
226 self
227 }
228}
229
230#[derive(Debug, Clone, Serialize)]
232pub struct SmsMessageCollection {
233 pub messages: Vec<SmsMessage>,
235}
236
237impl SmsMessageCollection {
238 pub fn new(messages: Vec<SmsMessage>) -> Self {
240 Self { messages }
241 }
242}
243
244#[derive(Debug, Clone, Default, Deserialize)]
247pub struct SmsSendData {
248 #[serde(default, deserialize_with = "deser_str_or_f64")]
251 pub total_price: Option<f64>,
252 #[serde(default)]
254 pub total_count: Option<u32>,
255 #[serde(default)]
257 pub queued_count: Option<u32>,
258 #[serde(default)]
260 pub blocked_count: Option<u32>,
261 #[serde(default)]
263 pub messages: Vec<SmsSendMessageResult>,
264 #[serde(default, rename = "_currency")]
266 pub currency: Option<Currency>,
267}
268
269#[derive(Debug, Clone, Default, Deserialize)]
271pub struct Currency {
272 #[serde(default)]
274 pub currency_name_short: Option<String>,
275 #[serde(default)]
277 pub currency_name_long: Option<String>,
278 #[serde(default)]
280 pub currency_prefix_d: Option<String>,
281 #[serde(default)]
283 pub currency_prefix_c: Option<String>,
284}
285
286#[derive(Debug, Clone, Default, Deserialize)]
292pub struct SmsSendMessageResult {
293 #[serde(default)]
295 pub direction: Option<String>,
296 #[serde(default)]
298 pub date: Option<i64>,
299 #[serde(default)]
301 pub to: Option<String>,
302 #[serde(default)]
304 pub body: Option<String>,
305 #[serde(default)]
307 pub from: Option<String>,
308 #[serde(default, deserialize_with = "deser_schedule")]
311 pub schedule: Option<i64>,
312 #[serde(default)]
314 pub message_id: Option<String>,
315 #[serde(default)]
317 pub message_parts: Option<u32>,
318 #[serde(default, deserialize_with = "deser_str_or_f64")]
320 pub message_price: Option<f64>,
321 #[serde(default)]
323 pub custom_string: Option<String>,
324 #[serde(default)]
326 pub user_id: Option<u64>,
327 #[serde(default)]
329 pub subaccount_id: Option<u64>,
330 #[serde(default)]
332 pub country: Option<String>,
333 #[serde(default)]
335 pub carrier: Option<String>,
336 #[serde(default)]
338 pub status: Option<String>,
339 #[serde(default)]
341 pub status_code: Option<String>,
342 #[serde(default)]
344 pub status_text: Option<String>,
345 #[serde(default)]
347 pub error_code: Option<String>,
348 #[serde(default)]
350 pub error_text: Option<String>,
351 #[serde(default)]
353 pub is_shared_system_number: Option<bool>,
354}
355
356#[derive(Debug, Clone, Default, Deserialize)]
359pub struct SmsHistoryItem {
360 #[serde(default)]
362 pub direction: Option<String>,
363 #[serde(default)]
365 pub date: Option<i64>,
366 #[serde(default)]
368 pub to: Option<String>,
369 #[serde(default)]
371 pub body: Option<String>,
372 #[serde(default)]
374 pub from: Option<String>,
375 #[serde(default, deserialize_with = "deser_schedule")]
377 pub schedule: Option<i64>,
378 #[serde(default)]
380 pub message_id: Option<String>,
381 #[serde(default)]
383 pub message_parts: Option<u32>,
384 #[serde(default, deserialize_with = "deser_str_or_f64")]
386 pub message_price: Option<f64>,
387 #[serde(default)]
389 pub custom_string: Option<String>,
390 #[serde(default)]
392 pub user_id: Option<u64>,
393 #[serde(default)]
395 pub subaccount_id: Option<u64>,
396 #[serde(default)]
398 pub country: Option<String>,
399 #[serde(default)]
401 pub carrier: Option<String>,
402 #[serde(default)]
404 pub status: Option<String>,
405 #[serde(default)]
407 pub status_code: Option<String>,
408 #[serde(default)]
410 pub status_text: Option<String>,
411 #[serde(default)]
413 pub error_code: Option<String>,
414 #[serde(default)]
416 pub error_text: Option<String>,
417}
418
419#[derive(Debug, Clone, Default, Deserialize)]
421pub struct SmsReceiptItem {
422 #[serde(default)]
424 pub message_id: Option<String>,
425 #[serde(default)]
427 pub status: Option<String>,
428 #[serde(default)]
430 pub status_code: Option<String>,
431 #[serde(default)]
433 pub status_text: Option<String>,
434 #[serde(default)]
436 pub timestamp: Option<i64>,
437 #[serde(default)]
439 pub originator: Option<String>,
440}
441
442#[derive(Debug, Clone, Default, Deserialize)]
444pub struct SmsInboundItem {
445 #[serde(default)]
447 pub message_id: Option<String>,
448 #[serde(default)]
450 pub from: Option<String>,
451 #[serde(default)]
453 pub to: Option<String>,
454 #[serde(default)]
456 pub body: Option<String>,
457 #[serde(default)]
459 pub timestamp: Option<i64>,
460 #[serde(default)]
462 pub custom_string: Option<String>,
463 #[serde(default)]
465 pub original_message_id: Option<String>,
466 #[serde(default)]
468 pub original_body: Option<String>,
469 #[serde(default)]
471 pub originator: Option<String>,
472}
473
474#[derive(Debug, Clone, Serialize, Default)]
478pub struct MmsMessage {
479 #[serde(skip_serializing_if = "Option::is_none")]
481 pub from: Option<String>,
482 pub body: String,
484 pub subject: String,
486 #[serde(skip_serializing_if = "Option::is_none")]
488 pub to: Option<String>,
489 #[serde(skip_serializing_if = "Option::is_none")]
491 pub source: Option<String>,
492 #[serde(skip_serializing_if = "Option::is_none")]
494 pub schedule: Option<i64>,
495 #[serde(skip_serializing_if = "Option::is_none", rename = "custom_string")]
497 pub custom_string: Option<String>,
498 #[serde(skip_serializing_if = "Option::is_none", rename = "list_id")]
500 pub list_id: Option<i64>,
501 #[serde(skip_serializing_if = "Option::is_none")]
503 pub country: Option<String>,
504 #[serde(skip_serializing_if = "Option::is_none", rename = "from_email")]
506 pub from_email: Option<String>,
507}
508
509impl MmsMessage {
510 pub fn new(
512 to: impl Into<String>,
513 subject: impl Into<String>,
514 body: impl Into<String>,
515 ) -> Self {
516 Self {
517 to: Some(to.into()),
518 subject: subject.into(),
519 body: body.into(),
520 source: Some("rust".into()),
521 ..Default::default()
522 }
523 }
524}
525
526#[derive(Debug, Clone, Serialize)]
531pub struct MmsMessageCollection {
532 pub media_file: String,
534 pub messages: Vec<MmsMessage>,
536}
537
538impl MmsMessageCollection {
539 pub fn new(media_file: impl Into<String>, messages: Vec<MmsMessage>) -> Self {
541 Self {
542 media_file: media_file.into(),
543 messages,
544 }
545 }
546}
547
548#[derive(Debug, Clone, Serialize, Default)]
555pub struct VoiceMessage {
556 #[serde(skip_serializing_if = "Option::is_none")]
558 pub to: Option<String>,
559 pub body: String,
561 pub voice: String,
563 pub lang: String,
565 #[serde(skip_serializing_if = "Option::is_none")]
567 pub country: Option<String>,
568 #[serde(skip_serializing_if = "Option::is_none")]
570 pub source: Option<String>,
571 #[serde(skip_serializing_if = "Option::is_none", rename = "custom_string")]
573 pub custom_string: Option<String>,
574 #[serde(skip_serializing_if = "Option::is_none", rename = "list_id")]
576 pub list_id: Option<i64>,
577 #[serde(skip_serializing_if = "Option::is_none")]
579 pub schedule: Option<i64>,
580 #[serde(skip_serializing_if = "Option::is_none", rename = "require_input")]
582 pub require_input: Option<u8>,
583 #[serde(skip_serializing_if = "Option::is_none", rename = "machine_detection")]
585 pub machine_detection: Option<u8>,
586}
587
588impl VoiceMessage {
589 pub fn new(to: impl Into<String>, body: impl Into<String>) -> Self {
592 Self {
593 to: Some(to.into()),
594 body: body.into(),
595 voice: "female".into(),
596 lang: "en-us".into(),
597 source: Some("rust".into()),
598 ..Default::default()
599 }
600 }
601
602 pub fn voice(mut self, v: impl Into<String>) -> Self {
604 self.voice = v.into();
605 self
606 }
607
608 pub fn lang(mut self, v: impl Into<String>) -> Self {
610 self.lang = v.into();
611 self
612 }
613}
614
615#[derive(Debug, Clone, Serialize)]
617pub struct VoiceMessageCollection {
618 pub messages: Vec<VoiceMessage>,
620}
621
622impl VoiceMessageCollection {
623 pub fn new(messages: Vec<VoiceMessage>) -> Self {
625 Self { messages }
626 }
627}
628
629#[derive(Debug, Clone, Serialize)]
633pub struct EmailRecipient {
634 pub email: String,
636 #[serde(skip_serializing_if = "Option::is_none")]
638 pub name: Option<String>,
639}
640
641#[derive(Debug, Clone, Serialize)]
647pub struct EmailFrom {
648 pub email_address_id: String,
650 #[serde(skip_serializing_if = "Option::is_none")]
652 pub name: Option<String>,
653}
654
655#[derive(Debug, Clone, Serialize)]
657pub struct Email {
658 pub to: Vec<EmailRecipient>,
660 #[serde(skip_serializing_if = "Option::is_none")]
662 pub cc: Option<Vec<EmailRecipient>>,
663 #[serde(skip_serializing_if = "Option::is_none")]
665 pub bcc: Option<Vec<EmailRecipient>>,
666 pub from: EmailFrom,
668 #[serde(skip_serializing_if = "Option::is_none")]
670 pub subject: Option<String>,
671 pub body: String,
673 #[serde(skip_serializing_if = "Option::is_none")]
675 pub attachments: Option<Vec<serde_json::Value>>,
676 #[serde(skip_serializing_if = "Option::is_none")]
678 pub schedule: Option<i64>,
679}
680
681#[derive(Debug, Clone, Default, Deserialize)]
687pub struct InboundSmsWebhook {
688 #[serde(default)]
690 pub message_id: Option<String>,
691 #[serde(default)]
693 pub from: Option<String>,
694 #[serde(default)]
696 pub to: Option<String>,
697 #[serde(default)]
699 pub body: Option<String>,
700 #[serde(default)]
702 pub timestamp: Option<i64>,
703 #[serde(default)]
705 pub custom_string: Option<String>,
706 #[serde(default)]
708 pub original_message_id: Option<String>,
709 #[serde(default)]
711 pub original_body: Option<String>,
712 #[serde(default)]
714 pub originator: Option<String>,
715}
716
717#[derive(Debug, Clone, Default, Deserialize)]
720pub struct DeliveryReceiptWebhook {
721 #[serde(default)]
723 pub message_id: Option<String>,
724 #[serde(default)]
726 pub status: Option<String>,
727 #[serde(default)]
729 pub status_code: Option<String>,
730 #[serde(default)]
732 pub status_text: Option<String>,
733 #[serde(default)]
735 pub error_code: Option<String>,
736 #[serde(default)]
738 pub error_text: Option<String>,
739 #[serde(default)]
741 pub timestamp: Option<i64>,
742 #[serde(default)]
744 pub custom_string: Option<String>,
745 #[serde(default)]
747 pub originator: Option<String>,
748}