use std::time::Duration; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(default)] pub struct EmailQueueConfig { pub enabled: Option, pub channel_prefix: Option, pub subscriber_group_id: Option, pub max_retries: Option, pub retry_backoff_ms: Option, } impl EmailQueueConfig { pub fn enabled(&self) -> bool { self.enabled.unwrap_or(true) } pub fn channel_prefix(&self) -> &str { self.channel_prefix.as_deref().unwrap_or("email") } pub fn subscriber_group_id(&self) -> &str { self.subscriber_group_id .as_deref() .unwrap_or("email-worker") } pub fn max_retries(&self) -> u32 { self.max_retries.unwrap_or(3) } pub fn retry_backoff(&self) -> Duration { Duration::from_millis(self.retry_backoff_ms.unwrap_or(500)) } pub fn channel(&self, name: &str) -> String { format!("{}.{}", self.channel_prefix(), name) } pub fn is_valid(&self) -> Result<(), String> { if !self.enabled() { return Ok(()); } if self.channel_prefix.as_deref().is_some_and(|s| s.is_empty()) { return Err("email_queue.channel_prefix must not be empty".to_string()); } if self .subscriber_group_id .as_deref() .is_some_and(|s| s.is_empty()) { return Err( "email_queue.subscriber_group_id must not be empty when enabled".to_string(), ); } if matches!(self.max_retries, Some(0)) { return Err("email_queue.max_retries must be greater than 0".to_string()); } if matches!(self.retry_backoff_ms, Some(0)) { return Err("email_queue.retry_backoff_ms must be greater than 0".to_string()); } Ok(()) } }