brawl_api/model/brawlers.rs
1//! Contains models related to the `/brawlers/...` endpoint of the Brawl Stars API.
2//! Included by the feature `"brawlers"`; removing that feature will disable the usage of this
3//! module.
4
5use std::ops::{Deref, DerefMut};
6use crate::traits::{FetchFrom, Refetchable};
7use crate::http::routes::Route;
8use crate::util::{fetch_route, a_fetch_route};
9use serde::{self, Serialize, Deserialize};
10use crate::error::Result;
11
12#[cfg(feature = "async")]
13use async_trait::async_trait;
14use crate::http::Client;
15
16use super::common::StarPower;
17
18#[cfg(feature = "players")]
19use super::players::{
20 player::PlayerBrawlerStat,
21 battlelog::BattleBrawler,
22};
23use crate::Brawlers;
24
25// region:BrawlerList
26
27/// Represents a fetchable list of all brawlers in the game, with data for each of them, such
28/// as their available star powers, names etc.
29///
30/// Use [`BrawlerList::fetch`] to fetch all brawlers.
31///
32/// [`BrawlerList::fetch`]: #method.fetch
33#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
34pub struct BrawlerList {
35 /// The brawlers in the game.
36 #[serde(default)]
37 pub items: Vec<Brawler>
38}
39
40impl Deref for BrawlerList {
41 type Target = Vec<Brawler>;
42
43 /// Obtain the brawlers vector - dereferencing returns the [`items`] field.
44 ///
45 /// # Examples
46 ///
47 /// ```rust,ignore
48 /// use brawl_api::{Client, BrawlerList, traits::*};
49 ///
50 /// # fn main() -> Result<(), Box<dyn ::std::error::Error>> {
51 /// let client = Client::new("my auth token");
52 /// let brawlers = BrawlerList::fetch(
53 /// &client, // <- the client containing the auth key
54 /// )?;
55 ///
56 /// assert_eq!(brawlers.items, *brawlers);
57 ///
58 /// # Ok(())
59 /// # }
60 ///
61 /// ```
62 ///
63 /// [`items`]: #structfield.items
64 fn deref(&self) -> &Vec<Brawler> {
65 &self.items
66 }
67}
68
69impl DerefMut for BrawlerList {
70 /// Obtain the brawlers vector - dereferencing returns the [`items`] field.
71 ///
72 /// # Examples
73 ///
74 /// ```rust,ignore
75 /// use brawl_api::{Client, BrawlerList, traits::*};
76 ///
77 /// # fn main() -> Result<(), Box<dyn ::std::error::Error>> {
78 /// let client = Client::new("my auth token");
79 /// let brawlers = BrawlerList::fetch(
80 /// &client, // <- the client containing the auth key
81 /// )?;
82 ///
83 /// assert_eq!(brawlers.items, *brawlers);
84 ///
85 /// # Ok(())
86 /// # }
87 ///
88 /// ```
89 ///
90 /// [`items`]: #structfield.items
91 fn deref_mut(&mut self) -> &mut Vec<Brawler> {
92 &mut self.items
93 }
94}
95
96impl BrawlerList {
97
98 /// Returns the [`Route`] object required for fetching a `BrawlerList` instance.
99 ///
100 /// # Examples
101 ///
102 /// ```rust
103 /// use brawl_api::{BrawlerList, http::Route};
104 ///
105 /// assert_eq!(
106 /// BrawlerList::get_route(),
107 /// Route::Brawlers
108 /// );
109 /// ```
110 ///
111 /// [`Route`]: http/routes/struct.Route.html
112 pub fn get_route() -> Route {
113 Route::Brawlers
114 }
115
116 /// (Sync) Fetches data for all brawlers in the game (see [`Brawler`]). To fetch for a specific
117 /// brawler, see [`Brawler::fetch`].
118 ///
119 /// # Errors
120 ///
121 /// This function may error:
122 /// - While requesting (will return an [`Error::Request`]);
123 /// - After receiving a bad status code (API or other error - returns an [`Error::Status`]);
124 /// - After a ratelimit is indicated by the API, while also specifying when it is lifted ([`Error::Ratelimited`]);
125 /// - While parsing incoming JSON (will return an [`Error::Json`]).
126 ///
127 /// (All of those, of course, wrapped inside an `Err`.)
128 ///
129 /// # Examples
130 ///
131 /// ```rust,ignore
132 /// use brawl_api::{Client, BrawlerList};
133 ///
134 /// # fn main() -> Result<(), Box<dyn ::std::error::Error>> {
135 /// let my_client = Client::new("my auth token");
136 /// let brawlers = BrawlerList::fetch(&my_client)?;
137 /// // now a vector with data for all brawlers in the game is available for use.
138 ///
139 /// # Ok(())
140 /// # }
141 /// ```
142 ///
143 /// [`Error::Request`]: error/enum.Error.html#variant.Request
144 /// [`Error::Status`]: error/enum.Error.html#variant.Status
145 /// [`Error::Ratelimited`]: error/enum.Error.html#variant.Ratelimited
146 /// [`Error::Json`]: error/enum.Error.html#variant.Json
147 /// [`Brawler`]: struct.Brawler.html
148 /// [`Brawler::fetch`]: struct.Brawler.html#method.fetch
149 pub fn fetch(client: &Client) -> Result<BrawlerList> {
150 let route = BrawlerList::get_route();
151 fetch_route::<BrawlerList>(client, &route)
152 }
153
154 /// (Sync) Fetches data for all brawlers in the game (see [`Brawler`]). To fetch for a specific
155 /// brawler, see [`Brawler::fetch`].
156 ///
157 /// # Errors
158 ///
159 /// This function may error:
160 /// - While requesting (will return an [`Error::Request`]);
161 /// - After receiving a bad status code (API or other error - returns an [`Error::Status`]);
162 /// - After a ratelimit is indicated by the API, while also specifying when it is lifted ([`Error::Ratelimited`]);
163 /// - While parsing incoming JSON (will return an [`Error::Json`]).
164 ///
165 /// (All of those, of course, wrapped inside an `Err`.)
166 ///
167 /// # Examples
168 ///
169 /// ```rust,ignore
170 /// use brawl_api::{Client, BrawlerList};
171 ///
172 /// # async fn main() -> Result<(), Box<dyn ::std::error::Error>> {
173 /// let my_client = Client::new("my auth token");
174 /// let player_brawlers = BrawlerList::a_fetch(&my_client).await?;
175 /// // now a vector with data for all brawlers in the game is available for use.
176 ///
177 /// # Ok(())
178 /// # }
179 /// ```
180 ///
181 /// [`Error::Request`]: error/enum.Error.html#variant.Request
182 /// [`Error::Status`]: error/enum.Error.html#variant.Status
183 /// [`Error::Ratelimited`]: error/enum.Error.html#variant.Ratelimited
184 /// [`Error::Json`]: error/enum.Error.html#variant.Json
185 /// [`Brawler`]: struct.Brawler.html
186 /// [`Brawler::fetch`]: struct.Brawler.html#method.fetch
187 #[cfg(feature = "async")]
188 pub async fn a_fetch(client: &Client) -> Result<BrawlerList> {
189 let route = BrawlerList::get_route();
190 a_fetch_route::<BrawlerList>(client, &route).await
191 }
192}
193
194// endregion:BrawlerList
195
196/// Contains information for a specific brawler, and allows for it to be fetched through
197/// [`Brawler::fetch`].
198///
199/// [`Brawler::fetch`]: #method.fetch
200#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
201#[serde(rename_all = "camelCase")]
202pub struct Brawler {
203 /// The brawler's name, in CAPS LOCK. E.g.: `"SHELLY"` for Shelly.
204 #[serde(default)]
205 pub name: String,
206
207 /// The brawler's ID (an arbitrary number representing it).
208 #[serde(default)]
209 pub id: usize,
210
211 /// The brawler's star powers, as a vector (note that this does **not** have a fixed size:
212 /// new brawlers start with 1 star power, while older ones have at least 2.)
213 #[serde(default)]
214 pub star_powers: Vec<StarPower>,
215}
216
217impl Default for Brawler {
218
219
220 /// Returns an instance of `Brawler` with initial values.
221 ///
222 /// # Examples
223 ///
224 /// ```rust
225 /// use brawl_api::Brawler;
226 ///
227 /// assert_eq!(
228 /// Brawler::default(),
229 /// Brawler {
230 /// name: String::from(""),
231 /// id: 0,
232 /// star_powers: vec![]
233 /// }
234 /// );
235 /// ```
236 fn default() -> Brawler {
237 Brawler {
238 name: String::from(""),
239 id: 0,
240 star_powers: vec![]
241 }
242 }
243}
244
245impl Brawler {
246 /// Returns this Brawler's ID, which is used for fetching.
247 ///
248 /// # Examples
249 ///
250 /// ```rust
251 /// use brawl_api::{Brawler, traits::*};
252 ///
253 /// // given an existing Brawler object
254 /// let brawler: Brawler;
255 /// # brawler = Brawler::default();
256 ///
257 /// assert_eq!(brawler.get_fetch_prop(), brawler.id);
258 /// ```
259 pub fn get_fetch_prop(&self) -> usize { self.id }
260
261 /// Returns the [`Route`] object required for fetching a `Brawler` instance.
262 ///
263 /// # Examples
264 ///
265 /// ```rust
266 /// use brawl_api::{BrawlerList, http::Route};
267 ///
268 /// assert_eq!(
269 /// BrawlerList::get_route(),
270 /// Route::Brawlers
271 /// );
272 /// ```
273 ///
274 /// [`Route`]: http/routes/struct.Route.html
275 pub fn get_route(id: usize) -> Route { Route::Brawler(id) }
276
277 /// (Sync) Fetches data for a brawler with a specific ID (see the [`Brawlers`] enum for a
278 /// humanized list with IDs).
279 ///
280 /// # Errors
281 ///
282 /// This function may error:
283 /// - While requesting (will return an [`Error::Request`]);
284 /// - After receiving a bad status code (API or other error - returns an [`Error::Status`]);
285 /// - After a ratelimit is indicated by the API, while also specifying when it is lifted ([`Error::Ratelimited`]);
286 /// - While parsing incoming JSON (will return an [`Error::Json`]).
287 ///
288 /// (All of those, of course, wrapped inside an `Err`.)
289 ///
290 /// # Examples
291 ///
292 /// ```rust,ignore
293 /// use brawl_api::{Client, Brawler, Brawlers, traits::*};
294 ///
295 /// # fn main() -> Result<(), Box<dyn ::std::error::Error>> {
296 /// let my_client = Client::new("my auth token");
297 /// let shelly = Brawler::fetch(&my_client, Brawlers::Shelly as usize)?;
298 /// // now the data for Shelly is available.
299 ///
300 /// # Ok(())
301 /// # }
302 /// ```
303 ///
304 /// [`Error::Request`]: error/enum.Error.html#variant.Request
305 /// [`Error::Status`]: error/enum.Error.html#variant.Status
306 /// [`Error::Ratelimited`]: error/enum.Error.html#variant.Ratelimited
307 /// [`Error::Json`]: error/enum.Error.html#variant.Json
308 pub fn fetch(client: &Client, id: usize) -> Result<Brawler> {
309 let route = Brawler::get_route(id);
310 fetch_route::<Brawler>(client, &route)
311 }
312
313 /// (Async) Fetches data for a brawler with a specific ID (see the [`Brawlers`] enum for a
314 /// humanized list with IDs).
315 ///
316 /// # Errors
317 ///
318 /// This function may error:
319 /// - While requesting (will return an [`Error::Request`]);
320 /// - After receiving a bad status code (API or other error - returns an [`Error::Status`]);
321 /// - After a ratelimit is indicated by the API, while also specifying when it is lifted ([`Error::Ratelimited`]);
322 /// - While parsing incoming JSON (will return an [`Error::Json`]).
323 ///
324 /// (All of those, of course, wrapped inside an `Err`.)
325 ///
326 /// # Examples
327 ///
328 /// ```rust,ignore
329 /// use brawl_api::{Client, Brawler, Brawlers, traits::*};
330 ///
331 /// # async fn main() -> Result<(), Box<dyn ::std::error::Error>> {
332 /// let my_client = Client::new("my auth token");
333 /// let shelly = Brawler::a_fetch(&my_client, Brawlers::Shelly as usize).await?;
334 /// // now the data for Shelly is available.
335 ///
336 /// # Ok(())
337 /// # }
338 /// ```
339 ///
340 /// [`Error::Request`]: error/enum.Error.html#variant.Request
341 /// [`Error::Status`]: error/enum.Error.html#variant.Status
342 /// [`Error::Ratelimited`]: error/enum.Error.html#variant.Ratelimited
343 /// [`Error::Json`]: error/enum.Error.html#variant.Json
344 #[cfg(feature="async")]
345 pub async fn a_fetch(client: &Client, id: usize) -> Result<Brawler> {
346 let route = Brawler::get_route(id);
347 a_fetch_route::<Brawler>(client, &route).await
348 }
349}
350
351#[cfg_attr(feature = "async", async_trait)]
352impl Refetchable for Brawler {
353 /// (Sync) Fetches data for this brawler again.
354 fn refetch(&self, client: &Client) -> Result<Brawler> {
355 Brawler::fetch(client, self.id)
356 }
357
358 /// (Async) Fetches data for this brawler again.
359 #[cfg(feature = "async")]
360 async fn a_refetch(&self, client: &Client) -> Result<Brawler> {
361 Brawler::a_fetch(client, self.id).await
362 }
363}
364
365#[cfg_attr(feature = "async", async_trait)]
366#[cfg(feature = "players")]
367impl FetchFrom<PlayerBrawlerStat> for Brawler {
368 /// (Sync) Attempts to fetch a `Brawler` from an existing [`PlayerBrawlerStat`] instance.
369 ///
370 /// [`PlayerBrawlerStat`]: ../players/player/struct.PlayerBrawlerStat.html
371 fn fetch_from(client: &Client, p_brawler: &PlayerBrawlerStat) -> Result<Brawler> {
372 Brawler::fetch(client, p_brawler.id)
373 }
374
375 /// (Async) Attempts to fetch a `Brawler` from an existing [`PlayerBrawlerStat`] instance.
376 ///
377 /// [`PlayerBrawlerStat`]: ../players/player/struct.PlayerBrawlerStat.html
378 #[cfg(feature = "async")]
379 async fn a_fetch_from(client: &Client, p_brawler: &PlayerBrawlerStat) -> Result<Brawler> {
380 Brawler::a_fetch(client, p_brawler.id).await
381 }
382}
383
384#[cfg_attr(feature = "async", async_trait)]
385#[cfg(feature = "players")]
386impl FetchFrom<BattleBrawler> for Brawler {
387 /// (Sync) Attempts to fetch a `Brawler` from an existing [`BattleBrawler`] instance.
388 ///
389 /// [`BattleBrawler`]: ../players/battlelog/struct.BattleBrawler.html
390 fn fetch_from(client: &Client, b_brawler: &BattleBrawler) -> Result<Brawler> {
391 Brawler::fetch(client, b_brawler.id)
392 }
393
394 /// (Async) Attempts to fetch a `Brawler` from an existing [`BattleBrawler`] instance.
395 ///
396 /// [`BattleBrawler`]: ../players/battlelog/struct.BattleBrawler.html
397 #[cfg(feature = "async")]
398 async fn a_fetch_from(client: &Client, b_brawler: &BattleBrawler) -> Result<Brawler> {
399 Brawler::a_fetch(client, b_brawler.id).await
400 }
401}
402
403#[cfg_attr(feature = "async", async_trait)]
404impl FetchFrom<Brawlers> for Brawler {
405 /// (Sync) Attempts to fetch a `Brawler` from an existing [`Brawlers`] variant.
406 ///
407 /// [`Brawlers`]: ../constants/enum.Brawlers.html
408 fn fetch_from(client: &Client, b_brawler: &Brawlers) -> Result<Brawler> {
409 Brawler::fetch(client, b_brawler.to_owned() as usize)
410 }
411
412 /// (Async) Attempts to fetch a `Brawler` from an existing [`Brawlers`] variant.
413 ///
414 /// [`Brawlers`]: ../constants/enum.Brawlers.html
415 #[cfg(feature = "async")]
416 async fn a_fetch_from(client: &Client, b_brawler: &Brawlers) -> Result<Brawler> {
417 Brawler::a_fetch(client, b_brawler.to_owned() as usize).await
418 }
419}
420
421/////////////////////////////////// tests ///////////////////////////////////
422
423#[cfg(test)]
424mod tests {
425 use serde_json;
426 use super::{BrawlerList, Brawler};
427 use super::super::common::StarPower;
428 use crate::error::Error;
429
430 /// Tests for Brawlers deserialization from API-provided JSON.
431 #[test]
432 fn brawlers_deser() -> Result<(), Box<dyn ::std::error::Error>> {
433
434 let brawlers_json_s = r##"{
435 "items": [
436 {
437 "id": 16000000,
438 "name": "SHELLY",
439 "starPowers": [
440 {
441 "id": 23000076,
442 "name": "Shell Shock"
443 },
444 {
445 "id": 23000135,
446 "name": "Band-Aid"
447 }
448 ]
449 },
450 {
451 "id": 16000001,
452 "name": "COLT",
453 "starPowers": [
454 {
455 "id": 23000077,
456 "name": "Slick Boots"
457 },
458 {
459 "id": 23000138,
460 "name": "Magnum Special"
461 }
462 ]
463 },
464 {
465 "id": 16000002,
466 "name": "BULL",
467 "starPowers": [
468 {
469 "id": 23000078,
470 "name": "Berserker"
471 },
472 {
473 "id": 23000137,
474 "name": "Tough Guy"
475 }
476 ]
477 },
478 {
479 "id": 16000003,
480 "name": "BROCK",
481 "starPowers": [
482 {
483 "id": 23000079,
484 "name": "Incendiary"
485 },
486 {
487 "id": 23000150,
488 "name": "Rocket No. Four"
489 }
490 ]
491 }
492 ]
493}"##;
494
495 let brawlers = serde_json::from_str::<BrawlerList>(brawlers_json_s)
496 .map_err(Error::Json)?;
497
498 assert_eq!(
499 brawlers,
500
501 BrawlerList {
502 items: vec![
503 Brawler {
504 id: 16000000,
505 name: String::from("SHELLY"),
506 star_powers: vec![
507 StarPower {
508 id: 23000076,
509 name: String::from("Shell Shock")
510 },
511 StarPower {
512 id: 23000135,
513 name: String::from("Band-Aid")
514 }
515 ]
516 },
517 Brawler {
518 id: 16000001,
519 name: String::from("COLT"),
520 star_powers: vec![
521 StarPower {
522 id: 23000077,
523 name: String::from("Slick Boots")
524 },
525 StarPower {
526 id: 23000138,
527 name: String::from("Magnum Special")
528 }
529 ]
530 },
531 Brawler {
532 id: 16000002,
533 name: String::from("BULL"),
534 star_powers: vec![
535 StarPower {
536 id: 23000078,
537 name: String::from("Berserker")
538 },
539 StarPower {
540 id: 23000137,
541 name: String::from("Tough Guy")
542 }
543 ]
544 },
545 Brawler {
546 id: 16000003,
547 name: String::from("BROCK"),
548 star_powers: vec![
549 StarPower {
550 id: 23000079,
551 name: String::from("Incendiary")
552 },
553 StarPower {
554 id: 23000150,
555 name: String::from("Rocket No. Four")
556 }
557 ]
558 }
559 ]
560 }
561 );
562
563 Ok(())
564 }
565
566 /// Tests for Brawler deserialization from API-provided JSON.
567 #[test]
568 fn brawler_deser() -> Result<(), Box<dyn ::std::error::Error>> {
569
570 let brawler_json_s = r##"{
571 "id": 16000000,
572 "name": "SHELLY",
573 "starPowers": [
574 {
575 "id": 23000076,
576 "name": "Shell Shock"
577 },
578 {
579 "id": 23000135,
580 "name": "Band-Aid"
581 }
582 ]
583}"##;
584
585 let brawler = serde_json::from_str::<Brawler>(brawler_json_s)
586 .map_err(Error::Json)?;
587
588 assert_eq!(
589 brawler,
590
591 Brawler {
592 id: 16000000,
593 name: String::from("SHELLY"),
594 star_powers: vec![
595 StarPower {
596 id: 23000076,
597 name: String::from("Shell Shock")
598 },
599 StarPower {
600 id: 23000135,
601 name: String::from("Band-Aid")
602 }
603 ]
604 }
605 );
606
607 Ok(())
608 }
609}