dota2_webapi_bindings/
lib.rs

1//! The crate serves as an bindings to the official (outdated)
2//! [dota2 webapi](https://dev.dota2.com/forum/dota-2/spectating/replays/webapi/60177-things-you-should-know-before-starting?t=58317)
3//! The crate has been made so you can call make calls directly and get a result back in a Struct.
4//!
5//! Read the full list of api(outdated) calls [here](https://wiki.teamfortress.com/wiki/WebAPI#Dota_2).
6//!
7//! Use [xpaw](https://steamapi.xpaw.me/#) for latest.
8//!
9//! The webapi terms are same as official except they are all in lowercase, Eg : `GetGameItems` is now `get_game_items()`.
10//!
11//! You also need a key that you can get [here](http://steamcommunity.com/dev/apikey).
12//! > Originally posted by Zoid at [forum](https://dev.dota2.com/forum/dota-2/spectating/replays/webapi/60177-things-you-should-know-before-starting?t=58317) When you go to http://steamcommunity.com/dev/apikey the "domain"
13//! > field is just a note. It's not actually used for anything and is just a helpful field so you can
14//! > tell us what your website is. You can just put your name in for now. Once you get a key, its what
15//! > uniquely identifies you when accessing our WebAPI calls.
16//!
17//! In your `main.rs` or anywhere you intend to use the library create a non-mutable string of
18//! you token pass first to use the library, there is no calls without the token.
19//! ```rust
20//! //main.rs
21//! use dota2_webapi_bindings::Dota2Api;
22//! static DOTA2_KEY: &str = "0123456789"; //example token
23//!
24//! fn main() {
25//!   let mut dota = Dota2Api::new(String::from(DOTA2_KEY));
26//!   // we use `set` to configure the URL first
27//!   dota.set_heroes().itemized_only(true).language("zh_zh");
28//!   // you can also write the above as just `dota.set_heroes();` or `dota.set_heroes().itemized_only(true);`
29//!   // or just `dota.set_heroes().language("zh_zh");` or `dota.set_heroes().language("zh_zh").itemized_only(true);`
30//!   // our builder like function takes care of optional parameters
31//!
32//!   // and finally `get` to retrieve our struct
33//!   let data = dota.get_heroes().expect("something went wrong, ez mid");
34//! }
35//!
36//! ```
37//!
38//! ##### Available calls :
39//! * IEconDOTA2_570
40//!     * GetGameItems
41//!     * GetHeroes
42//!     * GetRarities
43//!     * GetTournamentPrizePool
44//! * IDOTA2Match_205790
45//!     * GetLeagueListing
46//! * IDOTA2Match_570
47//!     * GetLiveLeagueGames
48//!     * GetTopLiveGame
49//!
50//! **Note:** Try using `language()` with everything, just put in any string, it seems like its gives better readable name
51//! and description for some reason, I have not set-up a default cause sometimes that might not be your intension.
52
53#[macro_use]
54extern crate serde_derive;
55extern crate hyper;
56extern crate serde_json;
57
58pub mod dota;
59
60use hyper::status::StatusCode;
61use hyper::Client;
62use std::io::Read;
63
64use crate::dota::{
65    get_game_items::*, get_heroes::*, get_league_listing::*, get_live_league_games::*,
66    get_rarities::*, get_top_live_game::*, get_tournament_prize_pool::*,
67};
68
69/// language macro for easy implementation in various builder struct
70///
71/// The language to retrieve results in (default is en_us) (see http://en.wikipedia.org/wiki/ISO_639-1 for
72/// the language codes (first two characters) and http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes for
73/// the country codes (last two characters))
74///
75/// language (Optional) (string) : The language to provide output in.
76///
77/// **Note:** Try using `language()` with everything, just put in any string, it seems like its gives better readable name
78/// and description for some reason
79macro_rules! language {
80    () => {
81        pub fn language(&mut self, param_value: &str) -> &mut Self {
82            self.url.push_str(&*format!("language={}&", param_value));
83            self
84        }
85    };
86}
87
88/// A `set!` macro to get our `set` functions
89macro_rules! set {
90    ($func: ident, $builder: ident, $build: ident) => {
91        pub fn $func(&mut self) -> &mut $build {
92            self.$builder = $build::build(&*self.key);
93            &mut self.$builder
94        }
95    };
96}
97
98/// A `get!` macro to get our `get` functions
99macro_rules! get {
100    ($func: ident, $return_type: ident, $builder: ident, $result: ident) => {
101        pub fn $func(&mut self) -> Result<$return_type, Error> {
102            let response = self.get(&*self.$builder.url.clone())?;
103            let data_result: $result = serde_json::from_str(response.as_str())?;
104            let data = data_result.result;
105            Ok(data)
106        }
107    };
108}
109
110/// builder to reduce boilerplate
111macro_rules! builder {
112    ($builder: ident, $url: expr) => {
113        #[derive(Debug, Default)]
114        pub struct $builder {
115            url: String,
116        }
117
118        impl $builder {
119            fn build(key: &str) -> Self {
120                Self {
121                    url: format!($url, key),
122                }
123            }
124        }
125    };
126}
127
128/// different type of errors we can receive during either fetching of data or just unpacking JSON
129#[derive(Debug)]
130pub enum Error {
131    Http(hyper::Error),
132    Json(serde_json::Error),
133    Forbidden(&'static str),
134    Message(String),
135}
136
137impl From<hyper::Error> for Error {
138    fn from(e: hyper::Error) -> Error {
139        Error::Http(e)
140    }
141}
142
143impl From<serde_json::Error> for Error {
144    fn from(e: serde_json::Error) -> Error {
145        Error::Json(e)
146    }
147}
148
149/// The main `Dota2Api` of you library works by saving states of all the invoked URLs (you only call the one you need)
150/// language macro for easy implementation in various builder struct
151///
152/// The language to retrieve results in (default is en_us) (see http://en.wikipedia.org/wiki/ISO_639-1 for
153/// the language codes (first two characters) and http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes for
154/// the country codes (last two characters))
155///
156/// language (Optional) (string) : The language to provide output in.
157#[derive(Debug, Default)]
158pub struct Dota2Api {
159    http_client: Client,
160    pub key: String,
161    get_heroes_builder: GetHeroesBuilder,
162    get_game_items_builder: GetGameItemsBuilder,
163    get_rarities_builder: GetRaritiesBuilder,
164    get_tournament_prize_pool_builder: GetTournamentPrizePoolBuilder,
165    get_league_listing_builder: GetLeagueListingBuilder,
166    get_live_league_games_builder: GetLiveLeagueGamesBuilder,
167    get_top_live_game_builder: GetTopLiveGameBuilder,
168}
169
170impl Dota2Api {
171    pub fn new(key: String) -> Self {
172        Dota2Api {
173            http_client: Client::new(),
174            key,
175            ..Default::default()
176        }
177    }
178
179    set!(set_heroes, get_heroes_builder, GetHeroesBuilder);
180    // use `set` before `get`
181    get!(get_heroes, GetHeroes, get_heroes_builder, GetHeroesResult);
182
183    set!(set_game_items, get_game_items_builder, GetGameItemsBuilder);
184    // use `set` before `get`
185    get!(
186        get_game_items,
187        GetGameItems,
188        get_game_items_builder,
189        GetGameItemsResult
190    );
191
192    set!(set_rarities, get_rarities_builder, GetRaritiesBuilder);
193    // use `set` before `get`
194    get!(
195        get_rarities,
196        GetRarities,
197        get_rarities_builder,
198        GetRaritiesResult
199    );
200
201    set!(
202        set_tournament_prize_pool,
203        get_tournament_prize_pool_builder,
204        GetTournamentPrizePoolBuilder
205    );
206    // use `set` before `get`
207    get!(
208        get_tournament_prize_pool,
209        GetTournamentPrizePool,
210        get_tournament_prize_pool_builder,
211        GetTournamentPrizePoolResult
212    );
213
214    set!(
215        set_league_listing,
216        get_league_listing_builder,
217        GetLeagueListingBuilder
218    );
219    // use `set` before `get`
220    get!(
221        get_league_listing,
222        GetLeagueListing,
223        get_league_listing_builder,
224        GetLeagueListingResult
225    );
226
227    set!(
228        set_live_league_games,
229        get_live_league_games_builder,
230        GetLiveLeagueGamesBuilder
231    );
232    // use `set` before `get`
233    get!(
234        get_live_league_games,
235        GetLiveLeagueGames,
236        get_live_league_games_builder,
237        GetLiveLeagueGamesResult
238    );
239
240    set!(
241        set_top_live_game,
242        get_top_live_game_builder,
243        GetTopLiveGameBuilder
244    );
245    // use `set` before `get`
246    pub fn get_top_live_game(&mut self) -> Result<GetTopLiveGame, Error> {
247        let response = self.get(&*self.get_top_live_game_builder.url.clone())?;
248        let data_result: GetTopLiveGame = serde_json::from_str(response.as_str())?;
249        let data = data_result;
250        Ok(data)
251    }
252
253    /// our get function to actually get the data from the api
254    fn get(&mut self, url: &str) -> Result<String, Error> {
255        let mut response = self.http_client.get(url).send()?;
256        let mut temp = String::new();
257        if response.status == StatusCode::Forbidden {
258            return Err(Error::Forbidden(
259                "Access is denied. Retrying will not help. Please check your API key.",
260            ));
261        }
262        let _ = response.read_to_string(&mut temp);
263        Ok(temp)
264    }
265}
266
267//==============================================================================
268//IEconDOTA2_570
269//==============================================================================
270
271builder!(
272    GetHeroesBuilder,
273    "http://api.steampowered.com/IEconDOTA2_570/GetHeroes/v1/?key={}&"
274);
275impl GetHeroesBuilder {
276    /// itemizedonly (Optional) (bool) : Return a list of itemized heroes only.
277    pub fn itemized_only(&mut self, param_value: bool) -> &mut Self {
278        self.url
279            .push_str(&*format!("itemizedonly={}&", param_value));
280        self
281    }
282    language!();
283}
284
285builder!(
286    GetGameItemsBuilder,
287    "http://api.steampowered.com/IEconDOTA2_570/GetGameItems/v1/?key={}&"
288);
289impl GetGameItemsBuilder {
290    language!();
291}
292
293builder!(
294    GetRaritiesBuilder,
295    "http://api.steampowered.com/IEconDOTA2_570/GetRarities/v1/?key={}&"
296);
297impl GetRaritiesBuilder {
298    language!();
299}
300
301builder!(
302    GetTournamentPrizePoolBuilder,
303    "http://api.steampowered.com/IEconDOTA2_570/GetTournamentPrizePool/v1/?key={}&"
304);
305impl GetTournamentPrizePoolBuilder {
306    /// leagueid (Optional) (int) : The ID of the league to get the prize pool of.
307    pub fn league_id(&mut self, param_value: usize) -> &mut Self {
308        self.url.push_str(&*format!("leagueid={}&", param_value));
309        self
310    }
311    language!();
312}
313
314//==============================================================================
315//IDOTA2Match_205790
316//==============================================================================
317
318builder!(
319    GetLeagueListingBuilder,
320    "http://api.steampowered.com/IDOTA2Match_205790/GetLeagueListing/v1/?key={}&"
321);
322impl GetLeagueListingBuilder {
323    language!();
324}
325
326//==============================================================================
327//IDOTA2Match_570
328//==============================================================================
329
330builder!(
331    GetLiveLeagueGamesBuilder,
332    "http://api.steampowered.com/IDOTA2Match_570/GetLiveLeagueGames/v1/?key={}&"
333);
334impl GetLiveLeagueGamesBuilder {
335    language!();
336    /// Only show matches of the specified league id
337    pub fn league_id(&mut self, param_value: usize) -> &mut Self {
338        self.url.push_str(&*format!("league_id={}&", param_value));
339        self
340    }
341    /// Only show matches of the specified match id
342    pub fn match_id(&mut self, param_value: usize) -> &mut Self {
343        self.url.push_str(&*format!("match_id={}&", param_value));
344        self
345    }
346}
347
348builder!(
349    GetTopLiveGameBuilder,
350    "http://api.steampowered.com/IDOTA2Match_570/GetTopLiveGame/v1/?key={}&"
351);
352
353impl GetTopLiveGameBuilder {
354    language!();
355    /// Which partner's games to use
356    pub fn partner(&mut self, param_value: usize) -> &mut Self {
357        self.url.push_str(&*format!("partner={}&", param_value));
358        self
359    }
360}