rusmpp_core/
command_id.rs

1use rusmpp_macros::Rusmpp;
2
3/// Identifies the `SMPP` operation e.g. submit_sm, bind_transmitter etc.
4///
5/// The [`CommandId`] is encoded as a 4-octet integer value.
6///
7/// [`CommandId`]s for request PDUs are allocated from a range of numbers; 0x00000000 to
8/// 0x000001FF.
9///
10/// [`CommandId`]s for response PDUs are allocated from a range of numbers; 0x80000000 to
11/// 0x800001FF.
12///
13/// The relationship between the [`CommandId`] for a request PDU and its associated response
14/// PDU is that bit 31 is cleared for the request and set for the response. For example,
15/// replace_sm has a [`CommandId`] = 0x00000007 and its’ response PDU replace_sm_resp has
16/// a [`CommandId`] = 0x80000007.
17#[repr(u32)]
18#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Rusmpp)]
19/// XXX: test impl is skipped because we have no default impl for CommandId
20#[rusmpp(test = skip)]
21#[cfg_attr(test, derive(strum_macros::EnumIter))]
22#[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))]
23#[cfg_attr(feature = "serde", derive(::serde::Serialize))]
24#[cfg_attr(feature = "serde-deserialize-unchecked", derive(::serde::Deserialize))]
25pub enum CommandId {
26    BindReceiver = 0x00000001,
27    BindTransmitter = 0x00000002,
28    QuerySm = 0x00000003,
29    SubmitSm = 0x00000004,
30    DeliverSm = 0x00000005,
31    Unbind = 0x00000006,
32    ReplaceSm = 0x00000007,
33    CancelSm = 0x00000008,
34    BindTransceiver = 0x00000009,
35    Outbind = 0x0000000B,
36    EnquireLink = 0x00000015,
37    SubmitMulti = 0x00000021,
38    AlertNotification = 0x00000102,
39    DataSm = 0x00000103,
40    BroadcastSm = 0x00000111,
41    QueryBroadcastSm = 0x00000112,
42    CancelBroadcastSm = 0x00000113,
43    GenericNack = 0x80000000,
44    BindReceiverResp = 0x80000001,
45    BindTransmitterResp = 0x80000002,
46    QuerySmResp = 0x80000003,
47    SubmitSmResp = 0x80000004,
48    DeliverSmResp = 0x80000005,
49    UnbindResp = 0x80000006,
50    ReplaceSmResp = 0x80000007,
51    CancelSmResp = 0x80000008,
52    BindTransceiverResp = 0x80000009,
53    EnquireLinkResp = 0x80000015,
54    SubmitMultiResp = 0x80000021,
55    DataSmResp = 0x80000103,
56    BroadcastSmResp = 0x80000111,
57    QueryBroadcastSmResp = 0x80000112,
58    CancelBroadcastSmResp = 0x80000113,
59    Other(u32),
60}
61
62impl CommandId {
63    /// Returns true if this [`CommandId`] represents a request PDU.
64    pub fn is_operation(self) -> bool {
65        let id: u32 = self.into();
66        id & 0x80000000 == 0x00000000
67    }
68
69    /// Returns true if this [`CommandId`] represents a response PDU.
70    pub fn is_response(self) -> bool {
71        let id: u32 = self.into();
72        id & 0x80000000 == 0x80000000
73    }
74
75    /// Returns the matching request [`CommandId`]
76    ///
77    /// Note that this function should be used only on response Ids.
78    pub fn matching_request(self) -> CommandId {
79        let id: u32 = self.into();
80        (id & 0x0FFFFFFF).into()
81    }
82
83    /// Returns the matching response [`CommandId`]
84    ///
85    /// Note that this function should be used only on request Ids.
86    /// If the command does not have a response, then it will return [`CommandId::Other`].
87    pub fn matching_response(self) -> CommandId {
88        let id: u32 = self.into();
89        (id | 0x80000000).into()
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    use crate::tests::TestInstance;
96
97    use super::*;
98
99    impl TestInstance for CommandId {
100        fn instances() -> alloc::vec::Vec<Self> {
101            alloc::vec![
102                CommandId::BindReceiver,
103                CommandId::BindTransmitter,
104                CommandId::DataSmResp,
105                CommandId::Other(0x00000010),
106            ]
107        }
108    }
109
110    #[test]
111    fn into() {
112        let id: u32 = CommandId::BindReceiver.into();
113        assert_eq!(id, 0x00000001);
114
115        let id: u32 = CommandId::Other(0x00000115).into();
116        assert_eq!(id, 0x00000115);
117    }
118
119    #[test]
120    fn from() {
121        let id = CommandId::from(0x00000001);
122        assert_eq!(id, CommandId::BindReceiver);
123
124        let id = CommandId::from(0x00000115);
125        assert_eq!(id, CommandId::Other(0x00000115));
126    }
127
128    #[test]
129    fn is_operation() {
130        assert!(CommandId::BindReceiver.is_operation());
131        assert!(CommandId::Outbind.is_operation());
132        assert!(!CommandId::BindReceiverResp.is_operation());
133    }
134
135    #[test]
136    fn is_response() {
137        assert!(!CommandId::Outbind.is_response());
138        assert!(!CommandId::SubmitSm.is_response());
139        assert!(CommandId::SubmitSmResp.is_response());
140    }
141
142    #[test]
143    fn get_matching_request() {
144        assert_eq!(
145            CommandId::BroadcastSmResp.matching_request(),
146            CommandId::BroadcastSm
147        );
148    }
149
150    #[test]
151    fn get_matching_response() {
152        assert_eq!(
153            CommandId::BroadcastSm.matching_response(),
154            CommandId::BroadcastSmResp
155        );
156        assert_eq!(
157            CommandId::Outbind.matching_response(),
158            CommandId::Other(0x8000000B)
159        );
160    }
161
162    #[test]
163    fn encode_decode() {
164        #[cfg(feature = "alloc")]
165        crate::tests::owned::encode_decode_test_instances::<CommandId>();
166        crate::tests::borrowed::encode_decode_test_instances::<CommandId>();
167    }
168}