rusmpp_core/pdus/borrowed/
submit_sm.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/// This operation is used by an ESME to submit a short message to the MC for onward
12/// transmission to a specified short message entity (SME).
13#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Rusmpp)]
14#[rusmpp(decode = borrowed, test = skip)]
15#[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))]
16#[cfg_attr(feature = "serde", derive(::serde::Serialize))]
17pub struct SubmitSm<'a, const N: usize> {
18    /// The service_type parameter can be used to
19    /// indicate the SMS Application service
20    /// associated with the message. Specifying the
21    /// service_type allows the ESME to avail of enhanced
22    /// messaging services such as “replace by service_type”
23    /// or to control the teleservice used on the
24    /// air interface.
25    ///
26    /// Set to NULL for default MC settings.
27    pub service_type: ServiceType<'a>,
28    /// Type of Number for source address.
29    pub source_addr_ton: Ton,
30    /// Numbering Plan Indicator for source address.
31    pub source_addr_npi: Npi,
32    /// Address of SME which originated this message.
33    pub source_addr: COctetString<'a, 1, 21>,
34    /// Type of Number for destination.
35    pub dest_addr_ton: Ton,
36    /// Numbering Plan Indicator for destination.
37    pub dest_addr_npi: Npi,
38    /// Destination address of this
39    /// short message For mobile
40    /// terminated messages, this
41    /// is the directory number of
42    /// the recipient MS
43    pub destination_addr: COctetString<'a, 1, 21>,
44    /// Indicates Message Mode
45    /// and Message Type.
46    pub esm_class: EsmClass,
47    /// Protocol Identifier.
48    /// Network specific field.
49    pub protocol_id: u8,
50    /// Designates the priority level of the message.
51    pub priority_flag: PriorityFlag,
52    /// The short message is to be
53    /// scheduled by the MC for delivery.
54    /// Set to NULL for immediate message delivery.
55    pub schedule_delivery_time: EmptyOrFullCOctetString<'a, 17>,
56    /// The validity period of this message.
57    /// Set to NULL to request the MC default validity period.
58    ///
59    /// Note: this is superseded by the qos_time_to_live TLV if
60    /// specified.
61    pub validity_period: EmptyOrFullCOctetString<'a, 17>,
62    /// Indicator to signify if a MC delivery receipt, manual
63    /// ACK, delivery ACK or an intermediate notification is required.
64    pub registered_delivery: RegisteredDelivery,
65    /// Flag indicating if the submitted message should replace an existing message.
66    pub replace_if_present_flag: ReplaceIfPresentFlag,
67    /// Defines the encoding scheme of the short message user data.
68    pub data_coding: DataCoding,
69    /// Indicates the short message to send from a list of pre- defined (‘canned’)
70    /// short messages stored on the MC. If not using a MC canned message, set to NULL.
71    pub sm_default_msg_id: u8,
72    /// Length in octets of the short_message user data.
73    sm_length: u8,
74    /// Up to 255 octets of short message user data.
75    ///
76    /// The exact physical limit for short_message size may
77    /// vary according to the underlying network
78    ///
79    /// Note: this field is superceded by the message_payload TLV if
80    /// specified.
81    #[rusmpp(length = sm_length)]
82    short_message: OctetString<'a, 0, 255>,
83    /// Message submission request TLVs ([`MessageSubmissionRequestTlvValue`]).
84    #[rusmpp(length = "unchecked")]
85    #[cfg_attr(feature = "arbitrary", arbitrary(default))]
86    tlvs: heapless::vec::Vec<Tlv<'a>, N>,
87}
88
89impl<'a, const N: usize> SubmitSm<'a, N> {
90    #[allow(clippy::too_many_arguments)]
91    pub fn new(
92        service_type: ServiceType<'a>,
93        source_addr_ton: Ton,
94        source_addr_npi: Npi,
95        source_addr: COctetString<'a, 1, 21>,
96        dest_addr_ton: Ton,
97        dest_addr_npi: Npi,
98        destination_addr: COctetString<'a, 1, 21>,
99        esm_class: EsmClass,
100        protocol_id: u8,
101        priority_flag: PriorityFlag,
102        schedule_delivery_time: EmptyOrFullCOctetString<'a, 17>,
103        validity_period: EmptyOrFullCOctetString<'a, 17>,
104        registered_delivery: RegisteredDelivery,
105        replace_if_present_flag: ReplaceIfPresentFlag,
106        data_coding: DataCoding,
107        sm_default_msg_id: u8,
108        short_message: OctetString<'a, 0, 255>,
109        tlvs: heapless::vec::Vec<MessageSubmissionRequestTlvValue<'a>, N>,
110    ) -> Self {
111        let tlvs = tlvs.into_iter().map(From::from).collect();
112
113        let sm_length = short_message.length() as u8;
114
115        Self {
116            service_type,
117            source_addr_ton,
118            source_addr_npi,
119            source_addr,
120            dest_addr_ton,
121            dest_addr_npi,
122            destination_addr,
123            esm_class,
124            protocol_id,
125            priority_flag,
126            schedule_delivery_time,
127            validity_period,
128            registered_delivery,
129            replace_if_present_flag,
130            data_coding,
131            sm_default_msg_id,
132            sm_length,
133            short_message,
134            tlvs,
135        }
136    }
137
138    pub fn sm_length(&self) -> u8 {
139        self.sm_length
140    }
141
142    pub fn short_message(&'_ self) -> &'_ OctetString<'_, 0, 255> {
143        &self.short_message
144    }
145
146    /// Sets the `short_message` and `sm_length`.
147    ///
148    /// # Note
149    ///
150    /// `short_message` is superceded by [`TlvValue::MessagePayload`](crate::tlvs::borrowed::TlvValue::MessagePayload) and should only be used if
151    /// [`TlvValue::MessagePayload`](crate::tlvs::borrowed::TlvValue::MessagePayload) is not present.
152    pub fn set_short_message(&mut self, short_message: OctetString<'a, 0, 255>) {
153        self.short_message = short_message;
154        self.sm_length = self.short_message.length() as u8;
155    }
156
157    pub fn tlvs(&'_ self) -> &'_ [Tlv<'_>] {
158        &self.tlvs
159    }
160
161    pub fn set_tlvs(&mut self, tlvs: heapless::vec::Vec<MessageSubmissionRequestTlvValue<'a>, N>) {
162        self.tlvs = tlvs.into_iter().map(From::from).collect();
163    }
164
165    pub fn clear_tlvs(&mut self) {
166        self.tlvs.clear();
167    }
168
169    pub fn push_tlv(
170        &mut self,
171        tlv: impl Into<MessageSubmissionRequestTlvValue<'a>>,
172    ) -> Result<(), Tlv<'a>> {
173        self.tlvs.push(Tlv::from(tlv.into()))?;
174        Ok(())
175    }
176
177    pub fn builder() -> SubmitSmBuilder<'a, N> {
178        SubmitSmBuilder::new()
179    }
180}
181
182impl<'a, const N: usize> From<SubmitSm<'a, N>> for Pdu<'a, N> {
183    fn from(value: SubmitSm<'a, N>) -> Self {
184        Self::SubmitSm(value)
185    }
186}
187
188#[derive(Debug, Default)]
189pub struct SubmitSmBuilder<'a, const N: usize> {
190    inner: SubmitSm<'a, N>,
191}
192
193impl<'a, const N: usize> SubmitSmBuilder<'a, N> {
194    pub fn new() -> Self {
195        Self::default()
196    }
197
198    pub fn service_type(mut self, service_type: ServiceType<'a>) -> Self {
199        self.inner.service_type = service_type;
200        self
201    }
202
203    pub fn source_addr_ton(mut self, source_addr_ton: Ton) -> Self {
204        self.inner.source_addr_ton = source_addr_ton;
205        self
206    }
207
208    pub fn source_addr_npi(mut self, source_addr_npi: Npi) -> Self {
209        self.inner.source_addr_npi = source_addr_npi;
210        self
211    }
212
213    pub fn source_addr(mut self, source_addr: COctetString<'a, 1, 21>) -> Self {
214        self.inner.source_addr = source_addr;
215        self
216    }
217
218    pub fn dest_addr_ton(mut self, dest_addr_ton: Ton) -> Self {
219        self.inner.dest_addr_ton = dest_addr_ton;
220        self
221    }
222
223    pub fn dest_addr_npi(mut self, dest_addr_npi: Npi) -> Self {
224        self.inner.dest_addr_npi = dest_addr_npi;
225        self
226    }
227
228    pub fn destination_addr(mut self, destination_addr: COctetString<'a, 1, 21>) -> Self {
229        self.inner.destination_addr = destination_addr;
230        self
231    }
232
233    pub fn esm_class(mut self, esm_class: EsmClass) -> Self {
234        self.inner.esm_class = esm_class;
235        self
236    }
237
238    pub fn protocol_id(mut self, protocol_id: u8) -> Self {
239        self.inner.protocol_id = protocol_id;
240        self
241    }
242
243    pub fn priority_flag(mut self, priority_flag: PriorityFlag) -> Self {
244        self.inner.priority_flag = priority_flag;
245        self
246    }
247
248    pub fn schedule_delivery_time(
249        mut self,
250        schedule_delivery_time: EmptyOrFullCOctetString<'a, 17>,
251    ) -> Self {
252        self.inner.schedule_delivery_time = schedule_delivery_time;
253        self
254    }
255
256    pub fn validity_period(mut self, validity_period: EmptyOrFullCOctetString<'a, 17>) -> Self {
257        self.inner.validity_period = validity_period;
258        self
259    }
260
261    pub fn registered_delivery(mut self, registered_delivery: RegisteredDelivery) -> Self {
262        self.inner.registered_delivery = registered_delivery;
263        self
264    }
265
266    pub fn replace_if_present_flag(
267        mut self,
268        replace_if_present_flag: ReplaceIfPresentFlag,
269    ) -> Self {
270        self.inner.replace_if_present_flag = replace_if_present_flag;
271        self
272    }
273
274    pub fn data_coding(mut self, data_coding: DataCoding) -> Self {
275        self.inner.data_coding = data_coding;
276        self
277    }
278
279    pub fn sm_default_msg_id(mut self, sm_default_msg_id: u8) -> Self {
280        self.inner.sm_default_msg_id = sm_default_msg_id;
281        self
282    }
283
284    pub fn short_message(mut self, short_message: OctetString<'a, 0, 255>) -> Self {
285        self.inner.set_short_message(short_message);
286        self
287    }
288
289    pub fn tlvs(
290        mut self,
291        tlvs: heapless::vec::Vec<MessageSubmissionRequestTlvValue<'a>, N>,
292    ) -> Self {
293        self.inner.set_tlvs(tlvs);
294        self
295    }
296
297    pub fn clear_tlvs(mut self) -> Self {
298        self.inner.clear_tlvs();
299        self
300    }
301
302    pub fn push_tlv(
303        mut self,
304        tlv: impl Into<MessageSubmissionRequestTlvValue<'a>>,
305    ) -> Result<Self, Tlv<'a>> {
306        self.inner.push_tlv(tlv)?;
307        Ok(self)
308    }
309
310    pub fn build(self) -> SubmitSm<'a, N> {
311        self.inner
312    }
313}
314
315#[cfg(test)]
316mod tests {
317    use crate::{tests::TestInstance, types::borrowed::AnyOctetString};
318
319    use super::*;
320
321    impl<const N: usize> TestInstance for SubmitSm<'static, N> {
322        fn instances() -> alloc::vec::Vec<Self> {
323            alloc::vec![
324                Self::default(),
325                Self::builder()
326                    .service_type(ServiceType::new(
327                        GenericServiceType::CellularMessaging.into(),
328                    ))
329                    .source_addr_ton(Ton::International)
330                    .source_addr_npi(Npi::Isdn)
331                    .source_addr(COctetString::new(b"Source Address\0").unwrap())
332                    .dest_addr_ton(Ton::International)
333                    .dest_addr_npi(Npi::Isdn)
334                    .destination_addr(COctetString::new(b"Destination Address\0").unwrap())
335                    .esm_class(EsmClass::new(
336                        MessagingMode::StoreAndForward,
337                        MessageType::ShortMessageContainsMCDeliveryReceipt,
338                        Ansi41Specific::ShortMessageContainsDeliveryAcknowledgement,
339                        GsmFeatures::SetUdhiAndReplyPath,
340                    ))
341                    .protocol_id(0)
342                    .priority_flag(PriorityFlag::from(PriorityFlagType::from(Ansi136::Bulk)))
343                    .schedule_delivery_time(
344                        EmptyOrFullCOctetString::new(b"2023-09-01T12:00\0").unwrap(),
345                    )
346                    .validity_period(EmptyOrFullCOctetString::new(b"2023-10-01T12:00\0").unwrap())
347                    .registered_delivery(RegisteredDelivery::request_all())
348                    .replace_if_present_flag(ReplaceIfPresentFlag::Replace)
349                    .data_coding(DataCoding::Ksc5601)
350                    .sm_default_msg_id(69)
351                    .short_message(OctetString::new(b"Short Message").unwrap())
352                    .build(),
353                Self::builder()
354                    .service_type(ServiceType::new(
355                        GenericServiceType::CellularMessaging.into(),
356                    ))
357                    .source_addr_ton(Ton::International)
358                    .source_addr_npi(Npi::Isdn)
359                    .source_addr(COctetString::new(b"Source Address\0").unwrap())
360                    .dest_addr_ton(Ton::International)
361                    .dest_addr_npi(Npi::Isdn)
362                    .destination_addr(COctetString::new(b"Destination Address\0").unwrap())
363                    .esm_class(EsmClass::new(
364                        MessagingMode::Default,
365                        MessageType::ShortMessageContainsIntermediateDeliveryNotification,
366                        Ansi41Specific::ShortMessageContainsUserAcknowledgment,
367                        GsmFeatures::SetUdhiAndReplyPath,
368                    ))
369                    .protocol_id(0)
370                    .priority_flag(PriorityFlag::from(PriorityFlagType::from(
371                        Ansi136::VeryUrgent,
372                    )))
373                    .schedule_delivery_time(
374                        EmptyOrFullCOctetString::new(b"2023-09-01T12:01\0").unwrap(),
375                    )
376                    .validity_period(EmptyOrFullCOctetString::new(b"2023-10-01T12:20\0").unwrap())
377                    .registered_delivery(RegisteredDelivery::request_all())
378                    .replace_if_present_flag(ReplaceIfPresentFlag::DoNotReplace)
379                    .data_coding(DataCoding::Jis)
380                    .sm_default_msg_id(96)
381                    .short_message(OctetString::new(b"Short Message").unwrap())
382                    .tlvs(
383                        [MessageSubmissionRequestTlvValue::MessagePayload(
384                            MessagePayload::new(AnyOctetString::new(b"Message Payload")),
385                        )]
386                        .into()
387                    )
388                    .build(),
389                Self::builder()
390                    .short_message(OctetString::new(b"Short Message").unwrap())
391                    .tlvs(
392                        [
393                            MessageSubmissionRequestTlvValue::MessagePayload(MessagePayload::new(
394                                AnyOctetString::new(b"Message Payload"),
395                            )),
396                            MessageSubmissionRequestTlvValue::UserResponseCode(3),
397                            MessageSubmissionRequestTlvValue::DestBearerType(
398                                BearerType::FlexReFlex
399                            ),
400                            MessageSubmissionRequestTlvValue::SourceSubaddress(Subaddress::new(
401                                SubaddressTag::NsapOdd,
402                                OctetString::new(b"Subaddress :D\0").unwrap(),
403                            )),
404                        ]
405                        .into()
406                    )
407                    .build(),
408            ]
409        }
410    }
411
412    #[test]
413    fn encode_decode() {
414        crate::tests::borrowed::encode_decode_with_length_test_instances::<SubmitSm<'static, 16>>();
415    }
416
417    #[test]
418    fn short_message_length() {
419        let short_message = OctetString::new(b"Short Message").unwrap();
420
421        let submit_sm = SubmitSm::<'static, 16>::builder()
422            .short_message(short_message.clone())
423            .build();
424
425        assert_eq!(submit_sm.short_message(), &short_message);
426        assert_eq!(submit_sm.sm_length(), short_message.length() as u8);
427    }
428}