Skip to main content

Email

Struct Email 

Source
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: String

Subject line: one non-empty line of text.

§text: String

Plain-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

Source

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));
Source

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>"]);
Source

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"]);
Source

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"]);
Source

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>"));
Source

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"));
Source

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>"));
Source

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"));
Source

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()));
Source

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");
Source

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"));

Trait Implementations§

Source§

impl Clone for Email

Source§

fn clone(&self) -> Email

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Email

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for Email

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl PartialEq for Email

Source§

fn eq(&self, other: &Email) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Serialize for Email

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl StructuralPartialEq for Email

Auto Trait Implementations§

§

impl Freeze for Email

§

impl RefUnwindSafe for Email

§

impl Send for Email

§

impl Sync for Email

§

impl Unpin for Email

§

impl UnsafeUnpin for Email

§

impl UnwindSafe for Email

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> FromRef<T> for T
where T: Clone,

§

fn from_ref(input: &T) -> T

Converts to this type from a reference to the input type.
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
§

impl<T> ToRawKvValue for T
where T: Serialize,

§

fn raw_kv_value(&self) -> Result<JsValue, KvError>

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<S, T> Upcast<T> for S
where T: UpcastFrom<S> + ?Sized, S: ?Sized,

Source§

fn upcast(&self) -> &T
where Self: ErasableGeneric, T: Sized + ErasableGeneric<Repr = Self::Repr>,

Perform a zero-cost type-safe upcast to a wider ref type within the Wasm bindgen generics type system. Read more
Source§

fn upcast_into(self) -> T
where Self: Sized + ErasableGeneric, T: Sized + ErasableGeneric<Repr = Self::Repr>,

Perform a zero-cost type-safe upcast to a wider type within the Wasm bindgen generics type system. Read more
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V