snapcast_proto/message/
base.rs1use std::io::{self, Read, Write};
4
5use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
6use thiserror::Error;
7
8use super::MessageType;
9use crate::types::Timeval;
10
11#[derive(Debug, Error)]
13pub enum ProtoError {
14 #[error("I/O error: {0}")]
16 Io(#[from] io::Error),
17 #[error("unknown message type: {0}")]
19 UnknownMessageType(u16),
20}
21
22#[derive(Debug, Clone, PartialEq, Eq)]
27pub struct BaseMessage {
28 pub msg_type: MessageType,
30 pub id: u16,
32 pub refers_to: u16,
34 pub sent: Timeval,
36 pub received: Timeval,
38 pub size: u32,
40}
41
42impl BaseMessage {
43 pub const HEADER_SIZE: usize = 26;
45
46 pub fn read_from<R: Read>(r: &mut R) -> Result<Self, ProtoError> {
48 let raw_type = r.read_u16::<LittleEndian>()?;
49 let msg_type = MessageType::from_u16(raw_type);
50 let id = r.read_u16::<LittleEndian>()?;
51 let refers_to = r.read_u16::<LittleEndian>()?;
52 let sent = Timeval::read_from(r)?;
53 let received = Timeval::read_from(r)?;
54 let size = r.read_u32::<LittleEndian>()?;
55 Ok(Self {
56 msg_type,
57 id,
58 refers_to,
59 sent,
60 received,
61 size,
62 })
63 }
64
65 pub fn write_to<W: Write>(&self, w: &mut W) -> Result<(), ProtoError> {
67 w.write_u16::<LittleEndian>(self.msg_type.into())?;
68 w.write_u16::<LittleEndian>(self.id)?;
69 w.write_u16::<LittleEndian>(self.refers_to)?;
70 self.sent.write_to(w)?;
71 self.received.write_to(w)?;
72 w.write_u32::<LittleEndian>(self.size)?;
73 Ok(())
74 }
75
76 pub fn to_bytes(&self) -> Result<Vec<u8>, ProtoError> {
78 let mut buf = Vec::with_capacity(Self::HEADER_SIZE);
79 self.write_to(&mut buf)?;
80 Ok(buf)
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 const HELLO_HEADER_BYTES: [u8; 26] = [
96 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0xE8, 0x03, 0x00, 0x00, 0x20, 0xA1, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, ];
105
106 fn hello_header() -> BaseMessage {
107 BaseMessage {
108 msg_type: MessageType::Hello,
109 id: 1,
110 refers_to: 0,
111 sent: Timeval {
112 sec: 1000,
113 usec: 500_000,
114 },
115 received: Timeval { sec: 0, usec: 0 },
116 size: 42,
117 }
118 }
119
120 #[test]
121 fn serialize_hello_header() {
122 let msg = hello_header();
123 let bytes = msg.to_bytes().unwrap();
124 assert_eq!(bytes.as_slice(), &HELLO_HEADER_BYTES);
125 }
126
127 #[test]
128 fn deserialize_hello_header() {
129 let mut cursor = io::Cursor::new(&HELLO_HEADER_BYTES);
130 let msg = BaseMessage::read_from(&mut cursor).unwrap();
131 assert_eq!(msg, hello_header());
132 }
133
134 #[test]
135 fn round_trip() {
136 let original = hello_header();
137 let bytes = original.to_bytes().unwrap();
138 let mut cursor = io::Cursor::new(&bytes);
139 let decoded = BaseMessage::read_from(&mut cursor).unwrap();
140 assert_eq!(original, decoded);
141 }
142
143 #[test]
144 fn header_size_is_26_bytes() {
145 let msg = hello_header();
146 let bytes = msg.to_bytes().unwrap();
147 assert_eq!(bytes.len(), BaseMessage::HEADER_SIZE);
148 }
149
150 #[test]
151 fn unknown_type_returns_unknown_variant() {
152 let mut bad_bytes = HELLO_HEADER_BYTES;
153 bad_bytes[0] = 0xFF;
154 bad_bytes[1] = 0xFF;
155 let mut cursor = io::Cursor::new(&bad_bytes);
156 let msg = BaseMessage::read_from(&mut cursor).unwrap();
157 #[cfg(not(feature = "custom-protocol"))]
158 assert_eq!(msg.msg_type, MessageType::Unknown(0xFFFF));
159 #[cfg(feature = "custom-protocol")]
160 assert_eq!(msg.msg_type, MessageType::Custom(0xFFFF));
161 }
162}