1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
/*!
[Documentation](https://docs.rs/hltv/latest/hltv/) | [Crates.io](https://crates.io/crates/hltv) | [Repository](https://github.com/dist1ll/hltv-rust)
**A crate for fetching and parsing esports data from [HLTV.org](https://www.hltv.org).**
This crate allows you to fetch and parse upcoming matches, results,
event information, player performance. This crate uses async calls via [`reqwest`]
and parses the HTML document with [`tl`]. This API mimics the way you discover
information on HLTV. Summary pages (like [HLTV Matches](https://www.hltv.org/matches))
contains less information in the HTML document than the detailed match-specific page.
Currently, the following API calls are supported:
- [`crate::upcoming`]
- [`crate::results`]
- [`crate::get_match`]
## Examples
The builders in `hltv` allow you to build a generic [`Request`] object with a [`fetch`][`Request::fetch`] method.
```rust
#[tokio::test]
async fn results() -> Result<(), hltv::Error> {
let req = hltv::results()
.map(Map::Inferno)
.team(4608) // Team Na'Vi
.year(2016)
.event_type(EventTypeFilter::Lan)
.build();
let matches = req.fetch().await?; // <-- this has type Vec<MatchResult>
Ok(())
}
```
## More examples
### Find out if specific match is live
```rust
# #[tokio::test]
# async fn results() -> Result<(), hltv::Error> {
let req = hltv::get_match(2346065);
let m = req.fetch().await?;
if m.status == hltv::data::MatchStatus::Live {
println!("match with id:[{}] is live!", m.id);
}
# }
```
### Get all upcoming matches for a team
```
```
*/
use std::marker::PhantomData;
pub mod converter;
pub mod data;
// Extensions to make the [`tl`] crate more ergonomic.
mod tl_extensions;
// Export builder methods
pub mod request;
pub use request::upcoming::upcoming;
pub use request::results::results;
pub use request::match_page::get_match;
/// Implements a conversion from a DOM object to a collection of its own type.
pub trait ConvertCollection
where
Self: Sized,
{
/// Converts a given VDOM into a vector of its own type. This is because
/// them DOM can contain multiple instances of that type.
fn convert<'a>(d: &'a tl::VDom<'a>) -> Result<Vec<Self>, crate::Error>;
}
/// Implements a conversion from a DOM object to a single instance of its own type.
pub trait ConvertInstance
where
Self: Sized,
{
/// Converts a given VDOM into a instance of its own type. If the DOM contains
/// multiple instances, the first one is chosen.
fn convert<'a>(d: &'a tl::VDom<'a>) -> Result<Self, crate::Error>;
}
/// A reusable request object, that fetches, parses and converts HLTV data
/// to the correct type.
#[derive(Debug)]
pub struct Request<T>
where
T: ConvertInstance,
{
/// Target url that the request will fetch.
url: String,
/// This PhantomData is used to maintain type information without dynamic dispatch.
_m: PhantomData<T>,
}
impl<T> Request<T>
where
T: ConvertInstance,
{
/// Creates a new request object with given url and conversion type.
pub fn new(url: String) -> Request<T> {
Request::<T> {
url,
_m: PhantomData,
}
}
/// Fetches HTML resource, parses DOM, and converts into type T.
/// Returns an error if the resource is not reachable.
/// If you want to create a custom data structure that can be fetched
/// and read from HLTV, refer to the [`converter`] module.
pub async fn fetch(&self) -> Result<T, Error> {
let html = reqwest::get(self.url.clone()).await?.text().await?;
let vdom = tl::parse(&html, tl::ParserOptions::default())?;
let x = T::convert(&vdom)?;
Ok(x)
}
}
/// Errors that happen during request, parse or conversion of data.
#[derive(Debug)]
pub enum Error {
/// Any non-200 status code.
HTTPError,
/// HTML document is invalid. Refer to `tl::parse`.
ParseError,
/// Parsed document can't be converted into target type.
ConversionError(&'static str),
}
impl From<reqwest::Error> for Error {
fn from(_: reqwest::Error) -> Self {
Error::HTTPError
}
}
impl From<tl::ParseError> for Error {
fn from(_: tl::ParseError) -> Self {
Error::ParseError
}
}
impl std::error::Error for Error {}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::HTTPError => write!(f, "error with http client or remote server"),
Error::ParseError => write!(f, "error parsing received data"),
Error::ConversionError(_) => write!(f, "error converting data into correct type"),
}
}
}