use serde::{Deserialize, Serialize};
const fn default_port() -> u16 {
993
}
fn default_mailbox() -> String {
"INBOX".to_string()
}
const fn default_poll_interval_secs() -> u64 {
60
}
const fn default_batch_size() -> u32 {
50
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MailboxConfig {
#[serde(default)]
pub imap: Option<ImapConfig>,
#[serde(default)]
pub smtp: Option<MailboxSmtpConfig>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ImapConfig {
pub host: String,
#[serde(default = "default_port")]
pub port: u16,
pub username: String,
pub password_env: String,
#[serde(default = "default_mailbox")]
pub mailbox: String,
#[serde(default = "default_poll_interval_secs")]
pub poll_interval_secs: u64,
#[serde(default = "default_batch_size")]
pub batch_size: u32,
#[serde(default)]
pub attachment_bucket: Option<String>,
#[serde(default)]
pub routing: Vec<RoutingRuleConfig>,
}
const fn default_smtp_port() -> u16 {
587
}
const fn default_smtp_timeout_secs() -> u64 {
30
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum SmtpTlsMode {
#[default]
StartTls,
Tls,
None,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct MailboxSmtpConfig {
pub host: String,
#[serde(default = "default_smtp_port")]
pub port: u16,
pub address: String,
pub username: String,
pub password_env: String,
#[serde(default)]
pub tls: SmtpTlsMode,
#[serde(default = "default_smtp_timeout_secs")]
pub timeout_secs: u64,
#[serde(default)]
pub return_path: Option<ReturnPathConfig>,
}
impl MailboxSmtpConfig {
#[must_use]
pub fn sending_domain(&self) -> &str {
self.address
.rsplit_once('@')
.map_or(self.address.as_str(), |(_, domain)| domain)
}
#[must_use]
pub fn return_path_local_part(&self) -> &str {
self.return_path
.as_ref()
.and_then(|rp| rp.local_part.as_deref())
.unwrap_or(DEFAULT_RETURN_PATH_LOCAL_PART)
}
#[must_use]
pub fn return_path_domain(&self) -> &str {
self.return_path
.as_ref()
.and_then(|rp| rp.domain.as_deref())
.unwrap_or_else(|| self.sending_domain())
}
}
const DEFAULT_RETURN_PATH_LOCAL_PART: &str = "bounces";
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ReturnPathConfig {
#[serde(default)]
pub local_part: Option<String>,
#[serde(default)]
pub domain: Option<String>,
}
const fn default_challenge_suppress_after() -> u32 {
2
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SendSettings {
#[serde(default = "default_challenge_suppress_after")]
pub challenge_suppress_after: u32,
#[serde(default)]
pub verp_probe_on_start: bool,
}
impl Default for SendSettings {
fn default() -> Self {
Self {
challenge_suppress_after: default_challenge_suppress_after(),
verp_probe_on_start: false,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RoutingRuleConfig {
pub address: String,
pub entity_type: String,
}
impl RoutingRuleConfig {
#[must_use]
pub fn to_rule(&self) -> fraiseql_functions::RoutingRule {
fraiseql_functions::RoutingRule {
address: self.address.clone(),
entity_type: self.entity_type.clone(),
}
}
}
#[cfg(test)]
mod tests;