use core::fmt::{self, Display};
use serde::Deserialize;
use super::{RankValue, RatingValue};
use crate::deserialize::{XmlFloatValue, XmlLink, XmlName, XmlSignedValue, XmlStringValue};
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum ItemType {
BoardGame,
BoardGameExpansion,
BoardGameAccessory,
BoardGameDesigner,
BoardGamePublisher,
BoardGameArtist,
BoardGameFamily,
BoardGameCategory,
BoardGameMechanic,
BoardGameCompilation,
BoardGameImplementation,
BoardGameVersion,
Language,
}
impl Display for ItemType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ItemType::BoardGame => write!(f, "boardgame"),
ItemType::BoardGameExpansion => write!(f, "boardgameexpansion"),
ItemType::BoardGameAccessory => write!(f, "boardgameaccessory"),
ItemType::BoardGameDesigner => write!(f, "boardgamedesigner"),
ItemType::BoardGamePublisher => write!(f, "boardgamepublisher"),
ItemType::BoardGameArtist => write!(f, "boardgameartist"),
ItemType::BoardGameFamily => write!(f, "boardgamefamily"),
ItemType::BoardGameCategory => write!(f, "boardgamecategory"),
ItemType::BoardGameMechanic => write!(f, "boardgamemechanic"),
ItemType::BoardGameCompilation => write!(f, "boardgamecompilation"),
ItemType::BoardGameImplementation => write!(f, "boardgameimplementation"),
ItemType::BoardGameVersion => write!(f, "boardgameversion"),
ItemType::Language => write!(f, "language"),
}
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum CollectionItemType {
BoardGame,
BoardGameExpansion,
BoardGameAccessory,
}
impl Display for CollectionItemType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CollectionItemType::BoardGame => write!(f, "boardgame"),
CollectionItemType::BoardGameExpansion => write!(f, "boardgameexpansion"),
CollectionItemType::BoardGameAccessory => write!(f, "boardgameaccessory"),
}
}
}
#[derive(Clone, Debug, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum GameType {
BoardGame,
BoardGameExpansion,
}
impl Display for GameType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
GameType::BoardGame => write!(f, "boardgame"),
GameType::BoardGameExpansion => write!(f, "boardgameexpansion"),
}
}
}
#[derive(Clone, Debug, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum NameType {
Primary,
Alternate,
}
#[derive(Clone, Debug, Deserialize, PartialEq)]
pub struct Game {
pub id: u64,
#[serde(rename = "value")]
pub name: String,
}
#[derive(Clone, Debug, Deserialize, PartialEq)]
pub struct GameAccessory {
pub id: u64,
#[serde(rename = "value")]
pub name: String,
}
#[derive(Clone, Debug, Deserialize, PartialEq)]
pub struct GameCategory {
pub id: u64,
#[serde(rename = "value")]
pub name: String,
}
#[derive(Clone, Debug, Deserialize, PartialEq)]
pub struct GameMechanic {
pub id: u64,
#[serde(rename = "value")]
pub name: String,
}
#[derive(Clone, Debug, Deserialize, PartialEq)]
pub struct GameFamilyName {
pub id: u64,
#[serde(rename = "value")]
pub name: String,
}
#[derive(Clone, Debug, Deserialize, PartialEq)]
pub struct GameCompilation {
pub id: u64,
#[serde(rename = "value")]
pub name: String,
}
#[derive(Clone, Debug, Deserialize, PartialEq)]
pub struct GameImplementation {
pub id: u64,
#[serde(rename = "value")]
pub name: String,
}
#[derive(Clone, Debug, Deserialize, PartialEq)]
pub struct GameDesigner {
pub id: u64,
#[serde(rename = "value")]
pub name: String,
}
#[derive(Clone, Debug, Deserialize, PartialEq)]
pub struct GamePublisher {
pub id: u64,
#[serde(rename = "value")]
pub name: String,
}
#[derive(Clone, Debug, Deserialize, PartialEq)]
pub struct GameArtist {
pub id: u64,
#[serde(rename = "value")]
pub name: String,
}
#[derive(Clone, Debug, Deserialize, PartialEq)]
pub struct Language {
pub id: u64,
#[serde(rename = "value")]
pub name: String,
}
#[derive(Clone, Debug, Deserialize, PartialEq)]
pub struct Dimensions {
pub width: f64,
pub length: f64,
pub depth: f64,
}
#[derive(Clone, Debug, PartialEq, Deserialize)]
pub struct ItemFamilyRank {
pub id: u64,
pub name: String,
#[serde(rename = "friendlyname")]
pub friendly_name: String,
pub value: RankValue,
#[serde(rename = "bayesaverage")]
pub bayesian_average: RatingValue,
}
#[derive(Clone, Debug, Deserialize, PartialEq)]
pub(crate) struct XmlVersions {
#[serde(rename = "item")]
pub(crate) versions: Vec<GameVersion>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct GameVersion {
pub id: u64,
pub name: String,
pub alternate_names: Vec<String>,
pub year_published: i64,
pub image: Option<String>,
pub thumbnail: Option<String>,
pub original_game: Game,
pub publishers: Vec<GamePublisher>,
pub artists: Vec<GameArtist>,
pub languages: Vec<Language>,
pub dimensions: Option<Dimensions>,
pub weight: Option<f64>,
pub product_code: Option<String>,
}
impl<'de> Deserialize<'de> for GameVersion {
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,
YearPublished,
ProductCode,
Width,
Length,
Depth,
Weight,
Link,
Type,
}
struct GameVersionVisitor;
impl<'de> serde::de::Visitor<'de> for GameVersionVisitor {
type Value = GameVersion;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a string containing the XML for game version information.")
}
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 publishers = vec![];
let mut artists = vec![];
let mut languages = vec![];
let mut year_published = None;
let mut image = None;
let mut thumbnail = None;
let mut original_game = None;
let mut width = None;
let mut length = None;
let mut depth = None;
let mut weight = None;
let mut product_code = None;
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::Link => {
let link: XmlLink = map.next_value()?;
match link.link_type {
ItemType::BoardGameVersion => {
if original_game.is_some() {
return Err(serde::de::Error::duplicate_field(
"link with type \"boardgameversion\"",
));
}
original_game = Some(Game {
id: link.id,
name: link.value,
});
},
ItemType::BoardGamePublisher => {
publishers.push(GamePublisher {
id: link.id,
name: link.value,
});
},
ItemType::BoardGameArtist => {
artists.push(GameArtist {
id: link.id,
name: link.value,
});
},
ItemType::Language => {
languages.push(Language {
id: link.id,
name: link.value,
});
},
link_type => {
return Err(serde::de::Error::custom(format!(
"found unexpected \"{link_type:?}\" link in version",
)));
},
}
},
Field::Width => {
if width.is_some() {
return Err(serde::de::Error::duplicate_field("width"));
}
let width_xml: XmlFloatValue = map.next_value()?;
if width_xml.value == 0.0 {
width = Some(None);
} else {
width = Some(Some(width_xml.value));
}
},
Field::Depth => {
if depth.is_some() {
return Err(serde::de::Error::duplicate_field("depth"));
}
let depth_xml: XmlFloatValue = map.next_value()?;
if depth_xml.value == 0.0 {
depth = Some(None);
} else {
depth = Some(Some(depth_xml.value));
}
},
Field::Length => {
if length.is_some() {
return Err(serde::de::Error::duplicate_field("length"));
}
let length_xml: XmlFloatValue = map.next_value()?;
if length_xml.value == 0.0 {
length = Some(None);
} else {
length = Some(Some(length_xml.value));
}
},
Field::Weight => {
if weight.is_some() {
return Err(serde::de::Error::duplicate_field("weight"));
}
let weight_xml: XmlFloatValue = map.next_value()?;
if weight_xml.value == 0.0 {
weight = Some(None);
} else {
weight = Some(Some(weight_xml.value));
}
},
Field::ProductCode => {
if product_code.is_some() {
return Err(serde::de::Error::duplicate_field("product_code"));
}
let product_code_xml: XmlStringValue = map.next_value()?;
if product_code_xml.value.is_empty() {
product_code = Some(None);
} else {
product_code = Some(Some(product_code_xml.value));
}
},
Field::YearPublished => {
if year_published.is_some() {
return Err(serde::de::Error::duplicate_field("yearpublished"));
}
let year_published_xml: XmlSignedValue = map.next_value()?;
year_published = Some(year_published_xml.value);
},
Field::Type => {
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 year_published = year_published
.ok_or_else(|| serde::de::Error::missing_field("yearpublished"))?;
let image = image.ok_or_else(|| serde::de::Error::missing_field("image"))?;
let thumbnail =
thumbnail.ok_or_else(|| serde::de::Error::missing_field("thumbnail"))?;
let original_game = original_game.ok_or_else(|| {
serde::de::Error::missing_field("link with type \"boardgameversion\"")
})?;
let width = width.ok_or_else(|| serde::de::Error::missing_field("width"))?;
let length = length.ok_or_else(|| serde::de::Error::missing_field("length"))?;
let depth = depth.ok_or_else(|| serde::de::Error::missing_field("depth"))?;
let weight = weight.ok_or_else(|| serde::de::Error::missing_field("weight"))?;
let product_code =
product_code.ok_or_else(|| serde::de::Error::missing_field("productcode"))?;
let dimensions;
if let (Some(width), Some(length), Some(depth)) = (width, length, depth) {
dimensions = Some(Dimensions {
width,
length,
depth,
});
} else if let (None, None, None) = (width, depth, length) {
dimensions = None;
} else {
return Err(serde::de::Error::custom("Invalid game dimensions, some but not all of width, length, depth, were set."));
}
Ok(Self::Value {
id,
name,
alternate_names,
year_published,
image,
thumbnail,
original_game,
publishers,
artists,
languages,
dimensions,
weight,
product_code,
})
}
}
deserializer.deserialize_any(GameVersionVisitor)
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
pub struct User {
pub user_id: u64,
pub username: String,
}