speedrun_api/api/
categories.rs

1//! # Categories
2//!
3//! Endpoints available for categories
4
5use std::{borrow::Cow, collections::BTreeSet, fmt::Display};
6
7use serde::{Deserialize, Serialize};
8
9use super::{
10    endpoint::Endpoint, error::BodyError, leaderboards::LeaderboardEmbeds,
11    query_params::QueryParams, Direction, Pageable, VariablesSorting,
12};
13
14/// Embeds available for categories.
15///
16/// NOTE: Embeds can be nested. That is not handled by this API.
17#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
18pub enum CategoryEmbeds {
19    /// Embed the `game` resource this category belongs to.
20    Game,
21    /// Embed `variables` applicable to this category
22    Variables,
23}
24
25/// Represents a category ID
26#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Hash)]
27pub struct CategoryId<'a>(Cow<'a, str>);
28
29impl<'a> CategoryId<'a> {
30    /// Create a new [`CategoryId`]
31    pub fn new<T>(id: T) -> Self
32    where
33        T: Into<Cow<'a, str>>,
34    {
35        Self(id.into())
36    }
37}
38
39impl<'a, T> From<T> for CategoryId<'a>
40where
41    T: Into<Cow<'a, str>>,
42{
43    fn from(value: T) -> Self {
44        CategoryId::new(value)
45    }
46}
47
48impl Display for CategoryId<'_> {
49    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50        write!(f, "{}", &self.0)
51    }
52}
53
54/// Retrieves a single category, identified by it's ID
55#[derive(Debug, Builder, Serialize, Clone)]
56#[builder(setter(into, strip_option))]
57pub struct Category<'a> {
58    #[serde(skip)]
59    #[doc = r"`ID` of this category."]
60    id: CategoryId<'a>,
61    #[builder(setter(name = "_embed"), private, default)]
62    #[serde(serialize_with = "super::utils::serialize_as_csv")]
63    #[serde(skip_serializing_if = "BTreeSet::is_empty")]
64    embed: BTreeSet<CategoryEmbeds>,
65}
66
67/// Retrieves all variables that are applicable to the category identified by
68/// ID.
69#[derive(Debug, Builder, Serialize, Clone)]
70#[builder(setter(into, strip_option))]
71#[serde(rename_all = "kebab-case")]
72pub struct CategoryVariables<'a> {
73    #[serde(skip)]
74    #[doc = r"`ID` of the category to retrieve variables for."]
75    id: CategoryId<'a>,
76    #[doc = r"Sorting options for results."]
77    #[builder(default)]
78    orderby: Option<VariablesSorting>,
79    #[doc = r"Sort direction"]
80    #[builder(default)]
81    direction: Option<Direction>,
82}
83
84/// Retrieves the records for the given category id.
85#[derive(Debug, Builder, Serialize, Clone)]
86#[builder(setter(into, strip_option))]
87#[serde(rename_all = "kebab-case")]
88pub struct CategoryRecords<'a> {
89    #[serde(skip)]
90    #[doc = r"`ID` for the category."]
91    id: CategoryId<'a>,
92    #[doc = r"Return `top` number of places (default: 3)."]
93    #[builder(default)]
94    top: Option<u32>,
95    #[doc = r"Do not return empty leaderboards when `true`."]
96    #[builder(default)]
97    skip_empty: Option<bool>,
98    #[builder(setter(name = "_embed"), private, default)]
99    #[serde(serialize_with = "super::utils::serialize_as_csv")]
100    #[serde(skip_serializing_if = "BTreeSet::is_empty")]
101    embed: BTreeSet<LeaderboardEmbeds>,
102}
103
104impl Category<'_> {
105    /// Create a builder for this endpoint.
106    pub fn builder<'a>() -> CategoryBuilder<'a> {
107        CategoryBuilder::default()
108    }
109}
110
111impl CategoryBuilder<'_> {
112    /// Add an embedded resource to this result
113    pub fn embed(&mut self, embed: CategoryEmbeds) -> &mut Self {
114        self.embed.get_or_insert_with(BTreeSet::new).insert(embed);
115        self
116    }
117
118    /// Add multiple embedded resources to this result
119    pub fn embeds<I>(&mut self, iter: I) -> &mut Self
120    where
121        I: Iterator<Item = CategoryEmbeds>,
122    {
123        self.embed.get_or_insert_with(BTreeSet::new).extend(iter);
124        self
125    }
126}
127
128impl CategoryVariables<'_> {
129    /// Create a builder for this endpoint
130    pub fn builder<'a>() -> CategoryVariablesBuilder<'a> {
131        CategoryVariablesBuilder::default()
132    }
133}
134
135impl CategoryRecords<'_> {
136    /// Create a builder for this endpoint
137    pub fn builder<'a>() -> CategoryRecordsBuilder<'a> {
138        CategoryRecordsBuilder::default()
139    }
140}
141
142impl CategoryRecordsBuilder<'_> {
143    /// Add an embedded resource to this result
144    pub fn embed(&mut self, embed: LeaderboardEmbeds) -> &mut Self {
145        self.embed.get_or_insert_with(BTreeSet::new).insert(embed);
146        self
147    }
148
149    /// Add multiple embedded resources to this result
150    pub fn embeds<I>(&mut self, iter: I) -> &mut Self
151    where
152        I: Iterator<Item = LeaderboardEmbeds>,
153    {
154        self.embed.get_or_insert_with(BTreeSet::new).extend(iter);
155        self
156    }
157}
158
159impl CategoryEmbeds {
160    fn as_str(&self) -> &'static str {
161        match self {
162            CategoryEmbeds::Game => "game",
163            CategoryEmbeds::Variables => "variables",
164        }
165    }
166}
167
168impl Endpoint for Category<'_> {
169    fn endpoint(&self) -> Cow<'static, str> {
170        format!("/categories/{}", self.id).into()
171    }
172
173    fn query_parameters(&self) -> Result<QueryParams<'_>, BodyError> {
174        QueryParams::with(self)
175    }
176}
177
178impl Endpoint for CategoryVariables<'_> {
179    fn endpoint(&self) -> Cow<'static, str> {
180        format!("/categories/{}/variables", self.id).into()
181    }
182
183    fn query_parameters(&self) -> Result<QueryParams<'_>, BodyError> {
184        QueryParams::with(self)
185    }
186}
187
188impl Endpoint for CategoryRecords<'_> {
189    fn endpoint(&self) -> Cow<'static, str> {
190        format!("/categories/{}/records", self.id).into()
191    }
192
193    fn query_parameters(&self) -> Result<QueryParams<'_>, BodyError> {
194        QueryParams::with(self)
195    }
196}
197
198impl From<&CategoryEmbeds> for &'static str {
199    fn from(value: &CategoryEmbeds) -> Self {
200        value.as_str()
201    }
202}
203
204impl Pageable for CategoryRecords<'_> {}