plunk-rs 0.1.0

Async Rust client for the Plunk transactional email API
Documentation
use crate::error::{Error, Result};
use serde::Serialize;
use serde_json::{Map, Value};

pub(crate) fn serialize_data_map<T>(data: T) -> Result<Map<String, Value>>
where
    T: Serialize,
{
    match serde_json::to_value(data).map_err(Error::TemplateDataSerialization)? {
        Value::Object(map) => Ok(map),
        _ => Err(Error::TemplateDataMustBeObject),
    }
}

pub(crate) fn normalize_email(value: String) -> Result<String> {
    let trimmed = value.trim();
    if trimmed.is_empty() {
        return Err(Error::InvalidEmailAddress {
            reason: "email cannot be empty",
            value,
        });
    }
    if trimmed.contains(char::is_whitespace) {
        return Err(Error::InvalidEmailAddress {
            reason: "email cannot contain whitespace",
            value,
        });
    }

    let mut parts = trimmed.split('@');
    let local = parts.next().unwrap_or_default();
    let domain = parts.next().unwrap_or_default();
    if local.is_empty() || domain.is_empty() || parts.next().is_some() {
        return Err(Error::InvalidEmailAddress {
            reason: "email must contain exactly one @ with non-empty local and domain parts",
            value,
        });
    }
    if !domain.contains('.') {
        return Err(Error::InvalidEmailAddress {
            reason: "email domain must contain a dot",
            value,
        });
    }

    Ok(trimmed.to_string())
}

pub(crate) fn normalize_non_empty(value: String, error: Error) -> Result<String> {
    let trimmed = value.trim();
    if trimmed.is_empty() {
        return Err(error);
    }
    Ok(trimmed.to_string())
}

pub(crate) fn normalize_header_key(value: String) -> Result<String> {
    let trimmed = value.trim();
    if trimmed.is_empty() {
        return Err(Error::InvalidHeaderName);
    }
    if trimmed.contains(':') {
        return Err(Error::InvalidHeaderName);
    }
    Ok(trimmed.to_string())
}