brawl_api/
traits.rs

1//! Traits used by the library. Those are of mostly internal use and normally shouldn't need
2//! implementation by some user-made type.
3//!
4//! Note that it is recommended to always import this module for things to work fine, such as
5//! using `X::fetch` methods, where `X` is any model implementing `PropFetchable` or
6//! `PropLimFetchable` (there are some that do not implement either, and rather have their own
7//! implementation of a `fetch` function, because they have 3 or more parameters).
8
9use crate::error::{Result};
10
11#[cfg(feature = "async")]
12use async_trait::async_trait;
13use crate::http::Client;
14// use serde::de::DeserializeOwned;
15
16use crate::http::routes::Route;
17
18pub mod propfetch {
19    use super::*;
20
21    /// A trait representing a type with a property used to be fetched, and a route with which to
22    /// fetch it. This can be either a tag or the limit of fetching, for example.
23    ///
24    /// The property must be returned by the `get_fetch_prop` function.
25    /// The route must be returned by the `get_route` function.
26    /// This trait is used in parallel with `PropFetchable`.
27    ///
28    /// [`PropFetchable`]: traits/propfetch/trait.PropFetchable.html
29    /// [`GetFetchProp`]: traits/propfetch/trait.GetFetchProp.html
30    pub trait GetFetchProp: Sized {
31        type Property: ?Sized;
32
33        /// Obtain the revelant property for fetching.
34        fn get_fetch_prop(&self) -> &Self::Property;
35        // necessary for Refetchable blanket impl
36
37        /// Obtain the route for fetching by using a property.
38        fn get_route(prop: &Self::Property) -> Route;
39    }
40
41    /// A trait representing a weaker variant of [`GetFetchProp`]; only indicates the fetching route can
42    /// be obtained by the `get_route` method by specifying a property, not that such property is
43    /// obtainable. Note that all `GetFetchProp` implementers also implement `PropRouteable`, thanks
44    /// to a blanket impl.
45    pub trait PropRouteable: Sized {
46        type Property: ?Sized;
47
48        /// Obtain the route for fetching by using a property.
49        fn get_route(prop: &Self::Property) -> Route;
50    }
51
52    impl<T: GetFetchProp> PropRouteable for T {
53        type Property = <T as GetFetchProp>::Property;
54
55        fn get_route(prop: &<Self as PropRouteable>::Property) -> Route {
56            <Self as GetFetchProp>::get_route(prop)
57        }
58    }
59
60    /// A trait representing a type whose instance can be fetched from the API using some property.
61    /// This is usually the object's tag.
62    #[cfg_attr(feature = "async", async_trait)]
63    pub trait PropFetchable: Sized {
64        type Property: ?Sized;
65
66        /// (Sync) Fetch and construct a new instance of this type.
67        ///
68        /// # Errors
69        ///
70        /// This function may error:
71        /// - While requesting (will return an [`Error::Request`]);
72        /// - After receiving a bad status code (API or other error - returns an [`Error::Status`]);
73        /// - After a ratelimit is indicated by the API, while also specifying when it is lifted ([`Error::Ratelimited`]);
74        /// - While parsing incoming JSON (will return an [`Error::Json`]).
75        ///
76        /// (All of those, of course, wrapped inside an `Err`.)
77        ///
78        /// # Examples
79        ///
80        /// Fetching a [`Player`] instance from a player tag:
81        ///
82        /// ```rust,ignore
83        /// use brawl_api::{Client, Player, traits::*};
84        ///
85        /// # fn main() -> Result<(), Box<dyn ::std::error::Error>> {
86        /// let my_client = Client::new("my auth token");
87        /// let player = Player::fetch(&my_client, "#PLAYERTAGHERE")?;
88        /// // now the data for the given player is available for use
89        ///
90        /// #     Ok(())
91        /// # }
92        /// ```
93        ///
94        /// [`Player`]: model/players/player/struct.Player.html
95        /// [`Error::Request`]: error/enum.Error.html#variant.Request
96        /// [`Error::Status`]: error/enum.Error.html#variant.Status
97        /// [`Error::Ratelimited`]: error/enum.Error.html#variant.Ratelimited
98        /// [`Error::Json`]: error/enum.Error.html#variant.Json
99        fn fetch(client: &Client, prop: &Self::Property) -> Result<Self>;
100
101        /// (Async) Fetch and construct a new instance of this type.
102        ///
103        /// # Errors
104        ///
105        /// This function may error:
106        /// - While requesting (will return an [`Error::Request`]);
107        /// - After receiving a bad status code (API or other error - returns an [`Error::Status`]);
108        /// - After a ratelimit is indicated by the API, while also specifying when it is lifted ([`Error::Ratelimited`]);
109        /// - While parsing incoming JSON (will return an [`Error::Json`]).
110        ///
111        /// (All of those, of course, wrapped inside an `Err`.)
112        ///
113        /// # Examples
114        ///
115        /// Fetching a [`Player`] instance from a player tag:
116        ///
117        /// ```rust,ignore
118        /// use brawl_api::{Client, Player, traits::*};
119        ///
120        /// # async fn main() -> Result<(), Box<dyn ::std::error::Error>> {
121        /// let my_client = Client::new("my auth token");
122        /// let player = Player::a_fetch(&my_client, "#PLAYERTAGHERE").await?;
123        /// // now the data for the given player is available for use
124        ///
125        /// #     Ok(())
126        /// # }
127        /// ```
128        ///
129        /// [`Player`]: model/players/player/struct.Player.html
130        /// [`Error::Request`]: error/enum.Error.html#variant.Request
131        /// [`Error::Status`]: error/enum.Error.html#variant.Status
132        /// [`Error::Ratelimited`]: error/enum.Error.html#variant.Ratelimited
133        /// [`Error::Json`]: error/enum.Error.html#variant.Json
134        #[cfg(feature = "async")]
135        async fn a_fetch(client: &Client, prop: &'async_trait Self::Property) -> Result<Self>
136            where Self: 'async_trait,
137                  Self::Property: 'async_trait;
138    }
139}
140
141pub use propfetch::*;
142
143pub mod proplimfetch {
144    use super::*;
145    use num_traits::PrimInt;
146
147    /// A trait representing a type that returns a [`Route`] object given a property and a limit
148    /// of how many to fetch.
149    pub trait PropLimRouteable {
150        type Property: ?Sized;
151        type Limit: PrimInt;  // must be numeric!
152
153        /// Obtain the route for fetching by using a property and specifying the limit of fetching.
154        fn get_route(prop: &Self::Property, limit: Self::Limit) -> Route;
155    }
156
157
158    /// A trait representing a type whose instance can be fetched from the API using some property
159    /// and specifying a limit of how many objects to fetch.
160    ///
161    /// **Note:** types which simply require the limit for fetching use [`PropFetchable`] instead
162    /// (with the limit being the property itself).
163    #[cfg_attr(feature = "async", async_trait)]
164    pub trait PropLimFetchable: Sized {
165        type Property: ?Sized;
166        type Limit: PrimInt;  // numeric
167
168        /// (Sync) Fetch and construct a new instance of this type.
169        ///
170        /// # Errors
171        ///
172        /// This function may error:
173        /// - While requesting (will return an [`Error::Request`]);
174        /// - After receiving a bad status code (API or other error - returns an [`Error::Status`]);
175        /// - After a ratelimit is indicated by the API, while also specifying when it is lifted ([`Error::Ratelimited`]);
176        /// - While parsing incoming JSON (will return an [`Error::Json`]).
177        ///
178        /// (All of those, of course, wrapped inside an `Err`.)
179        ///
180        /// # Examples
181        ///
182        /// Fetching a world-wide player leaderboard ([`PlayerLeaderboard`]):
183        /// ```rust,ignore
184        /// use brawl_api::{PlayerLeaderboard, Client, traits::PropLimFetchable};
185        ///
186        /// # fn main() -> Result<(), Box<dyn ::std::error::Error>> {
187        /// let client = Client::new("my auth key");
188        ///
189        /// // if the fetch is successful, then the variable below will have the global top 100 players
190        /// // in the 'items' field (i.e. '*top100players').
191        /// let top100players: PlayerLeaderboard = PlayerLeaderboard::fetch(&client, "global", 100)?;
192        ///
193        /// // get player ranked #1. The items are usually sorted (i.e. rank 1 on index [0], rank 2
194        /// // on index [1] etc.), but, to make the program absolutely safe, might want to .sort()
195        /// let player1 = &top100players[0];
196        ///
197        /// assert_eq!(player1.rank, 1);
198        ///
199        /// #     Ok(())
200        /// # }
201        /// ```
202        ///
203        /// [`PlayerLeaderboard`]: model/rankings/players/struct.PlayerLeaderboard.html
204        /// [`Error::Request`]: error/enum.Error.html#variant.Request
205        /// [`Error::Status`]: error/enum.Error.html#variant.Status
206        /// [`Error::Ratelimited`]: error/enum.Error.html#variant.Ratelimited
207        /// [`Error::Json`]: error/enum.Error.html#variant.Json
208        fn fetch(client: &Client, prop: &Self::Property, limit: Self::Limit) -> Result<Self>;
209
210        /// (Async) Fetch and construct a new instance of this type.
211        ///
212        /// # Errors
213        ///
214        /// This function may error:
215        /// - While requesting (will return an [`Error::Request`]);
216        /// - After receiving a bad status code (API or other error - returns an [`Error::Status`]);
217        /// - After a ratelimit is indicated by the API, while also specifying when it is lifted ([`Error::Ratelimited`]);
218        /// - While parsing incoming JSON (will return an [`Error::Json`]).
219        ///
220        /// (All of those, of course, wrapped inside an `Err`.)
221        ///
222        /// # Examples
223        ///
224        /// Fetching a world-wide player leaderboard ([`PlayerLeaderboard`]):
225        /// ```rust,ignore
226        /// use brawl_api::{PlayerLeaderboard, Client, traits::PropLimFetchable};
227        ///
228        /// # async fn main() -> Result<(), Box<dyn ::std::error::Error>> {
229        /// let client = Client::new("my auth key");
230        ///
231        /// // if the fetch is successful, then the variable below will have the global top 100 players
232        /// // in the 'items' field (i.e. '*top100players').
233        /// let top100players: PlayerLeaderboard = PlayerLeaderboard::a_fetch(&client, "global", 100).await?;
234        ///
235        /// // get player ranked #1. The items are usually sorted (i.e. rank 1 on index [0], rank 2
236        /// // on index [1] etc.), but, to make the program absolutely safe, might want to .sort()
237        /// let player1 = &top100players[0];
238        ///
239        /// assert_eq!(player1.rank, 1);
240        ///
241        /// #     Ok(())
242        /// # }
243        /// ```
244        ///
245        /// [`PlayerLeaderboard`]: model/rankings/players/struct.PlayerLeaderboard.html
246        /// [`Error::Request`]: error/enum.Error.html#variant.Request
247        /// [`Error::Status`]: error/enum.Error.html#variant.Status
248        /// [`Error::Ratelimited`]: error/enum.Error.html#variant.Ratelimited
249        /// [`Error::Json`]: error/enum.Error.html#variant.Json
250        #[cfg(feature = "async")]
251        async fn a_fetch(client: &Client, prop: &'async_trait Self::Property, limit: Self::Limit) -> Result<Self>
252            where Self: 'async_trait,
253                  Self::Property: 'async_trait,
254                  Self::Limit: 'async_trait;
255    }
256}
257
258pub use proplimfetch::*;
259
260// endregion:PropFetch
261
262/// A trait representing a type whose instance can be fetched again.
263/// Note that all types implementing [`GetFetchProp`] and [`PropFetchable`] also implement
264/// [`Refetchable`] due to a blanket implementation.
265///
266/// [`PropFetchable`]: traits/propfetch/trait.PropFetchable.html
267/// [`GetFetchProp`]: traits/propfetch/trait.GetFetchProp.html
268#[cfg_attr(feature = "async", async_trait)]
269pub trait Refetchable: Sized {
270    /// (Sync) Causes this instance to be re-fetched (i.e., updated to latest Brawl Stars data).
271    ///
272    /// # Examples
273    ///
274    /// ```rust,ignore
275    /// use brawl_api::prelude::*;
276    ///
277    /// # fn main() -> Result<(), Box<dyn ::std::error::Error>> {
278    /// let my_client = Client::new("my auth key");
279    ///
280    /// let player = Player::fetch(&my_client, "#PLAYER_TAG_HERE")?;
281    ///
282    /// // after using it a bit, we want to update its data
283    ///
284    /// let player = player.refetch(&my_client)?;  // `refetch` does not mutate the instance!
285    ///
286    /// // player variable is now up-to-date.
287    ///
288    /// #     Ok(())
289    /// # }
290    /// ```
291    fn refetch(&self, client: &Client) -> Result<Self>;
292
293    /// (Sync) Like `refetch`, but mutates the instance, returning an immutable reference to it.
294    ///
295    /// Its usage and errors are the same (it is called, after all), but the old variable does
296    /// not need to be assigned; rather, that is done for the programmer (the variable's value
297    /// **is entirely replaced** by a new one, if the fetching is successful).
298    fn refetch_update(&mut self, client: &Client) -> Result<&Self> {
299        *self = self.refetch(client)?;
300        Ok(self as &Self)
301    }
302
303    /// (Async) Causes this instance to be re-fetched (i.e., updated to latest Brawl Stars data).
304    ///
305    /// # Examples
306    ///
307    /// ```rust,ignore
308    /// use brawl_api::prelude::*;
309    ///
310    /// # async fn main() -> Result<(), Box<dyn ::std::error::Error>> {
311    /// let my_client = Client::new("my auth key");
312    ///
313    /// let player = Player::a_fetch(&my_client, "#PLAYER_TAG_HERE").await?;
314    ///
315    /// // after using it a bit, we want to update its data
316    ///
317    /// let player = player.a_refetch(&my_client).await?;  // this does not mutate the old instance!
318    ///
319    /// // player variable is now up-to-date.
320    ///
321    /// #     Ok(())
322    /// # }
323    /// ```
324    #[cfg(feature = "async")]
325    async fn a_refetch(&self, client: &Client) -> Result<Self>;
326
327    /// (Async) Like `a_refetch`, but mutates the instance, returning an immutable reference to it.
328    ///
329    /// Its usage and errors are the same (it is called, after all), but the old variable does
330    /// not need to be assigned; rather, that is done for the programmer (the variable's value
331    /// **is entirely replaced** by a new one, if the fetching is successful).
332    #[cfg(feature = "async")]
333    async fn a_refetch_update(&'async_trait mut self, client: &Client) -> Result<&'async_trait Self>
334        where Self: Send + Sync,
335    {
336        *self = self.a_refetch(client).await?;
337        Ok(self as &Self)
338    }
339}
340
341#[cfg_attr(feature = "async", async_trait)]
342impl<T> Refetchable for T
343    where T: PropFetchable<Property=<T as GetFetchProp>::Property> + GetFetchProp + Sized + Send + Sync,
344          <T as GetFetchProp>::Property: Sync + Send {
345    fn refetch(&self, client: &Client) -> Result<Self> {
346        Self::fetch(client, self.get_fetch_prop())
347    }
348
349    #[cfg(feature = "async")]
350    async fn a_refetch(&self, client: &Client) -> Result<Self>
351        where T: 'async_trait,
352        <T as GetFetchProp>::Property: 'async_trait,
353    {
354        Self::a_fetch(client, self.get_fetch_prop()).await
355    }
356}
357
358
359/// A trait indicating that another type can be converted into this one by fetching from the API.
360/// Note that, thanks to a blanket implementation, implementing this implies implementing
361/// [`FetchInto`] for the other type.
362///
363/// [`FetchInto`]: traits/trait.FetchInto.html
364#[cfg_attr(feature = "async", async_trait)]
365pub trait FetchFrom<T>: Sized {
366    /// (Sync) Attempts to request to the API and return a new instance of the type being turned
367    /// into.
368    ///
369    /// # Errors
370    ///
371    /// See the respective struct's `fetch` implementation (or `PropFetchable`/`PropLimFetchable`
372    /// implementation).
373    ///
374    /// # Examples
375    ///
376    /// ```rust,ignore
377    /// use brawl_api::{Client, Player, Club, traits::*};
378    ///
379    /// # fn main() -> Result<(), Box<dyn ::std::error::Error>> {
380    /// let my_client = Client::new("my auth token");
381    /// let club = Club::fetch(&my_client, "#CLUB_TAG_HERE")?;
382    /// let some_member = &club.members[0];
383    /// let some_player = Player::fetch_from(&my_client, some_member)?;
384    /// // now `some_member`'s full data, as a Player, is available for use.
385    ///
386    /// #     Ok(())
387    /// # }
388    /// ```
389    fn fetch_from(client: &Client, value: &T) -> Result<Self>;
390
391    /// (Async) Attempts to request to the API and return a new instance of the type being turned
392    /// into.
393    ///
394    /// # Errors
395    ///
396    /// See the respective struct's `a_fetch` implementation (or `PropFetchable`/`PropLimFetchable`
397    /// implementation).
398    ///
399    /// # Examples
400    ///
401    /// ```rust,ignore
402    /// use brawl_api::{Client, Player, Club, traits::*};
403    ///
404    /// # async fn main() -> Result<(), Box<dyn ::std::error::Error>> {
405    /// let my_client = Client::new("my auth token");
406    /// let club = Club::a_fetch(&my_client, "#CLUB_TAG_HERE").await?;
407    /// let some_member = &club.members[0];
408    /// let some_player = Player::a_fetch_from(&my_client, some_member).await?;
409    /// // now `some_member`'s full data, as a Player, is available for use.
410    ///
411    /// #     Ok(())
412    /// # }
413    /// ```
414    #[cfg(feature = "async")]
415    async fn a_fetch_from(client: &Client, value: &T) -> Result<Self>;
416}
417
418/// A trait indicating that this type can be converted into another by fetching from the API.
419/// Note that [`FetchFrom`] should be implemented, in order to apply the respective blanket
420/// implementation of this trait.
421///
422/// [`FetchFrom`]: traits/trait.FetchFrom.html
423#[cfg_attr(feature = "async", async_trait)]
424pub trait FetchInto<T>: Sized {
425    /// (Sync) Attempts to request to the API and return a new instance of the type being turned
426    /// into.
427    ///
428    /// # Errors
429    ///
430    /// See the respective into-type's `fetch` implementation (or `PropFetchable`/`PropLimFetchable`
431    /// implementation).
432    ///
433    /// # Examples
434    ///
435    /// See [`FetchFrom::<T>::fetch_from`].
436    fn fetch_into(&self, client: &Client) -> Result<T>;
437
438    #[cfg(feature = "async")]
439    /// (Async) Attempts to request to the API and return a new instance of the type being turned
440    /// into.
441    ///
442    /// # Errors
443    ///
444    /// See the respective into-type's `a_fetch` implementation (or
445    /// `PropFetchable`/`PropLimFetchable` implementation).
446    ///
447    /// # Examples
448    ///
449    /// See [`FetchFrom::<T>::a_fetch_from`].
450    async fn a_fetch_into(&self, client: &Client) -> Result<T>
451        where T: 'async_trait;
452}
453
454// FetchFrom implies FetchInto
455#[cfg_attr(feature = "async", async_trait)]
456impl<T, U> FetchInto<U> for T
457    where T: Sync + Send, U: FetchFrom<T> + Sync + Send
458{
459    fn fetch_into(&self, client: &Client) -> Result<U> {
460        U::fetch_from(client, self)
461    }
462
463    #[cfg(feature = "async")]
464    async fn a_fetch_into(&self, client: &Client) -> Result<U>
465        where U: 'async_trait
466    {
467        U::a_fetch_from(client, self).await
468    }
469}
470
471// FetchFrom (and thus FetchInto) is reflexive
472#[cfg_attr(feature = "async", async_trait)]
473impl<T: Sync + Send + Clone> FetchFrom<T> for T {
474    /// (Sync) Returns a copy of the current instance when attempting to fetch from itself.
475    /// In order to re-fetch, see [`Refetchable`].
476    ///
477    /// # Errors
478    ///
479    /// Never errors; is only a [`Result`] in order to match the trait signature.
480    ///
481    /// [`Refetchable`]: trait.Refetchable.html
482    /// [`Result`]: ../error/type.Result.html
483    fn fetch_from(_: &Client, t: &T) -> Result<T> { Ok(t.to_owned()) }
484
485    /// (Async) Returns a copy of the current instance when attempting to fetch from itself.
486    /// In order to re-fetch, see [`Refetchable`].
487    ///
488    /// # Errors
489    ///
490    /// Never errors; is only a [`Result`] in order to match the trait signature.
491    ///
492    /// [`Refetchable`]: trait.Refetchable.html
493    /// [`Result`]: ../error/type.Result.html
494    #[cfg(feature = "async")]
495    async fn a_fetch_from(_: &Client, t: &T) -> Result<Self> { Ok(t.to_owned()) }
496}