1use rusmpp_macros::Rusmpp;
2
3use crate::{
4 encode::Length,
5 pdus::borrowed::Pdu,
6 tlvs::borrowed::{MessageSubmissionRequestTlvValue, Tlv},
7 types::borrowed::{COctetString, EmptyOrFullCOctetString, OctetString},
8 values::{borrowed::*, *},
9};
10
11#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Rusmpp)]
14#[rusmpp(decode = borrowed, test = skip)]
15#[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))]
16#[cfg_attr(feature = "serde", derive(::serde::Serialize))]
17pub struct SubmitSm<'a, const N: usize> {
18 pub service_type: ServiceType<'a>,
28 pub source_addr_ton: Ton,
30 pub source_addr_npi: Npi,
32 pub source_addr: COctetString<'a, 1, 21>,
34 pub dest_addr_ton: Ton,
36 pub dest_addr_npi: Npi,
38 pub destination_addr: COctetString<'a, 1, 21>,
44 pub esm_class: EsmClass,
47 pub protocol_id: u8,
50 pub priority_flag: PriorityFlag,
52 pub schedule_delivery_time: EmptyOrFullCOctetString<'a, 17>,
56 pub validity_period: EmptyOrFullCOctetString<'a, 17>,
62 pub registered_delivery: RegisteredDelivery,
65 pub replace_if_present_flag: ReplaceIfPresentFlag,
67 pub data_coding: DataCoding,
69 pub sm_default_msg_id: u8,
72 sm_length: u8,
74 #[rusmpp(length = sm_length)]
82 short_message: OctetString<'a, 0, 255>,
83 #[rusmpp(length = "unchecked")]
85 #[cfg_attr(feature = "arbitrary", arbitrary(default))]
86 tlvs: heapless::vec::Vec<Tlv<'a>, N>,
87}
88
89impl<'a, const N: usize> SubmitSm<'a, N> {
90 #[allow(clippy::too_many_arguments)]
91 pub fn new(
92 service_type: ServiceType<'a>,
93 source_addr_ton: Ton,
94 source_addr_npi: Npi,
95 source_addr: COctetString<'a, 1, 21>,
96 dest_addr_ton: Ton,
97 dest_addr_npi: Npi,
98 destination_addr: COctetString<'a, 1, 21>,
99 esm_class: EsmClass,
100 protocol_id: u8,
101 priority_flag: PriorityFlag,
102 schedule_delivery_time: EmptyOrFullCOctetString<'a, 17>,
103 validity_period: EmptyOrFullCOctetString<'a, 17>,
104 registered_delivery: RegisteredDelivery,
105 replace_if_present_flag: ReplaceIfPresentFlag,
106 data_coding: DataCoding,
107 sm_default_msg_id: u8,
108 short_message: OctetString<'a, 0, 255>,
109 tlvs: heapless::vec::Vec<MessageSubmissionRequestTlvValue<'a>, N>,
110 ) -> Self {
111 let tlvs = tlvs.into_iter().map(From::from).collect();
112
113 let sm_length = short_message.length() as u8;
114
115 Self {
116 service_type,
117 source_addr_ton,
118 source_addr_npi,
119 source_addr,
120 dest_addr_ton,
121 dest_addr_npi,
122 destination_addr,
123 esm_class,
124 protocol_id,
125 priority_flag,
126 schedule_delivery_time,
127 validity_period,
128 registered_delivery,
129 replace_if_present_flag,
130 data_coding,
131 sm_default_msg_id,
132 sm_length,
133 short_message,
134 tlvs,
135 }
136 }
137
138 pub fn sm_length(&self) -> u8 {
139 self.sm_length
140 }
141
142 pub fn short_message(&'_ self) -> &'_ OctetString<'_, 0, 255> {
143 &self.short_message
144 }
145
146 pub fn set_short_message(&mut self, short_message: OctetString<'a, 0, 255>) {
153 self.short_message = short_message;
154 self.sm_length = self.short_message.length() as u8;
155 }
156
157 pub fn tlvs(&'_ self) -> &'_ [Tlv<'_>] {
158 &self.tlvs
159 }
160
161 pub fn set_tlvs(&mut self, tlvs: heapless::vec::Vec<MessageSubmissionRequestTlvValue<'a>, N>) {
162 self.tlvs = tlvs.into_iter().map(From::from).collect();
163 }
164
165 pub fn clear_tlvs(&mut self) {
166 self.tlvs.clear();
167 }
168
169 pub fn push_tlv(
170 &mut self,
171 tlv: impl Into<MessageSubmissionRequestTlvValue<'a>>,
172 ) -> Result<(), Tlv<'a>> {
173 self.tlvs.push(Tlv::from(tlv.into()))?;
174 Ok(())
175 }
176
177 pub fn builder() -> SubmitSmBuilder<'a, N> {
178 SubmitSmBuilder::new()
179 }
180}
181
182impl<'a, const N: usize> From<SubmitSm<'a, N>> for Pdu<'a, N> {
183 fn from(value: SubmitSm<'a, N>) -> Self {
184 Self::SubmitSm(value)
185 }
186}
187
188#[derive(Debug, Default)]
189pub struct SubmitSmBuilder<'a, const N: usize> {
190 inner: SubmitSm<'a, N>,
191}
192
193impl<'a, const N: usize> SubmitSmBuilder<'a, N> {
194 pub fn new() -> Self {
195 Self::default()
196 }
197
198 pub fn service_type(mut self, service_type: ServiceType<'a>) -> Self {
199 self.inner.service_type = service_type;
200 self
201 }
202
203 pub fn source_addr_ton(mut self, source_addr_ton: Ton) -> Self {
204 self.inner.source_addr_ton = source_addr_ton;
205 self
206 }
207
208 pub fn source_addr_npi(mut self, source_addr_npi: Npi) -> Self {
209 self.inner.source_addr_npi = source_addr_npi;
210 self
211 }
212
213 pub fn source_addr(mut self, source_addr: COctetString<'a, 1, 21>) -> Self {
214 self.inner.source_addr = source_addr;
215 self
216 }
217
218 pub fn dest_addr_ton(mut self, dest_addr_ton: Ton) -> Self {
219 self.inner.dest_addr_ton = dest_addr_ton;
220 self
221 }
222
223 pub fn dest_addr_npi(mut self, dest_addr_npi: Npi) -> Self {
224 self.inner.dest_addr_npi = dest_addr_npi;
225 self
226 }
227
228 pub fn destination_addr(mut self, destination_addr: COctetString<'a, 1, 21>) -> Self {
229 self.inner.destination_addr = destination_addr;
230 self
231 }
232
233 pub fn esm_class(mut self, esm_class: EsmClass) -> Self {
234 self.inner.esm_class = esm_class;
235 self
236 }
237
238 pub fn protocol_id(mut self, protocol_id: u8) -> Self {
239 self.inner.protocol_id = protocol_id;
240 self
241 }
242
243 pub fn priority_flag(mut self, priority_flag: PriorityFlag) -> Self {
244 self.inner.priority_flag = priority_flag;
245 self
246 }
247
248 pub fn schedule_delivery_time(
249 mut self,
250 schedule_delivery_time: EmptyOrFullCOctetString<'a, 17>,
251 ) -> Self {
252 self.inner.schedule_delivery_time = schedule_delivery_time;
253 self
254 }
255
256 pub fn validity_period(mut self, validity_period: EmptyOrFullCOctetString<'a, 17>) -> Self {
257 self.inner.validity_period = validity_period;
258 self
259 }
260
261 pub fn registered_delivery(mut self, registered_delivery: RegisteredDelivery) -> Self {
262 self.inner.registered_delivery = registered_delivery;
263 self
264 }
265
266 pub fn replace_if_present_flag(
267 mut self,
268 replace_if_present_flag: ReplaceIfPresentFlag,
269 ) -> Self {
270 self.inner.replace_if_present_flag = replace_if_present_flag;
271 self
272 }
273
274 pub fn data_coding(mut self, data_coding: DataCoding) -> Self {
275 self.inner.data_coding = data_coding;
276 self
277 }
278
279 pub fn sm_default_msg_id(mut self, sm_default_msg_id: u8) -> Self {
280 self.inner.sm_default_msg_id = sm_default_msg_id;
281 self
282 }
283
284 pub fn short_message(mut self, short_message: OctetString<'a, 0, 255>) -> Self {
285 self.inner.set_short_message(short_message);
286 self
287 }
288
289 pub fn tlvs(
290 mut self,
291 tlvs: heapless::vec::Vec<MessageSubmissionRequestTlvValue<'a>, N>,
292 ) -> Self {
293 self.inner.set_tlvs(tlvs);
294 self
295 }
296
297 pub fn clear_tlvs(mut self) -> Self {
298 self.inner.clear_tlvs();
299 self
300 }
301
302 pub fn push_tlv(
303 mut self,
304 tlv: impl Into<MessageSubmissionRequestTlvValue<'a>>,
305 ) -> Result<Self, Tlv<'a>> {
306 self.inner.push_tlv(tlv)?;
307 Ok(self)
308 }
309
310 pub fn build(self) -> SubmitSm<'a, N> {
311 self.inner
312 }
313}
314
315#[cfg(test)]
316mod tests {
317 use crate::{tests::TestInstance, types::borrowed::AnyOctetString};
318
319 use super::*;
320
321 impl<const N: usize> TestInstance for SubmitSm<'static, N> {
322 fn instances() -> alloc::vec::Vec<Self> {
323 alloc::vec![
324 Self::default(),
325 Self::builder()
326 .service_type(ServiceType::new(
327 GenericServiceType::CellularMessaging.into(),
328 ))
329 .source_addr_ton(Ton::International)
330 .source_addr_npi(Npi::Isdn)
331 .source_addr(COctetString::new(b"Source Address\0").unwrap())
332 .dest_addr_ton(Ton::International)
333 .dest_addr_npi(Npi::Isdn)
334 .destination_addr(COctetString::new(b"Destination Address\0").unwrap())
335 .esm_class(EsmClass::new(
336 MessagingMode::StoreAndForward,
337 MessageType::ShortMessageContainsMCDeliveryReceipt,
338 Ansi41Specific::ShortMessageContainsDeliveryAcknowledgement,
339 GsmFeatures::SetUdhiAndReplyPath,
340 ))
341 .protocol_id(0)
342 .priority_flag(PriorityFlag::from(PriorityFlagType::from(Ansi136::Bulk)))
343 .schedule_delivery_time(
344 EmptyOrFullCOctetString::new(b"2023-09-01T12:00\0").unwrap(),
345 )
346 .validity_period(EmptyOrFullCOctetString::new(b"2023-10-01T12:00\0").unwrap())
347 .registered_delivery(RegisteredDelivery::request_all())
348 .replace_if_present_flag(ReplaceIfPresentFlag::Replace)
349 .data_coding(DataCoding::Ksc5601)
350 .sm_default_msg_id(69)
351 .short_message(OctetString::new(b"Short Message").unwrap())
352 .build(),
353 Self::builder()
354 .service_type(ServiceType::new(
355 GenericServiceType::CellularMessaging.into(),
356 ))
357 .source_addr_ton(Ton::International)
358 .source_addr_npi(Npi::Isdn)
359 .source_addr(COctetString::new(b"Source Address\0").unwrap())
360 .dest_addr_ton(Ton::International)
361 .dest_addr_npi(Npi::Isdn)
362 .destination_addr(COctetString::new(b"Destination Address\0").unwrap())
363 .esm_class(EsmClass::new(
364 MessagingMode::Default,
365 MessageType::ShortMessageContainsIntermediateDeliveryNotification,
366 Ansi41Specific::ShortMessageContainsUserAcknowledgment,
367 GsmFeatures::SetUdhiAndReplyPath,
368 ))
369 .protocol_id(0)
370 .priority_flag(PriorityFlag::from(PriorityFlagType::from(
371 Ansi136::VeryUrgent,
372 )))
373 .schedule_delivery_time(
374 EmptyOrFullCOctetString::new(b"2023-09-01T12:01\0").unwrap(),
375 )
376 .validity_period(EmptyOrFullCOctetString::new(b"2023-10-01T12:20\0").unwrap())
377 .registered_delivery(RegisteredDelivery::request_all())
378 .replace_if_present_flag(ReplaceIfPresentFlag::DoNotReplace)
379 .data_coding(DataCoding::Jis)
380 .sm_default_msg_id(96)
381 .short_message(OctetString::new(b"Short Message").unwrap())
382 .tlvs(
383 [MessageSubmissionRequestTlvValue::MessagePayload(
384 MessagePayload::new(AnyOctetString::new(b"Message Payload")),
385 )]
386 .into()
387 )
388 .build(),
389 Self::builder()
390 .short_message(OctetString::new(b"Short Message").unwrap())
391 .tlvs(
392 [
393 MessageSubmissionRequestTlvValue::MessagePayload(MessagePayload::new(
394 AnyOctetString::new(b"Message Payload"),
395 )),
396 MessageSubmissionRequestTlvValue::UserResponseCode(3),
397 MessageSubmissionRequestTlvValue::DestBearerType(
398 BearerType::FlexReFlex
399 ),
400 MessageSubmissionRequestTlvValue::SourceSubaddress(Subaddress::new(
401 SubaddressTag::NsapOdd,
402 OctetString::new(b"Subaddress :D\0").unwrap(),
403 )),
404 ]
405 .into()
406 )
407 .build(),
408 ]
409 }
410 }
411
412 #[test]
413 fn encode_decode() {
414 crate::tests::borrowed::encode_decode_with_length_test_instances::<SubmitSm<'static, 16>>();
415 }
416
417 #[test]
418 fn short_message_length() {
419 let short_message = OctetString::new(b"Short Message").unwrap();
420
421 let submit_sm = SubmitSm::<'static, 16>::builder()
422 .short_message(short_message.clone())
423 .build();
424
425 assert_eq!(submit_sm.short_message(), &short_message);
426 assert_eq!(submit_sm.sm_length(), short_message.length() as u8);
427 }
428}