archstatus-rs 0.1.0

A simple Rust wrapper for the status.archlinux.org website API
Documentation
use std::{collections::HashMap, fmt::Display, str::FromStr, time::{SystemTime, UNIX_EPOCH}};

use reqwest::Client;
use thiserror::Error;

/// Link to the status.archlinux.org API endpoint
const ENDPOINT: &str = "https://status.archlinux.org/api";

#[derive(Debug, Error)]
pub enum RequestError {
    #[error("HTTP error: {0}")]
    HttpError(#[from] reqwest::Error),

    #[error("Date error: {0}")]
    Date(String),

    #[error("Unexpected error: {0}")]
    Other(String),

    #[error("Parsing JSON error: {0}")]
    ParsingJSON(String)
}

pub struct RequestWrapper {
    client: Client
}

impl RequestWrapper {
    /// Creating a wrapper for making API requests with specified headers
    ///
    /// ```rust, ignore
    /// let headers = HashMap::from([
    ///     ("User-agent", "Mozilla/5.0 (X11; Linux x86_64; rv:142.0) Gecko/20100101 Firefox/142.0")
    /// ]);
    /// let wrapper = request::RequestWrapper::with_headers(headers);
    /// ```
    pub fn with_headers(headers: HashMap<&str, &str>) -> Self {
        Self::new(Some(headers))
    }

    /// Creating a wrapper for making API requests with default
    /// let wrapper = request::RequestWrapper::default();
    pub fn default() -> Self {
        Self::new(None)
    }

    pub async fn get<T>(&self, path: T) -> Result<String, RequestError>
    where
        T: AsRef<str> + Display
    {
        let url = format!("{ENDPOINT}{}&_={}", &path, self.cache_buster());
        let resp = self.client.get(&url).send().await?;
        let body = resp.text().await?;
        Ok(body)
    }

    fn new(default_headers: Option<HashMap<&str, &str>>) -> Self {
        let mut headers = reqwest::header::HeaderMap::new();

        if let Some(map) = default_headers {
            for (key, value) in map {
                headers.insert(
                    reqwest::header::HeaderName::from_str(key).unwrap(),
                    reqwest::header::HeaderValue::from_str(value).unwrap()
                );
            }
        }

        let client = Client::builder()
            .default_headers(headers)
            .build()
            .unwrap();

        Self { client }
    }

    // It is necessary for generating the _= value, which is used to prevent request caching
    fn cache_buster(&self) -> u128 {
        let now = SystemTime::now();
        now.duration_since(UNIX_EPOCH)
            .expect("Time went backwards")
            .as_millis()
    }
}