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())
}