gegen_data/
lib.rs

1use chrono::NaiveDate;
2use const_format::concatcp;
3use reqwest::StatusCode;
4use types::LiveScoresResponse;
5
6pub mod types;
7mod utils;
8
9const BASE_URL: &str = "https://optaplayerstats.statsperform.com/api/";
10const LIVE_SCORE_URL: &str = concatcp!(BASE_URL, "en_GB/soccer/livescores");
11const MATTCHES_URL: &str = concatcp!(BASE_URL, "en_GB/soccer/matches");
12
13pub fn get_live_scores(
14    client: &reqwest::blocking::Client,
15) -> Result<LiveScoresResponse, GegenDataError> {
16    let live_score_query_params = types::LiveScoreQueryParams { offset: 0 };
17
18    let headers = utils::create_header_maps();
19    let resp = client
20        .get(LIVE_SCORE_URL)
21        .query(&live_score_query_params)
22        .headers(headers)
23        .send()
24        .map_err(|source| GegenDataError::Reqwest {
25            source,
26            url: LIVE_SCORE_URL.to_string(),
27        })?;
28
29    match resp.status() {
30        StatusCode::OK => {
31            resp.json::<LiveScoresResponse>()
32                .map_err(|source| GegenDataError::Serialisation {
33                    source,
34                    url: LIVE_SCORE_URL.to_string(),
35                })
36        }
37        StatusCode::TOO_MANY_REQUESTS => Err(GegenDataError::TooManyRequests {
38            url: LIVE_SCORE_URL.to_string(),
39        }),
40        other_status_code => Err(GegenDataError::Non200 {
41            status_code: other_status_code,
42            url: LIVE_SCORE_URL.to_string(),
43            body: resp.text().unwrap_or_default(),
44        }),
45    }
46}
47
48pub fn get_matches(
49    client: &reqwest::blocking::Client,
50    date: NaiveDate,
51) -> Result<LiveScoresResponse, GegenDataError> {
52    let url = format!("{MATTCHES_URL}/{date}");
53    let live_score_query_params = types::LiveScoreQueryParams { offset: 0 };
54
55    let headers = utils::create_header_maps();
56    let resp = client
57        .get(&url)
58        .query(&live_score_query_params)
59        .headers(headers)
60        .send()
61        .map_err(|source| GegenDataError::Reqwest {
62            source,
63            url: url.clone(),
64        })?;
65
66    match resp.status() {
67        StatusCode::OK => resp
68            .json::<LiveScoresResponse>()
69            .map_err(|source| GegenDataError::Serialisation { source, url }),
70        StatusCode::TOO_MANY_REQUESTS => Err(GegenDataError::TooManyRequests { url }),
71        other_status_code => Err(GegenDataError::Non200 {
72            status_code: other_status_code,
73            url,
74            body: resp.text().unwrap_or_default(),
75        }),
76    }
77}
78
79#[derive(Debug, thiserror::Error)]
80pub enum GegenDataError {
81    #[error("Failed to send request to {url}: {source}")]
82    Reqwest { source: reqwest::Error, url: String },
83    #[error("Got a 429 / too many rqeusts {url}")]
84    TooManyRequests { url: String },
85    #[error("Got a 429 / too many rqeusts {url}")]
86    Non200 {
87        status_code: StatusCode,
88        url: String,
89        body: String,
90    },
91    #[error("Failed to deserialise response for {url}: {source:?}")]
92    Serialisation { source: reqwest::Error, url: String },
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98
99    #[test]
100    fn test_live_scores() {
101        let client = reqwest::blocking::Client::new();
102        let _ = get_live_scores(&client).unwrap();
103    }
104
105    #[test]
106    fn test_fixtures() {
107        let date = NaiveDate::from_ymd_opt(2025, 4, 27).unwrap();
108
109        let client = reqwest::blocking::Client::new();
110
111        let _ = get_matches(&client, date).unwrap();
112    }
113}