1use crate::channels::ChannelSpec;
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Deserialize)]
9#[serde(tag = "type", rename_all = "kebab-case")]
10pub enum FromPilot {
11 Hello {
12 #[serde(default)]
13 proto: u8,
14 #[serde(default)]
15 session_id: String,
16 #[serde(default)]
17 client: serde_json::Value,
18 #[serde(default)]
19 token: Option<String>,
20 },
21 Ping {
22 t1: u64,
23 },
24 Loss {
25 ch: u8,
26 frame_id: u16,
27 #[serde(default)]
28 key: bool,
29 },
30 RequestKeyframe {
31 ch: u8,
32 },
33 SetQos {
34 profile: String,
35 },
36 PilotStats(serde_json::Value),
37 Bye,
38 #[serde(other)]
39 Unknown,
40}
41
42#[derive(Debug, Clone, Serialize)]
43#[serde(tag = "type", rename_all = "kebab-case")]
44pub enum ToPilot<'a> {
45 Welcome {
46 session_id: &'a str,
47 role: &'a str,
48 channels: &'a [ChannelSpec],
49 qos: serde_json::Value,
50 t_agent_us: u64,
51 },
52 Denied {
53 reason: &'a str,
54 },
55 Pong {
56 t1: u64,
57 t2: u64,
58 },
59 QosAck {
60 profile: &'a str,
61 qos: serde_json::Value,
62 publisher: &'a str,
63 },
64 AgentStats(serde_json::Value),
65}
66
67pub fn encode_line<T: Serialize>(msg: &T) -> String {
68 let mut s = serde_json::to_string(msg).expect("control messages are serialisable");
69 s.push('\n');
70 s
71}
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76
77 #[test]
78 fn parses_hello_and_unknown() {
79 let m: FromPilot = serde_json::from_str(
80 r#"{"type":"hello","proto":2,"session_id":"abc","client":{"kind":"browser"}}"#,
81 )
82 .unwrap();
83 assert!(matches!(m, FromPilot::Hello { proto: 2, .. }));
84 let m: FromPilot = serde_json::from_str(r#"{"type":"whatever"}"#).unwrap();
85 assert!(matches!(m, FromPilot::Unknown));
86 let m: FromPilot =
87 serde_json::from_str(r#"{"type":"loss","ch":1,"frame_id":9,"key":true}"#).unwrap();
88 assert!(matches!(
89 m,
90 FromPilot::Loss {
91 ch: 1,
92 frame_id: 9,
93 key: true
94 }
95 ));
96 }
97
98 #[test]
99 fn welcome_line() {
100 let line = encode_line(&ToPilot::Welcome {
101 session_id: "s",
102 role: "driver",
103 channels: &[],
104 qos: seyd_qos::BALANCED.pilot_config(),
105 t_agent_us: 1,
106 });
107 assert!(line.starts_with(r#"{"type":"welcome""#) && line.ends_with('\n'));
108 }
109}