rusmpp_core/pdus/owned/
submit_multi.rs

1use rusmpp_macros::Rusmpp;
2
3use crate::{
4    encode::Length,
5    pdus::owned::Pdu,
6    tlvs::{
7        TlvTag,
8        owned::{MessageSubmissionRequestTlvValue, Tlv},
9    },
10    types::owned::{COctetString, EmptyOrFullCOctetString, OctetString},
11    values::{owned::*, *},
12};
13
14#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Rusmpp)]
15#[rusmpp(decode = owned, test = skip)]
16#[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))]
17#[cfg_attr(feature = "serde", derive(::serde::Serialize))]
18#[cfg_attr(feature = "serde-deserialize-unchecked", derive(::serde::Deserialize))]
19pub struct SubmitMulti {
20    /// The service_type parameter can be used to indicate the
21    /// SMS Application service associated with the message.
22    /// Specifying the service_type allows the ESME to avail of
23    /// enhanced messaging services such as “replace by
24    /// service_type” or control the teleservice used on the air
25    /// interface.
26    ///
27    /// Set to NULL for default MC settings.
28    pub service_type: ServiceType,
29    /// Type of Number for source address.
30    ///
31    /// If not known, set to NULL (Unknown).
32    pub source_addr_ton: Ton,
33    /// Numbering Plan Indicator for source address.
34    ///
35    /// If not known, set to NULL (Unknown).
36    pub source_addr_npi: Npi,
37    /// Address of SME which originated this message.
38    ///
39    /// If not known, set to NULL (Unknown).
40    pub source_addr: COctetString<1, 21>,
41    /// Number of destination addresses – indicates the
42    /// number of destinations that are to follow.
43    ///
44    /// A maximum of 255 destination addresses are allowed.
45    ///
46    /// Note: Set to 1 when submitting to one SME Address or when
47    /// submitting to one Distribution List.
48    number_of_dests: u8,
49    /// Composite field.
50    #[rusmpp(count = number_of_dests)]
51    dest_address: alloc::vec::Vec<DestAddress>,
52    /// Indicates Message Mode and Message Type.
53    pub esm_class: EsmClass,
54    /// Protocol Identifier.
55    ///
56    /// Network specific field.
57    pub protocol_id: u8,
58    /// Designates the priority level of the message.
59    pub priority_flag: PriorityFlag,
60    /// The short message is to be scheduled by the MC for delivery.
61    ///
62    /// Set to NULL for immediate message delivery.
63    pub schedule_delivery_time: EmptyOrFullCOctetString<17>,
64    /// The validity period of this message.
65    ///
66    /// Set to NULL to request the SMSC default validity period.
67    ///
68    /// Note: this is superseded by the qos_time_to_live TLV if specified.
69    pub validity_period: EmptyOrFullCOctetString<17>,
70    /// Indicator to signify if a MC delivery receipt or an SME
71    /// acknowledgement is required.
72    pub registered_delivery: RegisteredDelivery,
73    // Flag indicating if submitted message should replace an
74    // existing message.
75    pub replace_if_present_flag: ReplaceIfPresentFlag,
76    /// Defines the encoding scheme of the short message user data.
77    pub data_coding: DataCoding,
78    /// Indicates the short message to send from a list of pre- defined
79    /// (‘canned’) short messages stored on the MC.
80    ///
81    /// If not using a MC canned message, set to NULL.
82    pub sm_default_msg_id: u8,
83    /// Length in octets of the short_message user data.
84    sm_length: u8,
85    /// Up to 255 octets of short message user data.
86    ///
87    /// The exact physical limit for short_message size may
88    /// vary according to the underlying network
89    ///
90    /// Note: this field is superceded by the message_payload TLV if
91    /// specified.
92    ///
93    /// Applications which need to send messages longer than
94    /// 255 octets should use the message_payload TLV. In
95    /// this case the sm_length field should be set to zero.
96    #[rusmpp(length = sm_length)]
97    short_message: OctetString<0, 255>,
98    /// Message submission request TLVs ([`MessageSubmissionRequestTlvValue`]).
99    #[rusmpp(length = "unchecked")]
100    tlvs: alloc::vec::Vec<Tlv>,
101}
102
103impl SubmitMulti {
104    #[allow(clippy::too_many_arguments)]
105    pub fn new(
106        service_type: ServiceType,
107        source_addr_ton: Ton,
108        source_addr_npi: Npi,
109        source_addr: COctetString<1, 21>,
110        dest_address: alloc::vec::Vec<DestAddress>,
111        esm_class: EsmClass,
112        protocol_id: u8,
113        priority_flag: PriorityFlag,
114        schedule_delivery_time: EmptyOrFullCOctetString<17>,
115        validity_period: EmptyOrFullCOctetString<17>,
116        registered_delivery: RegisteredDelivery,
117        replace_if_present_flag: ReplaceIfPresentFlag,
118        data_coding: DataCoding,
119        sm_default_msg_id: u8,
120        short_message: OctetString<0, 255>,
121        tlvs: alloc::vec::Vec<impl Into<MessageSubmissionRequestTlvValue>>,
122    ) -> Self {
123        let sm_length = short_message.length() as u8;
124        let number_of_dests = dest_address.len() as u8;
125
126        let tlvs = tlvs.into_iter().map(Into::into).map(From::from).collect();
127
128        let mut submit_multi = Self {
129            service_type,
130            source_addr_ton,
131            source_addr_npi,
132            source_addr,
133            number_of_dests,
134            dest_address,
135            esm_class,
136            protocol_id,
137            priority_flag,
138            schedule_delivery_time,
139            validity_period,
140            registered_delivery,
141            replace_if_present_flag,
142            data_coding,
143            sm_default_msg_id,
144            sm_length,
145            short_message,
146            tlvs,
147        };
148
149        submit_multi.clear_short_message_if_message_payload_exists();
150
151        submit_multi
152    }
153
154    pub const fn number_of_dests(&self) -> u8 {
155        self.number_of_dests
156    }
157
158    pub fn dest_address(&self) -> &[DestAddress] {
159        &self.dest_address
160    }
161
162    pub fn set_dest_address(&mut self, dest_address: alloc::vec::Vec<DestAddress>) {
163        self.dest_address = dest_address;
164        self.number_of_dests = self.dest_address.len() as u8;
165    }
166
167    pub fn push_dest_address(&mut self, dest_address: DestAddress) {
168        self.dest_address.push(dest_address);
169        self.number_of_dests = self.dest_address.len() as u8;
170    }
171
172    pub fn clear_dest_address(&mut self) {
173        self.dest_address.clear();
174        self.number_of_dests = self.dest_address.len() as u8;
175    }
176
177    pub const fn sm_length(&self) -> u8 {
178        self.sm_length
179    }
180
181    pub const fn short_message(&self) -> &OctetString<0, 255> {
182        &self.short_message
183    }
184
185    /// Sets the short message and short message length.
186    /// Updates the short message and short message length accordingly.
187    /// Has no effect if the message payload is set.
188    /// Returns true if the short message and short message length were set.
189    pub fn set_short_message(&mut self, short_message: OctetString<0, 255>) -> bool {
190        self.short_message = short_message;
191        self.sm_length = self.short_message.length() as u8;
192
193        !self.clear_short_message_if_message_payload_exists()
194    }
195
196    pub fn tlvs(&self) -> &[Tlv] {
197        &self.tlvs
198    }
199
200    pub fn set_tlvs(&mut self, tlvs: alloc::vec::Vec<impl Into<MessageSubmissionRequestTlvValue>>) {
201        self.tlvs = tlvs.into_iter().map(Into::into).map(From::from).collect();
202
203        self.clear_short_message_if_message_payload_exists();
204    }
205
206    pub fn clear_tlvs(&mut self) {
207        self.tlvs.clear();
208    }
209
210    pub fn push_tlv(&mut self, tlv: impl Into<MessageSubmissionRequestTlvValue>) {
211        self.tlvs.push(Tlv::from(tlv.into()));
212
213        self.clear_short_message_if_message_payload_exists();
214    }
215
216    /// Clears the short message and short message length if the message payload is set.
217    /// Returns true if the short message and short message length were cleared.
218    fn clear_short_message_if_message_payload_exists(&mut self) -> bool {
219        let message_payload_exists = self
220            .tlvs
221            .iter()
222            .any(|value| matches!(value.tag(), TlvTag::MessagePayload));
223
224        if message_payload_exists {
225            self.short_message = OctetString::empty();
226            self.sm_length = 0;
227
228            return true;
229        };
230
231        false
232    }
233
234    pub fn builder() -> SubmitMultiBuilder {
235        SubmitMultiBuilder::new()
236    }
237}
238
239impl From<SubmitMulti> for Pdu {
240    fn from(value: SubmitMulti) -> Self {
241        Self::SubmitMulti(value)
242    }
243}
244
245#[derive(Debug, Default)]
246pub struct SubmitMultiBuilder {
247    inner: SubmitMulti,
248}
249
250impl SubmitMultiBuilder {
251    pub fn new() -> Self {
252        Default::default()
253    }
254
255    pub fn service_type(mut self, service_type: ServiceType) -> Self {
256        self.inner.service_type = service_type;
257        self
258    }
259
260    pub fn source_addr_ton(mut self, source_addr_ton: Ton) -> Self {
261        self.inner.source_addr_ton = source_addr_ton;
262        self
263    }
264
265    pub fn source_addr_npi(mut self, source_addr_npi: Npi) -> Self {
266        self.inner.source_addr_npi = source_addr_npi;
267        self
268    }
269
270    pub fn source_addr(mut self, source_addr: COctetString<1, 21>) -> Self {
271        self.inner.source_addr = source_addr;
272        self
273    }
274
275    pub fn dest_address(mut self, dest_address: alloc::vec::Vec<DestAddress>) -> Self {
276        self.inner.set_dest_address(dest_address);
277        self
278    }
279
280    pub fn push_dest_address(mut self, dest_address: DestAddress) -> Self {
281        self.inner.push_dest_address(dest_address);
282        self
283    }
284
285    pub fn clear_dest_address(mut self) -> Self {
286        self.inner.clear_dest_address();
287        self
288    }
289
290    pub fn esm_class(mut self, esm_class: EsmClass) -> Self {
291        self.inner.esm_class = esm_class;
292        self
293    }
294
295    pub fn protocol_id(mut self, protocol_id: u8) -> Self {
296        self.inner.protocol_id = protocol_id;
297        self
298    }
299
300    pub fn priority_flag(mut self, priority_flag: PriorityFlag) -> Self {
301        self.inner.priority_flag = priority_flag;
302        self
303    }
304
305    pub fn schedule_delivery_time(
306        mut self,
307        schedule_delivery_time: EmptyOrFullCOctetString<17>,
308    ) -> Self {
309        self.inner.schedule_delivery_time = schedule_delivery_time;
310        self
311    }
312
313    pub fn validity_period(mut self, validity_period: EmptyOrFullCOctetString<17>) -> Self {
314        self.inner.validity_period = validity_period;
315        self
316    }
317
318    pub fn registered_delivery(mut self, registered_delivery: RegisteredDelivery) -> Self {
319        self.inner.registered_delivery = registered_delivery;
320        self
321    }
322
323    pub fn replace_if_present_flag(
324        mut self,
325        replace_if_present_flag: ReplaceIfPresentFlag,
326    ) -> Self {
327        self.inner.replace_if_present_flag = replace_if_present_flag;
328        self
329    }
330
331    pub fn data_coding(mut self, data_coding: DataCoding) -> Self {
332        self.inner.data_coding = data_coding;
333        self
334    }
335
336    pub fn sm_default_msg_id(mut self, sm_default_msg_id: u8) -> Self {
337        self.inner.sm_default_msg_id = sm_default_msg_id;
338        self
339    }
340
341    pub fn short_message(mut self, short_message: OctetString<0, 255>) -> Self {
342        self.inner.set_short_message(short_message);
343        self
344    }
345
346    pub fn tlvs(
347        mut self,
348        tlvs: alloc::vec::Vec<impl Into<MessageSubmissionRequestTlvValue>>,
349    ) -> Self {
350        self.inner.set_tlvs(tlvs);
351        self
352    }
353
354    pub fn clear_tlvs(mut self) -> Self {
355        self.inner.clear_tlvs();
356        self
357    }
358
359    pub fn push_tlv(mut self, tlv: impl Into<MessageSubmissionRequestTlvValue>) -> Self {
360        self.inner.push_tlv(tlv);
361        self
362    }
363
364    pub fn build(self) -> SubmitMulti {
365        self.inner
366    }
367}
368
369#[cfg(test)]
370mod tests {
371    use std::str::FromStr;
372
373    use crate::{tests::TestInstance, types::owned::AnyOctetString};
374
375    use super::*;
376
377    impl TestInstance for SubmitMulti {
378        fn instances() -> alloc::vec::Vec<Self> {
379            alloc::vec![
380                Self::default(),
381                Self::builder()
382                    .service_type(ServiceType::default())
383                    .source_addr_ton(Ton::International)
384                    .source_addr_npi(Npi::Isdn)
385                    .source_addr(COctetString::from_str("Source Address").unwrap())
386                    .esm_class(EsmClass::default())
387                    .protocol_id(0)
388                    .priority_flag(PriorityFlag::default())
389                    .schedule_delivery_time(EmptyOrFullCOctetString::empty())
390                    .validity_period(EmptyOrFullCOctetString::empty())
391                    .registered_delivery(RegisteredDelivery::default())
392                    .replace_if_present_flag(ReplaceIfPresentFlag::default())
393                    .data_coding(DataCoding::default())
394                    .sm_default_msg_id(0)
395                    .short_message(OctetString::new(b"Short Message").unwrap())
396                    .build(),
397                Self::builder()
398                    .short_message(OctetString::new(b"Short Message").unwrap())
399                    .push_tlv(MessageSubmissionRequestTlvValue::MessagePayload(
400                        MessagePayload::new(AnyOctetString::new(b"Message Payload")),
401                    ))
402                    .build(),
403                Self::builder()
404                    .push_dest_address(DestAddress::SmeAddress(SmeAddress::new(
405                        Ton::International,
406                        Npi::Isdn,
407                        COctetString::new(b"1234567890123456789\0").unwrap(),
408                    )))
409                    .push_dest_address(DestAddress::DistributionListName(
410                        DistributionListName::new(
411                            COctetString::new(b"1234567890123456789\0").unwrap(),
412                        ),
413                    ))
414                    .short_message(OctetString::new(b"Short Message").unwrap())
415                    .push_tlv(MessageSubmissionRequestTlvValue::MessagePayload(
416                        MessagePayload::new(AnyOctetString::new(b"Message Payload")),
417                    ))
418                    .push_tlv(MessageSubmissionRequestTlvValue::DestTelematicsId(16))
419                    .build(),
420            ]
421        }
422    }
423
424    #[test]
425    fn encode_decode() {
426        crate::tests::owned::encode_decode_with_length_test_instances::<SubmitMulti>();
427    }
428
429    #[test]
430    fn short_message_length() {
431        let short_message = OctetString::new(b"Short Message").unwrap();
432
433        let submit_sm = SubmitMulti::builder()
434            .short_message(short_message.clone())
435            .build();
436
437        assert_eq!(submit_sm.short_message(), &short_message);
438        assert_eq!(submit_sm.sm_length(), short_message.length() as u8);
439    }
440
441    #[test]
442    fn short_message_override() {
443        let short_message_1 = OctetString::new(b"Short Message 101").unwrap();
444        let short_message_2 = OctetString::new(b"Short Message 2").unwrap();
445
446        let submit_sm = SubmitMulti::builder()
447            .short_message(short_message_1)
448            .short_message(short_message_2.clone())
449            .build();
450
451        assert_eq!(submit_sm.short_message(), &short_message_2);
452        assert_eq!(submit_sm.sm_length(), short_message_2.length() as u8);
453    }
454
455    #[test]
456    fn message_payload_suppresses_short_message() {
457        let short_message = OctetString::new(b"Short Message").unwrap();
458        let message_payload = MessagePayload::new(AnyOctetString::new(b"Message Payload"));
459
460        // Using push_tlv
461        let submit_sm = SubmitMulti::builder()
462            .short_message(short_message.clone())
463            .push_tlv(MessageSubmissionRequestTlvValue::MessagePayload(
464                message_payload.clone(),
465            ))
466            .build();
467
468        assert_eq!(submit_sm.short_message(), &OctetString::empty());
469        assert_eq!(submit_sm.sm_length(), 0);
470
471        // Using tlvs
472        let submit_sm = SubmitMulti::builder()
473            .short_message(short_message.clone())
474            .tlvs(alloc::vec![
475                MessageSubmissionRequestTlvValue::MessagePayload(message_payload.clone(),)
476            ])
477            .build();
478
479        assert_eq!(submit_sm.short_message(), &OctetString::empty());
480        assert_eq!(submit_sm.sm_length(), 0);
481
482        // Even setting the short message after the message payload should not set the short message
483        // Using push_tlv
484        let submit_sm = SubmitMulti::builder()
485            .short_message(short_message.clone())
486            .push_tlv(MessageSubmissionRequestTlvValue::MessagePayload(
487                message_payload.clone(),
488            ))
489            .short_message(short_message.clone())
490            .build();
491
492        assert_eq!(submit_sm.short_message(), &OctetString::empty());
493        assert_eq!(submit_sm.sm_length(), 0);
494
495        // Using tlvs
496        let submit_multi = SubmitMulti::builder()
497            .short_message(short_message.clone())
498            .tlvs(alloc::vec![
499                MessageSubmissionRequestTlvValue::MessagePayload(message_payload.clone(),)
500            ])
501            .short_message(short_message.clone())
502            .build();
503
504        assert_eq!(submit_multi.short_message(), &OctetString::empty());
505        assert_eq!(submit_multi.sm_length(), 0);
506
507        // Removing the message payload and then setting the short message should set the short message
508        let submit_sm = SubmitMulti::builder()
509            .push_tlv(MessageSubmissionRequestTlvValue::MessagePayload(
510                message_payload.clone(),
511            ))
512            .clear_tlvs()
513            .short_message(short_message.clone())
514            .build();
515
516        assert_eq!(submit_sm.short_message(), &short_message);
517        assert_eq!(submit_sm.sm_length(), short_message.length() as u8);
518    }
519
520    #[test]
521    fn count() {
522        let submit_multi = SubmitMulti::default();
523
524        assert_eq!(submit_multi.number_of_dests(), 0);
525        assert!(submit_multi.dest_address().is_empty());
526
527        let submit_sm = SubmitMulti::builder()
528            .dest_address(alloc::vec![
529                DestAddress::SmeAddress(SmeAddress::new(
530                    Ton::International,
531                    Npi::Isdn,
532                    COctetString::new(b"1234567890123456789\0").unwrap(),
533                )),
534                DestAddress::DistributionListName(DistributionListName::new(
535                    COctetString::new(b"1234567890123456789\0").unwrap(),
536                )),
537            ])
538            .build();
539
540        assert_eq!(submit_sm.number_of_dests(), 2);
541        assert_eq!(submit_sm.dest_address().len(), 2);
542
543        let submit_sm = SubmitMulti::builder()
544            .push_dest_address(DestAddress::SmeAddress(SmeAddress::new(
545                Ton::International,
546                Npi::Isdn,
547                COctetString::new(b"1234567890123456789\0").unwrap(),
548            )))
549            .push_dest_address(DestAddress::DistributionListName(
550                DistributionListName::new(COctetString::new(b"1234567890123456789\0").unwrap()),
551            ))
552            .build();
553
554        assert_eq!(submit_sm.number_of_dests(), 2);
555        assert_eq!(submit_sm.dest_address().len(), 2);
556
557        let submit_sm = SubmitMulti::builder()
558            .push_dest_address(DestAddress::SmeAddress(SmeAddress::new(
559                Ton::International,
560                Npi::Isdn,
561                COctetString::new(b"1234567890123456789\0").unwrap(),
562            )))
563            .push_dest_address(DestAddress::DistributionListName(
564                DistributionListName::new(COctetString::new(b"1234567890123456789\0").unwrap()),
565            ))
566            .clear_dest_address()
567            .build();
568
569        assert_eq!(submit_sm.number_of_dests(), 0);
570        assert!(submit_sm.dest_address().is_empty());
571    }
572}