1use std::collections::BTreeMap;
2
3use reqwest::{header::HeaderMap, StatusCode};
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6
7#[derive(Clone, Debug)]
9pub struct UniResponse<T> {
10 pub status: StatusCode,
12 pub code: String,
14 pub message: String,
16 pub data: Option<T>,
18 pub request_id: Option<String>,
20 pub headers: HeaderMap,
22 pub raw_body: Value,
24}
25
26impl<T> UniResponse<T> {
27 pub fn into_data(self) -> crate::Result<T> {
30 self.data.ok_or_else(|| crate::UniError::InvalidResponse {
31 status: self.status,
32 request_id: self.request_id,
33 message: "response did not contain data".to_owned(),
34 body: self.raw_body.to_string(),
35 })
36 }
37}
38
39#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
41#[serde(untagged)]
42pub enum Recipients {
43 One(String),
45 Many(Vec<String>),
47}
48
49impl From<String> for Recipients {
50 fn from(value: String) -> Self {
51 Self::One(value)
52 }
53}
54
55impl From<&str> for Recipients {
56 fn from(value: &str) -> Self {
57 Self::One(value.to_owned())
58 }
59}
60
61impl From<Vec<String>> for Recipients {
62 fn from(value: Vec<String>) -> Self {
63 Self::Many(value)
64 }
65}
66
67impl<const N: usize> From<[&str; N]> for Recipients {
68 fn from(value: [&str; N]) -> Self {
69 Self::Many(value.into_iter().map(str::to_owned).collect())
70 }
71}
72
73#[derive(Clone, Debug, Serialize)]
75#[serde(rename_all = "camelCase")]
76pub struct SendMessageRequest {
77 pub to: Recipients,
79 #[serde(skip_serializing_if = "Option::is_none")]
81 pub signature: Option<String>,
82 #[serde(skip_serializing_if = "Option::is_none")]
84 pub template_id: Option<String>,
85 #[serde(skip_serializing_if = "Option::is_none")]
87 pub template_data: Option<BTreeMap<String, Value>>,
88 #[serde(skip_serializing_if = "Option::is_none")]
90 pub content: Option<String>,
91 #[serde(skip_serializing_if = "Option::is_none")]
93 pub text: Option<String>,
94}
95
96impl SendMessageRequest {
97 pub fn text(to: impl Into<Recipients>, text: impl Into<String>) -> Self {
99 Self {
100 to: to.into(),
101 signature: None,
102 template_id: None,
103 template_data: None,
104 content: None,
105 text: Some(text.into()),
106 }
107 }
108
109 pub fn template(
111 to: impl Into<Recipients>,
112 signature: impl Into<String>,
113 template_id: impl Into<String>,
114 ) -> Self {
115 Self {
116 to: to.into(),
117 signature: Some(signature.into()),
118 template_id: Some(template_id.into()),
119 template_data: None,
120 content: None,
121 text: None,
122 }
123 }
124
125 pub fn content(
127 to: impl Into<Recipients>,
128 signature: impl Into<String>,
129 content: impl Into<String>,
130 ) -> Self {
131 Self {
132 to: to.into(),
133 signature: Some(signature.into()),
134 template_id: None,
135 template_data: None,
136 content: Some(content.into()),
137 text: None,
138 }
139 }
140
141 pub fn with_template_data(mut self, data: BTreeMap<String, Value>) -> Self {
143 self.template_data = Some(data);
144 self
145 }
146}
147
148#[derive(Clone, Debug, Deserialize, Serialize)]
150#[serde(rename_all = "camelCase")]
151pub struct UniMessage {
152 pub id: String,
154 pub to: String,
156 pub iso: String,
158 pub cc: String,
160 pub count: u32,
162 pub price: String,
164}
165
166#[derive(Clone, Debug, Deserialize, Serialize)]
168#[serde(rename_all = "camelCase")]
169pub struct SendMessageData {
170 pub recipients: u32,
172 pub message_count: u32,
174 pub currency: String,
176 pub total_amount: String,
178 pub messages: Vec<UniMessage>,
180}
181
182#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
184#[serde(rename_all = "lowercase")]
185pub enum OtpChannel {
186 Auto,
188 Sms,
190 Call,
192 Voice,
194 Whatsapp,
196}
197
198#[derive(Clone, Debug, Serialize)]
200#[serde(rename_all = "camelCase")]
201pub struct SendOtpRequest {
202 pub to: String,
204 #[serde(skip_serializing_if = "Option::is_none")]
206 pub code: Option<String>,
207 #[serde(skip_serializing_if = "Option::is_none")]
209 pub ttl: Option<u32>,
210 #[serde(skip_serializing_if = "Option::is_none")]
212 pub digits: Option<u8>,
213 #[serde(skip_serializing_if = "Option::is_none")]
215 pub intent: Option<String>,
216 #[serde(skip_serializing_if = "Option::is_none")]
218 pub channel: Option<OtpChannel>,
219 #[serde(skip_serializing_if = "Option::is_none")]
221 pub signature: Option<String>,
222 #[serde(skip_serializing_if = "Option::is_none")]
224 pub template_id: Option<String>,
225}
226
227impl SendOtpRequest {
228 pub fn new(to: impl Into<String>) -> Self {
230 Self {
231 to: to.into(),
232 code: None,
233 ttl: None,
234 digits: None,
235 intent: None,
236 channel: None,
237 signature: None,
238 template_id: None,
239 }
240 }
241
242 pub fn with_code(mut self, code: impl Into<String>) -> Self {
244 self.code = Some(code.into());
245 self
246 }
247
248 pub fn with_ttl(mut self, ttl: u32) -> Self {
250 self.ttl = Some(ttl);
251 self
252 }
253
254 pub fn with_digits(mut self, digits: u8) -> Self {
256 self.digits = Some(digits);
257 self
258 }
259
260 pub fn with_intent(mut self, intent: impl Into<String>) -> Self {
262 self.intent = Some(intent.into());
263 self
264 }
265
266 pub fn with_channel(mut self, channel: OtpChannel) -> Self {
268 self.channel = Some(channel);
269 self
270 }
271
272 pub fn with_template(
274 mut self,
275 signature: impl Into<String>,
276 template_id: impl Into<String>,
277 ) -> Self {
278 self.signature = Some(signature.into());
279 self.template_id = Some(template_id.into());
280 self
281 }
282}
283
284#[derive(Clone, Debug, Serialize)]
286#[serde(rename_all = "camelCase")]
287pub struct VerifyOtpRequest {
288 pub to: String,
290 pub code: String,
292 #[serde(skip_serializing_if = "Option::is_none")]
294 pub ttl: Option<u32>,
295 #[serde(skip_serializing_if = "Option::is_none")]
297 pub intent: Option<String>,
298}
299
300impl VerifyOtpRequest {
301 pub fn new(to: impl Into<String>, code: impl Into<String>) -> Self {
303 Self {
304 to: to.into(),
305 code: code.into(),
306 ttl: None,
307 intent: None,
308 }
309 }
310
311 pub fn with_ttl(mut self, ttl: u32) -> Self {
313 self.ttl = Some(ttl);
314 self
315 }
316
317 pub fn with_intent(mut self, intent: impl Into<String>) -> Self {
319 self.intent = Some(intent.into());
320 self
321 }
322}
323
324#[derive(Clone, Debug, Deserialize, Serialize)]
326pub struct VerifyOtpData {
327 pub to: String,
329 pub valid: bool,
331}