pk_command/
types.rs

1use crate::util::msg_id;
2
3/// Defines the set of operations supported by the PK Command protocol.
4#[derive(PartialEq, Eq, Clone, Copy, Debug)]
5pub enum Operation {
6    SendVariable,    // SENDV
7    RequireVariable, // REQUV
8    Invoke,          // INVOK
9    GetVersion,      // PKVER
10    Start,           // START
11    EndTransaction,  // ENDTR
12    Acknowledge,     // ACKNO
13    Query,           // QUERY
14    Return,          // RTURN
15    Empty,           // EMPTY
16    Data,            // SDATA
17    Await,           // AWAIT
18    Error,           // ERROR
19}
20
21impl Operation {
22    /// Returns the 5-character string representation of the operation.
23    pub fn to_name(&self) -> &'static str {
24        match self {
25            Operation::SendVariable => "SENDV",
26            Operation::RequireVariable => "REQUV",
27            Operation::Invoke => "INVOK",
28            Operation::GetVersion => "PKVER",
29            Operation::Start => "START",
30            Operation::EndTransaction => "ENDTR",
31            Operation::Acknowledge => "ACKNO",
32            Operation::Query => "QUERY",
33            Operation::Return => "RTURN",
34            Operation::Empty => "EMPTY",
35            Operation::Data => "SDATA",
36            Operation::Await => "AWAIT",
37            Operation::Error => "ERROR",
38        }
39    }
40
41    /// Creates an `Operation` from its 5-character string representation.
42    pub fn from_name(name: &str) -> Option<Operation> {
43        match name {
44            "SENDV" => Some(Operation::SendVariable),
45            "REQUV" => Some(Operation::RequireVariable),
46            "INVOK" => Some(Operation::Invoke),
47            "PKVER" => Some(Operation::GetVersion),
48            "START" => Some(Operation::Start),
49            "ENDTR" => Some(Operation::EndTransaction),
50            "ACKNO" => Some(Operation::Acknowledge),
51            "QUERY" => Some(Operation::Query),
52            "RTURN" => Some(Operation::Return),
53            "EMPTY" => Some(Operation::Empty),
54            "SDATA" => Some(Operation::Data),
55            "AWAIT" => Some(Operation::Await),
56            "ERROR" => Some(Operation::Error),
57            _ => None,
58        }
59    }
60
61    /// Checks if the operation is a "root operation" that can initiate a transaction chain.
62    pub fn is_root(&self) -> bool {
63        match self {
64            Operation::SendVariable
65            | Operation::RequireVariable
66            | Operation::Invoke
67            | Operation::GetVersion => true,
68            _ => false,
69        }
70    }
71}
72
73/// Defines the role of a participant in a PK Command transaction.
74#[derive(PartialEq, Eq, Clone, Copy)]
75pub enum Role {
76    /// The initiator of the transaction.
77    Host, // 调用方(不一定是主机)
78    /// The receiver and executor of the transaction.
79    Device, // 接收方(不一定是设备)
80    /// Indicates that no transaction is active, and thus no specific role is assigned.
81    Idle, // (空闲期没有角色)
82}
83
84/// Represents a parsed or to-be-sent PK Command.
85#[derive(PartialEq, Clone, Debug)]
86pub struct Command {
87    pub msg_id: u16,
88    pub operation: Operation,
89    pub object: Option<String>,
90    pub data: Option<Vec<u8>>,
91}
92
93impl Command {
94    /// Parses a byte slice into a `Command` struct.
95    ///
96    /// The byte slice is expected to conform to the PK Command protocol format.
97    ///
98    /// # Arguments
99    /// * `msg_bytes`: A byte slice representing the raw command.
100    ///
101    /// # Returns
102    /// A `Result` containing the parsed `Command` or a static string slice describing the error.
103    pub fn parse(msg_bytes: &[u8]) -> Result<Command, &'static str> {
104        // 1. 检查最小长度
105        if msg_bytes.len() < 7 {
106            return Err("Invalid length: message is too short.");
107        }
108
109        // 2. 解析 MSG ID
110        let msg_id_slice = msg_bytes.get(0..2).ok_or("Failed to slice MSG ID")?;
111
112        // 3. 特殊处理 ERROR 指令
113        if msg_id_slice == b"  " {
114            // 检查 `ERROR ERROR` 或 `ACKNO ERROR` 结构
115            let op_name_slice = msg_bytes.get(2..7);
116            let space1_slice = msg_bytes.get(7..8);
117            let object_slice = msg_bytes.get(8..13);
118
119            let is_ackno_error = op_name_slice == Some(b"ACKNO")
120                && space1_slice == Some(b" ")
121                && object_slice == Some(b"ERROR");
122            let is_error_error = op_name_slice == Some(b"ERROR")
123                && space1_slice == Some(b" ")
124                && object_slice == Some(b"ERROR");
125
126            if !(is_ackno_error || is_error_error) {
127                return Err("Invalid ERROR command format.");
128            }
129
130            let data = if msg_bytes.len() > 14 {
131                // 检查数据前的空格
132                if msg_bytes.get(13..14) != Some(b" ") {
133                    return Err("Missing space before data in ERROR command.");
134                }
135                // unwrap is safe due to length check msg_bytes.len() > 14
136                Some(msg_bytes.get(14..).unwrap().to_vec())
137            } else if msg_bytes.len() == 13 {
138                // Exactly "  OP_NAME OBJECT"
139                None
140            } else {
141                return Err("Invalid length for ERROR command.");
142            };
143
144            return Ok(Command {
145                msg_id: 0,
146                operation: if op_name_slice == Some(b"ACKNO") {
147                    Operation::Acknowledge
148                } else {
149                    Operation::Error
150                },
151                object: Some("ERROR".to_string()),
152                data,
153            });
154        }
155
156        // 4. 处理常规指令
157        let msg_id_str =
158            std::str::from_utf8(msg_id_slice).map_err(|_| "MSG ID is not valid UTF-8")?;
159        let msg_id = msg_id::to_u16(msg_id_str).map_err(|_| "Invalid MSG ID format.")?;
160
161        let op_name_slice = msg_bytes
162            .get(2..7)
163            .ok_or("Failed to slice operation name.")?;
164        let op_name_str =
165            std::str::from_utf8(op_name_slice).map_err(|_| "Operation name is not valid UTF-8")?;
166        let operation = Operation::from_name(op_name_str).ok_or("Unrecognized operation name.")?;
167
168        // 5. 根据长度和分隔符判断 object 和 data
169        let (object, data) = match msg_bytes.len() {
170            // 只有 MSG ID 和 OP NAME
171            7 => (None, None),
172
173            // 包含 OBJECT
174            13 => {
175                if msg_bytes.get(7..8) != Some(b" ") {
176                    return Err("Missing space after operation name.");
177                }
178                let obj_slice = msg_bytes.get(8..13).ok_or("Failed to slice object.")?;
179                let obj_str =
180                    std::str::from_utf8(obj_slice).map_err(|_| "Object is not valid UTF-8")?;
181                (Some(obj_str.to_string()), None)
182            }
183
184            // 包含 OBJECT 和 DATA
185            len if len > 14 => {
186                if msg_bytes.get(7..8) != Some(b" ") || msg_bytes.get(13..14) != Some(b" ") {
187                    return Err("Missing space separator for object or data.");
188                }
189                let obj_slice = msg_bytes.get(8..13).ok_or("Failed to slice object.")?;
190                let obj_str =
191                    std::str::from_utf8(obj_slice).map_err(|_| "Object is not valid UTF-8")?;
192
193                // unwrap is safe due to length check (len > 14)
194                let data_slice = msg_bytes.get(14..).unwrap();
195                (Some(obj_str.to_string()), Some(data_slice.to_vec()))
196            }
197            // 其他所有长度都是无效的
198            _ => return Err("Invalid message length."),
199        };
200
201        Ok(Command {
202            msg_id,
203            operation,
204            object,
205            data,
206        })
207    }
208
209    /// Serializes the `Command` struct into a `Vec<u8>` according to the PK Command protocol format.
210    ///
211    /// # Panics
212    /// Panics if `self.msg_id` is invalid and cannot be converted by `msg_id::from_u16` (should not happen with valid IDs).
213    pub fn to_bytes(&self) -> Vec<u8> {
214        let id = match self.operation {
215            Operation::Error => String::from("  "),
216            // ERROR 指令的 ACKNO 的 id 也固定是两个空格
217            Operation::Acknowledge => {
218                if self.object == Some(String::from("ERROR")) {
219                    String::from("  ")
220                } else {
221                    msg_id::from_u16(self.msg_id)
222                        .map_err(|_| panic!("Invalid MSG ID"))
223                        .unwrap()
224                }
225            }
226            _ => msg_id::from_u16(self.msg_id)
227                .map_err(|_| panic!("Invalid MSG ID"))
228                .unwrap(),
229        };
230        if self.data.is_none() {
231            if self.object.is_none() {
232                format!("{}{}", id, self.operation.to_name())
233                    .as_bytes()
234                    .to_vec()
235            } else {
236                format!(
237                    "{}{} {}",
238                    id,
239                    self.operation.to_name(),
240                    self.object.clone().unwrap()
241                )
242                .as_bytes()
243                .to_vec()
244            }
245        } else {
246            let mut vec = format!(
247                "{}{} {}",
248                id,
249                self.operation.to_name(),
250                self.object.clone().unwrap()
251            )
252            .as_bytes()
253            .to_vec();
254            vec.push(b' ');
255            vec.append(&mut self.data.clone().unwrap());
256            vec
257        }
258    }
259}
260
261impl std::fmt::Display for Command {
262    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
263        let id = match self.operation {
264            Operation::Error => String::from("  "),
265            // ERROR 指令的 ACKNO 的 id 也固定是两个空格
266            Operation::Acknowledge => {
267                if self.object == Some(String::from("ERROR")) {
268                    String::from("  ")
269                } else {
270                    msg_id::from_u16(self.msg_id).map_err(|_| std::fmt::Error)?
271                }
272            }
273            _ => msg_id::from_u16(self.msg_id).map_err(|_| std::fmt::Error)?, // 将自定义错误转换为 fmt::Error
274        };
275
276        let op = self.operation.to_name();
277
278        write!(f, "{}{}", id, op)?;
279
280        if let Some(obj) = &self.object {
281            write!(f, " {}", obj)?;
282            if let Some(data_vec) = &self.data {
283                // 对于 ERROR 命令, data 应该是 UTF-8 描述字符串.
284                // 对于 SDATA 命令, data可能是任意二进制. String::from_utf8_lossy 在这里用于显示目的.
285                // 如果需要严格的二进制到文本的转换 (例如 Base64), 应该在这里实现.
286                let data_to_display = if self.operation == Operation::Error
287                    || !data_vec.iter().any(|&b| b == 0 || b > 127)
288                {
289                    String::from_utf8_lossy(data_vec)
290                } else {
291                    String::from_utf8_lossy(data_vec) // 或者如: format!("<BINARY DATA: {} bytes>", data_vec.len())
292                };
293                write!(f, " {}", data_to_display)?;
294            }
295        }
296
297        Ok(())
298    }
299}
300
301/// Indicates the specific status of the command sender/receiver regarding acknowledgments.
302// 指示当前收发指令方的特定状态
303#[derive(PartialEq, Eq, Clone, Copy)]
304pub enum Status {
305    /// Not currently awaiting an acknowledgment.
306    Other, // 没有等待 ACK
307    /// Awaiting a standard acknowledgment (`ACKNO`).
308    AwaitingAck, // 等待 ACK
309    /// Awaiting an acknowledgment for a sent `ERROR` command.
310    AwaitingErrAck, // 等待 ACK(发送 ERROR 后)
311}
312
313/// Indicates the current stage of a transaction chain.
314// 指示当前“链”的状态(传输阶段)
315#[derive(PartialEq, Eq, Clone, Copy)]
316pub enum Stage {
317    Idle,
318    Started,               // 已发出/收到 START 指令
319    RootOperationAssigned, // 已发出/收到根指令,等待发送参数
320    SendingParameter,      // 正在传输参数
321    ParameterSent,         // 已传输第一个“ENDTR”,等待 QUERY
322    SendingResponse,       // 正在传输返回值
323}
324
325#[cfg(test)]
326mod tests {
327    use super::*;
328    use crate::util::msg_id;
329
330    #[test]
331    fn test_command_parse_valid_simple() {
332        let bytes = b"!!START";
333        let cmd = Command::parse(bytes).unwrap();
334        assert_eq!(cmd.msg_id, 0);
335        assert_eq!(cmd.operation, Operation::Start);
336        assert!(cmd.object.is_none());
337        assert!(cmd.data.is_none());
338    }
339
340    #[test]
341    fn test_command_parse_valid_with_object() {
342        let bytes = b"!\"SENDV VARIA";
343        let cmd = Command::parse(bytes).unwrap();
344        assert_eq!(cmd.msg_id, msg_id::to_u16("!\"").unwrap());
345        assert_eq!(cmd.operation, Operation::SendVariable);
346        assert_eq!(cmd.object, Some("VARIA".to_string()));
347        assert!(cmd.data.is_none());
348    }
349
350    #[test]
351    fn test_command_parse_valid_with_object_and_data() {
352        let bytes = b"!#SENDV VARIA data_payload";
353        let cmd = Command::parse(bytes).unwrap();
354        assert_eq!(cmd.msg_id, msg_id::to_u16("!#").unwrap());
355        assert_eq!(cmd.operation, Operation::SendVariable);
356        assert_eq!(cmd.object, Some("VARIA".to_string()));
357        assert_eq!(cmd.data, Some(b"data_payload".to_vec()));
358    }
359
360    #[test]
361    fn test_command_parse_error_command() {
362        let bytes = b"  ERROR ERROR Some error description";
363        let cmd = Command::parse(bytes).unwrap();
364        assert_eq!(cmd.msg_id, 0);
365        assert_eq!(cmd.operation, Operation::Error);
366        assert_eq!(cmd.object, Some("ERROR".to_string()));
367        assert_eq!(cmd.data, Some(b"Some error description".to_vec()));
368    }
369
370    #[test]
371    fn test_command_parse_ackno_error_command() {
372        let bytes = b"  ACKNO ERROR";
373        let cmd = Command::parse(bytes).unwrap();
374        assert_eq!(cmd.msg_id, 0);
375        assert_eq!(cmd.operation, Operation::Acknowledge);
376        assert_eq!(cmd.object, Some("ERROR".to_string()));
377        assert!(cmd.data.is_none());
378    }
379
380    #[test]
381    fn test_command_parse_invalid_error_msg_id() {
382        // space msg_id's are only allowed in ERROR and ACKNO ERROR commands
383        let byetes = b"  START";
384        let result = Command::parse(byetes);
385        assert!(result.is_err());
386    }
387
388    #[test]
389    fn test_command_parse_invalid_too_short() {
390        // assert_eq!(
391        //     Command::parse(b"!!STA"),
392        //     Err("Invalid length: message is too short.")
393        // );
394        let result = Command::parse(b"!!STA");
395        assert!(result.is_err());
396        assert_eq!(result.unwrap_err(), "Invalid length: message is too short.");
397    }
398
399    #[test]
400    fn test_command_parse_invalid_msg_id() {
401        // LF(0x0A) and CR(0x0D) is not in the charset
402        let result = Command::parse(b"\n\rSTART");
403        assert!(result.is_err());
404        assert_eq!(result.unwrap_err(), "Invalid MSG ID format.");
405    }
406
407    #[test]
408    fn test_command_to_bytes_simple() {
409        let cmd = Command {
410            msg_id: 0,
411            operation: Operation::Start,
412            object: None,
413            data: None,
414        };
415        assert_eq!(cmd.to_bytes(), b"!!START".to_vec());
416    }
417
418    #[test]
419    fn test_command_to_bytes_with_object_and_data() {
420        let cmd = Command {
421            msg_id: msg_id::to_u16("!#").unwrap(),
422            operation: Operation::SendVariable,
423            object: Some("VARIA".to_string()),
424            data: Some(b"payload".to_vec()),
425        };
426
427        let mut expected = b"!#SENDV VARIA".to_vec();
428        expected.push(b' ');
429        expected.extend_from_slice(b"payload");
430        assert_eq!(cmd.to_bytes(), expected);
431    }
432
433    #[test]
434    fn test_command_to_bytes_error() {
435        let cmd = Command {
436            msg_id: 0, // msg_id is ignored for ERROR
437            operation: Operation::Error,
438            object: Some("ERROR".to_string()),
439            data: Some(b"Test error".to_vec()),
440        };
441        let mut expected = b"  ERROR ERROR".to_vec();
442        expected.push(b' ');
443        expected.extend_from_slice(b"Test error");
444        assert_eq!(cmd.to_bytes(), expected);
445    }
446}