use serde::ser::{SerializeStruct, SerializeTuple};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::{PerColor, Point};
impl Serialize for Point {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut pair = serializer.serialize_tuple(2)?;
pair.serialize_element(&self.x)?;
pair.serialize_element(&self.y)?;
pair.end()
}
}
impl<'de> Deserialize<'de> for Point {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let [x, y] = <[f64; 2]>::deserialize(deserializer)?;
Ok(Self::new(x, y))
}
}
impl<T: Serialize> Serialize for PerColor<T> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut pair = serializer.serialize_struct("PerColor", 2)?;
pair.serialize_field("black", self.black())?;
pair.serialize_field("white", self.white())?;
pair.end()
}
}
impl<'de, T: Deserialize<'de>> Deserialize<'de> for PerColor<T> {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
#[derive(Deserialize)]
struct Pair<T> {
black: T,
white: T,
}
let Pair { black, white } = Pair::deserialize(deserializer)?;
Ok(Self::new(black, white))
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used, clippy::expect_used)]
use crate::{Color, GameDelta, PerColor, Point, Stone, StoneId};
#[test]
fn a_point_is_a_two_element_array() {
let json = serde_json::to_string(&Point::new(4.0, -1.5)).unwrap();
assert_eq!(json, "[4.0,-1.5]");
let back: Point = serde_json::from_str(&json).unwrap();
assert_eq!(back, Point::new(4.0, -1.5));
}
#[test]
fn a_color_is_a_lowercase_string() {
assert_eq!(serde_json::to_string(&Color::Black).unwrap(), "\"black\"");
assert_eq!(serde_json::to_string(&Color::White).unwrap(), "\"white\"");
let back: Color = serde_json::from_str("\"white\"").unwrap();
assert_eq!(back, Color::White);
}
#[test]
fn a_stone_matches_the_protocol_shape() {
let stone = Stone::new(StoneId::new(3), Color::Black, Point::new(4.0, 4.0));
let json = serde_json::to_string(&stone).unwrap();
assert_eq!(json, r#"{"id":3,"color":"black","pos":[4.0,4.0]}"#);
assert_eq!(serde_json::from_str::<Stone>(&json).unwrap(), stone);
}
#[test]
fn a_delta_matches_the_protocol_shape() {
let stone = Stone::new(StoneId::new(3), Color::Black, Point::new(4.0, 4.0));
let delta = GameDelta::capture(stone, vec![StoneId::new(1), StoneId::new(7)]);
let json = serde_json::to_string(&delta).unwrap();
assert_eq!(
json,
r#"{"newStone":{"id":3,"color":"black","pos":[4.0,4.0]},"capturedStoneIds":[1,7]}"#
);
assert_eq!(serde_json::from_str::<GameDelta>(&json).unwrap(), delta);
}
#[test]
fn a_pass_delta_has_a_null_stone() {
let json = serde_json::to_string(&GameDelta::pass()).unwrap();
assert_eq!(json, r#"{"newStone":null,"capturedStoneIds":[]}"#);
}
#[test]
fn a_per_color_pair_is_keyed_by_colour_name() {
let counts = PerColor::new(3_u32, 1);
let json = serde_json::to_string(&counts).unwrap();
assert_eq!(json, r#"{"black":3,"white":1}"#);
assert_eq!(
serde_json::from_str::<PerColor<u32>>(&json).unwrap(),
counts
);
}
}