voronoi_go/
serde_support.rs1use serde::ser::{SerializeStruct, SerializeTuple};
7use serde::{Deserialize, Deserializer, Serialize, Serializer};
8
9use crate::{PerColor, Point};
10
11impl Serialize for Point {
12 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
13 let mut pair = serializer.serialize_tuple(2)?;
14 pair.serialize_element(&self.x)?;
15 pair.serialize_element(&self.y)?;
16 pair.end()
17 }
18}
19
20impl<'de> Deserialize<'de> for Point {
21 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
22 let [x, y] = <[f64; 2]>::deserialize(deserializer)?;
23 Ok(Self::new(x, y))
24 }
25}
26
27impl<T: Serialize> Serialize for PerColor<T> {
28 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
29 let mut pair = serializer.serialize_struct("PerColor", 2)?;
30 pair.serialize_field("black", self.black())?;
31 pair.serialize_field("white", self.white())?;
32 pair.end()
33 }
34}
35
36impl<'de, T: Deserialize<'de>> Deserialize<'de> for PerColor<T> {
37 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
38 #[derive(Deserialize)]
39 struct Pair<T> {
40 black: T,
41 white: T,
42 }
43
44 let Pair { black, white } = Pair::deserialize(deserializer)?;
45 Ok(Self::new(black, white))
46 }
47}
48
49#[cfg(test)]
50mod tests {
51 #![allow(clippy::unwrap_used, clippy::expect_used)]
52
53 use crate::{Color, GameDelta, PerColor, Point, Stone, StoneId};
54
55 #[test]
56 fn a_point_is_a_two_element_array() {
57 let json = serde_json::to_string(&Point::new(4.0, -1.5)).unwrap();
58 assert_eq!(json, "[4.0,-1.5]");
59 let back: Point = serde_json::from_str(&json).unwrap();
60 assert_eq!(back, Point::new(4.0, -1.5));
61 }
62
63 #[test]
64 fn a_color_is_a_lowercase_string() {
65 assert_eq!(serde_json::to_string(&Color::Black).unwrap(), "\"black\"");
66 assert_eq!(serde_json::to_string(&Color::White).unwrap(), "\"white\"");
67 let back: Color = serde_json::from_str("\"white\"").unwrap();
68 assert_eq!(back, Color::White);
69 }
70
71 #[test]
72 fn a_stone_matches_the_protocol_shape() {
73 let stone = Stone::new(StoneId::new(3), Color::Black, Point::new(4.0, 4.0));
74 let json = serde_json::to_string(&stone).unwrap();
75 assert_eq!(json, r#"{"id":3,"color":"black","pos":[4.0,4.0]}"#);
76 assert_eq!(serde_json::from_str::<Stone>(&json).unwrap(), stone);
77 }
78
79 #[test]
80 fn a_delta_matches_the_protocol_shape() {
81 let stone = Stone::new(StoneId::new(3), Color::Black, Point::new(4.0, 4.0));
82 let delta = GameDelta::capture(stone, vec![StoneId::new(1), StoneId::new(7)]);
83 let json = serde_json::to_string(&delta).unwrap();
84 assert_eq!(
85 json,
86 r#"{"newStone":{"id":3,"color":"black","pos":[4.0,4.0]},"capturedStoneIds":[1,7]}"#
87 );
88 assert_eq!(serde_json::from_str::<GameDelta>(&json).unwrap(), delta);
89 }
90
91 #[test]
92 fn a_pass_delta_has_a_null_stone() {
93 let json = serde_json::to_string(&GameDelta::pass()).unwrap();
94 assert_eq!(json, r#"{"newStone":null,"capturedStoneIds":[]}"#);
95 }
96
97 #[test]
98 fn a_per_color_pair_is_keyed_by_colour_name() {
99 let counts = PerColor::new(3_u32, 1);
100 let json = serde_json::to_string(&counts).unwrap();
101 assert_eq!(json, r#"{"black":3,"white":1}"#);
102 assert_eq!(
103 serde_json::from_str::<PerColor<u32>>(&json).unwrap(),
104 counts
105 );
106 }
107}