rusmpp_core/command/
borrowed.rs

1use rusmpp_macros::Rusmpp;
2
3use crate::{CommandId, CommandStatus, pdus::borrowed::Pdu};
4
5/// `SMPP` command.
6///
7/// The following PDU example illustrates how a `SMPP` PDU is decoded:
8///
9/// Sample PDU (Values are shown in Hex format):
10///
11/// 00 00 00 2F 00 00 00 02 00 00 00 00 00 00 00 01
12///
13/// 53 4D 50 50 33 54 45 53 54 00 73 65 63 72 65 74
14///
15/// 30 38 00 53 55 42 4D 49 54 31 00 50 01 01 00
16///
17/// The 16-octet header would be decoded as follows:
18///
19/// | Octets | Description |
20/// | ------ | ----------- |
21/// | 00 00 00 2F | Command Length (47) |
22/// | 00 00 00 02 | Command ID (bind_transmitter) |
23/// | 00 00 00 00 | Command Status (0) |
24/// | 00 00 00 01 | Sequence Number (1)|
25///
26/// The remaining data represents the PDU body (which in this example relates to the
27/// bind_transmitter PDU). This is diagnosed as follows:
28///
29/// | Octets | Value |
30/// | ------ | ----- |
31/// | 53 4D 50 50 33 54 45 53 54 00 | system_id (“SMPP3TEST”) |
32/// | 73 65 63 72 65 74 30 38 00    | password (“secret08”) |
33/// | 53 55 42 4D 49 54 31 00       | system_type (“SUBMIT1”) |
34/// | 50                            | interface_version (0x50 “V5.0 compliant”) |
35/// | 01                            | addr_ton (0x01) |
36/// | 01                            | addr_npi (0x01) |
37/// | 00                            | addr_range (NULL) |
38#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Rusmpp)]
39#[rusmpp(decode = borrowed)]
40#[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))]
41#[cfg_attr(feature = "serde", derive(::serde::Serialize))]
42pub struct Command<'a, const N: usize> {
43    /// See [`CommandId`]
44    id: CommandId,
45    /// See [`CommandStatus`]
46    pub status: CommandStatus,
47    /// The sequence_number represents a means of uniquely
48    /// identifying each PDU within a `SMPP` session. It also provides a means of correlating request
49    /// and response PDUs based on matching sequence number.
50    pub sequence_number: u32,
51    /// See [`Pdu`]
52    ///
53    /// Optional because incoming commands may not have a PDU.
54    #[rusmpp(key = id, length = "unchecked")]
55    pdu: Option<Pdu<'a, N>>,
56}
57
58impl<'a, const N: usize> Default for Command<'a, N> {
59    fn default() -> Self {
60        Self {
61            id: CommandId::EnquireLink,
62            status: CommandStatus::EsmeRok,
63            sequence_number: 0,
64            pdu: Some(Pdu::EnquireLink),
65        }
66    }
67}
68
69impl<'a, const N: usize> Command<'a, N> {
70    pub fn new(status: CommandStatus, sequence_number: u32, pdu: impl Into<Pdu<'a, N>>) -> Self {
71        Self::new_const(status, sequence_number, pdu.into())
72    }
73
74    pub const fn new_const(status: CommandStatus, sequence_number: u32, pdu: Pdu<'a, N>) -> Self {
75        let id = pdu.command_id();
76
77        Self {
78            id,
79            status,
80            sequence_number,
81            pdu: Some(pdu),
82        }
83    }
84
85    #[inline]
86    pub const fn id(&self) -> CommandId {
87        self.id
88    }
89
90    #[inline]
91    pub const fn status(&self) -> CommandStatus {
92        self.status
93    }
94
95    #[inline]
96    pub const fn sequence_number(&self) -> u32 {
97        self.sequence_number
98    }
99
100    #[inline]
101    pub const fn pdu(&self) -> Option<&Pdu<'a, N>> {
102        self.pdu.as_ref()
103    }
104
105    #[inline]
106    pub fn set_pdu(&mut self, pdu: impl Into<Pdu<'a, N>>) {
107        let pdu = pdu.into();
108
109        self.id = pdu.command_id();
110
111        self.pdu = Some(pdu);
112    }
113
114    #[inline]
115    pub fn builder() -> CommandStatusBuilder<'a, N> {
116        Default::default()
117    }
118}
119
120#[derive(Debug, Default)]
121pub struct CommandStatusBuilder<'a, const N: usize> {
122    inner: Command<'a, N>,
123}
124
125impl<'a, const N: usize> CommandStatusBuilder<'a, N> {
126    #[inline]
127    pub fn status(mut self, status: CommandStatus) -> SequenceNumberBuilder<'a, N> {
128        self.inner.status = status;
129
130        SequenceNumberBuilder { inner: self.inner }
131    }
132}
133
134#[derive(Debug)]
135pub struct SequenceNumberBuilder<'a, const N: usize> {
136    inner: Command<'a, N>,
137}
138
139impl<'a, const N: usize> SequenceNumberBuilder<'a, N> {
140    #[inline]
141    pub fn sequence_number(mut self, sequence_number: u32) -> PduBuilder<'a, N> {
142        self.inner.sequence_number = sequence_number;
143
144        PduBuilder { inner: self.inner }
145    }
146}
147
148#[derive(Debug)]
149pub struct PduBuilder<'a, const N: usize> {
150    inner: Command<'a, N>,
151}
152
153impl<'a, const N: usize> PduBuilder<'a, N> {
154    #[inline]
155    pub fn pdu(mut self, pdu: impl Into<Pdu<'a, N>>) -> Command<'a, N> {
156        self.inner.set_pdu(pdu);
157        self.inner
158    }
159}
160
161#[cfg(test)]
162mod tests {
163    use super::*;
164
165    #[test]
166    fn encode_decode() {
167        crate::tests::borrowed::encode_decode_with_length_test_instances::<Command<'static, 16>>();
168    }
169}