rusmpp_core/pdus/owned/
replace_sm.rs

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