rusmpp_core/pdus/borrowed/
replace_sm.rs

1use rusmpp_macros::Rusmpp;
2
3use crate::{
4    encode::Length,
5    pdus::borrowed::Pdu,
6    tlvs::borrowed::{Tlv, TlvValue},
7    types::borrowed::{COctetString, EmptyOrFullCOctetString, OctetString},
8    values::{borrowed::*, *},
9};
10
11/// This command is issued by the ESME to replace a previously submitted short message that
12/// is pending delivery. The matching mechanism is based on the message_id and source
13/// address of the original message.
14///
15/// Where the original submit_sm ‘source address’ was defaulted to NULL, then the source
16/// address in the replace_sm command should also be NULL.
17#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Rusmpp)]
18#[rusmpp(decode = borrowed, test = skip)]
19#[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))]
20#[cfg_attr(feature = "serde", derive(::serde::Serialize))]
21pub struct ReplaceSm<'a> {
22    /// Message ID of the message to be replaced.
23    /// This must be the MC assigned Message ID
24    /// allocated to the original short message when
25    /// submitted to the MC by the submit_sm, data_sm or
26    /// submit_multi command, and returned in the
27    /// response PDU by the MC.
28    pub message_id: COctetString<'a, 1, 65>,
29    /// Type of Number of message originator. This is used for
30    /// verification purposes, and must match that supplied in
31    /// the original request PDU (e.g. submit_sm).
32    ///
33    /// If not known, set to NULL.
34    pub source_addr_ton: Ton,
35    /// Numbering Plan Indicator for source address of
36    /// original message.
37    ///
38    /// If not known, set to NULL (Unknown).
39    pub source_addr_npi: Npi,
40    /// Address of SME, which originated this message.
41    /// If not known, set to NULL (Unknown).
42    pub source_addr: COctetString<'a, 1, 21>,
43    /// New scheduled delivery time for the short message.
44    // Set to NULL to preserve the original scheduled
45    // delivery time.
46    pub schedule_delivery_time: EmptyOrFullCOctetString<'a, 17>,
47    /// New expiry time for the short message.
48    ///
49    /// Set to NULL to preserve
50    /// the original validity period
51    /// setting.
52    pub validity_period: EmptyOrFullCOctetString<'a, 17>,
53    /// Indicator to signify if a MC delivery receipt,
54    /// user/manual or delivery ACK or intermediate
55    /// notification is required.
56    pub registered_delivery: RegisteredDelivery,
57    /// Indicates the short message to send from a list
58    /// of predefined (‘canned’) short messages stored on
59    /// the MC.
60    ///
61    /// If not using a MC canned message, set to NULL.
62    pub sm_default_msg_id: u8,
63    /// Length in octets of the short_message user data.
64    sm_length: u8,
65    /// Up to 255 octets of short message user data.
66    /// The exact physical limit for short_message size may
67    /// vary according to the underlying network
68    ///
69    /// Note: this field is superceded by the message_payload TLV if specified.
70    ///
71    /// Applications which need to send messages longer than
72    /// 255 octets should use the message_payload TLV. In
73    /// this case the sm_length field should be set to zero.
74    #[rusmpp(length = sm_length)]
75    short_message: OctetString<'a, 0, 255>,
76    /// Message replacement request TLVs. [`MessagePayload`].
77    #[rusmpp(length = "checked")]
78    message_payload: Option<Tlv<'a>>,
79}
80
81impl<'a> ReplaceSm<'a> {
82    #[allow(clippy::too_many_arguments)]
83    pub fn new(
84        message_id: COctetString<'a, 1, 65>,
85        source_addr_ton: Ton,
86        source_addr_npi: Npi,
87        source_addr: COctetString<'a, 1, 21>,
88        schedule_delivery_time: EmptyOrFullCOctetString<'a, 17>,
89        validity_period: EmptyOrFullCOctetString<'a, 17>,
90        registered_delivery: RegisteredDelivery,
91        sm_default_msg_id: u8,
92        short_message: OctetString<'a, 0, 255>,
93        message_payload: Option<MessagePayload<'a>>,
94    ) -> Self {
95        let message_payload = message_payload
96            .map(TlvValue::MessagePayload)
97            .map(From::from);
98
99        let sm_length = short_message.length() as u8;
100
101        let mut replace_sm = Self {
102            message_id,
103            source_addr_ton,
104            source_addr_npi,
105            source_addr,
106            schedule_delivery_time,
107            validity_period,
108            registered_delivery,
109            sm_default_msg_id,
110            sm_length,
111            short_message,
112            message_payload,
113        };
114
115        replace_sm.clear_short_message_if_message_payload_exists();
116
117        replace_sm
118    }
119
120    pub const fn sm_length(&self) -> u8 {
121        self.sm_length
122    }
123
124    pub fn short_message(&'_ self) -> &'_ OctetString<'_, 0, 255> {
125        &self.short_message
126    }
127
128    /// Sets the short message and short message length.
129    /// Updates the short message and short message length accordingly.
130    /// Has no effect if the message payload is set.
131    /// Returns true if the short message and short message length were set.
132    pub fn set_short_message(&mut self, short_message: OctetString<'a, 0, 255>) -> bool {
133        self.sm_length = short_message.length() as u8;
134        self.short_message = short_message;
135
136        !self.clear_short_message_if_message_payload_exists()
137    }
138
139    pub const fn message_payload_tlv(&'_ self) -> Option<&'_ Tlv<'_>> {
140        self.message_payload.as_ref()
141    }
142
143    pub fn message_payload(&'_ self) -> Option<&'_ MessagePayload<'_>> {
144        self.message_payload_tlv()
145            .and_then(|tlv| match tlv.value() {
146                Some(TlvValue::MessagePayload(value)) => Some(value),
147                _ => None,
148            })
149    }
150
151    /// Sets the message payload.
152    /// Updates the short message and short message length accordingly.
153    pub fn set_message_payload(&mut self, message_payload: Option<MessagePayload<'a>>) {
154        self.message_payload = message_payload
155            .map(TlvValue::MessagePayload)
156            .map(From::from);
157
158        self.clear_short_message_if_message_payload_exists();
159    }
160
161    /// Clears the short message and short message length if the message payload is set.
162    /// Returns true if the short message and short message length were cleared.
163    fn clear_short_message_if_message_payload_exists(&mut self) -> bool {
164        if self.message_payload.is_some() {
165            self.short_message = OctetString::empty();
166            self.sm_length = 0;
167
168            return true;
169        };
170
171        false
172    }
173
174    pub fn builder() -> ReplaceSmBuilder<'a> {
175        ReplaceSmBuilder::new()
176    }
177}
178
179impl<'a, const N: usize> From<ReplaceSm<'a>> for Pdu<'a, N> {
180    fn from(value: ReplaceSm<'a>) -> Self {
181        Self::ReplaceSm(value)
182    }
183}
184
185#[derive(Debug, Default)]
186pub struct ReplaceSmBuilder<'a> {
187    inner: ReplaceSm<'a>,
188}
189
190impl<'a> ReplaceSmBuilder<'a> {
191    pub fn new() -> Self {
192        Self::default()
193    }
194
195    pub fn message_id(mut self, message_id: COctetString<'a, 1, 65>) -> Self {
196        self.inner.message_id = message_id;
197        self
198    }
199
200    pub fn source_addr_ton(mut self, source_addr_ton: Ton) -> Self {
201        self.inner.source_addr_ton = source_addr_ton;
202        self
203    }
204
205    pub fn source_addr_npi(mut self, source_addr_npi: Npi) -> Self {
206        self.inner.source_addr_npi = source_addr_npi;
207        self
208    }
209
210    pub fn source_addr(mut self, source_addr: COctetString<'a, 1, 21>) -> Self {
211        self.inner.source_addr = source_addr;
212        self
213    }
214
215    pub fn schedule_delivery_time(
216        mut self,
217        schedule_delivery_time: EmptyOrFullCOctetString<'a, 17>,
218    ) -> Self {
219        self.inner.schedule_delivery_time = schedule_delivery_time;
220        self
221    }
222
223    pub fn validity_period(mut self, validity_period: EmptyOrFullCOctetString<'a, 17>) -> Self {
224        self.inner.validity_period = validity_period;
225        self
226    }
227
228    pub fn registered_delivery(mut self, registered_delivery: RegisteredDelivery) -> Self {
229        self.inner.registered_delivery = registered_delivery;
230        self
231    }
232
233    pub fn sm_default_msg_id(mut self, sm_default_msg_id: u8) -> Self {
234        self.inner.sm_default_msg_id = sm_default_msg_id;
235        self
236    }
237
238    pub fn short_message(mut self, short_message: OctetString<'a, 0, 255>) -> Self {
239        self.inner.set_short_message(short_message);
240        self
241    }
242
243    pub fn message_payload(mut self, message_payload: Option<MessagePayload<'a>>) -> Self {
244        self.inner.set_message_payload(message_payload);
245        self
246    }
247
248    pub fn build(self) -> ReplaceSm<'a> {
249        self.inner
250    }
251}
252
253#[cfg(test)]
254mod tests {
255    use crate::{tests::TestInstance, types::borrowed::AnyOctetString};
256
257    use super::*;
258
259    impl TestInstance for ReplaceSm<'_> {
260        fn instances() -> alloc::vec::Vec<Self> {
261            alloc::vec![
262                Self::default(),
263                Self::builder()
264                    .message_id(COctetString::new(b"123456789012345678901234\0").unwrap())
265                    .source_addr_ton(Ton::International)
266                    .source_addr_npi(Npi::Isdn)
267                    .source_addr(COctetString::new(b"Source Addr\0").unwrap())
268                    .schedule_delivery_time(
269                        EmptyOrFullCOctetString::new(b"2023-10-01T12:00\0").unwrap(),
270                    )
271                    .validity_period(EmptyOrFullCOctetString::new(b"2023-10-01T12:00\0").unwrap())
272                    .registered_delivery(RegisteredDelivery::default())
273                    .sm_default_msg_id(0)
274                    .short_message(OctetString::new(b"Short Message").unwrap())
275                    .build(),
276                Self::builder()
277                    .message_id(COctetString::new(b"123456789012345678901234\0").unwrap())
278                    .source_addr_ton(Ton::International)
279                    .source_addr_npi(Npi::Isdn)
280                    .source_addr(COctetString::new(b"Source Addr\0").unwrap())
281                    .schedule_delivery_time(
282                        EmptyOrFullCOctetString::new(b"2023-10-01T12:00\0").unwrap(),
283                    )
284                    .validity_period(EmptyOrFullCOctetString::new(b"2023-10-01T12:00\0").unwrap())
285                    .registered_delivery(RegisteredDelivery::default())
286                    .sm_default_msg_id(0)
287                    .message_payload(Some(MessagePayload::new(AnyOctetString::new(
288                        b"Message Payload",
289                    ))))
290                    .build(),
291                Self::builder()
292                    .validity_period(EmptyOrFullCOctetString::new(b"2023-10-01T12:00\0").unwrap())
293                    .short_message(OctetString::new(b"Short Message").unwrap())
294                    .message_payload(Some(MessagePayload::new(AnyOctetString::new(
295                        b"Message Payload",
296                    ))))
297                    .build(),
298            ]
299        }
300    }
301
302    #[test]
303    fn encode_decode() {
304        crate::tests::borrowed::encode_decode_with_length_test_instances::<ReplaceSm>();
305    }
306
307    #[test]
308    fn short_message_length() {
309        let short_message = OctetString::new(b"Short Message").unwrap();
310
311        let submit_sm = ReplaceSm::builder()
312            .short_message(short_message.clone())
313            .build();
314
315        assert_eq!(submit_sm.short_message(), &short_message);
316        assert_eq!(submit_sm.sm_length(), short_message.length() as u8);
317    }
318
319    #[test]
320    fn short_message_override() {
321        let short_message_1 = OctetString::new(b"Short Message 101").unwrap();
322        let short_message_2 = OctetString::new(b"Short Message 2").unwrap();
323
324        let submit_sm = ReplaceSm::builder()
325            .short_message(short_message_1)
326            .short_message(short_message_2.clone())
327            .build();
328
329        assert_eq!(submit_sm.short_message(), &short_message_2);
330        assert_eq!(submit_sm.sm_length(), short_message_2.length() as u8);
331    }
332
333    #[test]
334    fn message_payload_suppresses_short_message() {
335        let short_message = OctetString::new(b"Short Message").unwrap();
336        let message_payload = MessagePayload::new(AnyOctetString::new(b"Message Payload"));
337
338        // Using push_tlv
339        let replace_sm = ReplaceSm::builder()
340            .short_message(short_message.clone())
341            .message_payload(Some(message_payload.clone()))
342            .build();
343
344        assert_eq!(replace_sm.short_message(), &OctetString::empty());
345        assert_eq!(replace_sm.sm_length(), 0);
346
347        // Even setting the short message after the message payload should not set the short message
348        let submit_sm = ReplaceSm::builder()
349            .short_message(short_message.clone())
350            .message_payload(Some(message_payload.clone()))
351            .short_message(short_message.clone())
352            .build();
353
354        assert_eq!(submit_sm.short_message(), &OctetString::empty());
355        assert_eq!(submit_sm.sm_length(), 0);
356
357        // Removing the message payload and then setting the short message should set the short message
358        let submit_sm = ReplaceSm::builder()
359            .message_payload(Some(message_payload.clone()))
360            .message_payload(None)
361            .short_message(short_message.clone())
362            .build();
363
364        assert_eq!(submit_sm.short_message(), &short_message);
365        assert_eq!(submit_sm.sm_length(), short_message.length() as u8);
366    }
367}