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}