From 85b2a6bdce23592ee2799f680aa5a816cf2ef649 Mon Sep 17 00:00:00 2001 From: Thomas Oettli Date: Sat, 10 Oct 2026 20:53:54 +0200 Subject: [PATCH] document the *_orig fields --- README.md | 8 +++++--- src/lib.rs | 47 ++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 65f3ce8..15ff187 100644 --- a/README.md +++ b/README.md @@ -60,9 +60,11 @@ assert!(mail.is_multipart()); - **`CowMail`** is the message, and every part of a multipart message is a `CowMail` as well. Its fields are public: `headers`, `body_bytes` and `subparts` are meant to be edited directly when the provided methods do not fit. -- The `*_orig` fields (`bytes_orig`, `header_bytes_orig`, `body_bytes_orig`) keep the originally - parsed slices even after `body_bytes` has become owned, so the original and the modified message - can be compared. +- The `*_orig` fields (`bytes_orig`, `header_bytes_orig`, `body_bytes_orig`) are set while parsing + and never written to again, so the message **as it arrived** can be serialized or compared against + at any point, however much has been modified in the meantime — no copy has to be kept on the side. + `bytes_orig` is the whole part, subparts included; the other two are the original header block and + body on their own. - **`HeaderMap`** finds, iterates and edits headers by key; keys match case-insensitively and an `index` argument counts occurrences of that key, not positions in the list. It is implemented for `CowMail` and for `Vec`. diff --git a/src/lib.rs b/src/lib.rs index c8ec2cc..9319833 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -432,6 +432,37 @@ enum BodyCheckStatus { /// for anything the provided methods do not cover. The consequence is that nothing derived from the /// headers can be cached, since there is no way to notice a change — see [`Mail::content_type`]. /// +/// # Serializing the original +/// +/// The three `*_orig` fields are set while parsing and never written to again, so the message as it +/// arrived can be serialized at any point, however much has been modified in the meantime — no copy +/// has to be kept on the side: +/// +/// - [`bytes_orig`](Self::bytes_orig) is the whole part, subparts included. Writing it out +/// reproduces the original part exactly; on the top-level `CowMail` that is the original message. +/// - [`header_bytes_orig`](Self::header_bytes_orig) is the original header block, blank line +/// included, and [`body_bytes_orig`](Self::body_bytes_orig) the original body — for a container, +/// its preamble. Together they are the part without its terminating newline, which is useful for +/// writing out one half of it, or for comparing one half against the current state. +/// +/// All three are empty for a part that was built rather than parsed. +/// +/// ``` +/// use email_message::{CowMail, HeaderMap, Mail}; +/// +/// let raw = b"Subject: Hi\r\n\r\nbody\r\n"; +/// let mut mail = CowMail::try_from(&raw[..]).unwrap(); +/// +/// mail.replace_all_headers("Subject", "Changed"); +/// mail.set_text_body("changed").unwrap(); +/// assert_ne!(mail.serialize().concat(), raw.to_vec()); +/// +/// // The message as it arrived is still right there. +/// assert_eq!(mail.bytes_orig, raw); +/// assert_eq!(mail.header_bytes_orig, b"Subject: Hi\r\n\r\n"); +/// assert_eq!(mail.body_bytes_orig, b"body"); +/// ``` +/// /// # Examples /// /// ``` @@ -452,14 +483,19 @@ enum BodyCheckStatus { /// ``` #[derive(Debug, Clone)] pub struct CowMail<'a, H: Header<'a>> { - /// The part exactly as it was parsed, headers and body, never modified. + /// The part exactly as it was parsed — headers, body and subparts — never modified. + /// + /// Writing this out reproduces the part as it arrived, whatever has been changed since, so the + /// original message stays available for as long as the `CowMail` does. See + /// [*Serializing the original*](#serializing-the-original). /// /// Empty for a part that was built rather than parsed. pub bytes_orig: &'a [u8], /// The header block as it was parsed, up to and including the blank line that ends it. /// - /// Kept after [`headers`](Self::headers) has been modified, and used to recover the line ending - /// to write the header/body separator with when the part has no headers left. + /// Never modified, so the original headers can be written out on their own at any time — see + /// [*Serializing the original*](#serializing-the-original). Also used internally to recover the + /// line ending for the header/body separator when the part has no headers left. pub header_bytes_orig: &'a [u8], /// The headers of this part, in the order they appear in the message. /// @@ -471,10 +507,11 @@ pub struct CowMail<'a, H: Header<'a>> { /// For a multipart container this is the preamble before the first delimiter line, not the /// subparts. pub body_bytes: Cow<'a, [u8]>, - /// The body as it was parsed. + /// The body as it was parsed — the preamble only, for a multipart container. /// /// Unlike [`body_bytes`](Self::body_bytes) this stays put when the body is replaced, so the - /// original and the current body can be compared. Not used by this crate itself. + /// original body can be written out or compared against the current one at any time. See + /// [*Serializing the original*](#serializing-the-original). pub body_bytes_orig: &'a [u8], /// The parts of a multipart message, each one a `CowMail` of its own. ///