1use crate::{
6 get_json, get_response,
7 wrapper::{
8 ContainsChatMessage, ContainsChatMessages, ContainsEntries, ContainsEntry, ContainsRace,
9 ContainsRaces,
10 },
11 Attachment, ChatMessage, Client, Entry, Error, Race, Visibility,
12};
13use reqwest::Url;
14use std::ops::Deref;
15use uuid::Uuid;
16
17impl Race {
18 pub async fn get_active(client: &Client) -> Result<Vec<Race>, Error> {
20 self::get_active(client).await
21 }
22
23 pub async fn get(client: &Client, id: Uuid) -> Result<Race, Error> {
25 self::get(client, id).await
26 }
27
28 pub async fn create(client: &Client, settings: Settings<'_>) -> Result<Race, Error> {
30 self::create(client, settings).await
31 }
32
33 pub async fn update(
35 &self,
36 client: &Client,
37 settings: UpdateSettings<'_>,
38 ) -> Result<Race, Error> {
39 self::update(client, self.id, settings).await
40 }
41
42 pub async fn entries(&self, client: &Client) -> Result<Vec<Entry>, Error> {
44 self::get_entries(client, self.id).await
45 }
46
47 pub async fn my_entry(&self, client: &Client) -> Result<Entry, Error> {
49 self::get_entry(client, self.id).await
50 }
51
52 pub async fn join(
54 &self,
55 client: &Client,
56 join_as: JoinAs<'_>,
57 join_token: Option<&str>,
58 ) -> Result<Entry, Error> {
59 self::join(client, self.id, join_as, join_token).await
60 }
61
62 pub async fn leave(&self, client: &Client, entry_id: Uuid) -> Result<(), Error> {
64 self::leave(client, self.id, entry_id).await
65 }
66
67 pub async fn ready_up(&self, client: &Client, entry_id: Uuid) -> Result<Entry, Error> {
69 self::ready_up(client, self.id, entry_id).await
70 }
71
72 pub async fn unready(&self, client: &Client, entry_id: Uuid) -> Result<Entry, Error> {
74 self::unready(client, self.id, entry_id).await
75 }
76
77 pub async fn finish(&self, client: &Client, entry_id: Uuid) -> Result<Entry, Error> {
79 self::finish(client, self.id, entry_id).await
80 }
81
82 pub async fn undo_finish(&self, client: &Client, entry_id: Uuid) -> Result<Entry, Error> {
84 self::undo_finish(client, self.id, entry_id).await
85 }
86
87 pub async fn forfeit(&self, client: &Client, entry_id: Uuid) -> Result<Entry, Error> {
89 self::forfeit(client, self.id, entry_id).await
90 }
91
92 pub async fn undo_forfeit(&self, client: &Client, entry_id: Uuid) -> Result<Entry, Error> {
94 self::undo_forfeit(client, self.id, entry_id).await
95 }
96
97 pub async fn chat_messages(&self, client: &Client) -> Result<Vec<ChatMessage>, Error> {
99 self::get_chat(client, self.id).await
100 }
101
102 pub async fn send_chat_message(
104 &self,
105 client: &Client,
106 message: &str,
107 ) -> Result<ChatMessage, Error> {
108 self::send_chat_message(client, self.id, message).await
109 }
110}
111
112impl Attachment {
113 pub async fn download(&self, client: &Client) -> Result<impl Deref<Target = [u8]>, Error> {
115 get_response(client, client.client.get(&*self.url))
116 .await?
117 .bytes()
118 .await
119 .map_err(|source| Error::Download { source })
120 }
121}
122
123pub async fn get_active(client: &Client) -> Result<Vec<Race>, Error> {
125 let ContainsRaces { races } =
126 get_json(client, client.client.get("https://splits.io/api/v4/races")).await?;
127
128 Ok(races)
129}
130
131pub async fn get(client: &Client, id: Uuid) -> Result<Race, Error> {
135 let mut url = Url::parse("https://splits.io/api/v4/races").unwrap();
136 url.path_segments_mut()
137 .unwrap()
138 .push(id.hyphenated().encode_lower(&mut Uuid::encode_buffer()));
139
140 let ContainsRace { race } = get_json(client, client.client.get(url)).await?;
141
142 Ok(race)
143}
144
145#[derive(Default, serde_derive::Serialize)]
147pub struct Settings<'a> {
148 #[serde(skip_serializing_if = "Option::is_none")]
150 pub game_id: Option<&'a str>,
151 #[serde(skip_serializing_if = "Option::is_none")]
153 pub category_id: Option<&'a str>,
154 #[serde(skip_serializing_if = "Option::is_none")]
156 pub notes: Option<&'a str>,
157 #[serde(skip_serializing_if = "Option::is_none")]
159 pub visibility: Option<Visibility>,
160}
161
162#[derive(Default)]
164pub enum Update<T> {
165 #[default]
167 Keep,
168 Clear,
170 Set(T),
172}
173
174impl<T> Update<T> {
175 const fn is_keep(&self) -> bool {
176 matches!(self, Update::Keep)
177 }
178}
179
180impl<T: serde::Serialize> serde::Serialize for Update<T> {
181 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
182 where
183 S: serde::Serializer,
184 {
185 match self {
186 Update::Set(val) => serializer.serialize_some(val),
187 _ => serializer.serialize_none(),
188 }
189 }
190}
191
192#[derive(Default, serde_derive::Serialize)]
194pub struct UpdateSettings<'a> {
195 #[serde(skip_serializing_if = "Update::is_keep")]
197 pub game_id: Update<&'a str>,
198 #[serde(skip_serializing_if = "Update::is_keep")]
200 pub category_id: Update<&'a str>,
201 #[serde(skip_serializing_if = "Update::is_keep")]
203 pub notes: Update<&'a str>,
204 #[serde(skip_serializing_if = "Option::is_none")]
206 pub visibility: Option<Visibility>,
207}
208
209pub async fn create(client: &Client, settings: Settings<'_>) -> Result<Race, Error> {
211 let ContainsRace { race } = get_json(
212 client,
213 client
214 .client
215 .post("https://splits.io/api/v4/races")
216 .json(&settings),
217 )
218 .await?;
219
220 Ok(race)
221}
222
223pub async fn update(
225 client: &Client,
226 id: Uuid,
227 settings: UpdateSettings<'_>,
228) -> Result<Race, Error> {
229 let mut url = Url::parse("https://splits.io/api/v4/races").unwrap();
230 url.path_segments_mut()
231 .unwrap()
232 .push(id.hyphenated().encode_lower(&mut Uuid::encode_buffer()));
233
234 let ContainsRace { race } = get_json(client, client.client.patch(url).json(&settings)).await?;
235
236 Ok(race)
237}
238
239pub async fn get_entries(client: &Client, id: Uuid) -> Result<Vec<Entry>, Error> {
241 let mut url = Url::parse("https://splits.io/api/v4/races").unwrap();
242 url.path_segments_mut().unwrap().extend(&[
243 id.hyphenated().encode_lower(&mut Uuid::encode_buffer()),
244 "entries",
245 ]);
246
247 let ContainsEntries { entries } = get_json(client, client.client.get(url)).await?;
248
249 Ok(entries)
250}
251
252pub async fn get_entry(client: &Client, id: Uuid) -> Result<Entry, Error> {
254 let mut url = Url::parse("https://splits.io/api/v4/races").unwrap();
255 url.path_segments_mut().unwrap().extend(&[
256 id.hyphenated().encode_lower(&mut Uuid::encode_buffer()),
257 "entry",
258 ]);
259
260 let ContainsEntry { entry } = get_json(client, client.client.get(url)).await?;
261
262 Ok(entry)
263}
264
265pub enum JoinAs<'a> {
267 Myself,
269 Ghost(&'a str),
271}
272
273#[derive(serde_derive::Serialize)]
274struct JoinToken<'a> {
275 #[serde(skip_serializing_if = "Option::is_none")]
276 join_token: Option<&'a str>,
277 #[serde(skip_serializing_if = "Option::is_none")]
278 entry: Option<JoinEntry<'a>>,
279}
280
281#[derive(serde_derive::Serialize)]
282struct JoinEntry<'a> {
283 run_id: &'a str,
284}
285
286pub async fn join(
288 client: &Client,
289 race_id: Uuid,
290 join_as: JoinAs<'_>,
291 join_token: Option<&str>,
292) -> Result<Entry, Error> {
293 let mut url = Url::parse("https://splits.io/api/v4/races").unwrap();
294 url.path_segments_mut().unwrap().extend(&[
295 race_id
296 .hyphenated()
297 .encode_lower(&mut Uuid::encode_buffer()),
298 "entries",
299 ]);
300
301 let ContainsEntry { entry } = get_json(
302 client,
303 client.client.post(url).json(&JoinToken {
304 join_token,
305 entry: match join_as {
306 JoinAs::Myself => None,
307 JoinAs::Ghost(run_id) => Some(JoinEntry { run_id }),
308 },
309 }),
310 )
311 .await?;
312
313 Ok(entry)
314}
315
316pub async fn leave(client: &Client, race_id: Uuid, entry_id: Uuid) -> Result<(), Error> {
318 let mut url = Url::parse("https://splits.io/api/v4/races").unwrap();
319 url.path_segments_mut().unwrap().extend(&[
320 race_id
321 .hyphenated()
322 .encode_lower(&mut Uuid::encode_buffer()),
323 "entries",
324 entry_id
325 .hyphenated()
326 .encode_lower(&mut Uuid::encode_buffer()),
327 ]);
328
329 get_response(client, client.client.delete(url)).await?;
330
331 Ok(())
332}
333
334#[derive(serde_derive::Serialize)]
335struct UpdateEntry<T> {
336 entry: T,
337}
338
339#[derive(serde_derive::Serialize)]
340struct ReadyState {
341 readied_at: Option<&'static str>,
342}
343
344#[derive(serde_derive::Serialize)]
345struct FinishState {
346 finished_at: Option<&'static str>,
347}
348
349#[derive(serde_derive::Serialize)]
350struct ForfeitState {
351 forfeited_at: Option<&'static str>,
352}
353
354pub async fn ready_up(client: &Client, race_id: Uuid, entry_id: Uuid) -> Result<Entry, Error> {
356 let mut url = Url::parse("https://splits.io/api/v4/races").unwrap();
357 url.path_segments_mut().unwrap().extend(&[
358 race_id
359 .hyphenated()
360 .encode_lower(&mut Uuid::encode_buffer()),
361 "entries",
362 entry_id
363 .hyphenated()
364 .encode_lower(&mut Uuid::encode_buffer()),
365 ]);
366
367 let ContainsEntry { entry } = get_json(
368 client,
369 client.client.patch(url).json(&UpdateEntry {
370 entry: ReadyState {
371 readied_at: Some("now"),
372 },
373 }),
374 )
375 .await?;
376
377 Ok(entry)
378}
379
380pub async fn unready(client: &Client, race_id: Uuid, entry_id: Uuid) -> Result<Entry, Error> {
382 let mut url = Url::parse("https://splits.io/api/v4/races").unwrap();
383 url.path_segments_mut().unwrap().extend(&[
384 race_id
385 .hyphenated()
386 .encode_lower(&mut Uuid::encode_buffer()),
387 "entries",
388 entry_id
389 .hyphenated()
390 .encode_lower(&mut Uuid::encode_buffer()),
391 ]);
392
393 let ContainsEntry { entry } = get_json(
394 client,
395 client.client.patch(url).json(&UpdateEntry {
396 entry: ReadyState { readied_at: None },
397 }),
398 )
399 .await?;
400
401 Ok(entry)
402}
403
404pub async fn finish(client: &Client, race_id: Uuid, entry_id: Uuid) -> Result<Entry, Error> {
406 let mut url = Url::parse("https://splits.io/api/v4/races").unwrap();
407 url.path_segments_mut().unwrap().extend(&[
408 race_id
409 .hyphenated()
410 .encode_lower(&mut Uuid::encode_buffer()),
411 "entries",
412 entry_id
413 .hyphenated()
414 .encode_lower(&mut Uuid::encode_buffer()),
415 ]);
416
417 let ContainsEntry { entry } = get_json(
418 client,
419 client.client.patch(url).json(&UpdateEntry {
420 entry: FinishState {
421 finished_at: Some("now"),
422 },
423 }),
424 )
425 .await?;
426
427 Ok(entry)
428}
429
430pub async fn undo_finish(client: &Client, race_id: Uuid, entry_id: Uuid) -> Result<Entry, Error> {
432 let mut url = Url::parse("https://splits.io/api/v4/races").unwrap();
433 url.path_segments_mut().unwrap().extend(&[
434 race_id
435 .hyphenated()
436 .encode_lower(&mut Uuid::encode_buffer()),
437 "entries",
438 entry_id
439 .hyphenated()
440 .encode_lower(&mut Uuid::encode_buffer()),
441 ]);
442
443 let ContainsEntry { entry } = get_json(
444 client,
445 client.client.patch(url).json(&UpdateEntry {
446 entry: FinishState { finished_at: None },
447 }),
448 )
449 .await?;
450
451 Ok(entry)
452}
453
454pub async fn forfeit(client: &Client, race_id: Uuid, entry_id: Uuid) -> Result<Entry, Error> {
456 let mut url = Url::parse("https://splits.io/api/v4/races").unwrap();
457 url.path_segments_mut().unwrap().extend(&[
458 race_id
459 .hyphenated()
460 .encode_lower(&mut Uuid::encode_buffer()),
461 "entries",
462 entry_id
463 .hyphenated()
464 .encode_lower(&mut Uuid::encode_buffer()),
465 ]);
466
467 let ContainsEntry { entry } = get_json(
468 client,
469 client.client.patch(url).json(&UpdateEntry {
470 entry: ForfeitState {
471 forfeited_at: Some("now"),
472 },
473 }),
474 )
475 .await?;
476
477 Ok(entry)
478}
479
480pub async fn undo_forfeit(client: &Client, race_id: Uuid, entry_id: Uuid) -> Result<Entry, Error> {
482 let mut url = Url::parse("https://splits.io/api/v4/races").unwrap();
483 url.path_segments_mut().unwrap().extend(&[
484 race_id
485 .hyphenated()
486 .encode_lower(&mut Uuid::encode_buffer()),
487 "entries",
488 entry_id
489 .hyphenated()
490 .encode_lower(&mut Uuid::encode_buffer()),
491 ]);
492
493 let ContainsEntry { entry } = get_json(
494 client,
495 client.client.patch(url).json(&UpdateEntry {
496 entry: ForfeitState { forfeited_at: None },
497 }),
498 )
499 .await?;
500
501 Ok(entry)
502}
503
504pub async fn get_chat(client: &Client, id: Uuid) -> Result<Vec<ChatMessage>, Error> {
506 let mut url = Url::parse("https://splits.io/api/v4/races").unwrap();
507 url.path_segments_mut().unwrap().extend(&[
508 id.hyphenated().encode_lower(&mut Uuid::encode_buffer()),
509 "chat",
510 ]);
511
512 let ContainsChatMessages { chat_messages } = get_json(client, client.client.get(url)).await?;
513
514 Ok(chat_messages)
515}
516
517#[derive(serde_derive::Serialize)]
518struct SendMessage<'a> {
519 chat_message: SendMessageBody<'a>,
520}
521
522#[derive(serde_derive::Serialize)]
523struct SendMessageBody<'a> {
524 body: &'a str,
525}
526
527pub async fn send_chat_message(
529 client: &Client,
530 id: Uuid,
531 message: &str,
532) -> Result<ChatMessage, Error> {
533 let mut url = Url::parse("https://splits.io/api/v4/races").unwrap();
534 url.path_segments_mut().unwrap().extend(&[
535 id.hyphenated().encode_lower(&mut Uuid::encode_buffer()),
536 "chat",
537 ]);
538
539 let ContainsChatMessage { chat_message } = get_json(
540 client,
541 client.client.post(url).json(&SendMessage {
542 chat_message: SendMessageBody { body: message },
543 }),
544 )
545 .await?;
546
547 Ok(chat_message)
548}