twitch_oauth_token 4.4.0

Type-safe Twitch OAuth 2.0 authentication library with CSRF protection and full scope support
Documentation
use std::{collections::HashSet, ops::Deref};

use asknothingx2_util::api::{IntoRequestBuilder, Method, preset};

use crate::{
    AuthUrl, ClientId, ClientSecret, Error,
    scope::{Scope, ScopesMut, scopes_mut},
    types::GrantType,
};

#[derive(Debug, Clone)]
pub struct TestAccessToken<'a> {
    client_id: &'a ClientId,
    client_secret: &'a ClientSecret,
    grant_type: GrantType,
    user_id: Option<&'a str>,
    scopes: HashSet<Scope>,
    auth_url: AuthUrl,
}

impl<'a> TestAccessToken<'a> {
    pub fn new(
        client_id: &'a ClientId,
        client_secret: &'a ClientSecret,
        grant_type: GrantType,
        user_id: Option<&'a str>,
        scopes: HashSet<Scope>,
        auth_url: AuthUrl,
    ) -> Self {
        Self {
            client_id,
            client_secret,
            grant_type,
            user_id,
            scopes,
            auth_url,
        }
    }

    pub fn scopes_mut(&mut self) -> ScopesMut<'_> {
        scopes_mut(&mut self.scopes)
    }

    pub async fn send(self) -> Result<crate::UserToken, crate::Error> {
        let client = preset::testing("twitch-oauth-test/1.0").build().unwrap();
        crate::oauth::json(&client, self).await
    }
}

impl IntoRequestBuilder for TestAccessToken<'_> {
    type Error = Error;
    fn into_request_builder(
        self,
        client: &reqwest::Client,
    ) -> Result<reqwest::RequestBuilder, Self::Error> {
        let mut url = self.auth_url.to_url();

        let mut params = vec![
            ("client_id", self.client_id.deref()),
            ("client_secret", self.client_secret.secret()),
            ("grant_type", self.grant_type.as_str()),
        ];

        let user_id = self.user_id.unwrap_or_default();

        if !user_id.is_empty() {
            params.push(("user_id", user_id));
        }

        let scopes = self
            .scopes
            .clone()
            .into_iter()
            .map(String::from)
            .collect::<Vec<String>>()
            .join(" ");

        if !scopes.is_empty() {
            params.push(("scope", &scopes));
        }

        url.query_pairs_mut().extend_pairs(params);
        Ok(client.request(Method::POST, url))
    }
}