use std::collections::HashMap;
use std::iter::IntoIterator;
use typed_builder::TypedBuilder;
#[derive(Debug, Clone, PartialEq)]
pub struct TlsSettings {
pub cert_path: String,
pub key_path: String,
pub ca_path: Option<String>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ProxyConfig {
pub proxy_url: String,
pub username: Option<String>,
pub password: Option<String>,
}
#[derive(TypedBuilder, Debug, PartialEq, Default)]
pub struct EnvelopeData {
#[builder(default, setter(strip_option))]
pub from: Option<String>,
#[builder(default)]
pub rcpt: Vec<String>,
#[builder(default, setter(strip_option))]
pub ip: Option<String>,
#[builder(default, setter(strip_option))]
pub user: Option<String>,
#[builder(default, setter(strip_option))]
pub helo: Option<String>,
#[builder(default, setter(strip_option))]
pub hostname: Option<String>,
#[builder(default, setter(strip_option))]
pub file_path: Option<String>,
#[builder(default)]
pub body_block: bool,
#[builder(default)]
pub additional_headers: HashMap<String, String>,
}
impl IntoIterator for EnvelopeData {
type Item = (String, String);
type IntoIter = std::vec::IntoIter<(String, String)>;
fn into_iter(self) -> Self::IntoIter {
let mut headers = Vec::with_capacity(self.rcpt.len() + self.additional_headers.len() + 7);
if let Some(from) = self.from {
headers.push(("From".to_string(), from));
}
if let Some(ip) = self.ip {
headers.push(("IP".to_string(), ip));
}
if let Some(user) = self.user {
headers.push(("User".to_string(), user));
}
if let Some(helo) = self.helo {
headers.push(("Helo".to_string(), helo));
}
if let Some(hostname) = self.hostname {
headers.push(("Hostname".to_string(), hostname));
}
if let Some(file_path) = self.file_path {
headers.push(("File".to_string(), file_path));
}
if self.body_block {
headers.push(("Flags".to_string(), "body_block".to_string()));
}
for rcpt in self.rcpt {
headers.push(("Rcpt".to_string(), rcpt));
}
headers.extend(self.additional_headers);
headers.into_iter()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn envelope_emits_one_rcpt_header_per_recipient() {
let envelope = EnvelopeData::builder()
.from("sender@example.com".to_string())
.rcpt(vec![
"one@example.com".to_string(),
"two@example.com".to_string(),
"three@example.com".to_string(),
])
.build();
let headers: Vec<(String, String)> = envelope.into_iter().collect();
let rcpts: Vec<&str> = headers
.iter()
.filter(|(k, _)| k == "Rcpt")
.map(|(_, v)| v.as_str())
.collect();
assert_eq!(
rcpts,
["one@example.com", "two@example.com", "three@example.com"]
);
}
}
#[derive(TypedBuilder, Debug, PartialEq)]
pub struct Config {
pub base_url: String,
#[builder(default, setter(strip_option))]
pub password: Option<String>,
#[builder(default = 30.0)]
pub timeout: f64,
#[builder(default = 1)]
pub retries: u32,
#[builder(default, setter(strip_option))]
pub tls_settings: Option<TlsSettings>,
#[builder(default, setter(strip_option))]
pub proxy_config: Option<ProxyConfig>,
#[builder(default = true)]
pub zstd: bool,
#[builder(default, setter(strip_option))]
pub encryption_key: Option<String>,
}