pub struct Email {
pub from: Option<String>,
pub to: Vec<String>,
pub cc: Vec<String>,
pub bcc: Vec<String>,
pub subject: String,
pub text: String,
pub html: Option<String>,
pub reply_to: Option<String>,
pub headers: Vec<(String, String)>,
pub attachments: Vec<Attachment>,
pub delivery_method: Option<String>,
}Expand description
An outgoing email: recipients, a subject, a plain-text body, and optionally HTML, headers and attachments.
Build it with Email::new (one recipient), then add more with
also_to, cc and bcc, an
HTML version with html, a Reply-To with
reply_to, another sender with from,
custom headers with header, and files with
attach and inline; send it with
send or deliver_later. Nothing is checked while building:
addresses, headers, attachments and the subject are checked when sending
(an invalid recipient is a 400, the rest a 500 naming the fix). Every
address may carry a display name, Ada <ada@example.com> (see
address_with_name). Generated mailers (ocre g mailer) return one per
action, rendered from templates/mailers/<name>/<action>.{txt,html}.
It is serde-serializable because deliver_later puts it in a queue
message (128 KB at most, attachments included, base64-encoded).
§Examples
use ocre::mail::Email;
let email = Email::new("ada@example.com", "Reset your password", "Open https://example.com/reset/abc")
.html("<a href=\"https://example.com/reset/abc\">Reset your password</a>")
.reply_to("support@example.com");
assert_eq!(email.to, ["ada@example.com"]);
assert_eq!(email.subject, "Reset your password");
assert_eq!(email.text, "Open https://example.com/reset/abc");
assert_eq!(email.html.as_deref(), Some("<a href=\"https://example.com/reset/abc\">Reset your password</a>"));
assert_eq!(email.reply_to.as_deref(), Some("support@example.com"));Fields§
§from: Option<String>Sender, when not MAIL_FROM: billing@example.com or Billing <billing@example.com>,
on a domain verified with the provider.
to: Vec<String>To recipients, ada@example.com or Ada <ada@example.com>; an invalid one makes sending a 400.
cc: Vec<String>Cc recipients, checked like to.
bcc: Vec<String>Bcc recipients, checked like to; the other recipients do not see them.
subject: StringSubject line: one non-empty line of text.
text: StringPlain-text body; always sent, so every mail client can read it.
html: Option<String>Optional HTML body, shown instead of text by clients that render HTML.
reply_to: Option<String>Where replies go, when not to the sender; checked like to.
headers: Vec<(String, String)>Extra headers, (name, value): threading (In-Reply-To, References), List-Unsubscribe, X-....
attachments: Vec<Attachment>Files attached to the email, and inline images the HTML shows with cid:.
delivery_method: Option<String>Adapter for this email instead of MAIL_ADAPTER (resend or cloudflare), set by
delivery_method; ignored while MAIL_ADAPTER is log.
Implementations§
Source§impl Email
impl Email
Sourcepub fn new(
to: impl Into<String>,
subject: impl Into<String>,
text: impl Into<String>,
) -> Self
pub fn new( to: impl Into<String>, subject: impl Into<String>, text: impl Into<String>, ) -> Self
Creates a text-only email to one recipient.
Everything else starts empty. Nothing is validated here.
§Examples
let email = ocre::mail::Email::new("ada@example.com", "Hi", "Hello Ada");
assert_eq!((email.subject.as_str(), email.html, email.reply_to), ("Hi", None, None));Sourcepub fn also_to(self, address: impl Into<String>) -> Self
pub fn also_to(self, address: impl Into<String>) -> Self
Adds another To recipient.
All To and Cc recipients see each other; use bcc
or one email each to keep addresses private. 50 recipients at most
(to, cc and bcc together), Resend’s limit.
§Examples
let email = ocre::mail::Email::new("ada@example.com", "Hi", "Hello").also_to("Grace <grace@example.com>");
assert_eq!(email.to, ["ada@example.com", "Grace <grace@example.com>"]);Sourcepub fn cc(self, address: impl Into<String>) -> Self
pub fn cc(self, address: impl Into<String>) -> Self
Adds a Cc recipient.
§Examples
let email = ocre::mail::Email::new("ada@example.com", "Hi", "Hello").cc("team@example.com");
assert_eq!(email.cc, ["team@example.com"]);Sourcepub fn bcc(self, address: impl Into<String>) -> Self
pub fn bcc(self, address: impl Into<String>) -> Self
Adds a Bcc recipient: it gets the email, the other recipients do not see it.
§Examples
let email = ocre::mail::Email::new("ada@example.com", "Hi", "Hello").bcc("archive@example.com");
assert_eq!(email.bcc, ["archive@example.com"]);Sourcepub fn from(self, address: impl Into<String>) -> Self
pub fn from(self, address: impl Into<String>) -> Self
Sends from address instead of the MAIL_FROM variable.
Like Rails’ mail(from: ...); the domain must be verified with the
provider, like MAIL_FROM’s. An unparsable sender is a 500.
§Examples
let email = ocre::mail::Email::new("ada@example.com", "Invoice", "...").from("Billing <billing@example.com>");
assert_eq!(email.from.as_deref(), Some("Billing <billing@example.com>"));Sourcepub fn delivery_method(self, adapter: impl Into<String>) -> Self
pub fn delivery_method(self, adapter: impl Into<String>) -> Self
Sends this email with another adapter than MAIL_ADAPTER: "resend" or "cloudflare" (Rails’ delivery_method).
For an app that uses both providers, e.g. Cloudflare Email Service
(free, to the team’s verified addresses) for internal alerts and
Resend for customer mail. The adapter’s own configuration applies
(the RESEND_API_KEY secret or the EMAIL binding). While
MAIL_ADAPTER is log (development), the email is still only
logged, so a development machine never sends by accident. An unknown
name is a 500 when sending, naming the fix.
§Examples
let alert = ocre::mail::Email::new("ops@example.com", "Disk almost full", "...").delivery_method("cloudflare");
assert_eq!(alert.delivery_method.as_deref(), Some("cloudflare"));Sourcepub fn html(self, html: impl Into<String>) -> Self
pub fn html(self, html: impl Into<String>) -> Self
Adds the HTML version of the body, replacing any previous one.
The text body is still sent alongside it. The HTML is sent as given: escape user input when building it (askama templates do).
§Examples
let email = ocre::mail::Email::new("ada@example.com", "Hi", "Hello").html("<p>Hello</p>");
assert_eq!(email.html.as_deref(), Some("<p>Hello</p>"));Sourcepub fn reply_to(self, address: impl Into<String>) -> Self
pub fn reply_to(self, address: impl Into<String>) -> Self
Sets the Reply-To address, so replies go there instead of to the sender.
Checked when sending: an invalid address is a 400, like the recipient.
§Examples
let email = ocre::mail::Email::new("ada@example.com", "Hi", "Hello").reply_to("team@example.com");
assert_eq!(email.reply_to.as_deref(), Some("team@example.com"));Sourcepub fn header(self, name: impl Into<String>, value: impl Into<String>) -> Self
pub fn header(self, name: impl Into<String>, value: impl Into<String>) -> Self
Adds a header, e.g. In-Reply-To and References to thread a reply, or List-Unsubscribe.
When sending, the name must be letters, digits and -, the value one
line, and the name not one Ocre sets itself (From, To, Cc,
Bcc, Subject, Reply-To, Content-Type, …); otherwise sending
is a 500 naming the header.
§Examples
let reply = ocre::mail::Email::new("ada@example.com", "Re: Order 42", "Shipped today.")
.header("In-Reply-To", "<order-42@example.com>")
.header("References", "<order-42@example.com>");
assert_eq!(reply.headers[0], ("In-Reply-To".to_owned(), "<order-42@example.com>".to_owned()));Sourcepub fn attach(
self,
filename: impl Into<String>,
content_type: impl Into<String>,
content: Vec<u8>,
) -> Self
pub fn attach( self, filename: impl Into<String>, content_type: impl Into<String>, content: Vec<u8>, ) -> Self
Attaches a file, like Rails’ attachments["invoice.pdf"] = bytes.
The whole email must fit the provider’s limit (Resend: 40 MB), and
128 KB for deliver_later (base64 makes files a third larger):
store big files in R2 and send a link instead.
§Examples
let csv = "id,total\n1,42\n".as_bytes().to_vec();
let email = ocre::mail::Email::new("ada@example.com", "Report", "Attached.").attach("report.csv", "text/csv", csv);
assert_eq!(email.attachments[0].content, b"id,total\n1,42\n");Sourcepub fn inline(
self,
content_id: impl Into<String>,
filename: impl Into<String>,
content_type: impl Into<String>,
content: Vec<u8>,
) -> Self
pub fn inline( self, content_id: impl Into<String>, filename: impl Into<String>, content_type: impl Into<String>, content: Vec<u8>, ) -> Self
Embeds an image that the HTML shows with <img src="cid:<content_id>">, like Rails’ attachments.inline.
content_id is letters, digits and .-_ (checked when sending).
Mail clients show inline images without loading anything remote;
the text body cannot show them.
§Examples
let logo = vec![0x89, b'P', b'N', b'G'];
let email = ocre::mail::Email::new("ada@example.com", "Welcome", "Welcome!")
.html("<img src=\"cid:logo\" alt=\"Shop\"><p>Welcome!</p>")
.inline("logo", "logo.png", "image/png", logo);
assert_eq!(email.attachments[0].content_id.as_deref(), Some("logo"));