eml-codec 0.4.0

Email enCOder DECoder in Rust. Support Internet Message Format and MIME (RFC 822, 5322, 2045, 2046, 2047, 2048, 2049, 6532).
Documentation
#![doc = include_str!("../README.md")]

/// Parse and represent full "top-level" emails (RFC 822, RFC 2045, RFC 2046)
pub mod message;

/// Parse and represent emails "parts" as defined by MIME (RFC 2046)
pub mod part;

/// Parse and represent IMF (Internet Message Format) headers (RFC 822, RFC 5322)
pub mod imf;

/// Parse and represent MIME headers (RFC 2045, RFC 2047)
pub mod mime;

/// MIME and IMF represent headers the same way: module contains their common logic
pub mod header;

/// Low-level email-specific text-based representation for data
pub mod text;

/// Printing with email-specific line folding
pub mod print;

/// Helpers related to UTF-8 support in headers (RFC 6532)
pub mod i18n;

/// Support for storing references to raw input slices in AST nodes.
pub mod raw_input;

/// Custom equality trait used for fuzz-checking
#[cfg(feature = "arbitrary")]
pub mod fuzz_eq;

mod utils;

#[cfg(feature = "arbitrary")]
mod arbitrary_utils;

// Re-export bounded_static because we implement its traits
pub use bounded_static;

/// Parse a whole email including its (MIME) body
///
/// # Arguments
///
/// * `input` - A buffer of bytes containing your full email
///
/// # Returns
///
/// * `msg` - The parsed message
///
/// # Examples
///
/// ```
/// let input = br#"Date: 7 Mar 2023 08:00:00 +0200
/// From: deuxfleurs@example.com
/// To: someone_else@example.com
/// Subject: An RFC 822 formatted message
/// MIME-Version: 1.0
/// Content-Type: text/plain; charset=us-ascii
///
/// This is the plain text body of the message. Note the blank line
/// between the header information and the body of the message."#;
///
/// let email = eml_codec::parse_message(input);
/// println!(
///     "{} message structure is:\n{:#?}",
///     email.imf.from_or_sender().unwrap().to_string(),
///     email,
/// );
/// ```
pub fn parse_message(input: &[u8]) -> message::Message<'_> {
    message::message(input)
}

/// Print a whole email.
///
/// The `seed` parameter controls the RNG used to generate multipart boundaries.
/// Passing `None` will use randomness from the operating system.
pub fn print_message(msg: message::Message<'_>, seed: Option<u64>) -> Vec<u8> {
    print::print_to_vec(print::FMT_DEFAULT.with_seed(seed), msg)
}

/// Only extract the headers of the email that are part of the Internet Message Format spec
///
/// Emails headers contain MIME and IMF (Internet Message Format) headers.
/// Sometimes you only need to know the recipient or the sender of an email,
/// and are not interested in its content. In this case, you only need to parse the IMF
/// fields and can ignore the MIME headers + the body. This is what this function does.
///
/// # Arguments
///
/// * `input` - A buffer of bytes containing either only the headers of your email or your full
///   email (in both cases, the body will be ignored)
///
/// # Returns
///
/// * `rest` - The rest of the buffer, ie. the body of your email as raw bytes
/// * `imf` - The parsed IMF headers of your email
///
/// # Examples
///
/// ```
/// let input = br#"Date: 7 Mar 2023 08:00:00 +0200
/// From: deuxfleurs@example.com
/// To: someone_else@example.com
/// Subject: An RFC 822 formatted message
/// MIME-Version: 1.0
/// Content-Type: text/plain; charset=us-ascii
///
/// This is the plain text body of the message. Note the blank line
/// between the header information and the body of the message."#;
///
/// let (_, imf) = eml_codec::parse_imf(input);
/// println!(
///     "{} just sent you an email with subject \"{}\"",
///     imf.from_or_sender().unwrap().to_string(),
///     imf.subject.unwrap().to_string(),
/// );
/// ```
pub fn parse_imf(input: &[u8]) -> (&[u8], imf::Imf<'_>) {
    message::imf(input)
}

/// Get the raw subslice of the input that corresponds to the header section.
///
/// It can be later parsed using `parse_imf` or `parse_message` (resulting in an
/// empty body). This is equivalent to directly parsing the full input, but
/// allows the header section to e.g. be stored separately without storing the
/// body.
pub fn raw_headers(input: &[u8]) -> &[u8] {
    let (rest, _) = header::header_kv(input);
    &input[0..input.len() - rest.len()]
}