discord_rpc_client/models/
message.rs

1use std::{io::{Write, Read}, mem::size_of};
2use byteorder::{WriteBytesExt, ReadBytesExt, LittleEndian};
3use num_derive::FromPrimitive;
4use num_traits::FromPrimitive;
5use serde::Serialize;
6use crate::{Error, Result};
7
8#[repr(u32)]
9#[derive(Debug, Copy, Clone, PartialEq, FromPrimitive)]
10pub enum OpCode {
11    Handshake,
12    Frame,
13    Close,
14    Ping,
15    Pong,
16}
17
18#[derive(Debug, PartialEq, Clone)]
19pub struct Message {
20    pub opcode: OpCode,
21    pub payload: String,
22}
23
24impl Message {
25    pub fn new<T>(opcode: OpCode, payload: T) -> Self
26        where T: Serialize
27    {
28        Self { opcode, payload: serde_json::to_string(&payload).unwrap() }
29    }
30
31    pub fn encode(&self) -> Result<Vec<u8>> {
32        let payload_len = self.payload.len();
33        let mut bytes: Vec<u8> = Vec::with_capacity(2 * size_of::<u32>() + payload_len);
34
35        bytes.write_u32::<LittleEndian>(self.opcode as u32)?;
36        bytes.write_u32::<LittleEndian>(payload_len as u32)?;
37        bytes.write_all(self.payload.as_bytes())?;
38
39        Ok(bytes)
40    }
41
42    pub fn decode(mut bytes: &[u8]) -> Result<Self> {
43        let opcode = OpCode::from_u32(bytes.read_u32::<LittleEndian>()?).ok_or(Error::Conversion)?;
44        let len = bytes.read_u32::<LittleEndian>()? as usize;
45        let mut payload = String::with_capacity(len);
46        bytes.read_to_string(&mut payload)?;
47
48        Ok(Self { opcode, payload })
49    }
50}
51
52#[cfg(test)]
53mod tests {
54    use super::*;
55
56    #[derive(Debug, PartialEq, Serialize, Deserialize)]
57    struct Something {
58        empty: bool
59    }
60
61    #[test]
62    fn test_encoder() {
63        let msg = Message::new(OpCode::Frame, Something { empty: true });
64        let encoded = msg.encode().unwrap();
65        let decoded = Message::decode(&encoded).unwrap();
66        assert_eq!(msg, decoded);
67    }
68
69    #[test]
70    fn test_opcode() {
71        assert_eq!(OpCode::from_u32(0), Some(OpCode::Handshake));
72        assert_eq!(OpCode::from_u32(4), Some(OpCode::Pong));
73        assert_eq!(OpCode::from_u32(5), None);
74    }
75}