1use std::collections::BTreeMap;
2use std::net::IpAddr;
3
4use crate::domain::validation::ValidationError;
5use crate::domain::value::{
6 MessageText, PartnerId, RawPhoneNumber, SenderId, SmsId, TtlMinutes, UnixTimestamp,
7};
8
9pub const SEND_SMS_MAX_RECIPIENTS: usize = 100;
11pub const CHECK_STATUS_MAX_SMS_IDS: usize = 100;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
15pub enum JsonMode {
19 #[default]
20 Json,
22 Plain,
24}
25
26#[derive(Debug, Clone, Default)]
27pub struct SendOptions {
31 pub json: JsonMode,
33 pub from: Option<SenderId>,
35 pub ip: Option<IpAddr>,
37 pub time: Option<UnixTimestamp>,
39 pub ttl: Option<TtlMinutes>,
41 pub daytime: bool,
43 pub translit: bool,
45 pub test: bool,
47 pub partner_id: Option<PartnerId>,
49}
50
51#[derive(Debug, Clone)]
52pub enum SendSms {
57 ToMany(ToMany),
59 PerRecipient(PerRecipient),
61}
62
63#[derive(Debug, Clone)]
64pub struct ToMany {
66 recipients: Vec<RawPhoneNumber>,
67 msg: MessageText,
68 options: SendOptions,
69}
70
71#[derive(Debug, Clone)]
72pub struct PerRecipient {
74 messages: BTreeMap<RawPhoneNumber, MessageText>,
75 options: SendOptions,
76}
77
78#[derive(Debug, Clone)]
79pub struct CheckStatus {
83 sms_ids: Vec<SmsId>,
84}
85
86impl SendSms {
87 pub fn to_many(
93 recipients: Vec<RawPhoneNumber>,
94 msg: MessageText,
95 options: SendOptions,
96 ) -> Result<Self, ValidationError> {
97 if recipients.is_empty() {
98 return Err(ValidationError::Empty {
99 field: RawPhoneNumber::FIELD,
100 });
101 }
102 if recipients.len() > SEND_SMS_MAX_RECIPIENTS {
103 return Err(ValidationError::TooManyRecipients {
104 max: SEND_SMS_MAX_RECIPIENTS,
105 actual: recipients.len(),
106 });
107 }
108 Ok(Self::ToMany(ToMany {
109 recipients,
110 msg,
111 options,
112 }))
113 }
114
115 pub fn per_recipient(
121 messages: BTreeMap<RawPhoneNumber, MessageText>,
122 options: SendOptions,
123 ) -> Result<Self, ValidationError> {
124 if messages.is_empty() {
125 return Err(ValidationError::Empty {
126 field: RawPhoneNumber::FIELD,
127 });
128 }
129 if messages.len() > SEND_SMS_MAX_RECIPIENTS {
130 return Err(ValidationError::TooManyRecipients {
131 max: SEND_SMS_MAX_RECIPIENTS,
132 actual: messages.len(),
133 });
134 }
135 Ok(Self::PerRecipient(PerRecipient { messages, options }))
136 }
137}
138
139impl ToMany {
140 pub fn recipients(&self) -> &[RawPhoneNumber] {
142 &self.recipients
143 }
144
145 pub fn msg(&self) -> &MessageText {
147 &self.msg
148 }
149
150 pub fn options(&self) -> &SendOptions {
152 &self.options
153 }
154}
155
156impl PerRecipient {
157 pub fn messages(&self) -> &BTreeMap<RawPhoneNumber, MessageText> {
159 &self.messages
160 }
161
162 pub fn options(&self) -> &SendOptions {
164 &self.options
165 }
166}
167
168impl CheckStatus {
169 pub fn new(sms_ids: Vec<SmsId>) -> Result<Self, ValidationError> {
175 if sms_ids.is_empty() {
176 return Err(ValidationError::Empty {
177 field: SmsId::FIELD,
178 });
179 }
180 if sms_ids.len() > CHECK_STATUS_MAX_SMS_IDS {
181 return Err(ValidationError::TooManySmsIds {
182 max: CHECK_STATUS_MAX_SMS_IDS,
183 actual: sms_ids.len(),
184 });
185 }
186 Ok(Self { sms_ids })
187 }
188
189 pub fn one(sms_id: SmsId) -> Self {
191 Self {
192 sms_ids: vec![sms_id],
193 }
194 }
195
196 pub fn sms_ids(&self) -> &[SmsId] {
198 &self.sms_ids
199 }
200}
201
202#[cfg(test)]
203mod tests {
204 use super::*;
205
206 fn make_recipients(count: usize) -> Vec<RawPhoneNumber> {
207 (0..count)
208 .map(|idx| RawPhoneNumber::new(format!("+792512300{idx:02}")).unwrap())
209 .collect()
210 }
211
212 #[test]
213 fn to_many_rejects_empty_recipients() {
214 let msg = MessageText::new("hi").unwrap();
215 let err = SendSms::to_many(Vec::new(), msg, SendOptions::default()).unwrap_err();
216 assert_eq!(
217 err,
218 ValidationError::Empty {
219 field: RawPhoneNumber::FIELD
220 }
221 );
222 }
223
224 #[test]
225 fn to_many_rejects_too_many_recipients() {
226 let msg = MessageText::new("hi").unwrap();
227 let recipients = make_recipients(SEND_SMS_MAX_RECIPIENTS + 1);
228 let err = SendSms::to_many(recipients, msg, SendOptions::default()).unwrap_err();
229 assert_eq!(
230 err,
231 ValidationError::TooManyRecipients {
232 max: SEND_SMS_MAX_RECIPIENTS,
233 actual: SEND_SMS_MAX_RECIPIENTS + 1
234 }
235 );
236 }
237
238 #[test]
239 fn per_recipient_rejects_empty_messages() {
240 let err = SendSms::per_recipient(BTreeMap::new(), SendOptions::default()).unwrap_err();
241 assert_eq!(
242 err,
243 ValidationError::Empty {
244 field: RawPhoneNumber::FIELD
245 }
246 );
247 }
248
249 #[test]
250 fn per_recipient_rejects_too_many_messages() {
251 let mut messages = BTreeMap::new();
252 for idx in 0..(SEND_SMS_MAX_RECIPIENTS + 1) {
253 messages.insert(
254 RawPhoneNumber::new(format!("+792512310{idx:02}")).unwrap(),
255 MessageText::new("hi").unwrap(),
256 );
257 }
258 let err = SendSms::per_recipient(messages, SendOptions::default()).unwrap_err();
259 assert_eq!(
260 err,
261 ValidationError::TooManyRecipients {
262 max: SEND_SMS_MAX_RECIPIENTS,
263 actual: SEND_SMS_MAX_RECIPIENTS + 1
264 }
265 );
266 }
267
268 #[test]
269 fn to_many_exposes_fields() {
270 let recipients = make_recipients(2);
271 let msg = MessageText::new("hello").unwrap();
272 let options = SendOptions::default();
273 let req = SendSms::to_many(recipients.clone(), msg.clone(), options.clone()).unwrap();
274
275 match req {
276 SendSms::ToMany(to_many) => {
277 assert_eq!(to_many.recipients(), recipients.as_slice());
278 assert_eq!(to_many.msg(), &msg);
279 assert_eq!(to_many.options().json, options.json);
280 }
281 SendSms::PerRecipient(_) => panic!("expected to_many request"),
282 }
283 }
284
285 #[test]
286 fn per_recipient_exposes_fields() {
287 let mut messages = BTreeMap::new();
288 let p1 = RawPhoneNumber::new("+79251234567").unwrap();
289 let msg = MessageText::new("hello").unwrap();
290 messages.insert(p1.clone(), msg.clone());
291 let options = SendOptions::default();
292
293 let req = SendSms::per_recipient(messages.clone(), options.clone()).unwrap();
294 match req {
295 SendSms::PerRecipient(per_recipient) => {
296 assert_eq!(per_recipient.messages(), &messages);
297 assert_eq!(per_recipient.options().json, options.json);
298 }
299 SendSms::ToMany(_) => panic!("expected per_recipient request"),
300 }
301 }
302
303 #[test]
304 fn check_status_rejects_empty_sms_ids() {
305 let err = CheckStatus::new(Vec::new()).unwrap_err();
306 assert_eq!(
307 err,
308 ValidationError::Empty {
309 field: SmsId::FIELD
310 }
311 );
312 }
313
314 #[test]
315 fn check_status_rejects_too_many_sms_ids() {
316 let sms_ids = (0..(CHECK_STATUS_MAX_SMS_IDS + 1))
317 .map(|idx| SmsId::new(format!("000000-{:06}", idx)).unwrap())
318 .collect::<Vec<_>>();
319 let err = CheckStatus::new(sms_ids).unwrap_err();
320 assert_eq!(
321 err,
322 ValidationError::TooManySmsIds {
323 max: CHECK_STATUS_MAX_SMS_IDS,
324 actual: CHECK_STATUS_MAX_SMS_IDS + 1
325 }
326 );
327 }
328
329 #[test]
330 fn check_status_one_creates_single_id_request() {
331 let sms_id = SmsId::new("000000-000001").unwrap();
332 let request = CheckStatus::one(sms_id.clone());
333 assert_eq!(request.sms_ids(), &[sms_id]);
334 }
335
336 #[test]
337 fn check_status_new_exposes_sms_ids() {
338 let ids = vec![
339 SmsId::new("000000-000001").unwrap(),
340 SmsId::new("000000-000002").unwrap(),
341 ];
342 let request = CheckStatus::new(ids.clone()).unwrap();
343 assert_eq!(request.sms_ids(), ids.as_slice());
344 }
345}