speedrun_api/api/
categories.rs1use 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#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
18pub enum CategoryEmbeds {
19 Game,
21 Variables,
23}
24
25#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Hash)]
27pub struct CategoryId<'a>(Cow<'a, str>);
28
29impl<'a> CategoryId<'a> {
30 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#[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#[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#[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 pub fn builder<'a>() -> CategoryBuilder<'a> {
107 CategoryBuilder::default()
108 }
109}
110
111impl CategoryBuilder<'_> {
112 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 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 pub fn builder<'a>() -> CategoryVariablesBuilder<'a> {
131 CategoryVariablesBuilder::default()
132 }
133}
134
135impl CategoryRecords<'_> {
136 pub fn builder<'a>() -> CategoryRecordsBuilder<'a> {
138 CategoryRecordsBuilder::default()
139 }
140}
141
142impl CategoryRecordsBuilder<'_> {
143 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 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<'_> {}