botstadia_interface/
player.rs1use serde::{Deserialize, Serialize};
13
14pub const PROTOCOL: u32 = 1;
16
17#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
19#[serde(tag = "type", rename_all = "snake_case")]
20pub enum SiteMessage {
21 Init {
22 protocol: u32,
23 replay_url: Option<String>,
26 seats: Vec<String>,
28 control_rate: u32,
29 turns: u32,
31 },
32 Play,
33 Pause,
34 Seek {
37 turn: u32,
38 seq: u32,
39 },
40 Speed {
41 multiplier: f64,
42 },
43 View {
44 id: String,
45 },
46}
47
48#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
50#[serde(tag = "type", rename_all = "snake_case")]
51pub enum PlayerMessage {
52 Ready {
53 protocol: u32,
54 },
55 Progress {
56 fraction: f32,
57 label: String,
58 },
59 Loaded {
61 turns: u32,
62 views: Vec<View>,
63 view: String,
64 },
65 Turn {
67 turn: u32,
68 seq: Option<u32>,
69 },
70 Playing {
71 playing: bool,
72 speed: f64,
73 },
74 Finished,
75 Error {
77 message: String,
78 },
79}
80
81#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
83pub struct View {
84 pub id: String,
85 pub label: String,
86}
87
88macro_rules! json {
89 ($type:ty) => {
90 impl $type {
91 pub fn to_json(&self) -> String {
92 serde_json::to_string(self).expect("messages always serialise")
93 }
94
95 pub fn from_json(text: &str) -> Option<Self> {
98 serde_json::from_str(text).ok()
99 }
100 }
101 };
102}
103
104json!(SiteMessage);
105json!(PlayerMessage);
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110
111 #[test]
112 fn tagged_json_round_trips_and_strangers_are_ignored() {
113 let init = SiteMessage::Init {
114 protocol: PROTOCOL,
115 replay_url: None,
116 seats: vec!["a".into()],
117 control_rate: 50,
118 turns: 10,
119 };
120 assert!(init.to_json().contains(r#""type":"init""#));
121 assert_eq!(SiteMessage::from_json(&init.to_json()), Some(init));
122 let turn = PlayerMessage::Turn {
123 turn: 3,
124 seq: Some(1),
125 };
126 assert_eq!(PlayerMessage::from_json(&turn.to_json()), Some(turn));
127 assert_eq!(PlayerMessage::from_json(r#"{"type":"future_thing"}"#), None);
128 }
129}