mail-builder

A flexible, dependency-light Rust library for building RFC 5322 compliant, MIME-encoded email messages.

Library
Cargo
v0.5.0
82stars
Apache-2.0 OR MIT

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
47/100Fair
Development Activity48
Maintenance24
Community40
Maturity56
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
74/100Good
Architecture80
Code Quality82
Innovation78
Learning Curve55

mail-builder is a Rust library for constructing RFC 5322 compliant e-mail messages with full MIME support (RFC 2045-2049). It automatically selects the most compact valid encoding — 7bit, quoted-printable, or base64 — for each message part, so callers don’t have to reason about encoding rules themselves.

The API is a chainable MessageBuilder that accepts plain-text and HTML bodies, inline content, and binary attachments, then writes a complete, spec-compliant message to any std::io::Write sink. For more complex layouts, a lower-level MimePart/body() API lets callers construct arbitrary nested multipart trees directly.

It has no required runtime dependencies (an optional gethostname feature aids Message-ID generation) and forbids unsafe code. It’s a companion to the mail-parser and mail-send crates from the same maintainers — mail-builder only builds messages; parsing and sending are handled by those sibling crates.

What You Get

  • A fluent MessageBuilder API for headers, plain-text/HTML bodies, inline content, and attachments
  • Automatic MIME encoding selection (7bit, quoted-printable, base64) per message part
  • A fast, table-based base64 encoder adapted from Chromium’s implementation
  • Support for arbitrarily nested multipart MIME structures via a lower-level MimePart API
  • RFC 2047-encoded, internationalized header support for names and grouped address lists
  • Automatic generation of Message-ID, Date, and MIME-Version headers when not explicitly set

Common Use Cases

  • Building transactional or notification e-mails in a Rust backend service before handing them to an SMTP client
  • Composing multipart messages with plain-text and HTML alternatives plus inline images
  • Attaching binary or text files (with internationalized filenames) to outgoing messages
  • Generating raw .eml files for testing, archival, or message-queue payloads
  • Constructing complex nested MIME structures (e.g. RFC 8621-style multipart trees) for interoperability testing

Under The Hood

Architecture The library is organized into three clean modules — encoders (base64, quoted-printable, and an encode module that picks the optimal encoding), headers (address, content_type, date, message_id, raw, text, url — each implementing a shared Header trait), and mime.rs, which defines the MimePart/BodyPart tree — with lib.rs’s MessageBuilder acting as the top-level, self-consuming builder that orchestrates all three. Data flows one way: MessageBuilder accumulates headers and body parts, then write_to/write_body walks the MimePart tree recursively, selecting an encoding for each part before writing headers and encoded content to any io::Write sink. Separation of concerns is clean — encoders are unaware of MIME structure, headers are unaware of encoding, and MimePart composes both — a straightforward layered design with no dependency injection, since the domain is a one-shot builder pipeline. Changing the core MimePart/BodyPart enum would ripple into every header’s write_header implementation and the recursive write logic in mime.rs.

Tech Stack Pure Rust (edition 2024) with a single optional dependency, gethostname, used only to generate a hostname-qualified Message-ID (enabled by default, can be disabled). Dev-dependencies include mail-parser for round-trip test verification and serde/serde_yaml/serde_json for test fixtures. There is no database, web framework, or build tooling beyond standard cargo build/cargo test; the crate is published to crates.io and consumed as a building block by sibling Stalwart Labs crates such as mail-send. CI runs on GitHub Actions (ubuntu-latest), executing cargo build and cargo test on every push and pull request.

Code Quality The test suite covers base64/quoted-printable encoding edge cases and full message construction, including deeply nested multipart trees, and round-trips generated output through mail-parser to confirm it parses as valid mail — a stronger check than isolated unit assertions. Error handling is idiomatic: functions return io::Result and propagate failures with ?; the crate additionally enforces #![forbid(unsafe_code)] and #![deny(rust_2018_idioms)] at the compiler level. One explicit, documented panic exists (unwrap_address, for callers who assert a specific Address variant) rather than any silent failure. Naming is consistent snake_case throughout, lifetimes are threaded through nearly every type to minimize allocation, and CI runs the full suite on every change. No separate linter config is present beyond the compiler’s deny/forbid attributes.

API Design MessageBuilder exposes a chainable, self-consuming builder (.from().to().subject().text_body().attachment().write_to_vec()) that needs almost no boilerplate to emit a valid RFC 5322 message — Message-ID, Date, and MIME-Version headers are generated automatically when omitted. Extensive Into<T> conversions let callers pass raw tuples like ("name", "email") or nested Vec structures directly as header values without constructing intermediate types first. Documentation is doc-included from the README via #![doc = include_str!("../README.md")], so the crate’s published docs mirror the same usage examples developers read on GitHub — a small but genuinely useful DX touch.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search