Skip to main content

smpp_codec/pdus/session_pdus/
alert_notification.rs

1use crate::common::{Npi, PduError, Ton, CMD_ALERT_NOTIFICATION, HEADER_LEN};
2use crate::tlv::Tlv;
3use std::io::{Cursor, Read, Write};
4
5/// Represents an Alert Notification PDU.
6///
7/// Sent by the SMSC to the ESME to provide information about a message state (e.g., delivered).
8#[derive(Debug, Clone)]
9pub struct AlertNotification {
10    pub sequence_number: u32,
11    pub source_addr_ton: Ton,
12    pub source_addr_npi: Npi,
13    pub source_addr: String, // Max 65
14    pub esme_addr_ton: Ton,
15    pub esme_addr_npi: Npi,
16    pub esme_addr: String, // Max 65
17    pub optional_params: Vec<Tlv>,
18}
19
20impl AlertNotification {
21    /// Create a new Alert Notification.
22    ///
23    /// # Examples
24    ///
25    /// ```
26    /// use smpp_codec::pdus::AlertNotification;
27    ///
28    /// let sequence_number: u32 = 1;
29    /// let alert = AlertNotification::new(
30    ///     sequence_number,
31    ///     "source_addr".to_string(),
32    ///     "esme_addr".to_string()
33    /// );
34    /// ```
35    pub fn new(sequence_number: u32, source_addr: String, esme_addr: String) -> Self {
36        Self {
37            sequence_number,
38            source_addr_ton: Ton::Unknown,
39            source_addr_npi: Npi::Unknown,
40            source_addr,
41            esme_addr_ton: Ton::Unknown,
42            esme_addr_npi: Npi::Unknown,
43            esme_addr,
44            optional_params: Vec::new(),
45        }
46    }
47
48    // Builder for TON/NPI
49    pub fn with_source_addr(mut self, ton: Ton, npi: Npi, addr: String) -> Self {
50        self.source_addr_ton = ton;
51        self.source_addr_npi = npi;
52        self.source_addr = addr;
53        self
54    }
55
56    pub fn with_esme_addr(mut self, ton: Ton, npi: Npi, addr: String) -> Self {
57        self.esme_addr_ton = ton;
58        self.esme_addr_npi = npi;
59        self.esme_addr = addr;
60        self
61    }
62
63    /// Add a generic TLV
64    pub fn add_tlv(&mut self, tlv: Tlv) {
65        self.optional_params.push(tlv);
66    }
67
68    /// Encode the struct into raw bytes for the network.
69    ///
70    /// # Errors
71    ///
72    /// Returns a [`PduError`] if:
73    /// * `source_addr` exceeds 65 characters.
74    /// * `esme_addr` exceeds 65 characters.
75    /// * An I/O error occurs while writing.
76    ///
77    /// # Examples
78    ///
79    /// ```
80    /// # use smpp_codec::pdus::AlertNotification;
81    /// # let sequence_number: u32 = 1;
82    /// # let alert = AlertNotification::new(sequence_number, "src".into(), "dst".into());
83    /// let mut buffer = Vec::new();
84    /// alert.encode(&mut buffer).expect("Encoding failed");
85    /// ```
86    pub fn encode(&self, writer: &mut impl Write) -> Result<(), PduError> {
87        // Validate lengths
88        if self.source_addr.len() > 65 {
89            return Err(PduError::StringTooLong("source_addr".into(), 65));
90        }
91        if self.esme_addr.len() > 65 {
92            return Err(PduError::StringTooLong("esme_addr".into(), 65));
93        }
94
95        let mut body = Vec::new();
96
97        // Source Address
98        body.write_all(&[self.source_addr_ton as u8, self.source_addr_npi as u8])?;
99        write_c_string(&mut body, &self.source_addr)?;
100
101        // ESME Address
102        body.write_all(&[self.esme_addr_ton as u8, self.esme_addr_npi as u8])?;
103        write_c_string(&mut body, &self.esme_addr)?;
104
105        // Optional Params
106        // [Change] Simplified TLV Encoding
107        for tlv in &self.optional_params {
108            tlv.encode(&mut body)?;
109        }
110
111        // Header
112        let command_len = (HEADER_LEN + body.len()) as u32;
113        writer.write_all(&command_len.to_be_bytes())?;
114        writer.write_all(&CMD_ALERT_NOTIFICATION.to_be_bytes())?;
115        writer.write_all(&0u32.to_be_bytes())?;
116        writer.write_all(&self.sequence_number.to_be_bytes())?;
117
118        // Body
119        writer.write_all(&body)?;
120
121        Ok(())
122    }
123
124    /// Decode raw bytes from the network into the struct.
125    ///
126    /// # Errors
127    ///
128    /// Returns a [`PduError`] if:
129    /// * The buffer is too short.
130    /// * The buffer data is malformed.
131    ///
132    /// # Examples
133    ///
134    /// ```
135    /// # use smpp_codec::pdus::AlertNotification;
136    /// # let sequence_number: u32 = 1;
137    /// # let alert = AlertNotification::new(sequence_number, "src".into(), "dst".into());
138    /// # let mut buffer = Vec::new();
139    /// # alert.encode(&mut buffer).unwrap();
140    /// let decoded = AlertNotification::decode(&buffer).expect("Decoding failed");
141    /// assert_eq!(decoded.source_addr, "src");
142    /// ```
143    pub fn decode(buffer: &[u8]) -> Result<Self, PduError> {
144        if buffer.len() < HEADER_LEN {
145            return Err(PduError::BufferTooShort);
146        }
147
148        let mut cursor = Cursor::new(buffer);
149        // Skip Header
150        cursor.set_position(12);
151        let mut bytes = [0u8; 4];
152        cursor.read_exact(&mut bytes)?;
153        let sequence_number = u32::from_be_bytes(bytes);
154
155        // 3. Read Body
156        let mut u8_buf = [0u8; 1];
157
158        cursor.read_exact(&mut u8_buf)?;
159        let source_addr_ton = Ton::from(u8_buf[0]);
160        cursor.read_exact(&mut u8_buf)?;
161        let source_addr_npi = Npi::from(u8_buf[0]);
162        let source_addr = crate::common::read_c_string(&mut cursor)?;
163
164        cursor.read_exact(&mut u8_buf)?;
165        let esme_addr_ton = Ton::from(u8_buf[0]);
166        cursor.read_exact(&mut u8_buf)?;
167        let esme_addr_npi = Npi::from(u8_buf[0]);
168        let esme_addr = crate::common::read_c_string(&mut cursor)?;
169
170        // Optional Params (TLVs)
171        let mut optional_params = Vec::new();
172        while let Some(tlv) = Tlv::decode(&mut cursor)? {
173            optional_params.push(tlv);
174        }
175
176        Ok(Self {
177            sequence_number,
178            source_addr_ton,
179            source_addr_npi,
180            source_addr,
181            esme_addr_ton,
182            esme_addr_npi,
183            esme_addr,
184            optional_params,
185        })
186    }
187}
188
189// Helpers
190fn write_c_string(w: &mut impl Write, s: &str) -> std::io::Result<()> {
191    w.write_all(s.as_bytes())?;
192    w.write_all(&[0])
193}