Writing Mails from Rust (2/3): The mail crate

Introduction to the mail crate, a rust library for creating mails

The mail crate is a modular Rust library for creating, modifying and then encoding mails. It also has bindings to our new crate new-tokio-smtp to allow sending mails asynchronously, as well as bindings to handlebars for creating mails from templates. It currently does not support parsing mails, but is designed in a way that decoding capabilities could be easily added in the future (contact me if that is something you'd be interested in working on!). At 1aim, we are already using mail in production.

The actual mail crate itself is just a facade for a number of underlying crates like mail-core, mail-headers or mail-smtp (i.e. it re-exports the functionality of these crates). Other projects are welcome to use some of the sub-crates in their own mail-related projects.

The core element of the mail crate is the Mail type, which consists of a number of type safe mail headers and a mail body. As already mentioned in the previous post mail bodies can either contain some data directly or a number of mail bodies. As each of the bodies has their own headers it's mostly the same as having a mail inside of a mail (you can actually do that). So the Mail types body is either a Resource representing some data (as well as metadata) or a vector of mails.

After creating a Mail object it is still possible to modify it, compose it with other Mail instances to create a new Mail object or encode it into a byte buffer/string to create an actual mail. There is a number of restrictions to mails, e.g., they have to have a Date header and if there are multiple mailboxes in the From header there has to be a Sender header. The mail crate validates these constraints only before encoding (when turning a Mail into a EncodableMail). This means that the Mail type instances do intentionally not check these restrictions as this makes it much easier to work with them. E.g. you can create a mail from a template in some function and then pass it to another function which adds From/To and then to another which adds some special headers. This makes it very easy to write reusable functions for parts of the mail creation process and keeps concerns like creating a mail content from a template and adding special headers cleanly separated. Due to the type safe nature of the mail headers it is furthermore possible to access headers in a Mail instance to e.g. get all mail addresses in a From/To header or the Sender headers mailbox to use them as sender/recipient in SMTP.

Below is a general overview of features supported by the mail create:

In the next part of this blog post I will go through how to setup a Context, a handlebars mail template then create a mail from it and send it over SMTP covering the "basic" needs of most applications for sending mails. It will also cover why some of the design is the way it is.