Skip to content

Use minijinja templates for emails #11420

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Jun 26, 2025
Merged

Conversation

Turbo87
Copy link
Member

@Turbo87 Turbo87 commented Jun 24, 2025

This PR refactors our email sending code to use minijinja templates instead of format!() and string concatenation. In the long term this even enables us to add dedicated templates for HTML emails too, or use template inheritance to use consistent email footers, etc.

Before:

impl Email for UserConfirmEmail<'_> {
    fn subject(&self) -> String { /* ... */ }
    fn body(&self) -> String { /* ... */ }
}

emails.send(recipient, user_confirm_email).await?;

After (with templates):

let email_message = EmailMessage::from_template("user_confirm", context! {
    user_name => user_name,
    domain => domain,
    token => token.expose_secret()
})?;

emails.send(recipient, email_message).await?;
Hello {{ user_name }}! Welcome to crates.io. Please click the
link below to verify your email address. Thank you!

https://{{ domain }}/confirm/{{ token }}

@Turbo87 Turbo87 added C-internal 🔧 Category: Nonessential work that would make the codebase more consistent or clear A-backend ⚙️ labels Jun 24, 2025
@Turbo87 Turbo87 requested a review from a team June 24, 2025 12:45
@Turbo87 Turbo87 force-pushed the jinja-emails branch 3 times, most recently from 9ec4a0d to 0f87495 Compare June 25, 2025 13:12
@Turbo87
Copy link
Member Author

Turbo87 commented Jun 25, 2025

rebased to include the new email type from #11419

Copy link
Contributor

@eth3lbert eth3lbert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A quick glance, and overall LGTM, thanks! I've also left a few nitpicks more related to unified coding style.

@Turbo87
Copy link
Member Author

Turbo87 commented Jun 26, 2025

Thanks for the review! All of the feedback should be addressed now :)

@LawnGnome
Copy link
Contributor

I defer to @eth3lbert's excellent review on the details.

I definitely think that moving to a templating engine is the right call, but I'm not so sure I like the loss of the explicit input fields for each template, both from the perspective of type safety and for knowing what might be interpolated into the e-mails. That said, I don't know that I have a great alternative: the thought of having a proc macro or something to ensure that templates and contexts are in sync feels... terrifying, and well beyond the scope of what we should do here, minijinja doesn't really give us a lot of alternatives, and I don't like any of the minijinja alternatives more than minijinja itself.

So, 👍, but I also think we should remain open to continuing to iterate on the details over time.

@Turbo87
Copy link
Member Author

Turbo87 commented Jun 26, 2025

@LawnGnome yeah, I understand the concern. technically minijinja works with anything that is Serializable and context!() just happens to be one such things. we could also go back to explicit structs, but that still wouldn't solve the problem of the template referring to fields that don't exist. I know there are some template engines that check this at compile time, but IMHO that seems like overkill for this use case.

I guess we can try it out like this and see whether it is sufficient or not. If we see any issues with this approach we can always go back to explicit structs as the template inputs and/or improve our test suite for the templates.

@Turbo87 Turbo87 merged commit 5811485 into rust-lang:main Jun 26, 2025
10 checks passed
@Turbo87 Turbo87 deleted the jinja-emails branch June 26, 2025 17:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-backend ⚙️ C-internal 🔧 Category: Nonessential work that would make the codebase more consistent or clear
Projects
Status: For next meeting
Development

Successfully merging this pull request may close these issues.

3 participants