use std::{borrow::Cow, collections::BTreeSet, fmt::Display};
use serde::{Deserialize, Serialize};
use thiserror::Error;
use super::{
categories::CategoryEmbeds, developers::DeveloperId, endpoint::Endpoint, engines::EngineId,
error::BodyError, gametypes::GameTypeId, genres::GenreId, leaderboards::LeaderboardEmbeds,
platforms::PlatformId, publishers::PublisherId, query_params::QueryParams, regions::RegionId,
users::UserId, CategoriesSorting, Direction, Pageable, VariablesSorting,
};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum GameEmbeds {
Levels,
Categories,
Moderators,
Gametypes,
Platforms,
Regions,
Genres,
Engines,
Developers,
Publishers,
Variables,
}
#[derive(Debug, Serialize, Clone, Copy)]
#[serde(rename_all = "kebab-case")]
pub enum GamesSorting {
#[serde(rename = "name.int")]
NameInternational,
#[serde(rename = "name.jap")]
NameJapanese,
Abbreviation,
Released,
Created,
Similarity,
}
#[derive(Debug, Serialize, Clone, Copy)]
#[serde(rename_all = "kebab-case")]
pub enum LevelsSorting {
Name,
Pos,
}
#[derive(Debug, Serialize, Clone, Copy)]
#[serde(rename_all = "kebab-case")]
pub enum LeaderboardScope {
FullGame,
Levels,
All,
}
#[derive(Debug, Error)]
pub enum GameDerivedGamesBuilderError {
#[error("{0} must be initialized")]
UninitializedField(&'static str),
#[error(transparent)]
Inner(#[from] GamesBuilderError),
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
pub struct GameId<'a>(Cow<'a, str>);
impl<'a> GameId<'a> {
pub fn new<T>(id: T) -> Self
where
T: Into<Cow<'a, str>>,
{
Self(id.into())
}
}
impl<'a, T> From<T> for GameId<'a>
where
T: Into<Cow<'a, str>>,
{
fn from(value: T) -> Self {
Self::new(value)
}
}
impl Display for GameId<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", &self.0)
}
}
#[derive(Default, Debug, Builder, Serialize, Clone)]
#[builder(default, setter(into, strip_option))]
#[serde(rename_all = "kebab-case")]
pub struct Games<'a> {
#[doc = r"Performs a fuzzy search across game names and abbreviations."]
name: Option<Cow<'a, str>>,
#[doc = r"Perform an exact-match search for this abbreviation."]
abbreviation: Option<Cow<'a, str>>,
#[doc = r"Restrict results to games released in the given year."]
released: Option<i64>,
#[doc = r"Restrict results to the given game type."]
gametype: Option<GameTypeId<'a>>,
#[doc = r"Restrict results to the given platform."]
platform: Option<PlatformId<'a>>,
#[doc = r"Restrict results to the given region."]
region: Option<RegionId<'a>>,
#[doc = r"Restrict results to the given genre."]
genre: Option<GenreId<'a>>,
#[doc = r"Restrict results to the given engine."]
engine: Option<EngineId<'a>>,
#[doc = r"Restrict results to the given developer."]
developer: Option<DeveloperId<'a>>,
#[doc = r"Restrict results to the given publisher."]
publisher: Option<PublisherId<'a>>,
#[doc = r"Only return games moderated by the given user."]
moderator: Option<UserId<'a>>,
#[doc = r"Enable bulk access."]
#[serde(rename = "_bulk")]
bulk: Option<bool>,
#[doc = r"Sorting options for results."]
orderby: Option<GamesSorting>,
#[doc = r"Sort direction."]
direction: Option<Direction>,
#[builder(setter(name = "_embed"), private)]
#[serde(serialize_with = "super::utils::serialize_as_csv")]
#[serde(skip_serializing_if = "BTreeSet::is_empty")]
embed: BTreeSet<GameEmbeds>,
}
#[derive(Debug, Builder, Clone)]
#[builder(setter(into, strip_option))]
pub struct Game<'a> {
#[doc = r"`ID` of the game."]
id: GameId<'a>,
}
#[derive(Debug, Builder, Serialize, Clone)]
#[builder(setter(into, strip_option))]
#[serde(rename_all = "kebab-case")]
pub struct GameCategories<'a> {
#[doc = r"`ID` of the game to retrieve categories for."]
#[serde(skip)]
id: GameId<'a>,
#[doc = r"Filter miscellaneous categories."]
#[builder(default)]
miscellaneous: Option<bool>,
#[doc = r"Sorting options for results."]
#[builder(default)]
orderby: Option<CategoriesSorting>,
#[doc = r"Sort direction."]
#[builder(default)]
direction: Option<Direction>,
#[builder(setter(name = "_embed"), private, default)]
#[serde(serialize_with = "super::utils::serialize_as_csv")]
#[serde(skip_serializing_if = "BTreeSet::is_empty")]
embed: BTreeSet<CategoryEmbeds>,
}
impl GameCategoriesBuilder<'_> {
pub fn embed(&mut self, embed: CategoryEmbeds) -> &mut Self {
self.embed.get_or_insert_with(BTreeSet::new).insert(embed);
self
}
pub fn embeds<I>(&mut self, iter: I) -> &mut Self
where
I: Iterator<Item = CategoryEmbeds>,
{
self.embed.get_or_insert_with(BTreeSet::new).extend(iter);
self
}
}
#[derive(Debug, Builder, Serialize, Clone)]
#[builder(setter(into, strip_option))]
#[serde(rename_all = "kebab-case")]
pub struct GameLevels<'a> {
#[doc = r"`ID` of the game to retrieve levels for."]
#[serde(skip)]
id: GameId<'a>,
#[doc = r"Sorting options for results."]
#[builder(default)]
orderby: Option<LevelsSorting>,
#[doc = r"Sort direction."]
#[builder(default)]
direction: Option<Direction>,
}
#[derive(Debug, Builder, Serialize, Clone)]
#[builder(setter(into, strip_option))]
#[serde(rename_all = "kebab-case")]
pub struct GameVariables<'a> {
#[doc = r"`ID` of the game to retrieve variables for."]
#[serde(skip)]
id: GameId<'a>,
#[doc = r"Sorting options for results."]
#[builder(default)]
orderby: Option<VariablesSorting>,
#[doc = r"Sort direction."]
#[builder(default)]
direction: Option<Direction>,
}
#[derive(Default, Clone)]
pub struct GameDerivedGamesBuilder<'a> {
id: Option<GameId<'a>>,
inner: GamesBuilder<'a>,
}
#[derive(Debug, Clone)]
pub struct GameDerivedGames<'a> {
id: GameId<'a>,
inner: Games<'a>,
}
#[derive(Debug, Builder, Serialize, Clone)]
#[builder(setter(into, strip_option))]
#[serde(rename_all = "kebab-case")]
pub struct GameRecords<'a> {
#[doc = r"`ID` of the game to retrieve records for."]
id: GameId<'a>,
#[doc = r"Return the `top` *places* (this can result in more than `top` runs!). Defaults to 3."]
#[builder(default)]
top: Option<i64>,
#[doc = r"When set to [`LeaderboardScope::FullGame`], only full-game categories will be included. When set to [`LeaderboardScope::Levels`] only individual levels are returned. Defaults to [`LeaderboardScope::All`]."]
#[builder(default)]
scope: Option<LeaderboardScope>,
#[doc = r"When `false`, miscellaneous categories will not be included in the results."]
#[builder(default)]
miscellaneous: Option<bool>,
#[doc = r"When `true`, empty leaderboards will not be included in the results."]
#[builder(default)]
skip_empty: Option<bool>,
#[builder(setter(name = "_embed"), private, default)]
#[serde(serialize_with = "super::utils::serialize_as_csv")]
#[serde(skip_serializing_if = "BTreeSet::is_empty")]
embed: BTreeSet<LeaderboardEmbeds>,
}
impl Games<'_> {
pub fn builder<'a>() -> GamesBuilder<'a> {
GamesBuilder::default()
}
}
impl GamesBuilder<'_> {
pub fn embed(&mut self, embed: GameEmbeds) -> &mut Self {
self.embed.get_or_insert_with(BTreeSet::new).insert(embed);
self
}
pub fn embeds<I>(&mut self, iter: I) -> &mut Self
where
I: Iterator<Item = GameEmbeds>,
{
self.embed.get_or_insert_with(BTreeSet::new).extend(iter);
self
}
}
impl Game<'_> {
pub fn builder<'a>() -> GameBuilder<'a> {
GameBuilder::default()
}
}
impl GameCategories<'_> {
pub fn builder<'a>() -> GameCategoriesBuilder<'a> {
GameCategoriesBuilder::default()
}
}
impl GameLevels<'_> {
pub fn builder<'a>() -> GameLevelsBuilder<'a> {
GameLevelsBuilder::default()
}
}
impl GameVariables<'_> {
pub fn builder<'a>() -> GameVariablesBuilder<'a> {
GameVariablesBuilder::default()
}
}
impl<'a> GameDerivedGamesBuilder<'a> {
pub fn id<S>(&mut self, value: S) -> &mut Self
where
S: Into<GameId<'a>>,
{
self.id = Some(value.into());
self
}
pub fn name<S>(&mut self, value: S) -> &mut Self
where
S: Into<Cow<'a, str>>,
{
self.inner.name(value);
self
}
pub fn abbreviation<S>(&mut self, value: S) -> &mut Self
where
S: Into<Cow<'a, str>>,
{
self.inner.abbreviation(value);
self
}
pub fn released<T>(&mut self, value: T) -> &mut Self
where
T: Into<i64>,
{
self.inner.released(value);
self
}
pub fn gametype<S>(&mut self, value: S) -> &mut Self
where
S: Into<GameTypeId<'a>>,
{
self.inner.gametype(value);
self
}
pub fn platform<S>(&mut self, value: S) -> &mut Self
where
S: Into<PlatformId<'a>>,
{
self.inner.platform(value);
self
}
pub fn region<S>(&mut self, value: S) -> &mut Self
where
S: Into<RegionId<'a>>,
{
self.inner.region(value);
self
}
pub fn genre<S>(&mut self, value: S) -> &mut Self
where
S: Into<GenreId<'a>>,
{
self.inner.genre(value);
self
}
pub fn engine<S>(&mut self, value: S) -> &mut Self
where
S: Into<EngineId<'a>>,
{
self.inner.engine(value);
self
}
pub fn developer<S>(&mut self, value: S) -> &mut Self
where
S: Into<DeveloperId<'a>>,
{
self.inner.developer(value);
self
}
pub fn publisher<S>(&mut self, value: S) -> &mut Self
where
S: Into<PublisherId<'a>>,
{
self.inner.publisher(value);
self
}
pub fn moderator<S>(&mut self, value: S) -> &mut Self
where
S: Into<UserId<'a>>,
{
self.inner.moderator(value);
self
}
pub fn bulk<T>(&mut self, value: T) -> &mut Self
where
T: Into<bool>,
{
self.inner.bulk(value);
self
}
pub fn orderby<V>(&mut self, value: V) -> &mut Self
where
V: Into<GamesSorting>,
{
self.inner.orderby(value);
self
}
pub fn direction<V>(&mut self, value: V) -> &mut Self
where
V: Into<Direction>,
{
self.inner.direction(value);
self
}
pub fn build(&self) -> Result<GameDerivedGames<'a>, GameDerivedGamesBuilderError> {
let inner = self.inner.build()?;
Ok(GameDerivedGames {
id: self
.id
.as_ref()
.cloned()
.ok_or(GameDerivedGamesBuilderError::UninitializedField("id"))?,
inner,
})
}
}
impl GameDerivedGames<'_> {
pub fn builder<'a>() -> GameDerivedGamesBuilder<'a> {
GameDerivedGamesBuilder::default()
}
}
impl GameRecords<'_> {
pub fn builder<'a>() -> GameRecordsBuilder<'a> {
GameRecordsBuilder::default()
}
}
impl GameRecordsBuilder<'_> {
pub fn embed(&mut self, embed: LeaderboardEmbeds) -> &mut Self {
self.embed.get_or_insert_with(BTreeSet::new).insert(embed);
self
}
pub fn embeds<I>(&mut self, iter: I) -> &mut Self
where
I: Iterator<Item = LeaderboardEmbeds>,
{
self.embed.get_or_insert_with(BTreeSet::new).extend(iter);
self
}
}
impl GameEmbeds {
fn as_str(&self) -> &'static str {
match self {
GameEmbeds::Levels => "levels",
GameEmbeds::Categories => "categories",
GameEmbeds::Moderators => "moderators",
GameEmbeds::Gametypes => "gametypes",
GameEmbeds::Platforms => "platforms",
GameEmbeds::Regions => "regions",
GameEmbeds::Genres => "genres",
GameEmbeds::Engines => "engines",
GameEmbeds::Developers => "developers",
GameEmbeds::Publishers => "publishers",
GameEmbeds::Variables => "variables",
}
}
}
impl Default for LevelsSorting {
fn default() -> Self {
Self::Pos
}
}
impl Endpoint for Games<'_> {
fn endpoint(&self) -> Cow<'static, str> {
"games".into()
}
fn query_parameters(&self) -> Result<QueryParams<'_>, BodyError> {
QueryParams::with(self)
}
}
impl Endpoint for Game<'_> {
fn endpoint(&self) -> Cow<'static, str> {
format!("/games/{}", self.id).into()
}
}
impl Endpoint for GameCategories<'_> {
fn endpoint(&self) -> Cow<'static, str> {
format!("/games/{}/categories", self.id).into()
}
fn query_parameters(&self) -> Result<QueryParams<'_>, BodyError> {
QueryParams::with(self)
}
}
impl Endpoint for GameLevels<'_> {
fn endpoint(&self) -> Cow<'static, str> {
format!("/games/{}/levels", self.id).into()
}
fn query_parameters(&self) -> Result<QueryParams<'_>, BodyError> {
QueryParams::with(self)
}
}
impl Endpoint for GameVariables<'_> {
fn endpoint(&self) -> Cow<'static, str> {
format!("/games/{}/variables", self.id).into()
}
fn query_parameters(&self) -> Result<QueryParams<'_>, BodyError> {
QueryParams::with(self)
}
}
impl Endpoint for GameDerivedGames<'_> {
fn endpoint(&self) -> Cow<'static, str> {
format!("/games/{}/derived-games", self.id).into()
}
fn query_parameters(&self) -> Result<QueryParams<'_>, BodyError> {
QueryParams::with(&self.inner)
}
}
impl Endpoint for GameRecords<'_> {
fn endpoint(&self) -> Cow<'static, str> {
format!("/games/{}/records", self.id).into()
}
fn query_parameters(&self) -> Result<QueryParams<'_>, BodyError> {
QueryParams::with(self)
}
}
impl From<&GameEmbeds> for &'static str {
fn from(value: &GameEmbeds) -> Self {
value.as_str()
}
}
impl Pageable for GameDerivedGames<'_> {}
impl Pageable for Games<'_> {}
impl Pageable for GameRecords<'_> {}