caesura 0.20.0

An all-in-one command line tool to **transcode FLAC** audio files and **upload to gazelle** based indexers/trackers.
Documentation
use std::time::Duration;

use di::{injectable, Ref};
use reqwest::header::{HeaderMap, HeaderValue};
use reqwest::{header, Client, ClientBuilder};

use crate::api::Api;
use crate::built_info;
use crate::options::SharedOptions;

/// The number of requests allowed per duration
const ALLOWED_REQUESTS_PER_DURATION: u64 = 10;
const REQUEST_LIMIT_DURATION: Duration = Duration::from_secs(10);

/// Create an [API]
pub struct ApiFactory {
    api_key: String,
    api_url: String,
}

#[injectable]
impl ApiFactory {
    /// Create a new [`ApiFactory`]
    #[must_use]
    pub fn new(options: Ref<SharedOptions>) -> Self {
        Self {
            api_key: options.api_key.clone().expect("api_key should be set"),
            api_url: options
                .indexer_url
                .clone()
                .expect("indexer_url should be set"),
        }
    }

    #[must_use]
    pub fn create(&self) -> Api {
        let client = self.create_client();
        let rate_limited_client = tower::ServiceBuilder::new()
            .rate_limit(ALLOWED_REQUESTS_PER_DURATION, REQUEST_LIMIT_DURATION)
            .service(client);
        Api {
            api_url: self.api_url.clone(),
            client: rate_limited_client,
        }
    }

    fn create_client(&self) -> Client {
        ClientBuilder::new()
            .default_headers(self.get_headers())
            .build()
            .expect("Client builder should not fail")
    }

    fn get_headers(&self) -> HeaderMap {
        let mut headers = HeaderMap::new();
        headers.insert(header::USER_AGENT, get_user_agent());
        headers.insert(header::ACCEPT, HeaderValue::from_static("application/json"));
        headers.insert(header::AUTHORIZATION, self.get_authorization());
        headers
    }

    fn get_authorization(&self) -> HeaderValue {
        let mut value = HeaderValue::try_from(self.api_key.clone())
            .expect("Authorization header should not fail");
        value.set_sensitive(true);
        value
    }
}

fn get_user_agent() -> HeaderValue {
    let user_agent = format!(
        "{}/{} ({})",
        built_info::PKG_NAME,
        built_info::PKG_VERSION,
        built_info::PKG_HOMEPAGE
    );
    HeaderValue::try_from(user_agent).expect("User agent header should not fail")
}