daaki-message 0.2.0

RFC 5322 email message parser and builder
Documentation

daaki-message

An RFC 5322 email message parser and builder.

Highlights

  • Parse and build — turn raw bytes into structured data, or structured data into raw bytes.
  • Full MIME support — multipart messages, attachments, Content-Transfer-Encoding, boundary handling.
  • Encoded words — RFC 2047 decoding and encoding for non-ASCII headers.
  • Internationalized headers — RFC 6532 UTF-8 support.
  • Validated typesHeaderName, MessageId, Address enforce RFC syntax at construction time.
  • 7-bit safe output — always produces 7bit or quoted-printable encoding, compatible with any SMTP server.
  • Zero unsafe code — enforced by #![deny(unsafe_code)] crate-wide.
  • No runtime dependency — works with any async runtime or without one.
  • Optional serde — enable the serde feature for Serialize/Deserialize on all public types.

Quick Start

[dependencies]
daaki-message = "0.2"

Parsing

Parse a raw email into its structured parts:

use daaki_message::parse_email;

let raw = b"From: alice@example.com\r\n\
            To: bob@example.com\r\n\
            Subject: Hi\r\n\
            \r\n\
            Hello!";

let email = parse_email(raw).unwrap();
assert_eq!(email.subject.as_deref(), Some("Hi"));
assert_eq!(email.body_text.as_deref(), Some("Hello!"));

Building

Construct a well-formed RFC 5322 message from typed inputs:

use daaki_message::{build_message, OutgoingEmail, Address};

let mut email = OutgoingEmail::default();
email.from = vec![Address::with_name("Alice", "alice@example.com").unwrap()];
email.to = vec![Address::new("bob@example.com").unwrap()];
email.subject = "Hello from daaki".into();
email.body_text = Some("Plain text body".into());

let built = build_message(&email).unwrap();
assert!(String::from_utf8_lossy(&built.raw).contains("Subject: Hello from daaki"));
assert_eq!(built.envelope_recipients, vec!["bob@example.com".to_string()]);

Headers-only parsing

Parse just headers for fast metadata extraction (skips body and MIME processing):

use daaki_message::parse_headers_only;

let raw = b"From: alice@example.com\r\nSubject: Hi\r\n\r\nBody here";
let email = parse_headers_only(raw).unwrap();
assert_eq!(email.subject.as_deref(), Some("Hi"));
// body_text, body_html, attachments are not populated

Parsing address lists

Parse a comma-separated address list — for example, a user-typed recipient field in a compose form or a decoded To/Cc header value — using the liberal RFC 5322 Section 3.4 parser that daaki-message uses internally for inbound headers. It handles quoted display names, parenthesized comments, group syntax, domain literals, and RFC 2047 encoded-word display names:

use daaki_message::{parse_address_list, Address};

let raw = r#""Doe, Jane" <jane@example.com>, alice@example.com"#;
let addrs = parse_address_list(raw);

assert_eq!(addrs.len(), 2);
assert_eq!(addrs[0].name.as_deref(), Some("Doe, Jane"));
assert_eq!(addrs[0].email, "jane@example.com");
assert_eq!(addrs[1].email, "alice@example.com");

// `parse_address_list` is liberal (Postel's law) and never errors —
// the returned `Address` records are not validated for outgoing mail.
// For outgoing mail, re-validate each result through `Address::new` /
// `Address::with_name`, which enforce the same strict rules the
// message builder uses:
let validated: Result<Vec<Address>, _> = addrs
    .into_iter()
    .map(|a| match a.name {
        Some(name) => Address::with_name(name, a.email),
        None => Address::new(a.email),
    })
    .collect();
assert!(validated.is_ok());

Note: this function expects already-decoded text. It does not perform header unfolding, charset detection, or transfer-encoding decoding — use parse_email for raw message bytes.

Encoding

build_message always produces 7-bit safe output. Pure ASCII with conforming lines uses Content-Transfer-Encoding: 7bit; anything else uses quoted-printable. This guarantees the built bytes can be sent to any SMTP server — with or without 8BITMIME — and stored via IMAP APPEND without re-encoding.

use daaki_message::{build_message, OutgoingEmail, Address};

let mut email = OutgoingEmail::default();
email.from = vec![Address::new("a@example.com").unwrap()];
email.to = vec![Address::new("b@example.com").unwrap()];
email.subject = "Héllo".into();
email.body_text = Some("Café".into());
let built = build_message(&email).unwrap();
// Non-ASCII body is automatically quoted-printable encoded
assert!(String::from_utf8_lossy(&built.raw).contains("quoted-printable"));

Standards

Standard Coverage
RFC 5322 Internet Message Format — headers, date-time, addresses, message identification
RFC 2045 MIME Part One — Content-Transfer-Encoding (base64, quoted-printable, 7bit, 8bit)
RFC 2046 MIME Part Two — multipart structure, boundary handling, media types
RFC 2047 MIME Part Three — encoded words for non-ASCII in headers (Q and B encoding)
RFC 2183 Content-Disposition — inline and attachment handling
RFC 2387 multipart/related — HTML with inline images via Content-ID
RFC 2231 MIME parameter encoding — charset, language, and continuations
RFC 2392 Content-ID — cid: URL references for inline attachments
RFC 6531 Internationalized email — non-ASCII local-parts and domains
RFC 6532 Internationalized headers — UTF-8 throughout header fields

License

The contents of this package are licensed under the terms of the MIT license.