snapcast_proto/message/
hello.rs1use std::io::{Read, Write};
7
8use serde::{Deserialize, Serialize};
9
10use crate::message::base::ProtoError;
11use crate::message::wire;
12
13#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
15pub struct Auth {
16 pub scheme: String,
18 pub param: String,
20}
21
22#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
24#[serde(rename_all = "PascalCase")]
25pub struct Hello {
26 #[serde(rename = "MAC")]
28 pub mac: String,
29 pub host_name: String,
31 pub version: String,
33 pub client_name: String,
35 #[serde(rename = "OS")]
37 pub os: String,
38 pub arch: String,
40 pub instance: u32,
42 #[serde(rename = "ID")]
44 pub id: String,
45 pub snap_stream_protocol_version: u32,
47 #[serde(skip_serializing_if = "Option::is_none")]
49 pub auth: Option<Auth>,
50}
51
52impl Hello {
53 pub fn wire_size(&self) -> u32 {
55 let json = serde_json::to_string(self).unwrap_or_default();
56 wire::string_wire_size(&json)
57 }
58
59 pub fn read_from<R: Read>(r: &mut R) -> Result<Self, ProtoError> {
61 let json_str = wire::read_string(r)?;
62 serde_json::from_str(&json_str)
63 .map_err(|e| ProtoError::Io(std::io::Error::new(std::io::ErrorKind::InvalidData, e)))
64 }
65
66 pub fn write_to<W: Write>(&self, w: &mut W) -> Result<(), ProtoError> {
68 let json_str = serde_json::to_string(self)
69 .map_err(|e| ProtoError::Io(std::io::Error::new(std::io::ErrorKind::InvalidData, e)))?;
70 wire::write_string(w, &json_str)
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77
78 fn sample_hello() -> Hello {
79 Hello {
80 mac: "00:11:22:33:44:55".into(),
81 host_name: "testhost".into(),
82 version: "0.32.0".into(),
83 client_name: "Snapclient".into(),
84 os: "Linux".into(),
85 arch: "x86_64".into(),
86 instance: 1,
87 id: "00:11:22:33:44:55".into(),
88 snap_stream_protocol_version: 2,
89 auth: None,
90 }
91 }
92
93 #[test]
94 fn round_trip() {
95 let original = sample_hello();
96 let mut buf = Vec::new();
97 original.write_to(&mut buf).unwrap();
98 let mut cursor = std::io::Cursor::new(&buf);
99 let decoded = Hello::read_from(&mut cursor).unwrap();
100 assert_eq!(original, decoded);
101 }
102
103 #[test]
104 fn round_trip_with_auth() {
105 let mut hello = sample_hello();
106 hello.auth = Some(Auth {
107 scheme: "Basic".into(),
108 param: "dXNlcjpwYXNz".into(),
109 });
110 let mut buf = Vec::new();
111 hello.write_to(&mut buf).unwrap();
112 let mut cursor = std::io::Cursor::new(&buf);
113 let decoded = Hello::read_from(&mut cursor).unwrap();
114 assert_eq!(hello, decoded);
115 assert_eq!(decoded.auth.unwrap().scheme, "Basic");
116 }
117
118 #[test]
119 fn json_field_names_match_cpp() {
120 let hello = sample_hello();
121 let json_str = serde_json::to_string(&hello).unwrap();
122 assert!(json_str.contains("\"MAC\""));
124 assert!(json_str.contains("\"HostName\""));
125 assert!(json_str.contains("\"SnapStreamProtocolVersion\""));
126 assert!(json_str.contains("\"ID\""));
127 assert!(json_str.contains("\"OS\""));
128 assert!(!json_str.contains("\"Auth\""));
130 }
131
132 #[test]
133 fn deserialize_cpp_json() {
134 let json = r#"{"Arch":"x86_64","ClientName":"Snapclient","HostName":"myhost","ID":"aa:bb:cc:dd:ee:ff","Instance":1,"MAC":"aa:bb:cc:dd:ee:ff","OS":"Arch Linux","SnapStreamProtocolVersion":2,"Version":"0.32.0"}"#;
136 let hello: Hello = serde_json::from_str(json).unwrap();
137 assert_eq!(hello.mac, "aa:bb:cc:dd:ee:ff");
138 assert_eq!(hello.snap_stream_protocol_version, 2);
139 assert!(hello.auth.is_none());
140 }
141}