covid19 0.3.0

Wrapper for cv19api https://lbandc.github.io/2020/04/14/project-cv19api.html
Documentation
use super::data::{DeathsResponse, RegionsResponse, TrustsResponse};
use crate::utils::fetch;
use anyhow::Result;
use chrono::NaiveDate;
use serde::de::DeserializeOwned;

fn url_for(
    base_url: &str,
    endpoint: &str,
    use_ground_truth: Option<bool>,
    from_date: NaiveDate,
    to_date: NaiveDate,
) -> String {
    let (from_param, to_param) = if use_ground_truth.unwrap_or(true) {
        ("from", "to")
    } else {
        ("recordedOnFrom", "recordedOnTo")
    };
    format!(
        "{}{}?{}={}&{}={}",
        base_url, endpoint, from_param, from_date, to_param, to_date
    )
}

const BASE_URL: &str = "https://api.cv19api.com/api/v1/";
const DEATHS_ENDPOINT: &str = "deaths";
const REGIONS_ENDPOINT: &str = "deaths/regions";
const TRUSTS_ENDPOINT: &str = "deaths/trusts";

pub fn deaths() -> Result<DeathsResponse> {
    get_data(DEATHS_ENDPOINT, None, None, None)
}

pub fn parameterised_deaths(
    from: NaiveDate,
    to: NaiveDate,
    use_ground_truth: bool,
) -> Result<DeathsResponse> {
    get_data(
        DEATHS_ENDPOINT,
        Some(from),
        Some(to),
        Some(use_ground_truth),
    )
}

pub fn deaths_by_region() -> Result<RegionsResponse> {
    get_data(REGIONS_ENDPOINT, None, None, None)
}

pub fn parameterised_deaths_by_region(
    from: NaiveDate,
    to: NaiveDate,
    use_ground_truth: bool,
) -> Result<RegionsResponse> {
    get_data(
        REGIONS_ENDPOINT,
        Some(from),
        Some(to),
        Some(use_ground_truth),
    )
}

pub fn deaths_by_trust() -> Result<TrustsResponse> {
    get_data(TRUSTS_ENDPOINT, None, None, None)
}

pub fn parameterised_deaths_by_trust(
    from: NaiveDate,
    to: NaiveDate,
    use_ground_truth: bool,
) -> Result<TrustsResponse> {
    get_data(
        TRUSTS_ENDPOINT,
        Some(from),
        Some(to),
        Some(use_ground_truth),
    )
}

fn get_data<T>(
    endpoint: &str,
    from: Option<NaiveDate>,
    to: Option<NaiveDate>,
    use_ground_truth: Option<bool>,
) -> Result<T>
where
    T: DeserializeOwned,
{
    let url = match (from, to) {
        (Some(from_date), Some(to_date)) => {
            url_for(BASE_URL, endpoint, use_ground_truth, from_date, to_date)
        }
        _ => format!("{}{}", BASE_URL, endpoint),
    };
    fetch(&url)
}

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

    #[test]
    fn having_none_or_true_for_ground_truth_sets_from_to_param_string() {
        let use_ground_truth_none: Option<bool> = None;
        let use_ground_truth_true = Some(true);
        let base_url = "";
        let endpoint = "";
        let from_date = NaiveDate::from_ymd(2020, 05, 4);
        let to_date = NaiveDate::from_ymd(2020, 05, 12);

        let constructed_url_with_none_ground_truth = url_for(
            base_url,
            endpoint,
            use_ground_truth_none,
            from_date,
            to_date,
        );
        assert_eq!(
            "?from=2020-05-04&to=2020-05-12",
            constructed_url_with_none_ground_truth
        );

        let constructed_url_with_true_ground_truth = url_for(
            base_url,
            endpoint,
            use_ground_truth_true,
            from_date,
            to_date,
        );
        assert_eq!(
            "?from=2020-05-04&to=2020-05-12",
            constructed_url_with_true_ground_truth
        );
    }

    #[test]
    fn having_false_for_ground_truth_sets_recordedonfrom_recordedonto_to_param_string() {
        let use_ground_truth_false = Some(false);
        let base_url = "";
        let endpoint = "";
        let from_date = NaiveDate::from_ymd(2020, 05, 4);
        let to_date = NaiveDate::from_ymd(2020, 05, 12);

        let constructed_url_with_false_ground_truth = url_for(
            base_url,
            endpoint,
            use_ground_truth_false,
            from_date,
            to_date,
        );

        assert_eq!(
            "?recordedOnFrom=2020-05-04&recordedOnTo=2020-05-12",
            constructed_url_with_false_ground_truth
        );
    }
}