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