rusmpp_core/pdus/borrowed/
broadcast_sm.rs

1use rusmpp_macros::Rusmpp;
2
3use crate::{
4    pdus::borrowed::Pdu,
5    tlvs::borrowed::{BroadcastRequestTlvValue, Tlv},
6    types::borrowed::{COctetString, EmptyOrFullCOctetString},
7    values::{borrowed::*, *},
8};
9
10/// This operation is issued by the ESME to submit a message to the Message Centre for
11/// broadcast to a specified geographical area or set of geographical areas.
12#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Default, Rusmpp)]
13#[rusmpp(decode = borrowed, test = skip)]
14#[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))]
15#[cfg_attr(feature = "serde", derive(::serde::Serialize))]
16pub struct BroadcastSm<'a, const N: usize> {
17    /// The service_type parameter can be used to
18    /// indicate the SMS Application service
19    /// associated with the message. Specifying the
20    /// service_type allows the ESME to avail of enhanced
21    /// messaging services such as “replace by
22    /// service_type” or control the teleservice used on the air
23    /// interface.
24    ///
25    /// Set to NULL for default MC settings.
26    pub service_type: ServiceType<'a>,
27    /// Type of Number for source address.
28    ///
29    /// If not known, set to NULL (Unknown).
30    pub source_addr_ton: Ton,
31    /// Numbering Plan Indicator for source address.
32    ///
33    /// If not known, set to NULL (Unknown).
34    pub source_addr_npi: Npi,
35    /// Address of SME which originated this message.
36    ///
37    /// If not known, set to NULL (Unknown).
38    pub source_addr: COctetString<'a, 1, 21>,
39    /// If using broadcast_sm to replace a message,
40    /// previously submitted for broadcast, then set
41    /// message_id to the MC assigned message ID
42    /// allocated to the original message and returned in
43    /// the broadcast_sm_resp  (to the original broadcast_sm request).
44    ///
45    /// Note: For "broadcast replace", either the message_id or the
46    /// user_message_reference field should be used. Both
47    /// fields must not be used simultaneously.
48    /// Set to NULL:
49    ///
50    /// * if not using MC message ID in broadcast_sm to
51    ///   replace a message, previously submitted for broadcast.
52    /// * if setting user_message_reference TLV.
53    pub message_id: COctetString<'a, 1, 65>,
54    /// Designates the propriety level of the message.
55    pub priority_flag: PriorityFlag,
56    /// The short message is to be scheduled by the MC for
57    /// delivery.
58    ///
59    /// Set to NULL for immediate message broadcast.
60    pub schedule_delivery_time: EmptyOrFullCOctetString<'a, 17>,
61    /// The validity period of this message.
62    ///
63    /// Set to NULL to specify that a ‘broadcast_rep_num’
64    /// parameter and a ‘broadcast_frequency_interval’
65    /// parameter have been specified from which a
66    /// default value should be derived.
67    pub validity_period: EmptyOrFullCOctetString<'a, 17>,
68    /// Flag indicating if the submitted message should
69    /// replace an existing message which has:
70    /// * (1) MC message ID matching the ID supplied in
71    ///   the message_id field.
72    /// * (2) or ESME assigned message reference number
73    ///   supplied in the user_message_reference field.
74    pub replace_if_present_flag: ReplaceIfPresentFlag,
75    /// Defines the encoding scheme of the short
76    /// message user data.
77    pub data_coding: DataCoding,
78    /// Indicates the short message to send from a list of pre-
79    /// defined (‘canned’) short messages stored on the MC.
80    ///
81    /// If not using a MC canned message, set to NULL.
82    pub sm_default_msg_id: u8,
83    /// Broadcast request TLVs ([`BroadcastRequestTlvValue`]).
84    #[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> BroadcastSm<'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        message_id: COctetString<'a, 1, 65>,
97        priority_flag: PriorityFlag,
98        schedule_delivery_time: EmptyOrFullCOctetString<'a, 17>,
99        validity_period: EmptyOrFullCOctetString<'a, 17>,
100        replace_if_present_flag: ReplaceIfPresentFlag,
101        data_coding: DataCoding,
102        sm_default_msg_id: u8,
103        tlvs: heapless::vec::Vec<impl Into<BroadcastRequestTlvValue<'a>>, N>,
104    ) -> Self {
105        let tlvs = tlvs.into_iter().map(Into::into).map(From::from).collect();
106
107        Self {
108            service_type,
109            source_addr_ton,
110            source_addr_npi,
111            source_addr,
112            message_id,
113            priority_flag,
114            schedule_delivery_time,
115            validity_period,
116            replace_if_present_flag,
117            data_coding,
118            sm_default_msg_id,
119            tlvs,
120        }
121    }
122
123    pub fn tlvs(&'_ self) -> &'_ [Tlv<'_>] {
124        &self.tlvs
125    }
126
127    pub fn set_tlvs(
128        &mut self,
129        tlvs: heapless::vec::Vec<impl Into<BroadcastRequestTlvValue<'a>>, N>,
130    ) {
131        self.tlvs = tlvs.into_iter().map(Into::into).map(From::from).collect();
132    }
133
134    pub fn clear_tlvs(&mut self) {
135        self.tlvs.clear();
136    }
137
138    pub fn push_tlv(
139        &mut self,
140        tlv: impl Into<BroadcastRequestTlvValue<'a>>,
141    ) -> Result<(), Tlv<'a>> {
142        self.tlvs.push(Tlv::from(tlv.into()))?;
143        Ok(())
144    }
145
146    pub fn builder() -> BroadcastSmBuilder<'a, N> {
147        BroadcastSmBuilder::new()
148    }
149}
150
151impl<'a, const N: usize> From<BroadcastSm<'a, N>> for Pdu<'a, N> {
152    fn from(value: BroadcastSm<'a, N>) -> Self {
153        Self::BroadcastSm(value)
154    }
155}
156
157#[derive(Debug, Default)]
158pub struct BroadcastSmBuilder<'a, const N: usize> {
159    inner: BroadcastSm<'a, N>,
160}
161
162impl<'a, const N: usize> BroadcastSmBuilder<'a, N> {
163    pub fn new() -> Self {
164        Self::default()
165    }
166
167    pub fn service_type(mut self, service_type: ServiceType<'a>) -> Self {
168        self.inner.service_type = service_type;
169        self
170    }
171
172    pub fn source_addr_ton(mut self, source_addr_ton: Ton) -> Self {
173        self.inner.source_addr_ton = source_addr_ton;
174        self
175    }
176
177    pub fn source_addr_npi(mut self, source_addr_npi: Npi) -> Self {
178        self.inner.source_addr_npi = source_addr_npi;
179        self
180    }
181
182    pub fn source_addr(mut self, source_addr: COctetString<'a, 1, 21>) -> Self {
183        self.inner.source_addr = source_addr;
184        self
185    }
186
187    pub fn message_id(mut self, message_id: COctetString<'a, 1, 65>) -> Self {
188        self.inner.message_id = message_id;
189        self
190    }
191
192    pub fn priority_flag(mut self, priority_flag: PriorityFlag) -> Self {
193        self.inner.priority_flag = priority_flag;
194        self
195    }
196
197    pub fn schedule_delivery_time(
198        mut self,
199        schedule_delivery_time: EmptyOrFullCOctetString<'a, 17>,
200    ) -> Self {
201        self.inner.schedule_delivery_time = schedule_delivery_time;
202        self
203    }
204
205    pub fn validity_period(mut self, validity_period: EmptyOrFullCOctetString<'a, 17>) -> Self {
206        self.inner.validity_period = validity_period;
207        self
208    }
209
210    pub fn replace_if_present_flag(
211        mut self,
212        replace_if_present_flag: ReplaceIfPresentFlag,
213    ) -> Self {
214        self.inner.replace_if_present_flag = replace_if_present_flag;
215        self
216    }
217
218    pub fn data_coding(mut self, data_coding: DataCoding) -> Self {
219        self.inner.data_coding = data_coding;
220        self
221    }
222
223    pub fn sm_default_msg_id(mut self, sm_default_msg_id: u8) -> Self {
224        self.inner.sm_default_msg_id = sm_default_msg_id;
225        self
226    }
227
228    pub fn tlvs(
229        mut self,
230        tlvs: heapless::vec::Vec<impl Into<BroadcastRequestTlvValue<'a>>, N>,
231    ) -> Self {
232        self.inner.set_tlvs(tlvs);
233        self
234    }
235
236    pub fn clear_tlvs(mut self) -> Self {
237        self.inner.clear_tlvs();
238        self
239    }
240
241    pub fn push_tlv(
242        mut self,
243        tlv: impl Into<BroadcastRequestTlvValue<'a>>,
244    ) -> Result<Self, Tlv<'a>> {
245        self.inner.push_tlv(tlv)?;
246        Ok(self)
247    }
248
249    pub fn build(self) -> BroadcastSm<'a, N> {
250        self.inner
251    }
252}
253
254#[cfg(test)]
255mod tests {
256    use crate::{tests::TestInstance, types::borrowed::OctetString};
257
258    use super::*;
259
260    impl<const N: usize> TestInstance for BroadcastSm<'_, N> {
261        fn instances() -> alloc::vec::Vec<Self> {
262            alloc::vec![
263                Self::default(),
264                Self::builder()
265                    .service_type(ServiceType::new(
266                        GenericServiceType::CellularMessaging.into(),
267                    ))
268                    .source_addr_ton(Ton::International)
269                    .source_addr_npi(Npi::Isdn)
270                    .source_addr(COctetString::new(b"SourceAddr\0").unwrap())
271                    .message_id(COctetString::new(b"MessageId\0").unwrap())
272                    .priority_flag(PriorityFlag::from(PriorityFlagType::from(GsmSms::from(1))))
273                    .schedule_delivery_time(EmptyOrFullCOctetString::empty())
274                    .validity_period(EmptyOrFullCOctetString::empty())
275                    .replace_if_present_flag(ReplaceIfPresentFlag::Replace)
276                    .data_coding(DataCoding::LatinHebrew)
277                    .sm_default_msg_id(0)
278                    .build(),
279                Self::builder()
280                    .service_type(ServiceType::new(
281                        GenericServiceType::UnstructuredSupplementaryServicesData.into(),
282                    ))
283                    .source_addr_ton(Ton::International)
284                    .source_addr_npi(Npi::Isdn)
285                    .source_addr(COctetString::new(b"SourceAddr\0").unwrap())
286                    .message_id(COctetString::new(b"MessageId\0").unwrap())
287                    .priority_flag(PriorityFlag::from(PriorityFlagType::from(Ansi136::Bulk)))
288                    .schedule_delivery_time(
289                        EmptyOrFullCOctetString::new(b"2023-10-01T00:00\0").unwrap(),
290                    )
291                    .validity_period(EmptyOrFullCOctetString::empty())
292                    .replace_if_present_flag(ReplaceIfPresentFlag::DoNotReplace)
293                    .data_coding(DataCoding::GsmMessageClassControl)
294                    .sm_default_msg_id(255)
295                    .tlvs(
296                        [
297                            BroadcastRequestTlvValue::CallbackNum(
298                                OctetString::new(b"1234567890").unwrap(),
299                            ),
300                            BroadcastRequestTlvValue::LanguageIndicator(LanguageIndicator::German),
301                            BroadcastRequestTlvValue::SmsSignal(1024),
302                        ]
303                        .into()
304                    )
305                    .build(),
306            ]
307        }
308    }
309
310    #[test]
311    fn encode_decode() {
312        crate::tests::borrowed::encode_decode_with_length_test_instances::<BroadcastSm<'static, 16>>(
313        );
314    }
315}