rusmpp_core/pdus/borrowed/
submit_multi.rs

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