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 inline_css: bool,
36 pub smtp: SmtpConfig,
38}
39
40impl Default for EmailConfig {
41 fn default() -> Self {
42 Self {
43 templates_path: "emails".into(),
44 layouts_path: "emails/layouts".into(),
45 default_from_name: String::new(),
46 default_from_email: String::new(),
47 default_reply_to: None,
48 default_locale: "en".into(),
49 cache_templates: true,
50 template_cache_size: 100,
51 inline_css: true,
52 smtp: SmtpConfig::default(),
53 }
54 }
55}
56
57#[non_exhaustive]
59#[derive(Debug, Clone, Deserialize)]
60#[serde(default)]
61pub struct SmtpConfig {
62 pub host: String,
64 pub port: u16,
66 pub username: Option<String>,
68 pub password: Option<String>,
70 pub security: SmtpSecurity,
72}
73
74impl Default for SmtpConfig {
75 fn default() -> Self {
76 Self {
77 host: "localhost".into(),
78 port: 587,
79 username: None,
80 password: None,
81 security: SmtpSecurity::default(),
82 }
83 }
84}
85
86#[derive(Debug, Clone, Default, Deserialize, PartialEq)]
88#[serde(rename_all = "lowercase")]
89pub enum SmtpSecurity {
90 #[default]
92 StartTls,
93 Tls,
95 None,
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 #[test]
104 fn email_config_defaults() {
105 let config = EmailConfig::default();
106 assert_eq!(config.templates_path, "emails");
107 assert_eq!(config.layouts_path, "emails/layouts");
108 assert_eq!(config.default_from_name, "");
109 assert_eq!(config.default_from_email, "");
110 assert!(config.default_reply_to.is_none());
111 assert_eq!(config.default_locale, "en");
112 assert!(config.cache_templates);
113 assert_eq!(config.template_cache_size, 100);
114 assert!(config.inline_css);
115 }
116
117 #[test]
118 fn smtp_config_defaults() {
119 let config = SmtpConfig::default();
120 assert_eq!(config.host, "localhost");
121 assert_eq!(config.port, 587);
122 assert!(config.username.is_none());
123 assert!(config.password.is_none());
124 assert_eq!(config.security, SmtpSecurity::StartTls);
125 }
126
127 #[test]
128 fn email_config_from_yaml() {
129 let yaml = r#"
130 templates_path: custom/emails
131 default_from_name: TestApp
132 default_from_email: test@example.com
133 default_reply_to: reply@example.com
134 default_locale: uk
135 cache_templates: false
136 template_cache_size: 50
137 inline_css: false
138 smtp:
139 host: smtp.example.com
140 port: 465
141 username: user
142 password: pass
143 security: tls
144 "#;
145 let config: EmailConfig = serde_yaml_ng::from_str(yaml).unwrap();
146 assert_eq!(config.templates_path, "custom/emails");
147 assert_eq!(config.default_from_name, "TestApp");
148 assert_eq!(config.default_from_email, "test@example.com");
149 assert_eq!(
150 config.default_reply_to.as_deref(),
151 Some("reply@example.com")
152 );
153 assert_eq!(config.default_locale, "uk");
154 assert!(!config.cache_templates);
155 assert_eq!(config.template_cache_size, 50);
156 assert!(!config.inline_css);
157 assert_eq!(config.smtp.host, "smtp.example.com");
158 assert_eq!(config.smtp.port, 465);
159 assert_eq!(config.smtp.username.as_deref(), Some("user"));
160 assert_eq!(config.smtp.password.as_deref(), Some("pass"));
161 assert_eq!(config.smtp.security, SmtpSecurity::Tls);
162 }
163
164 #[test]
165 fn email_config_partial_yaml_uses_defaults() {
166 let yaml = r#"
167 default_from_email: noreply@app.com
168 "#;
169 let config: EmailConfig = serde_yaml_ng::from_str(yaml).unwrap();
170 assert_eq!(config.templates_path, "emails");
171 assert_eq!(config.default_from_email, "noreply@app.com");
172 assert_eq!(config.smtp.host, "localhost");
173 assert_eq!(config.smtp.port, 587);
174 }
175
176 #[test]
177 fn smtp_security_none_variant() {
178 let yaml = r#"security: none"#;
179 let config: SmtpConfig = serde_yaml_ng::from_str(yaml).unwrap();
180 assert_eq!(config.security, SmtpSecurity::None);
181 }
182
183 #[test]
184 fn email_config_inline_css_default_true() {
185 let config = EmailConfig::default();
186 assert!(config.inline_css);
187 }
188
189 #[test]
190 fn email_config_inline_css_from_yaml() {
191 let yaml = "inline_css: false";
192 let config: EmailConfig = serde_yaml_ng::from_str(yaml).unwrap();
193 assert!(!config.inline_css);
194 }
195
196 #[test]
197 fn email_config_inline_css_omitted_uses_default() {
198 let yaml = "default_from_email: noreply@app.com";
199 let config: EmailConfig = serde_yaml_ng::from_str(yaml).unwrap();
200 assert!(config.inline_css);
201 }
202}