1use serde::Deserialize;
2
3#[non_exhaustive]
8#[derive(Debug, Clone, Deserialize)]
9#[serde(default)]
10pub struct EmailConfig {
11 pub templates_path: String,
14 pub layouts_path: String,
17 pub default_from_name: String,
19 pub default_from_email: String,
21 pub default_reply_to: Option<String>,
23 pub default_locale: String,
26 pub cache_templates: bool,
29 pub template_cache_size: usize,
31 pub smtp: SmtpConfig,
33}
34
35impl Default for EmailConfig {
36 fn default() -> Self {
37 Self {
38 templates_path: "emails".into(),
39 layouts_path: "emails/layouts".into(),
40 default_from_name: String::new(),
41 default_from_email: String::new(),
42 default_reply_to: None,
43 default_locale: "en".into(),
44 cache_templates: true,
45 template_cache_size: 100,
46 smtp: SmtpConfig::default(),
47 }
48 }
49}
50
51#[non_exhaustive]
53#[derive(Debug, Clone, Deserialize)]
54#[serde(default)]
55pub struct SmtpConfig {
56 pub host: String,
58 pub port: u16,
60 pub username: Option<String>,
62 pub password: Option<String>,
64 pub security: SmtpSecurity,
66}
67
68impl Default for SmtpConfig {
69 fn default() -> Self {
70 Self {
71 host: "localhost".into(),
72 port: 587,
73 username: None,
74 password: None,
75 security: SmtpSecurity::default(),
76 }
77 }
78}
79
80#[derive(Debug, Clone, Default, Deserialize, PartialEq)]
82#[serde(rename_all = "lowercase")]
83pub enum SmtpSecurity {
84 #[default]
86 StartTls,
87 Tls,
89 None,
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96
97 #[test]
98 fn email_config_defaults() {
99 let config = EmailConfig::default();
100 assert_eq!(config.templates_path, "emails");
101 assert_eq!(config.layouts_path, "emails/layouts");
102 assert_eq!(config.default_from_name, "");
103 assert_eq!(config.default_from_email, "");
104 assert!(config.default_reply_to.is_none());
105 assert_eq!(config.default_locale, "en");
106 assert!(config.cache_templates);
107 assert_eq!(config.template_cache_size, 100);
108 }
109
110 #[test]
111 fn smtp_config_defaults() {
112 let config = SmtpConfig::default();
113 assert_eq!(config.host, "localhost");
114 assert_eq!(config.port, 587);
115 assert!(config.username.is_none());
116 assert!(config.password.is_none());
117 assert_eq!(config.security, SmtpSecurity::StartTls);
118 }
119
120 #[test]
121 fn email_config_from_yaml() {
122 let yaml = r#"
123 templates_path: custom/emails
124 default_from_name: TestApp
125 default_from_email: test@example.com
126 default_reply_to: reply@example.com
127 default_locale: uk
128 cache_templates: false
129 template_cache_size: 50
130 smtp:
131 host: smtp.example.com
132 port: 465
133 username: user
134 password: pass
135 security: tls
136 "#;
137 let config: EmailConfig = serde_yaml_ng::from_str(yaml).unwrap();
138 assert_eq!(config.templates_path, "custom/emails");
139 assert_eq!(config.default_from_name, "TestApp");
140 assert_eq!(config.default_from_email, "test@example.com");
141 assert_eq!(
142 config.default_reply_to.as_deref(),
143 Some("reply@example.com")
144 );
145 assert_eq!(config.default_locale, "uk");
146 assert!(!config.cache_templates);
147 assert_eq!(config.template_cache_size, 50);
148 assert_eq!(config.smtp.host, "smtp.example.com");
149 assert_eq!(config.smtp.port, 465);
150 assert_eq!(config.smtp.username.as_deref(), Some("user"));
151 assert_eq!(config.smtp.password.as_deref(), Some("pass"));
152 assert_eq!(config.smtp.security, SmtpSecurity::Tls);
153 }
154
155 #[test]
156 fn email_config_partial_yaml_uses_defaults() {
157 let yaml = r#"
158 default_from_email: noreply@app.com
159 "#;
160 let config: EmailConfig = serde_yaml_ng::from_str(yaml).unwrap();
161 assert_eq!(config.templates_path, "emails");
162 assert_eq!(config.default_from_email, "noreply@app.com");
163 assert_eq!(config.smtp.host, "localhost");
164 assert_eq!(config.smtp.port, 587);
165 }
166
167 #[test]
168 fn smtp_security_none_variant() {
169 let yaml = r#"security: none"#;
170 let config: SmtpConfig = serde_yaml_ng::from_str(yaml).unwrap();
171 assert_eq!(config.security, SmtpSecurity::None);
172 }
173}