rust-gmail 0.2.1

A simple crate to send emails using the Gmail API
Documentation
use std::collections::BTreeMap;

use crate::error::Result;
use chrono::{Duration, Utc};
use jwt::{PKeyWithDigest, SignWithKey};
use openssl::{hash::MessageDigest, pkey::PKey};
use serde::{Deserialize, Serialize};

use crate::service_account::ServiceAccount;

pub const GMAIL_SEND_EMAIL_SCOPE: &str = "https://www.googleapis.com/auth/gmail.send";
pub const GOOGLE_AUD_VALUE: &str = "https://oauth2.googleapis.com/token";

#[derive(Serialize, Deserialize)]
pub struct GoogleAuthResponse {
    pub access_token: String,
    pub expires_in: u32,
    pub token_type: String,
}

#[derive(Serialize, Deserialize)]
pub struct GoogleAuthRequest {
    grant_type: String,
    assertion: String,
}

const GRANT_TYPE_SERVICE_ACCOUNT: &str = "urn:ietf:params:oauth:grant-type:jwt-bearer";
impl GoogleAuthRequest {
    pub fn new(assertion: String) -> Self {
        Self {
            grant_type: GRANT_TYPE_SERVICE_ACCOUNT.to_string(),
            assertion,
        }
    }
}

pub fn create_jwt(service_account: &ServiceAccount, send_from_email: &str) -> Result<String> {
    let private_key = PKey::private_key_from_pem(service_account.private_key.as_bytes())?;
    let key_with_digest = PKeyWithDigest {
        digest: MessageDigest::sha256(),
        key: private_key,
    };

    let mut claims: BTreeMap<&str, &str> = BTreeMap::new();

    claims.insert("iss", &service_account.client_email);
    claims.insert("scope", GMAIL_SEND_EMAIL_SCOPE);
    claims.insert("aud", GOOGLE_AUD_VALUE);

    let now = Utc::now();
    let now_timestamp = now.timestamp().to_string();
    claims.insert("iat", &now_timestamp);

    let exp_time = now + Duration::hours(1);

    let exp_time_timestamp = exp_time.timestamp().to_string();
    claims.insert("exp", &exp_time_timestamp);
    claims.insert("sub", send_from_email);

    Ok(claims.sign_with_key(&key_with_digest)?)
}