interlude 0.1.0

Convert share links of different music streaming services.
Documentation
use std::{
    collections::HashMap,
    time::{SystemTime, UNIX_EPOCH},
};

use reqwest::Client;
use serde::{Deserialize, Serialize};
use thiserror::Error;

use crate::config::ClientCredentials;

#[derive(Debug, Error)]
pub enum AuthorizationError {
    #[error(transparent)]
    RequestError(#[from] reqwest::Error),
    #[error(transparent)]
    ParsingError(#[from] serde_json::Error),
}

pub struct AccessToken {
    pub token: String,
    expiry: u64,
    auth_endpoint: String,
}

impl AccessToken {
    pub async fn new(
        client: &Client,
        credentials: &ClientCredentials,
        auth_endpoint: &str,
    ) -> Result<Self, AuthorizationError> {
        #[derive(Serialize, Deserialize)]
        struct ResponseBody {
            access_token: String,
            expires_in: u64,
        }

        let time_now = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .expect("Don't mess with the system clock. Use a reliable NTP server pls.")
            .as_secs();

        let mut form = HashMap::new();
        form.insert("grant_type", "client_credentials");
        let response = client
            .post(auth_endpoint)
            .form(&form)
            .basic_auth(&credentials.client_id, Some(&credentials.client_secret))
            .send()
            .await?
            .text()
            .await?;
        let body: ResponseBody = serde_json::from_str(&response)?;

        Ok(Self {
            token: body.access_token,
            expiry: time_now + body.expires_in - 5,
            auth_endpoint: auth_endpoint.to_owned(),
        })
    }
    pub async fn refresh(
        &mut self,
        client: &Client,
        credentials: &ClientCredentials,
    ) -> Result<(), AuthorizationError> {
        let refreshed_token = AccessToken::new(client, credentials, &self.auth_endpoint).await?;
        self.token = refreshed_token.token;
        self.expiry = refreshed_token.expiry;
        Ok(())
    }

    pub fn is_expired(&self) -> bool {
        let time_now = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .expect("Don't mess with the system clock. Use a reliable NTP server pls.")
            .as_secs();

        if time_now < self.expiry {
            return false;
        }

        return true;
    }
}