From c3b5d760f857c490d5ad234bd5701e35663f0faf Mon Sep 17 00:00:00 2001 From: Paolo Barbolini Date: Tue, 22 Oct 2024 22:49:25 +0200 Subject: [PATCH] wip --- src/message/body.rs | 24 +++++++++++---- src/message/header/mod.rs | 14 +++++++++ src/message/mimebody.rs | 61 +++++++++++++++++++-------------------- src/message/mod.rs | 36 +++++++++++++++-------- 4 files changed, 85 insertions(+), 50 deletions(-) diff --git a/src/message/body.rs b/src/message/body.rs index a1b68cc..cba8d4e 100644 --- a/src/message/body.rs +++ b/src/message/body.rs @@ -1,11 +1,11 @@ -use std::{mem, ops::Deref}; +use std::{mem, ops::Deref, sync::Arc}; use crate::message::header::ContentTransferEncoding; /// A [`Message`][super::Message] or [`SinglePart`][super::SinglePart] body that has already been encoded. #[derive(Debug, Clone)] pub struct Body { - buf: Vec, + buf: Arc<[u8]>, encoding: ContentTransferEncoding, } @@ -39,7 +39,7 @@ impl Body { let encoding = buf.encoding(false); buf.encode_crlf(); - Self::new_impl(buf.into(), encoding) + Self::new_impl(Vec::from(buf).into(), encoding) } /// Encode the supplied `buf`, using the provided `encoding`. @@ -77,7 +77,7 @@ impl Body { } buf.encode_crlf(); - Ok(Self::new_impl(buf.into(), encoding)) + Ok(Self::new_impl(Vec::from(buf).into(), encoding)) } /// Builds a new `Body` using a pre-encoded buffer. @@ -87,11 +87,14 @@ impl Body { /// `buf` shouldn't contain non-ascii characters, lines longer than 1000 characters or nul bytes. #[inline] pub fn dangerous_pre_encoded(buf: Vec, encoding: ContentTransferEncoding) -> Self { - Self { buf, encoding } + Self { + buf: buf.into(), + encoding, + } } /// Encodes the supplied `buf` using the provided `encoding` - fn new_impl(buf: Vec, encoding: ContentTransferEncoding) -> Self { + fn new_impl(buf: Arc<[u8]>, encoding: ContentTransferEncoding) -> Self { match encoding { ContentTransferEncoding::SevenBit | ContentTransferEncoding::EightBit @@ -133,7 +136,16 @@ impl Body { /// Consumes `Body` and returns the inner `Vec` #[inline] + #[deprecated( + note = "The inner memory is not stored into `Vec` anymore. Consider using `into_inner`" + )] pub fn into_vec(self) -> Vec { + self.buf.to_vec() + } + + /// Consumes `Body` and returns the inner `Arc<[u8]>` + #[inline] + pub fn into_inner(self) -> Arc<[u8]> { self.buf } } diff --git a/src/message/header/mod.rs b/src/message/header/mod.rs index 12ac6e7..7636a6d 100644 --- a/src/message/header/mod.rs +++ b/src/message/header/mod.rs @@ -18,6 +18,7 @@ pub use self::{ special::*, textual::*, }; +use super::EmailFormat; use crate::BoxError; mod content; @@ -154,6 +155,19 @@ impl Display for Headers { } } +impl EmailFormat for Headers { + fn format<'a>(&'a self, out: &mut impl Extend>) { + for value in &self.headers { + out.extend([ + Cow::Borrowed(value.name.as_bytes()), + Cow::Borrowed(b": "), + Cow::Borrowed(value.encoded_value.as_bytes()), + Cow::Borrowed(b"\r\n"), + ]); + } + } +} + /// A possible error when converting a `HeaderName` from another type. // comes from `http` crate #[allow(missing_copy_implementations)] diff --git a/src/message/mimebody.rs b/src/message/mimebody.rs index 78ae12e..ac29829 100644 --- a/src/message/mimebody.rs +++ b/src/message/mimebody.rs @@ -1,4 +1,4 @@ -use std::{io::Write, iter::repeat_with}; +use std::{borrow::Cow, iter::repeat_with, sync::Arc}; use mime::Mime; @@ -28,7 +28,7 @@ impl Part { } impl EmailFormat for Part { - fn format(&self, out: &mut Vec) { + fn format<'a>(&'a self, out: &mut impl Extend>) { match self { Part::Single(part) => part.format(out), Part::Multi(part) => part.format(out), @@ -71,7 +71,7 @@ impl SinglePartBuilder { SinglePart { headers: self.headers, - body: body.into_vec(), + body: body.into_inner(), } } } @@ -100,7 +100,7 @@ impl Default for SinglePartBuilder { #[derive(Debug, Clone)] pub struct SinglePart { headers: Headers, - body: Vec, + body: Arc<[u8]>, } impl SinglePart { @@ -138,24 +138,18 @@ impl SinglePart { /// Get message content formatted for sending pub fn formatted(&self) -> Vec { - let mut out = Vec::new(); - self.format(&mut out); - out - } - - /// Format only the signlepart body - fn format_body(&self, out: &mut Vec) { - out.extend_from_slice(&self.body); - out.extend_from_slice(b"\r\n"); + self.format_to_vec() } } impl EmailFormat for SinglePart { - fn format(&self, out: &mut Vec) { - write!(out, "{}", self.headers) - .expect("A Write implementation panicked while formatting headers"); - out.extend_from_slice(b"\r\n"); - self.format_body(out); + fn format<'a>(&'a self, out: &mut impl Extend>) { + self.headers.format(out); + out.extend([ + Cow::Borrowed("\r\n".as_bytes()), + Cow::Borrowed(&self.body), + Cow::Borrowed(b"\r\n"), + ]); } } @@ -384,33 +378,36 @@ impl MultiPart { /// Get message content formatted for SMTP pub fn formatted(&self) -> Vec { - let mut out = Vec::new(); - self.format(&mut out); - out + self.format_to_vec() } /// Format only the multipart body - fn format_body(&self, out: &mut Vec) { + fn format_body<'a>(&'a self, out: &mut impl Extend>) { let boundary = self.boundary(); for part in &self.parts { - out.extend_from_slice(b"--"); - out.extend_from_slice(boundary.as_bytes()); - out.extend_from_slice(b"\r\n"); + out.extend([ + Cow::Borrowed("--".as_bytes()), + // FIXME: this clone shouldn't exist + Cow::Owned(boundary.clone().into()), + Cow::Borrowed("\r\n".as_bytes()), + ]); part.format(out); } - out.extend_from_slice(b"--"); - out.extend_from_slice(boundary.as_bytes()); - out.extend_from_slice(b"--\r\n"); + out.extend([ + Cow::Borrowed("--".as_bytes()), + Cow::Owned(boundary.into()), + Cow::Borrowed("--\r\n".as_bytes()), + ]); } } impl EmailFormat for MultiPart { - fn format(&self, out: &mut Vec) { - write!(out, "{}", self.headers) - .expect("A Write implementation panicked while formatting headers"); - out.extend_from_slice(b"\r\n"); + fn format<'a>(&'a self, out: &mut impl Extend>) { + self.headers.format(out); + out.extend([Cow::Borrowed("\r\n".as_bytes())]); + self.format_body(out); } } diff --git a/src/message/mod.rs b/src/message/mod.rs index 01f31da..9cede38 100644 --- a/src/message/mod.rs +++ b/src/message/mod.rs @@ -198,7 +198,7 @@ //! ``` //! -use std::{io::Write, iter, time::SystemTime}; +use std::{borrow::Cow, iter, sync::Arc, time::SystemTime}; pub use attachment::Attachment; pub use body::{Body, IntoBody, MaybeString}; @@ -226,7 +226,23 @@ const DEFAULT_MESSAGE_ID_DOMAIN: &str = "localhost"; /// Something that can be formatted as an email message trait EmailFormat { // Use a writer? - fn format(&self, out: &mut Vec); + fn format<'a>(&'a self, out: &mut impl Extend>); + + fn format_to_vec(&self) -> Vec { + struct Formatter(Vec); + + impl<'a> Extend> for Formatter { + fn extend>>(&mut self, iter: T) { + for chunk in iter { + self.0.extend_from_slice(&chunk); + } + } + } + + let mut formatted = Formatter(Vec::new()); + self.format(&mut formatted); + formatted.0 + } } /// A builder for messages @@ -454,7 +470,7 @@ impl MessageBuilder { let body = body.into_body(maybe_encoding); self.headers.set(body.encoding()); - self.build(MessageBody::Raw(body.into_vec())) + self.build(MessageBody::Raw(body.into_inner())) } /// Create message using mime body ([`MultiPart`][self::MultiPart]) @@ -489,7 +505,7 @@ pub struct Message { #[derive(Clone, Debug)] enum MessageBody { Mime(Part), - Raw(Vec), + Raw(Arc<[u8]>), } impl Message { @@ -515,9 +531,7 @@ impl Message { /// Get message content formatted for SMTP pub fn formatted(&self) -> Vec { - let mut out = Vec::new(); - self.format(&mut out); - out + self.format_to_vec() } #[cfg(feature = "dkim")] @@ -593,15 +607,13 @@ impl Message { } impl EmailFormat for Message { - fn format(&self, out: &mut Vec) { - write!(out, "{}", self.headers) - .expect("A Write implementation panicked while formatting headers"); + fn format<'a>(&'a self, out: &mut impl Extend>) { + self.headers.format(out); match &self.body { MessageBody::Mime(p) => p.format(out), MessageBody::Raw(r) => { - out.extend_from_slice(b"\r\n"); - out.extend_from_slice(r) + out.extend([Cow::Borrowed("\r\n".as_bytes()), Cow::Borrowed(r)]); } } }