rusmpp_core/pdus/borrowed/
submit_multi.rs

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