1mod client;
23mod email;
24mod events;
25mod fake;
26mod mailable;
27
28#[cfg(feature = "markdown")]
29mod markdown;
30
31pub use client::{Mail, MailClient, SmtpBuilder};
32pub use email::{Email, EmailSnapshot};
33pub use events::MailSent;
34pub use fake::FakeMail;
35pub use mailable::{Content, Envelope, Mailable};
36
37use sova_core::{App, Plugin, Request};
38
39pub trait MailExt {
41 fn mail(&self) -> Email;
42}
43
44impl MailExt for Request {
45 fn mail(&self) -> Email {
46 let email = self.state::<MailClient>().compose();
47 #[cfg(feature = "templates")]
48 {
49 if let Some(templates) = self.try_state::<sova_templates::MiniJinjaTemplates>() {
50 return email.with_ambient(templates.freeze_ambient(self));
51 }
52 }
53 email
54 }
55}
56
57impl Plugin for Mail {
58 fn id(&self) -> &'static str {
59 "mail"
60 }
61
62 fn meta(&self) -> sova_core::PluginMeta {
63 sova_core::PluginMeta::new("Mail")
64 .description("Outbound email via lettre (SMTP / fake / file)")
65 .version(env!("CARGO_PKG_VERSION"))
66 }
67
68 fn install(self, app: &mut App) {
69 let mut mail = self;
70 if !mail.is_from_explicit() {
72 if let Some(doc) = app.config_doc() {
73 if let Some(section) = doc.section("mail") {
74 if let Some(from) = section.get("from").and_then(|v| v.as_str()) {
75 mail = mail.from(from);
76 }
77 }
78 }
79 }
80 let mut client = mail.into_client();
81 client.set_events(app.events());
82
83 #[cfg(feature = "templates")]
84 {
85 if let Some(t) = app.try_state::<sova_templates::MiniJinjaTemplates>() {
86 client.set_templates(t.as_ref().clone());
87 }
88 let wire = client.clone();
90 app.on_startup(move |state| {
91 let wire = wire.clone();
92 async move {
93 if wire.templates().is_none() {
94 if let Some(t) = state.get::<sova_templates::MiniJinjaTemplates>() {
95 wire.set_templates(t.as_ref().clone());
96 }
97 }
98 Ok(())
99 }
100 });
101 }
102
103 app.state(client);
104 }
105}
106
107impl Plugin for client::SmtpBuilder {
108 fn id(&self) -> &'static str {
109 "mail"
110 }
111
112 fn meta(&self) -> sova_core::PluginMeta {
113 sova_core::PluginMeta::new("Mail")
114 .description("Outbound email via lettre (SMTP / fake / file)")
115 .version(env!("CARGO_PKG_VERSION"))
116 }
117
118 fn install(self, app: &mut App) {
119 match self.build() {
120 Ok(mail) => mail.install(app),
121 Err(e) => panic!("mail smtp install failed (refusing silent fake): {e}"),
122 }
123 }
124}