1use rusmpp_macros::Rusmpp;
2
3use crate::{
4 encode::Length,
5 pdus::borrowed::Pdu,
6 tlvs::{
7 TlvTag,
8 borrowed::{MessageSubmissionRequestTlvValue, Tlv},
9 },
10 types::borrowed::{COctetString, EmptyOrFullCOctetString, OctetString},
11 values::{borrowed::*, *},
12};
13
14#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Rusmpp)]
17#[rusmpp(decode = borrowed, test = skip)]
18#[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))]
19#[cfg_attr(feature = "serde", derive(::serde::Serialize))]
20pub struct SubmitSm<'a, const N: usize> {
21 pub service_type: ServiceType<'a>,
31 pub source_addr_ton: Ton,
33 pub source_addr_npi: Npi,
35 pub source_addr: COctetString<'a, 1, 21>,
37 pub dest_addr_ton: Ton,
39 pub dest_addr_npi: Npi,
41 pub destination_addr: COctetString<'a, 1, 21>,
47 pub esm_class: EsmClass,
50 pub protocol_id: u8,
53 pub priority_flag: PriorityFlag,
55 pub schedule_delivery_time: EmptyOrFullCOctetString<'a, 17>,
59 pub validity_period: EmptyOrFullCOctetString<'a, 17>,
65 pub registered_delivery: RegisteredDelivery,
68 pub replace_if_present_flag: ReplaceIfPresentFlag,
70 pub data_coding: DataCoding,
72 pub sm_default_msg_id: u8,
75 sm_length: u8,
77 #[rusmpp(length = sm_length)]
85 short_message: OctetString<'a, 0, 255>,
86 #[rusmpp(length = "unchecked")]
88 #[cfg_attr(feature = "arbitrary", arbitrary(default))]
89 tlvs: heapless::vec::Vec<Tlv<'a>, N>,
90}
91
92impl<'a, const N: usize> SubmitSm<'a, N> {
93 #[allow(clippy::too_many_arguments)]
94 pub fn new(
95 service_type: ServiceType<'a>,
96 source_addr_ton: Ton,
97 source_addr_npi: Npi,
98 source_addr: COctetString<'a, 1, 21>,
99 dest_addr_ton: Ton,
100 dest_addr_npi: Npi,
101 destination_addr: COctetString<'a, 1, 21>,
102 esm_class: EsmClass,
103 protocol_id: u8,
104 priority_flag: PriorityFlag,
105 schedule_delivery_time: EmptyOrFullCOctetString<'a, 17>,
106 validity_period: EmptyOrFullCOctetString<'a, 17>,
107 registered_delivery: RegisteredDelivery,
108 replace_if_present_flag: ReplaceIfPresentFlag,
109 data_coding: DataCoding,
110 sm_default_msg_id: u8,
111 short_message: OctetString<'a, 0, 255>,
112 tlvs: heapless::vec::Vec<impl Into<MessageSubmissionRequestTlvValue<'a>>, N>,
113 ) -> Self {
114 let tlvs = tlvs.into_iter().map(Into::into).map(From::from).collect();
115
116 let sm_length = short_message.length() as u8;
117
118 let mut submit_sm = Self {
119 service_type,
120 source_addr_ton,
121 source_addr_npi,
122 source_addr,
123 dest_addr_ton,
124 dest_addr_npi,
125 destination_addr,
126 esm_class,
127 protocol_id,
128 priority_flag,
129 schedule_delivery_time,
130 validity_period,
131 registered_delivery,
132 replace_if_present_flag,
133 data_coding,
134 sm_default_msg_id,
135 sm_length,
136 short_message,
137 tlvs,
138 };
139
140 submit_sm.clear_short_message_if_message_payload_exists();
141
142 submit_sm
143 }
144
145 pub fn sm_length(&self) -> u8 {
146 self.sm_length
147 }
148
149 pub fn short_message(&'_ self) -> &'_ OctetString<'_, 0, 255> {
150 &self.short_message
151 }
152
153 pub fn set_short_message(&mut self, short_message: OctetString<'a, 0, 255>) -> bool {
158 self.short_message = short_message;
159 self.sm_length = self.short_message.length() as u8;
160
161 !self.clear_short_message_if_message_payload_exists()
162 }
163
164 pub fn tlvs(&'_ self) -> &'_ [Tlv<'_>] {
165 &self.tlvs
166 }
167
168 pub fn set_tlvs(
169 &mut self,
170 tlvs: heapless::vec::Vec<impl Into<MessageSubmissionRequestTlvValue<'a>>, N>,
171 ) {
172 self.tlvs = tlvs.into_iter().map(Into::into).map(From::from).collect();
173
174 self.clear_short_message_if_message_payload_exists();
175 }
176
177 pub fn clear_tlvs(&mut self) {
178 self.tlvs.clear();
179 }
180
181 pub fn push_tlv(
182 &mut self,
183 tlv: impl Into<MessageSubmissionRequestTlvValue<'a>>,
184 ) -> Result<(), Tlv<'a>> {
185 self.tlvs.push(Tlv::from(tlv.into()))?;
186
187 self.clear_short_message_if_message_payload_exists();
188
189 Ok(())
190 }
191
192 fn clear_short_message_if_message_payload_exists(&mut self) -> bool {
195 let message_payload_exists = self
196 .tlvs
197 .iter()
198 .any(|value| matches!(value.tag(), TlvTag::MessagePayload));
199
200 if message_payload_exists {
201 self.short_message = OctetString::empty();
202 self.sm_length = 0;
203
204 return true;
205 };
206
207 false
208 }
209
210 pub fn builder() -> SubmitSmBuilder<'a, N> {
211 SubmitSmBuilder::new()
212 }
213}
214
215impl<'a, const N: usize> From<SubmitSm<'a, N>> for Pdu<'a, N> {
216 fn from(value: SubmitSm<'a, N>) -> Self {
217 Self::SubmitSm(value)
218 }
219}
220
221#[derive(Debug, Default)]
222pub struct SubmitSmBuilder<'a, const N: usize> {
223 inner: SubmitSm<'a, N>,
224}
225
226impl<'a, const N: usize> SubmitSmBuilder<'a, N> {
227 pub fn new() -> Self {
228 Self::default()
229 }
230
231 pub fn service_type(mut self, service_type: ServiceType<'a>) -> Self {
232 self.inner.service_type = service_type;
233 self
234 }
235
236 pub fn source_addr_ton(mut self, source_addr_ton: Ton) -> Self {
237 self.inner.source_addr_ton = source_addr_ton;
238 self
239 }
240
241 pub fn source_addr_npi(mut self, source_addr_npi: Npi) -> Self {
242 self.inner.source_addr_npi = source_addr_npi;
243 self
244 }
245
246 pub fn source_addr(mut self, source_addr: COctetString<'a, 1, 21>) -> Self {
247 self.inner.source_addr = source_addr;
248 self
249 }
250
251 pub fn dest_addr_ton(mut self, dest_addr_ton: Ton) -> Self {
252 self.inner.dest_addr_ton = dest_addr_ton;
253 self
254 }
255
256 pub fn dest_addr_npi(mut self, dest_addr_npi: Npi) -> Self {
257 self.inner.dest_addr_npi = dest_addr_npi;
258 self
259 }
260
261 pub fn destination_addr(mut self, destination_addr: COctetString<'a, 1, 21>) -> Self {
262 self.inner.destination_addr = destination_addr;
263 self
264 }
265
266 pub fn esm_class(mut self, esm_class: EsmClass) -> Self {
267 self.inner.esm_class = esm_class;
268 self
269 }
270
271 pub fn protocol_id(mut self, protocol_id: u8) -> Self {
272 self.inner.protocol_id = protocol_id;
273 self
274 }
275
276 pub fn priority_flag(mut self, priority_flag: PriorityFlag) -> Self {
277 self.inner.priority_flag = priority_flag;
278 self
279 }
280
281 pub fn schedule_delivery_time(
282 mut self,
283 schedule_delivery_time: EmptyOrFullCOctetString<'a, 17>,
284 ) -> Self {
285 self.inner.schedule_delivery_time = schedule_delivery_time;
286 self
287 }
288
289 pub fn validity_period(mut self, validity_period: EmptyOrFullCOctetString<'a, 17>) -> Self {
290 self.inner.validity_period = validity_period;
291 self
292 }
293
294 pub fn registered_delivery(mut self, registered_delivery: RegisteredDelivery) -> Self {
295 self.inner.registered_delivery = registered_delivery;
296 self
297 }
298
299 pub fn replace_if_present_flag(
300 mut self,
301 replace_if_present_flag: ReplaceIfPresentFlag,
302 ) -> Self {
303 self.inner.replace_if_present_flag = replace_if_present_flag;
304 self
305 }
306
307 pub fn data_coding(mut self, data_coding: DataCoding) -> Self {
308 self.inner.data_coding = data_coding;
309 self
310 }
311
312 pub fn sm_default_msg_id(mut self, sm_default_msg_id: u8) -> Self {
313 self.inner.sm_default_msg_id = sm_default_msg_id;
314 self
315 }
316
317 pub fn short_message(mut self, short_message: OctetString<'a, 0, 255>) -> Self {
318 self.inner.set_short_message(short_message);
319 self
320 }
321
322 pub fn tlvs(
323 mut self,
324 tlvs: heapless::vec::Vec<impl Into<MessageSubmissionRequestTlvValue<'a>>, N>,
325 ) -> Self {
326 self.inner.set_tlvs(tlvs);
327 self
328 }
329
330 pub fn clear_tlvs(mut self) -> Self {
331 self.inner.clear_tlvs();
332 self
333 }
334
335 pub fn push_tlv(
336 mut self,
337 tlv: impl Into<MessageSubmissionRequestTlvValue<'a>>,
338 ) -> Result<Self, Tlv<'a>> {
339 self.inner.push_tlv(tlv)?;
340 Ok(self)
341 }
342
343 pub fn build(self) -> SubmitSm<'a, N> {
344 self.inner
345 }
346}
347
348#[cfg(test)]
349mod tests {
350 use crate::{tests::TestInstance, types::borrowed::AnyOctetString};
351
352 use super::*;
353
354 impl<const N: usize> TestInstance for SubmitSm<'static, N> {
355 fn instances() -> alloc::vec::Vec<Self> {
356 alloc::vec![
357 Self::default(),
358 Self::builder()
359 .service_type(ServiceType::new(
360 GenericServiceType::CellularMessaging.into(),
361 ))
362 .source_addr_ton(Ton::International)
363 .source_addr_npi(Npi::Isdn)
364 .source_addr(COctetString::new(b"Source Address\0").unwrap())
365 .dest_addr_ton(Ton::International)
366 .dest_addr_npi(Npi::Isdn)
367 .destination_addr(COctetString::new(b"Destination Address\0").unwrap())
368 .esm_class(EsmClass::new(
369 MessagingMode::StoreAndForward,
370 MessageType::ShortMessageContainsMCDeliveryReceipt,
371 Ansi41Specific::ShortMessageContainsDeliveryAcknowledgement,
372 GsmFeatures::SetUdhiAndReplyPath,
373 ))
374 .protocol_id(0)
375 .priority_flag(PriorityFlag::from(PriorityFlagType::from(Ansi136::Bulk)))
376 .schedule_delivery_time(
377 EmptyOrFullCOctetString::new(b"2023-09-01T12:00\0").unwrap(),
378 )
379 .validity_period(EmptyOrFullCOctetString::new(b"2023-10-01T12:00\0").unwrap())
380 .registered_delivery(RegisteredDelivery::request_all())
381 .replace_if_present_flag(ReplaceIfPresentFlag::Replace)
382 .data_coding(DataCoding::Ksc5601)
383 .sm_default_msg_id(69)
384 .short_message(OctetString::new(b"Short Message").unwrap())
385 .build(),
386 Self::builder()
387 .service_type(ServiceType::new(
388 GenericServiceType::CellularMessaging.into(),
389 ))
390 .source_addr_ton(Ton::International)
391 .source_addr_npi(Npi::Isdn)
392 .source_addr(COctetString::new(b"Source Address\0").unwrap())
393 .dest_addr_ton(Ton::International)
394 .dest_addr_npi(Npi::Isdn)
395 .destination_addr(COctetString::new(b"Destination Address\0").unwrap())
396 .esm_class(EsmClass::new(
397 MessagingMode::Default,
398 MessageType::ShortMessageContainsIntermediateDeliveryNotification,
399 Ansi41Specific::ShortMessageContainsUserAcknowledgment,
400 GsmFeatures::SetUdhiAndReplyPath,
401 ))
402 .protocol_id(0)
403 .priority_flag(PriorityFlag::from(PriorityFlagType::from(
404 Ansi136::VeryUrgent,
405 )))
406 .schedule_delivery_time(
407 EmptyOrFullCOctetString::new(b"2023-09-01T12:01\0").unwrap(),
408 )
409 .validity_period(EmptyOrFullCOctetString::new(b"2023-10-01T12:20\0").unwrap())
410 .registered_delivery(RegisteredDelivery::request_all())
411 .replace_if_present_flag(ReplaceIfPresentFlag::DoNotReplace)
412 .data_coding(DataCoding::Jis)
413 .sm_default_msg_id(96)
414 .short_message(OctetString::new(b"Short Message").unwrap())
415 .tlvs(
416 [MessageSubmissionRequestTlvValue::MessagePayload(
417 MessagePayload::new(AnyOctetString::new(b"Message Payload")),
418 )]
419 .into()
420 )
421 .build(),
422 Self::builder()
423 .short_message(OctetString::new(b"Short Message").unwrap())
424 .tlvs(
425 [
426 MessageSubmissionRequestTlvValue::MessagePayload(MessagePayload::new(
427 AnyOctetString::new(b"Message Payload"),
428 )),
429 MessageSubmissionRequestTlvValue::UserResponseCode(3),
430 MessageSubmissionRequestTlvValue::DestBearerType(
431 BearerType::FlexReFlex
432 ),
433 MessageSubmissionRequestTlvValue::SourceSubaddress(Subaddress::new(
434 SubaddressTag::NsapOdd,
435 OctetString::new(b"Subaddress :D\0").unwrap(),
436 )),
437 ]
438 .into()
439 )
440 .build(),
441 ]
442 }
443 }
444
445 #[test]
446 fn encode_decode() {
447 crate::tests::borrowed::encode_decode_with_length_test_instances::<SubmitSm<'static, 16>>();
448 }
449
450 #[test]
451 fn short_message_length() {
452 let short_message = OctetString::new(b"Short Message").unwrap();
453
454 let submit_sm = SubmitSm::<'static, 16>::builder()
455 .short_message(short_message.clone())
456 .build();
457
458 assert_eq!(submit_sm.short_message(), &short_message);
459 assert_eq!(submit_sm.sm_length(), short_message.length() as u8);
460 }
461
462 #[test]
463 fn short_message_override() {
464 let short_message_1 = OctetString::new(b"Short Message 101").unwrap();
465 let short_message_2 = OctetString::new(b"Short Message 2").unwrap();
466
467 let submit_sm = SubmitSm::<'static, 16>::builder()
468 .short_message(short_message_1)
469 .short_message(short_message_2.clone())
470 .build();
471
472 assert_eq!(submit_sm.short_message(), &short_message_2);
473 assert_eq!(submit_sm.sm_length(), short_message_2.length() as u8);
474 }
475
476 #[test]
477 fn message_payload_suppresses_short_message() {
478 let short_message = OctetString::new(b"Short Message").unwrap();
479 let message_payload = MessagePayload::new(AnyOctetString::new(b"Message Payload"));
480
481 let submit_sm = SubmitSm::<'static, 16>::builder()
483 .short_message(short_message.clone())
484 .push_tlv(MessageSubmissionRequestTlvValue::MessagePayload(
485 message_payload.clone(),
486 ))
487 .expect("Failed to push TLV")
488 .build();
489
490 assert_eq!(submit_sm.short_message(), &OctetString::empty());
491 assert_eq!(submit_sm.sm_length(), 0);
492
493 let submit_sm = SubmitSm::<'static, 16>::builder()
495 .short_message(short_message.clone())
496 .tlvs(
497 [MessageSubmissionRequestTlvValue::MessagePayload(
498 message_payload.clone(),
499 )]
500 .into(),
501 )
502 .build();
503
504 assert_eq!(submit_sm.short_message(), &OctetString::empty());
505 assert_eq!(submit_sm.sm_length(), 0);
506
507 let submit_sm = SubmitSm::<'static, 16>::builder()
510 .short_message(short_message.clone())
511 .push_tlv(MessageSubmissionRequestTlvValue::MessagePayload(
512 message_payload.clone(),
513 ))
514 .expect("Failed to push TLV")
515 .short_message(short_message.clone())
516 .build();
517
518 assert_eq!(submit_sm.short_message(), &OctetString::empty());
519 assert_eq!(submit_sm.sm_length(), 0);
520
521 let submit_sm = SubmitSm::<'static, 16>::builder()
523 .short_message(short_message.clone())
524 .tlvs(
525 [MessageSubmissionRequestTlvValue::MessagePayload(
526 message_payload.clone(),
527 )]
528 .into(),
529 )
530 .short_message(short_message.clone())
531 .build();
532
533 assert_eq!(submit_sm.short_message(), &OctetString::empty());
534 assert_eq!(submit_sm.sm_length(), 0);
535
536 let submit_sm = SubmitSm::<'static, 16>::builder()
538 .push_tlv(MessageSubmissionRequestTlvValue::MessagePayload(
539 message_payload.clone(),
540 ))
541 .expect("Failed to push TLV")
542 .clear_tlvs()
543 .short_message(short_message.clone())
544 .build();
545
546 assert_eq!(submit_sm.short_message(), &short_message);
547 assert_eq!(submit_sm.sm_length(), short_message.length() as u8);
548 }
549}