gegen-data 0.1.4

data fetching for gegen
Documentation
use chrono::NaiveDate;
use const_format::concatcp;
use reqwest::StatusCode;
use types::LiveScoresResponse;

pub mod types;
mod utils;

const BASE_URL: &str = "https://optaplayerstats.statsperform.com/api/";
const LIVE_SCORE_URL: &str = concatcp!(BASE_URL, "en_GB/soccer/livescores");
const MATTCHES_URL: &str = concatcp!(BASE_URL, "en_GB/soccer/matches");

pub fn get_live_scores(
    client: &reqwest::blocking::Client,
) -> Result<LiveScoresResponse, GegenDataError> {
    let live_score_query_params = types::LiveScoreQueryParams { offset: 0 };

    let headers = utils::create_header_maps();
    let resp = client
        .get(LIVE_SCORE_URL)
        .query(&live_score_query_params)
        .headers(headers)
        .send()
        .map_err(|source| GegenDataError::Reqwest {
            source,
            url: LIVE_SCORE_URL.to_string(),
        })?;

    match resp.status() {
        StatusCode::OK => {
            resp.json::<LiveScoresResponse>()
                .map_err(|source| GegenDataError::Serialisation {
                    source,
                    url: LIVE_SCORE_URL.to_string(),
                })
        }
        StatusCode::TOO_MANY_REQUESTS => Err(GegenDataError::TooManyRequests {
            url: LIVE_SCORE_URL.to_string(),
        }),
        other_status_code => Err(GegenDataError::Non200 {
            status_code: other_status_code,
            url: LIVE_SCORE_URL.to_string(),
            body: resp.text().unwrap_or_default(),
        }),
    }
}

pub fn get_matches(
    client: &reqwest::blocking::Client,
    date: NaiveDate,
) -> Result<LiveScoresResponse, GegenDataError> {
    let url = format!("{MATTCHES_URL}/{date}");
    let live_score_query_params = types::LiveScoreQueryParams { offset: 0 };

    let headers = utils::create_header_maps();
    let resp = client
        .get(&url)
        .query(&live_score_query_params)
        .headers(headers)
        .send()
        .map_err(|source| GegenDataError::Reqwest {
            source,
            url: url.clone(),
        })?;

    match resp.status() {
        StatusCode::OK => resp
            .json::<LiveScoresResponse>()
            .map_err(|source| GegenDataError::Serialisation { source, url }),
        StatusCode::TOO_MANY_REQUESTS => Err(GegenDataError::TooManyRequests { url }),
        other_status_code => Err(GegenDataError::Non200 {
            status_code: other_status_code,
            url,
            body: resp.text().unwrap_or_default(),
        }),
    }
}

#[derive(Debug, thiserror::Error)]
pub enum GegenDataError {
    #[error("Failed to send request to {url}: {source}")]
    Reqwest { source: reqwest::Error, url: String },
    #[error("Got a 429 / too many rqeusts {url}")]
    TooManyRequests { url: String },
    #[error("Got a 429 / too many rqeusts {url}")]
    Non200 {
        status_code: StatusCode,
        url: String,
        body: String,
    },
    #[error("Failed to deserialise response for {url}: {source:?}")]
    Serialisation { source: reqwest::Error, url: String },
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_live_scores() {
        let client = reqwest::blocking::Client::new();
        let _ = get_live_scores(&client).unwrap();
    }

    #[test]
    fn test_fixtures() {
        let date = NaiveDate::from_ymd_opt(2025, 4, 27).unwrap();

        let client = reqwest::blocking::Client::new();

        let _ = get_matches(&client, date).unwrap();
    }
}