1use std::{
6 borrow::Cow,
7 collections::{BTreeSet, HashMap},
8};
9
10use serde::Serialize;
11
12use crate::{
13 api::{endpoint::Endpoint, error::BodyError},
14 types::TimingMethod,
15};
16
17use super::{
18 categories::CategoryId,
19 games::GameId,
20 levels::LevelId,
21 platforms::PlatformId,
22 query_params::QueryParams,
23 regions::RegionId,
24 variables::{ValueId, VariableId},
25};
26
27#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
31pub enum LeaderboardEmbeds {
32 Game,
34 Category,
36 Level,
38 Players,
41 Regions,
43 Platforms,
45 Variables,
47}
48
49#[derive(Debug, Builder, Serialize, Clone)]
51#[builder(setter(into, strip_option))]
52#[serde(rename_all = "kebab-case")]
53pub struct FullGameLeaderboard<'a> {
54 #[doc = r"Game `ID` or abbreviation."]
55 #[serde(skip)]
56 game: GameId<'a>,
57 #[doc = r"Category `ID` or abbreviation."]
58 #[serde(skip)]
59 category: CategoryId<'a>,
60
61 #[doc = r"Only return `top` places."]
63 #[builder(default)]
64 top: Option<i64>,
65 #[doc = r"Only return runs done on `platform`."]
66 #[builder(default)]
67 platform: Option<PlatformId<'a>>,
68 #[doc = r"Only return runs done in `region`."]
69 #[builder(default)]
70 region: Option<RegionId<'a>>,
71 #[doc = r"When unset, real devices and emulator results are returned. When `true` only emulator runs are returned, otherwise only real deivces are returned."]
72 #[builder(default)]
73 emulators: Option<bool>,
74 #[doc = r"When `true` only runs with videos will be returned. (default: `false`)"]
75 #[builder(default)]
76 video_only: Option<bool>,
77 #[doc = r"What [`TimingMethod`] to use to determine the sorting of runs."]
78 #[builder(default)]
79 timing: Option<TimingMethod>,
80 #[doc = r"Only return runs done on or before this date. [ISO 8601 date string](https://en.wikipedia.org/wiki/ISO_8601#Dates)."]
81 #[builder(default)]
82 date: Option<String>,
83 #[builder(setter(name = "_variables"), private, default)]
84 #[serde(skip)]
85 variables: HashMap<VariableId<'a>, ValueId<'a>>,
86 #[builder(setter(name = "_embed"), private, default)]
87 #[serde(serialize_with = "super::utils::serialize_as_csv")]
88 #[serde(skip_serializing_if = "BTreeSet::is_empty")]
89 embed: BTreeSet<LeaderboardEmbeds>,
90}
91
92#[derive(Debug, Builder, Serialize, Clone)]
95#[builder(setter(into, strip_option))]
96#[serde(rename_all = "kebab-case")]
97pub struct IndividualLevelLeaderboard<'a> {
98 #[doc = r"Game `ID` or abbreviation."]
99 #[serde(skip)]
100 game: GameId<'a>,
101 #[doc = r"Level `ID` or abbreviation."]
102 #[serde(skip)]
103 level: LevelId<'a>,
104 #[doc = r"Category `ID` or abbreviation."]
105 #[serde(skip)]
106 category: CategoryId<'a>,
107
108 #[doc = r"Only return `top` places."]
110 #[builder(default)]
111 top: Option<i64>,
112 #[doc = r"Only return runs done on `platform`."]
113 #[builder(default)]
114 platform: Option<PlatformId<'a>>,
115 #[doc = r"Only return runs done in `region`."]
116 #[builder(default)]
117 region: Option<RegionId<'a>>,
118 #[doc = r"When unset, real devices and emulator results are returned. When `true` only emulator runs are returned, otherwise only real deivces are returned."]
119 #[builder(default)]
120 emulators: Option<bool>,
121 #[doc = r"When `true` only runs with videos will be returned. (default: `false`)"]
122 #[builder(default)]
123 video_only: Option<bool>,
124 #[doc = r"What [`TimingMethod`] to use to determine the sorting of runs."]
125 #[builder(default)]
126 timing: Option<TimingMethod>,
127 #[doc = r"Only return runs done on or before this date. [ISO 8601 date string](https://en.wikipedia.org/wiki/ISO_8601#Dates)."]
128 #[builder(default)]
129 date: Option<String>,
130 #[builder(setter(name = "_variables"), private, default)]
131 #[serde(skip)]
132 variables: HashMap<VariableId<'a>, ValueId<'a>>,
133 #[builder(setter(name = "_embed"), private, default)]
134 #[serde(serialize_with = "super::utils::serialize_as_csv")]
135 #[serde(skip_serializing_if = "BTreeSet::is_empty")]
136 embed: BTreeSet<LeaderboardEmbeds>,
137}
138
139impl FullGameLeaderboard<'_> {
140 pub fn builder<'a>() -> FullGameLeaderboardBuilder<'a> {
142 FullGameLeaderboardBuilder::default()
143 }
144}
145
146impl<'a> FullGameLeaderboardBuilder<'a> {
147 pub fn embed(&mut self, embed: LeaderboardEmbeds) -> &mut Self {
149 self.embed.get_or_insert_with(BTreeSet::new).insert(embed);
150 self
151 }
152
153 pub fn embeds<I>(&mut self, iter: I) -> &mut Self
155 where
156 I: Iterator<Item = LeaderboardEmbeds>,
157 {
158 self.embed.get_or_insert_with(BTreeSet::new).extend(iter);
159 self
160 }
161
162 pub fn variable<Var, Val>(&mut self, variable: Var, value: Val) -> &mut Self
164 where
165 Var: Into<VariableId<'a>>,
166 Val: Into<ValueId<'a>>,
167 {
168 self.variables
169 .get_or_insert_with(HashMap::new)
170 .insert(variable.into(), value.into());
171 self
172 }
173
174 pub fn variables<I, Var, Val>(&mut self, iter: I) -> &mut Self
176 where
177 I: IntoIterator<Item = (Var, Val)>,
178 Var: Into<VariableId<'a>>,
179 Val: Into<ValueId<'a>>,
180 {
181 self.variables
182 .get_or_insert_with(HashMap::new)
183 .extend(iter.into_iter().map(|(k, v)| (k.into(), v.into())));
184 self
185 }
186}
187
188impl IndividualLevelLeaderboard<'_> {
189 pub fn builder<'a>() -> IndividualLevelLeaderboardBuilder<'a> {
191 IndividualLevelLeaderboardBuilder::default()
192 }
193}
194
195impl<'a> IndividualLevelLeaderboardBuilder<'a> {
196 pub fn embed(&mut self, embed: LeaderboardEmbeds) -> &mut Self {
198 self.embed.get_or_insert_with(BTreeSet::new).insert(embed);
199 self
200 }
201
202 pub fn embeds<I>(&mut self, iter: I) -> &mut Self
204 where
205 I: Iterator<Item = LeaderboardEmbeds>,
206 {
207 self.embed.get_or_insert_with(BTreeSet::new).extend(iter);
208 self
209 }
210
211 pub fn variable<Var, Val>(&mut self, variable: Var, value: Val) -> &mut Self
213 where
214 Var: Into<VariableId<'a>>,
215 Val: Into<ValueId<'a>>,
216 {
217 self.variables
218 .get_or_insert_with(HashMap::new)
219 .insert(variable.into(), value.into());
220 self
221 }
222
223 pub fn variables<I, Var, Val>(&mut self, iter: I) -> &mut Self
225 where
226 I: IntoIterator<Item = (Var, Val)>,
227 Var: Into<VariableId<'a>>,
228 Val: Into<ValueId<'a>>,
229 {
230 self.variables
231 .get_or_insert_with(HashMap::new)
232 .extend(iter.into_iter().map(|(k, v)| (k.into(), v.into())));
233 self
234 }
235}
236
237impl LeaderboardEmbeds {
238 fn as_str(&self) -> &'static str {
239 match self {
240 LeaderboardEmbeds::Game => "game",
241 LeaderboardEmbeds::Category => "category",
242 LeaderboardEmbeds::Level => "level",
243 LeaderboardEmbeds::Players => "players",
244 LeaderboardEmbeds::Regions => "regions",
245 LeaderboardEmbeds::Platforms => "platforms",
246 LeaderboardEmbeds::Variables => "variables",
247 }
248 }
249}
250
251impl Endpoint for FullGameLeaderboard<'_> {
252 fn endpoint(&self) -> Cow<'static, str> {
253 format!("/leaderboards/{}/category/{}", self.game, self.category).into()
254 }
255
256 fn query_parameters(&self) -> Result<QueryParams<'_>, BodyError> {
257 let mut params = QueryParams::with(self)?;
258 params.extend_pairs(
259 self.variables
260 .iter()
261 .map(|(var, val)| (format!("var-{var}"), val.to_string())),
262 );
263 Ok(params)
264 }
265}
266
267impl Endpoint for IndividualLevelLeaderboard<'_> {
268 fn endpoint(&self) -> Cow<'static, str> {
269 format!(
270 "/leaderboards/{}/level/{}/{}",
271 self.game, self.level, self.category
272 )
273 .into()
274 }
275
276 fn query_parameters(&self) -> Result<QueryParams<'_>, BodyError> {
277 let mut params = QueryParams::with(self)?;
278 params.extend_pairs(
279 self.variables
280 .iter()
281 .map(|(var, val)| (format!("var-{var}"), val.to_string())),
282 );
283 Ok(params)
284 }
285}
286
287impl From<&LeaderboardEmbeds> for &'static str {
288 fn from(value: &LeaderboardEmbeds) -> Self {
289 value.as_str()
290 }
291}