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        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
116    pub const fn sm_length(&self) -> u8 {
117        self.sm_length
118    }
119
120    pub fn short_message(&'_ self) -> &'_ OctetString<'_, 0, 255> {
121        &self.short_message
122    }
123
124    /// Sets the `short_message` and `sm_length`.
125    ///
126    /// # Note
127    ///
128    /// `short_message` is superceded by [`TlvValue::MessagePayload`](crate::tlvs::borrowed::TlvValue::MessagePayload) and should only be used if
129    /// [`TlvValue::MessagePayload`](crate::tlvs::borrowed::TlvValue::MessagePayload) is not present.
130    pub fn set_short_message(&mut self, short_message: OctetString<'a, 0, 255>) {
131        self.short_message = short_message;
132        self.sm_length = self.short_message.length() as u8;
133    }
134
135    pub const fn message_payload_tlv(&'_ self) -> Option<&'_ Tlv<'_>> {
136        self.message_payload.as_ref()
137    }
138
139    pub fn message_payload(&'_ self) -> Option<&'_ MessagePayload<'_>> {
140        self.message_payload_tlv()
141            .and_then(|tlv| match tlv.value() {
142                Some(TlvValue::MessagePayload(value)) => Some(value),
143                _ => None,
144            })
145    }
146
147    /// Sets the `message_payload` TLV.
148    ///
149    /// # Note
150    ///
151    /// `short_message` is superceded by [`TlvValue::MessagePayload`](crate::tlvs::borrowed::TlvValue::MessagePayload) and should only be used if
152    /// [`TlvValue::MessagePayload`](crate::tlvs::borrowed::TlvValue::MessagePayload) is not present.
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
159    pub fn builder() -> ReplaceSmBuilder<'a> {
160        ReplaceSmBuilder::new()
161    }
162}
163
164impl<'a, const N: usize> From<ReplaceSm<'a>> for Pdu<'a, N> {
165    fn from(value: ReplaceSm<'a>) -> Self {
166        Self::ReplaceSm(value)
167    }
168}
169
170#[derive(Debug, Default)]
171pub struct ReplaceSmBuilder<'a> {
172    inner: ReplaceSm<'a>,
173}
174
175impl<'a> ReplaceSmBuilder<'a> {
176    pub fn new() -> Self {
177        Self::default()
178    }
179
180    pub fn message_id(mut self, message_id: COctetString<'a, 1, 65>) -> Self {
181        self.inner.message_id = message_id;
182        self
183    }
184
185    pub fn source_addr_ton(mut self, source_addr_ton: Ton) -> Self {
186        self.inner.source_addr_ton = source_addr_ton;
187        self
188    }
189
190    pub fn source_addr_npi(mut self, source_addr_npi: Npi) -> Self {
191        self.inner.source_addr_npi = source_addr_npi;
192        self
193    }
194
195    pub fn source_addr(mut self, source_addr: COctetString<'a, 1, 21>) -> Self {
196        self.inner.source_addr = source_addr;
197        self
198    }
199
200    pub fn schedule_delivery_time(
201        mut self,
202        schedule_delivery_time: EmptyOrFullCOctetString<'a, 17>,
203    ) -> Self {
204        self.inner.schedule_delivery_time = schedule_delivery_time;
205        self
206    }
207
208    pub fn validity_period(mut self, validity_period: EmptyOrFullCOctetString<'a, 17>) -> Self {
209        self.inner.validity_period = validity_period;
210        self
211    }
212
213    pub fn registered_delivery(mut self, registered_delivery: RegisteredDelivery) -> Self {
214        self.inner.registered_delivery = registered_delivery;
215        self
216    }
217
218    pub fn sm_default_msg_id(mut self, sm_default_msg_id: u8) -> Self {
219        self.inner.sm_default_msg_id = sm_default_msg_id;
220        self
221    }
222
223    pub fn short_message(mut self, short_message: OctetString<'a, 0, 255>) -> Self {
224        self.inner.set_short_message(short_message);
225        self
226    }
227
228    pub fn message_payload(mut self, message_payload: Option<MessagePayload<'a>>) -> Self {
229        self.inner.set_message_payload(message_payload);
230        self
231    }
232
233    pub fn build(self) -> ReplaceSm<'a> {
234        self.inner
235    }
236}
237
238#[cfg(test)]
239mod tests {
240    use crate::{tests::TestInstance, types::borrowed::AnyOctetString};
241
242    use super::*;
243
244    impl TestInstance for ReplaceSm<'_> {
245        fn instances() -> alloc::vec::Vec<Self> {
246            alloc::vec![
247                Self::default(),
248                Self::builder()
249                    .message_id(COctetString::new(b"123456789012345678901234\0").unwrap())
250                    .source_addr_ton(Ton::International)
251                    .source_addr_npi(Npi::Isdn)
252                    .source_addr(COctetString::new(b"Source Addr\0").unwrap())
253                    .schedule_delivery_time(
254                        EmptyOrFullCOctetString::new(b"2023-10-01T12:00\0").unwrap(),
255                    )
256                    .validity_period(EmptyOrFullCOctetString::new(b"2023-10-01T12:00\0").unwrap())
257                    .registered_delivery(RegisteredDelivery::default())
258                    .sm_default_msg_id(0)
259                    .short_message(OctetString::new(b"Short Message").unwrap())
260                    .build(),
261                Self::builder()
262                    .message_id(COctetString::new(b"123456789012345678901234\0").unwrap())
263                    .source_addr_ton(Ton::International)
264                    .source_addr_npi(Npi::Isdn)
265                    .source_addr(COctetString::new(b"Source Addr\0").unwrap())
266                    .schedule_delivery_time(
267                        EmptyOrFullCOctetString::new(b"2023-10-01T12:00\0").unwrap(),
268                    )
269                    .validity_period(EmptyOrFullCOctetString::new(b"2023-10-01T12:00\0").unwrap())
270                    .registered_delivery(RegisteredDelivery::default())
271                    .sm_default_msg_id(0)
272                    .message_payload(Some(MessagePayload::new(AnyOctetString::new(
273                        b"Message Payload",
274                    ))))
275                    .build(),
276                Self::builder()
277                    .validity_period(EmptyOrFullCOctetString::new(b"2023-10-01T12:00\0").unwrap())
278                    .short_message(OctetString::new(b"Short Message").unwrap())
279                    .message_payload(Some(MessagePayload::new(AnyOctetString::new(
280                        b"Message Payload",
281                    ))))
282                    .build(),
283            ]
284        }
285    }
286
287    #[test]
288    fn encode_decode() {
289        crate::tests::borrowed::encode_decode_with_length_test_instances::<ReplaceSm>();
290    }
291
292    #[test]
293    fn short_message_length() {
294        let short_message = OctetString::new(b"Short Message").unwrap();
295
296        let submit_sm = ReplaceSm::builder()
297            .short_message(short_message.clone())
298            .build();
299
300        assert_eq!(submit_sm.short_message(), &short_message);
301        assert_eq!(submit_sm.sm_length(), short_message.length() as u8);
302    }
303}