1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
use core::fmt;
use serde::Deserialize;
use super::Game;
use crate::deserialize::{XmlLink, XmlName};
use crate::{ItemType, NameType};
// A list of game families. Which are groups of games in a particular series.
#[derive(Clone, Debug, Deserialize, PartialEq)]
pub(crate) struct GameFamilies {
// List of game families, and the games that they contain.
#[serde(default, rename = "item")]
pub(crate) game_families: Vec<GameFamily>,
}
/// A family of games in a particular series or group. Contains the description for the
/// family as well as the list of games.
#[derive(Clone, Debug, PartialEq)]
pub struct GameFamily {
/// The ID of the game family.
pub id: u64,
/// The name of the game family.
pub name: String,
/// A list of alternate names for the game family.
pub alternate_names: Vec<String>,
/// A link to a jpg image for the game family.
pub image: Option<String>,
/// A link to a jpg thumbnail image for the game family.
pub thumbnail: Option<String>,
/// A description of the group of games.
pub description: String,
/// The list of games in this game family.
pub games: Vec<Game>,
}
impl<'de> Deserialize<'de> for GameFamily {
fn deserialize<D: serde::de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
#[derive(Deserialize)]
#[serde(field_identifier, rename_all = "lowercase")]
enum Field {
Id,
Name,
Image,
Thumbnail,
Description,
// Each game is in an individual XML tag called `link`
Link,
Type,
}
struct GameFamilyVisitor;
impl<'de> serde::de::Visitor<'de> for GameFamilyVisitor {
type Value = GameFamily;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a string containing the XML for a family of games.")
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: serde::de::MapAccess<'de>,
{
let mut id = None;
let mut name = None;
let mut alternate_names = vec![];
let mut image = None;
let mut thumbnail = None;
let mut description = None;
let mut games = vec![];
while let Some(key) = map.next_key()? {
match key {
Field::Id => {
if id.is_some() {
return Err(serde::de::Error::duplicate_field("id"));
}
id = Some(map.next_value()?);
},
Field::Name => {
let name_xml: XmlName = map.next_value()?;
match name_xml.name_type {
NameType::Primary => {
if name.is_some() {
return Err(serde::de::Error::duplicate_field(
"name type=\"primary\"",
));
}
name = Some(name_xml.value);
},
NameType::Alternate => {
alternate_names.push(name_xml.value);
},
}
},
Field::Image => {
if image.is_some() {
return Err(serde::de::Error::duplicate_field("image"));
}
image = Some(map.next_value()?);
},
Field::Thumbnail => {
if thumbnail.is_some() {
return Err(serde::de::Error::duplicate_field("thumbnail"));
}
thumbnail = Some(map.next_value()?);
},
Field::Description => {
if description.is_some() {
return Err(serde::de::Error::duplicate_field("description"));
}
description = Some(map.next_value()?);
},
Field::Link => {
let link: XmlLink = map.next_value()?;
match link.link_type {
ItemType::BoardGameFamily => {
games.push(Game {
id: link.id,
name: link.value,
});
},
link_type => {
return Err(serde::de::Error::custom(format!(
"found unexpected \"{link_type:?}\" link in game family",
)));
},
}
},
Field::Type => {
// Type is fixed at "boardgamefamily", even for the list of games
// contained so we don't add it. But we need
// to consume the value.
let _: String = map.next_value()?;
},
}
}
let id = id.ok_or_else(|| serde::de::Error::missing_field("id"))?;
let name = name.ok_or_else(|| serde::de::Error::missing_field("name"))?;
let thumbnail =
thumbnail.ok_or_else(|| serde::de::Error::missing_field("thumbnail"))?;
let image = image.ok_or_else(|| serde::de::Error::missing_field("image"))?;
let description =
description.ok_or_else(|| serde::de::Error::missing_field("description"))?;
Ok(Self::Value {
id,
name,
alternate_names,
image,
thumbnail,
description,
games,
})
}
}
deserializer.deserialize_any(GameFamilyVisitor)
}
}