use serde::{Deserialize, Serialize};
pub const PROTOCOL: u32 = 1;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum SiteMessage {
Init {
protocol: u32,
replay_url: Option<String>,
seats: Vec<String>,
control_rate: u32,
turns: u32,
},
Play,
Pause,
Seek {
turn: u32,
seq: u32,
},
Speed {
multiplier: f64,
},
View {
id: String,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum PlayerMessage {
Ready {
protocol: u32,
},
Progress {
fraction: f32,
label: String,
},
Loaded {
turns: u32,
views: Vec<View>,
view: String,
},
Turn {
turn: u32,
seq: Option<u32>,
},
Playing {
playing: bool,
speed: f64,
},
Finished,
Error {
message: String,
},
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct View {
pub id: String,
pub label: String,
}
macro_rules! json {
($type:ty) => {
impl $type {
pub fn to_json(&self) -> String {
serde_json::to_string(self).expect("messages always serialise")
}
pub fn from_json(text: &str) -> Option<Self> {
serde_json::from_str(text).ok()
}
}
};
}
json!(SiteMessage);
json!(PlayerMessage);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn tagged_json_round_trips_and_strangers_are_ignored() {
let init = SiteMessage::Init {
protocol: PROTOCOL,
replay_url: None,
seats: vec!["a".into()],
control_rate: 50,
turns: 10,
};
assert!(init.to_json().contains(r#""type":"init""#));
assert_eq!(SiteMessage::from_json(&init.to_json()), Some(init));
let turn = PlayerMessage::Turn {
turn: 3,
seq: Some(1),
};
assert_eq!(PlayerMessage::from_json(&turn.to_json()), Some(turn));
assert_eq!(PlayerMessage::from_json(r#"{"type":"future_thing"}"#), None);
}
}