azure-communications 0.1.1

API Wrapper for the Azure Communication Services in Rust.
Documentation
use anyhow::Result;
use base64::{engine::general_purpose::STANDARD as base64, Engine};
use lazy_static::lazy_static;
use reqwest::Url;
use ring::{
    digest::{digest, SHA256},
    hmac,
};
use time::{format_description, OffsetDateTime};

pub fn create_authorization_signature(
    endpoint: &Url,
    request_body: &str,
    access_key: &str,
    date: &str,
) -> Result<String> {
    let uri_path_and_query = if let Some(query) = endpoint.query() {
        format!("{}?{}", endpoint.path(), query)
    } else {
        endpoint.path().to_string()
    };

    let host = endpoint
        .host_str()
        .ok_or_else(|| anyhow::anyhow!("Invalid host"))?;

    let content_hash = digest(&SHA256, request_body.as_bytes());
    let content_hash_base64 = base64.encode(content_hash.as_ref());

    let decoded_key = base64.decode(access_key)?;

    let string_to_sign = format!(
        "POST\n{}\n{};{};{}",
        uri_path_and_query, date, host, content_hash_base64
    );

    let signed_key = hmac::Key::new(hmac::HMAC_SHA256, &decoded_key);
    let signature = hmac::sign(&signed_key, string_to_sign.as_bytes());

    Ok(base64.encode(signature))
}

pub fn format_utc_string(timestamp: OffsetDateTime) -> Result<String> {
    lazy_static! {
        static ref RFC1123: Vec<format_description::FormatItem<'static>> = format_description::parse(
            "[weekday repr:short], [day] [month repr:short] [year] [hour]:[minute]:[second] GMT",
        )
        .expect("Failed to parse format description for RFC1123");
    }

    let formatted = timestamp.format(&RFC1123)?;

    Ok(formatted)
}