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<impl Into<MessageSubmissionRequestTlvValue<'a>>, N>,
84    ) -> Self {
85        let tlvs = tlvs.into_iter().map(Into::into).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(
107        &mut self,
108        tlvs: heapless::vec::Vec<impl Into<MessageSubmissionRequestTlvValue<'a>>, N>,
109    ) {
110        self.tlvs = tlvs.into_iter().map(Into::into).map(From::from).collect();
111    }
112
113    pub fn clear_tlvs(&mut self) {
114        self.tlvs.clear();
115    }
116
117    pub fn push_tlv(
118        &mut self,
119        tlv: impl Into<MessageSubmissionRequestTlvValue<'a>>,
120    ) -> Result<(), Tlv<'a>> {
121        self.tlvs.push(Tlv::from(tlv.into()))?;
122        Ok(())
123    }
124
125    pub fn builder() -> DataSmBuilder<'a, N> {
126        DataSmBuilder::new()
127    }
128}
129
130impl<'a, const N: usize> From<DataSm<'a, N>> for Pdu<'a, N> {
131    fn from(value: DataSm<'a, N>) -> Self {
132        Self::DataSm(value)
133    }
134}
135
136#[derive(Debug, Default)]
137pub struct DataSmBuilder<'a, const N: usize> {
138    inner: DataSm<'a, N>,
139}
140
141impl<'a, const N: usize> DataSmBuilder<'a, N> {
142    pub fn new() -> Self {
143        Default::default()
144    }
145
146    pub fn service_type(mut self, service_type: ServiceType<'a>) -> Self {
147        self.inner.service_type = service_type;
148        self
149    }
150
151    pub fn source_addr_ton(mut self, source_addr_ton: Ton) -> Self {
152        self.inner.source_addr_ton = source_addr_ton;
153        self
154    }
155
156    pub fn source_addr_npi(mut self, source_addr_npi: Npi) -> Self {
157        self.inner.source_addr_npi = source_addr_npi;
158        self
159    }
160
161    pub fn source_addr(mut self, source_addr: COctetString<'a, 1, 21>) -> Self {
162        self.inner.source_addr = source_addr;
163        self
164    }
165
166    pub fn dest_addr_ton(mut self, dest_addr_ton: Ton) -> Self {
167        self.inner.dest_addr_ton = dest_addr_ton;
168        self
169    }
170
171    pub fn dest_addr_npi(mut self, dest_addr_npi: Npi) -> Self {
172        self.inner.dest_addr_npi = dest_addr_npi;
173        self
174    }
175
176    pub fn destination_addr(mut self, destination_addr: COctetString<'a, 1, 21>) -> Self {
177        self.inner.destination_addr = destination_addr;
178        self
179    }
180
181    pub fn esm_class(mut self, esm_class: EsmClass) -> Self {
182        self.inner.esm_class = esm_class;
183        self
184    }
185
186    pub fn registered_delivery(mut self, registered_delivery: RegisteredDelivery) -> Self {
187        self.inner.registered_delivery = registered_delivery;
188        self
189    }
190
191    pub fn data_coding(mut self, data_coding: DataCoding) -> Self {
192        self.inner.data_coding = data_coding;
193        self
194    }
195
196    pub fn tlvs(
197        mut self,
198        tlvs: heapless::vec::Vec<impl Into<MessageSubmissionRequestTlvValue<'a>>, N>,
199    ) -> Self {
200        self.inner.set_tlvs(tlvs);
201        self
202    }
203
204    pub fn clear_tlvs(mut self) -> Self {
205        self.inner.clear_tlvs();
206        self
207    }
208
209    pub fn push_tlv(
210        mut self,
211        tlv: impl Into<MessageSubmissionRequestTlvValue<'a>>,
212    ) -> Result<Self, Tlv<'a>> {
213        self.inner.push_tlv(tlv)?;
214        Ok(self)
215    }
216
217    pub fn build(self) -> DataSm<'a, N> {
218        self.inner
219    }
220}
221
222#[cfg(test)]
223mod tests {
224    use crate::tests::TestInstance;
225
226    use super::*;
227
228    impl<const N: usize> TestInstance for DataSm<'_, N> {
229        fn instances() -> alloc::vec::Vec<Self> {
230            alloc::vec![
231                Self::default(),
232                Self::builder()
233                    .service_type(ServiceType::default())
234                    .source_addr_ton(Ton::International)
235                    .source_addr_npi(Npi::Isdn)
236                    .source_addr(COctetString::new(b"source_addr\0").unwrap())
237                    .dest_addr_ton(Ton::International)
238                    .dest_addr_npi(Npi::Isdn)
239                    .destination_addr(COctetString::new(b"destination_addr\0").unwrap())
240                    .esm_class(EsmClass::default())
241                    .registered_delivery(RegisteredDelivery::request_all())
242                    .data_coding(DataCoding::Ucs2)
243                    .build(),
244                Self::builder()
245                    .service_type(ServiceType::default())
246                    .source_addr_ton(Ton::International)
247                    .source_addr_npi(Npi::Isdn)
248                    .source_addr(COctetString::new(b"source_addr\0").unwrap())
249                    .dest_addr_ton(Ton::International)
250                    .dest_addr_npi(Npi::Isdn)
251                    .destination_addr(COctetString::new(b"destination_addr\0").unwrap())
252                    .esm_class(EsmClass::default())
253                    .registered_delivery(RegisteredDelivery::new(
254                        MCDeliveryReceipt::NoMcDeliveryReceiptRequested,
255                        SmeOriginatedAcknowledgement::SmeUserAcknowledgementRequested,
256                        IntermediateNotification::IntermediateNotificationRequested,
257                        0,
258                    ))
259                    .data_coding(DataCoding::Ucs2)
260                    .push_tlv(MessageSubmissionRequestTlvValue::SourceAddrSubunit(
261                        AddrSubunit::MobileEquipment,
262                    ))
263                    .unwrap()
264                    .push_tlv(MessageSubmissionRequestTlvValue::UssdServiceOp(
265                        UssdServiceOp::UssnConfirm,
266                    ))
267                    .unwrap()
268                    .build(),
269            ]
270        }
271    }
272
273    #[test]
274    fn encode_decode() {
275        crate::tests::borrowed::encode_decode_with_length_test_instances::<DataSm<'static, 16>>();
276    }
277}