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}