rustlavel_mail/lib.rs
1//! rustlavel-mail: sending email, and notifications that fan out to several
2//! channels from one definition.
3//!
4//! The SMTP client is written here, on Tokio's TCP, the same way the HTTP
5//! server and the PostgreSQL driver are — only TLS is delegated, to rustls.
6//!
7//! ```ignore
8//! use rustlavel_mail::{Mail, Message};
9//!
10//! let mailer = Mail::from_config(&config)?;
11//! mailer.send(
12//! Message::new()
13//! .to(("Ada Lovelace", "ada@example.com"))
14//! .subject("Your receipt")
15//! .html("<p>Thank you.</p>"),
16//! ).await?;
17//! ```
18//!
19//! # The three transports
20//!
21//! `mail.transport` picks one, and nothing else in an application changes:
22//!
23//! - `log` — writes the rendered message to the log. The local default: mail
24//! you can read without a mail server, an inbox, or a network.
25//! - `file` — one `.eml` per message under `mail.path`, which any mail client
26//! will open, so a designer can look at the real thing.
27//! - `smtp` — the real one.
28//!
29//! # Testing
30//!
31//! [`Mail::fake`] records what would have been sent and sends nothing, so a
32//! test can assert on the subject and the body without a server anywhere:
33//!
34//! ```ignore
35//! let mailer = Mail::fake();
36//! ship_the_order(&mailer).await?;
37//! mailer.fake().unwrap().assert_sent_to("ada@example.com");
38//! ```
39
40pub mod address;
41pub mod config;
42pub mod encode;
43pub mod fake;
44pub mod mailable;
45pub mod message;
46pub mod notification;
47pub mod smtp;
48pub mod transport;
49
50pub use address::{Address, IntoAddress};
51pub use config::{MailConfig, TransportKind};
52pub use fake::Fake;
53pub use mailable::{Mailable, html_to_text};
54pub use message::{Attachment, Message};
55pub use notification::{Delivery, Notification, Notified, Notifier, Recipient, channel};
56pub use smtp::{Encryption, Reply, SmtpClient, SmtpConfig};
57pub use transport::{FileTransport, LogTransport, Mailer, SmtpTransport, Transport};
58
59use rustlavel_core::{Config, Result};
60use std::path::PathBuf;
61
62/// The entry point, in the shape Laravel's `Mail` facade has.
63///
64/// Every constructor returns a [`Mailer`]; this type only exists so the call
65/// that picks a transport reads like the thing it does.
66pub struct Mail;
67
68impl Mail {
69 /// Build the transport named in `mail.transport`.
70 pub fn from_config(config: &Config) -> Result<Mailer> {
71 Mailer::from_config(config)
72 }
73
74 /// Record instead of sending, for tests. This is `Mail::fake()`.
75 pub fn fake() -> Mailer {
76 Mailer::new(transport::LogTransport::silent()).faking(Fake::new())
77 }
78
79 /// Write each message to the log — the local development default.
80 pub fn log() -> Mailer {
81 Mailer::new(transport::LogTransport::new())
82 }
83
84 /// Write one `.eml` per message under `path`.
85 pub fn file(path: impl Into<PathBuf>) -> Mailer {
86 Mailer::new(transport::FileTransport::new(path))
87 }
88
89 /// Talk to a real SMTP server.
90 pub fn smtp(settings: SmtpConfig) -> Mailer {
91 Mailer::new(transport::SmtpTransport::new(settings))
92 }
93}