rusmpp_core/pdus/borrowed/
data_sm.rs

1use rusmpp_macros::Rusmpp;
2
3use crate::{
4    pdus::borrowed::Pdu,
5    tlvs::borrowed::{MessageSubmissionRequestTlvValue, Tlv},
6    types::borrowed::COctetString,
7    values::{borrowed::*, *},
8};
9
10/// The data_sm operation is similar to the submit_sm in that it provides a means to submit a
11/// mobile-terminated message. However, data_sm is intended for packet-based applications
12/// such as WAP in that it features a reduced PDU body containing fields relevant to WAP or
13/// packet-based applications.
14#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Rusmpp)]
15#[rusmpp(decode = borrowed, test = skip)]
16#[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))]
17#[cfg_attr(feature = "serde", derive(::serde::Serialize))]
18pub struct DataSm<'a, const N: usize> {
19    /// The service_type parameter can be used to indicate the
20    /// SMS Application service associated with the message.
21    /// Specifying the service_type allows the ESME to avail of
22    /// enhanced messaging services such as “replace by
23    /// service_type” or control the teleservice used on the air
24    /// interface.
25    ///
26    /// Set to NULL for default MC
27    /// settings.
28    pub service_type: ServiceType<'a>,
29    /// Type of Number for source
30    /// address.
31    ///
32    /// If not known, set to NULL
33    /// (Unknown).
34    pub source_addr_ton: Ton,
35    /// Numbering Plan Indicator for
36    /// source address.
37    ///
38    /// If not known, set to NULL
39    /// (Unknown).
40    pub source_addr_npi: Npi,
41    /// Address of SME which
42    /// originated this message.
43    ///
44    /// If not known, set to NULL
45    /// (Unknown).
46    pub source_addr: COctetString<'a, 1, 21>,
47    /// Type of Number for destination.
48    pub dest_addr_ton: Ton,
49    /// Numbering Plan Indicator for destination.
50    pub dest_addr_npi: Npi,
51    /// Destination address of this short message For mobile
52    /// terminated messages, this is the directory number of the
53    /// recipient MS.
54    pub destination_addr: COctetString<'a, 1, 21>,
55    /// Indicates Message Mode and Message Type.
56    pub esm_class: EsmClass,
57    /// Indicator to signify if a MC
58    /// delivery receipt or an SME
59    /// acknowledgement is required.
60    pub registered_delivery: RegisteredDelivery,
61    /// Defines the encoding scheme
62    /// of the short message user data.
63    pub data_coding: DataCoding,
64    /// Message submission request TLVs ([`MessageSubmissionRequestTlvValue`])
65    #[rusmpp(length = "unchecked")]
66    #[cfg_attr(feature = "arbitrary", arbitrary(default))]
67    tlvs: heapless::vec::Vec<Tlv<'a>, N>,
68}
69
70impl<'a, const N: usize> DataSm<'a, N> {
71    #[allow(clippy::too_many_arguments)]
72    pub fn new(
73        service_type: ServiceType<'a>,
74        source_addr_ton: Ton,
75        source_addr_npi: Npi,
76        source_addr: COctetString<'a, 1, 21>,
77        dest_addr_ton: Ton,
78        dest_addr_npi: Npi,
79        destination_addr: COctetString<'a, 1, 21>,
80        esm_class: EsmClass,
81        registered_delivery: RegisteredDelivery,
82        data_coding: DataCoding,
83        tlvs: heapless::vec::Vec<MessageSubmissionRequestTlvValue<'a>, N>,
84    ) -> Self {
85        let tlvs = tlvs.into_iter().map(From::from).collect();
86
87        Self {
88            service_type,
89            source_addr_ton,
90            source_addr_npi,
91            source_addr,
92            dest_addr_ton,
93            dest_addr_npi,
94            destination_addr,
95            esm_class,
96            registered_delivery,
97            data_coding,
98            tlvs,
99        }
100    }
101
102    pub fn tlvs(&'_ self) -> &'_ [Tlv<'_>] {
103        &self.tlvs
104    }
105
106    pub fn set_tlvs(&mut self, tlvs: heapless::vec::Vec<MessageSubmissionRequestTlvValue<'a>, N>) {
107        self.tlvs = tlvs.into_iter().map(From::from).collect();
108    }
109
110    pub fn clear_tlvs(&mut self) {
111        self.tlvs.clear();
112    }
113
114    pub fn push_tlv(
115        &mut self,
116        tlv: impl Into<MessageSubmissionRequestTlvValue<'a>>,
117    ) -> Result<(), Tlv<'a>> {
118        self.tlvs.push(Tlv::from(tlv.into()))?;
119        Ok(())
120    }
121
122    pub fn builder() -> DataSmBuilder<'a, N> {
123        DataSmBuilder::new()
124    }
125}
126
127impl<'a, const N: usize> From<DataSm<'a, N>> for Pdu<'a, N> {
128    fn from(value: DataSm<'a, N>) -> Self {
129        Self::DataSm(value)
130    }
131}
132
133#[derive(Debug, Default)]
134pub struct DataSmBuilder<'a, const N: usize> {
135    inner: DataSm<'a, N>,
136}
137
138impl<'a, const N: usize> DataSmBuilder<'a, N> {
139    pub fn new() -> Self {
140        Default::default()
141    }
142
143    pub fn service_type(mut self, service_type: ServiceType<'a>) -> Self {
144        self.inner.service_type = service_type;
145        self
146    }
147
148    pub fn source_addr_ton(mut self, source_addr_ton: Ton) -> Self {
149        self.inner.source_addr_ton = source_addr_ton;
150        self
151    }
152
153    pub fn source_addr_npi(mut self, source_addr_npi: Npi) -> Self {
154        self.inner.source_addr_npi = source_addr_npi;
155        self
156    }
157
158    pub fn source_addr(mut self, source_addr: COctetString<'a, 1, 21>) -> Self {
159        self.inner.source_addr = source_addr;
160        self
161    }
162
163    pub fn dest_addr_ton(mut self, dest_addr_ton: Ton) -> Self {
164        self.inner.dest_addr_ton = dest_addr_ton;
165        self
166    }
167
168    pub fn dest_addr_npi(mut self, dest_addr_npi: Npi) -> Self {
169        self.inner.dest_addr_npi = dest_addr_npi;
170        self
171    }
172
173    pub fn destination_addr(mut self, destination_addr: COctetString<'a, 1, 21>) -> Self {
174        self.inner.destination_addr = destination_addr;
175        self
176    }
177
178    pub fn esm_class(mut self, esm_class: EsmClass) -> Self {
179        self.inner.esm_class = esm_class;
180        self
181    }
182
183    pub fn registered_delivery(mut self, registered_delivery: RegisteredDelivery) -> Self {
184        self.inner.registered_delivery = registered_delivery;
185        self
186    }
187
188    pub fn data_coding(mut self, data_coding: DataCoding) -> Self {
189        self.inner.data_coding = data_coding;
190        self
191    }
192
193    pub fn tlvs(
194        mut self,
195        tlvs: heapless::vec::Vec<MessageSubmissionRequestTlvValue<'a>, N>,
196    ) -> Self {
197        self.inner.set_tlvs(tlvs);
198        self
199    }
200
201    pub fn clear_tlvs(mut self) -> Self {
202        self.inner.clear_tlvs();
203        self
204    }
205
206    pub fn push_tlv(
207        mut self,
208        tlv: impl Into<MessageSubmissionRequestTlvValue<'a>>,
209    ) -> Result<Self, Tlv<'a>> {
210        self.inner.push_tlv(tlv)?;
211        Ok(self)
212    }
213
214    pub fn build(self) -> DataSm<'a, N> {
215        self.inner
216    }
217}
218
219#[cfg(test)]
220mod tests {
221    use crate::tests::TestInstance;
222
223    use super::*;
224
225    impl<const N: usize> TestInstance for DataSm<'_, N> {
226        fn instances() -> alloc::vec::Vec<Self> {
227            alloc::vec![
228                Self::default(),
229                Self::builder()
230                    .service_type(ServiceType::default())
231                    .source_addr_ton(Ton::International)
232                    .source_addr_npi(Npi::Isdn)
233                    .source_addr(COctetString::new(b"source_addr\0").unwrap())
234                    .dest_addr_ton(Ton::International)
235                    .dest_addr_npi(Npi::Isdn)
236                    .destination_addr(COctetString::new(b"destination_addr\0").unwrap())
237                    .esm_class(EsmClass::default())
238                    .registered_delivery(RegisteredDelivery::request_all())
239                    .data_coding(DataCoding::Ucs2)
240                    .build(),
241                Self::builder()
242                    .service_type(ServiceType::default())
243                    .source_addr_ton(Ton::International)
244                    .source_addr_npi(Npi::Isdn)
245                    .source_addr(COctetString::new(b"source_addr\0").unwrap())
246                    .dest_addr_ton(Ton::International)
247                    .dest_addr_npi(Npi::Isdn)
248                    .destination_addr(COctetString::new(b"destination_addr\0").unwrap())
249                    .esm_class(EsmClass::default())
250                    .registered_delivery(RegisteredDelivery::new(
251                        MCDeliveryReceipt::NoMcDeliveryReceiptRequested,
252                        SmeOriginatedAcknowledgement::SmeUserAcknowledgementRequested,
253                        IntermediateNotification::IntermediateNotificationRequested,
254                        0,
255                    ))
256                    .data_coding(DataCoding::Ucs2)
257                    .push_tlv(MessageSubmissionRequestTlvValue::SourceAddrSubunit(
258                        AddrSubunit::MobileEquipment,
259                    ))
260                    .unwrap()
261                    .push_tlv(MessageSubmissionRequestTlvValue::UssdServiceOp(
262                        UssdServiceOp::UssnConfirm,
263                    ))
264                    .unwrap()
265                    .build(),
266            ]
267        }
268    }
269
270    #[test]
271    fn encode_decode() {
272        crate::tests::borrowed::encode_decode_with_length_test_instances::<DataSm<'static, 16>>();
273    }
274}