1use serde::Deserialize;
2
3#[derive(Debug, Clone, Deserialize, Default, PartialEq, Eq)]
7#[serde(rename_all = "lowercase")]
8pub enum TransportBackend {
9 #[default]
11 Smtp,
12 Resend,
14}
15
16#[derive(Debug, Clone, Deserialize)]
24#[serde(default)]
25pub struct EmailConfig {
26 pub transport: TransportBackend,
28 pub templates_path: String,
30 pub default_from_name: String,
32 pub default_from_email: String,
34 pub default_reply_to: Option<String>,
36 pub cache_templates: bool,
39 pub template_cache_size: usize,
42
43 #[cfg(feature = "smtp")]
45 pub smtp: SmtpConfig,
46
47 #[cfg(feature = "resend")]
49 pub resend: ResendConfig,
50}
51
52impl Default for EmailConfig {
53 fn default() -> Self {
54 Self {
55 transport: TransportBackend::default(),
56 templates_path: "emails".to_string(),
57 default_from_name: String::new(),
58 default_from_email: String::new(),
59 default_reply_to: None,
60 cache_templates: true,
61 template_cache_size: 100,
62 #[cfg(feature = "smtp")]
63 smtp: SmtpConfig::default(),
64 #[cfg(feature = "resend")]
65 resend: ResendConfig::default(),
66 }
67 }
68}
69
70#[cfg(feature = "smtp")]
72#[derive(Debug, Clone, Deserialize, Default, PartialEq, Eq)]
73#[serde(rename_all = "snake_case")]
74pub enum SmtpSecurity {
75 None,
77 #[default]
79 #[serde(rename = "starttls")]
80 StartTls,
81 ImplicitTls,
83}
84
85#[cfg(feature = "smtp")]
87#[derive(Debug, Clone, Deserialize)]
88#[serde(default)]
89pub struct SmtpConfig {
90 pub host: String,
92 pub port: u16,
94 pub username: String,
96 pub password: String,
98 pub security: SmtpSecurity,
100}
101
102#[cfg(feature = "smtp")]
103impl Default for SmtpConfig {
104 fn default() -> Self {
105 Self {
106 host: "localhost".to_string(),
107 port: 587,
108 username: String::new(),
109 password: String::new(),
110 security: SmtpSecurity::default(),
111 }
112 }
113}
114
115#[cfg(feature = "resend")]
117#[derive(Debug, Clone, Deserialize, Default)]
118#[serde(default)]
119pub struct ResendConfig {
120 pub api_key: String,
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 #[test]
129 fn default_values() {
130 let config = EmailConfig::default();
131 assert_eq!(config.templates_path, "emails");
132 assert_eq!(config.default_from_name, "");
133 assert_eq!(config.default_from_email, "");
134 assert!(config.default_reply_to.is_none());
135 assert_eq!(config.transport, TransportBackend::Smtp);
136 }
137
138 #[test]
139 fn partial_yaml_deserialization() {
140 let yaml = r#"
141templates_path: "mail"
142default_from_name: "Acme"
143default_from_email: "hi@acme.com"
144"#;
145 let config: EmailConfig = serde_yaml_ng::from_str(yaml).unwrap();
146 assert_eq!(config.templates_path, "mail");
147 assert_eq!(config.default_from_name, "Acme");
148 assert_eq!(config.default_from_email, "hi@acme.com");
149 assert_eq!(config.transport, TransportBackend::Smtp);
150 }
151
152 #[test]
153 fn transport_backend_deserialization() {
154 let yaml = "transport: resend";
155 let config: EmailConfig = serde_yaml_ng::from_str(yaml).unwrap();
156 assert_eq!(config.transport, TransportBackend::Resend);
157 }
158
159 #[cfg(feature = "smtp")]
160 #[test]
161 fn smtp_security_none_deserialization() {
162 let yaml = r#"
163transport: smtp
164smtp:
165 host: "localhost"
166 security: none
167"#;
168 let config: EmailConfig = serde_yaml_ng::from_str(yaml).unwrap();
169 assert_eq!(config.smtp.security, SmtpSecurity::None);
170 }
171
172 #[cfg(feature = "smtp")]
173 #[test]
174 fn smtp_security_starttls_deserialization() {
175 let yaml = r#"
176transport: smtp
177smtp:
178 host: "mail.example.com"
179 security: starttls
180"#;
181 let config: EmailConfig = serde_yaml_ng::from_str(yaml).unwrap();
182 assert_eq!(config.smtp.security, SmtpSecurity::StartTls);
183 }
184
185 #[cfg(feature = "smtp")]
186 #[test]
187 fn smtp_security_implicit_tls_deserialization() {
188 let yaml = r#"
189transport: smtp
190smtp:
191 host: "mail.example.com"
192 port: 465
193 security: implicit_tls
194"#;
195 let config: EmailConfig = serde_yaml_ng::from_str(yaml).unwrap();
196 assert_eq!(config.smtp.security, SmtpSecurity::ImplicitTls);
197 assert_eq!(config.smtp.port, 465);
198 }
199
200 #[cfg(feature = "smtp")]
201 #[test]
202 fn smtp_security_default_is_starttls() {
203 let config = SmtpConfig::default();
204 assert_eq!(config.security, SmtpSecurity::StartTls);
205 }
206
207 #[test]
208 fn cache_config_deserialization() {
209 let yaml = r#"
210cache_templates: false
211template_cache_size: 50
212"#;
213 let config: EmailConfig = serde_yaml_ng::from_str(yaml).unwrap();
214 assert!(!config.cache_templates);
215 assert_eq!(config.template_cache_size, 50);
216 }
217
218 #[test]
219 fn cache_config_defaults() {
220 let config = EmailConfig::default();
221 assert!(config.cache_templates);
222 assert_eq!(config.template_cache_size, 100);
223 }
224}