Compare commits

...

1 Commits

Author SHA1 Message Date
Paolo Barbolini
c3b5d760f8 wip 2024-10-22 22:49:25 +02:00
4 changed files with 85 additions and 50 deletions

View File

@@ -1,11 +1,11 @@
use std::{mem, ops::Deref}; use std::{mem, ops::Deref, sync::Arc};
use crate::message::header::ContentTransferEncoding; use crate::message::header::ContentTransferEncoding;
/// A [`Message`][super::Message] or [`SinglePart`][super::SinglePart] body that has already been encoded. /// A [`Message`][super::Message] or [`SinglePart`][super::SinglePart] body that has already been encoded.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Body { pub struct Body {
buf: Vec<u8>, buf: Arc<[u8]>,
encoding: ContentTransferEncoding, encoding: ContentTransferEncoding,
} }
@@ -39,7 +39,7 @@ impl Body {
let encoding = buf.encoding(false); let encoding = buf.encoding(false);
buf.encode_crlf(); 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`. /// Encode the supplied `buf`, using the provided `encoding`.
@@ -77,7 +77,7 @@ impl Body {
} }
buf.encode_crlf(); 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. /// 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. /// `buf` shouldn't contain non-ascii characters, lines longer than 1000 characters or nul bytes.
#[inline] #[inline]
pub fn dangerous_pre_encoded(buf: Vec<u8>, encoding: ContentTransferEncoding) -> Self { pub fn dangerous_pre_encoded(buf: Vec<u8>, encoding: ContentTransferEncoding) -> Self {
Self { buf, encoding } Self {
buf: buf.into(),
encoding,
}
} }
/// Encodes the supplied `buf` using the provided `encoding` /// Encodes the supplied `buf` using the provided `encoding`
fn new_impl(buf: Vec<u8>, encoding: ContentTransferEncoding) -> Self { fn new_impl(buf: Arc<[u8]>, encoding: ContentTransferEncoding) -> Self {
match encoding { match encoding {
ContentTransferEncoding::SevenBit ContentTransferEncoding::SevenBit
| ContentTransferEncoding::EightBit | ContentTransferEncoding::EightBit
@@ -133,7 +136,16 @@ impl Body {
/// Consumes `Body` and returns the inner `Vec<u8>` /// Consumes `Body` and returns the inner `Vec<u8>`
#[inline] #[inline]
#[deprecated(
note = "The inner memory is not stored into `Vec<u8>` anymore. Consider using `into_inner`"
)]
pub fn into_vec(self) -> Vec<u8> { pub fn into_vec(self) -> Vec<u8> {
self.buf.to_vec()
}
/// Consumes `Body` and returns the inner `Arc<[u8]>`
#[inline]
pub fn into_inner(self) -> Arc<[u8]> {
self.buf self.buf
} }
} }

View File

@@ -18,6 +18,7 @@ pub use self::{
special::*, special::*,
textual::*, textual::*,
}; };
use super::EmailFormat;
use crate::BoxError; use crate::BoxError;
mod content; mod content;
@@ -154,6 +155,19 @@ impl Display for Headers {
} }
} }
impl EmailFormat for Headers {
fn format<'a>(&'a self, out: &mut impl Extend<Cow<'a, [u8]>>) {
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. /// A possible error when converting a `HeaderName` from another type.
// comes from `http` crate // comes from `http` crate
#[allow(missing_copy_implementations)] #[allow(missing_copy_implementations)]

View File

@@ -1,4 +1,4 @@
use std::{io::Write, iter::repeat_with}; use std::{borrow::Cow, iter::repeat_with, sync::Arc};
use mime::Mime; use mime::Mime;
@@ -28,7 +28,7 @@ impl Part {
} }
impl EmailFormat for Part { impl EmailFormat for Part {
fn format(&self, out: &mut Vec<u8>) { fn format<'a>(&'a self, out: &mut impl Extend<Cow<'a, [u8]>>) {
match self { match self {
Part::Single(part) => part.format(out), Part::Single(part) => part.format(out),
Part::Multi(part) => part.format(out), Part::Multi(part) => part.format(out),
@@ -71,7 +71,7 @@ impl SinglePartBuilder {
SinglePart { SinglePart {
headers: self.headers, headers: self.headers,
body: body.into_vec(), body: body.into_inner(),
} }
} }
} }
@@ -100,7 +100,7 @@ impl Default for SinglePartBuilder {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct SinglePart { pub struct SinglePart {
headers: Headers, headers: Headers,
body: Vec<u8>, body: Arc<[u8]>,
} }
impl SinglePart { impl SinglePart {
@@ -138,24 +138,18 @@ impl SinglePart {
/// Get message content formatted for sending /// Get message content formatted for sending
pub fn formatted(&self) -> Vec<u8> { pub fn formatted(&self) -> Vec<u8> {
let mut out = Vec::new(); self.format_to_vec()
self.format(&mut out);
out
}
/// Format only the signlepart body
fn format_body(&self, out: &mut Vec<u8>) {
out.extend_from_slice(&self.body);
out.extend_from_slice(b"\r\n");
} }
} }
impl EmailFormat for SinglePart { impl EmailFormat for SinglePart {
fn format(&self, out: &mut Vec<u8>) { fn format<'a>(&'a self, out: &mut impl Extend<Cow<'a, [u8]>>) {
write!(out, "{}", self.headers) self.headers.format(out);
.expect("A Write implementation panicked while formatting headers"); out.extend([
out.extend_from_slice(b"\r\n"); Cow::Borrowed("\r\n".as_bytes()),
self.format_body(out); Cow::Borrowed(&self.body),
Cow::Borrowed(b"\r\n"),
]);
} }
} }
@@ -384,33 +378,36 @@ impl MultiPart {
/// Get message content formatted for SMTP /// Get message content formatted for SMTP
pub fn formatted(&self) -> Vec<u8> { pub fn formatted(&self) -> Vec<u8> {
let mut out = Vec::new(); self.format_to_vec()
self.format(&mut out);
out
} }
/// Format only the multipart body /// Format only the multipart body
fn format_body(&self, out: &mut Vec<u8>) { fn format_body<'a>(&'a self, out: &mut impl Extend<Cow<'a, [u8]>>) {
let boundary = self.boundary(); let boundary = self.boundary();
for part in &self.parts { for part in &self.parts {
out.extend_from_slice(b"--"); out.extend([
out.extend_from_slice(boundary.as_bytes()); Cow::Borrowed("--".as_bytes()),
out.extend_from_slice(b"\r\n"); // FIXME: this clone shouldn't exist
Cow::Owned(boundary.clone().into()),
Cow::Borrowed("\r\n".as_bytes()),
]);
part.format(out); part.format(out);
} }
out.extend_from_slice(b"--"); out.extend([
out.extend_from_slice(boundary.as_bytes()); Cow::Borrowed("--".as_bytes()),
out.extend_from_slice(b"--\r\n"); Cow::Owned(boundary.into()),
Cow::Borrowed("--\r\n".as_bytes()),
]);
} }
} }
impl EmailFormat for MultiPart { impl EmailFormat for MultiPart {
fn format(&self, out: &mut Vec<u8>) { fn format<'a>(&'a self, out: &mut impl Extend<Cow<'a, [u8]>>) {
write!(out, "{}", self.headers) self.headers.format(out);
.expect("A Write implementation panicked while formatting headers"); out.extend([Cow::Borrowed("\r\n".as_bytes())]);
out.extend_from_slice(b"\r\n");
self.format_body(out); self.format_body(out);
} }
} }

View File

@@ -198,7 +198,7 @@
//! ``` //! ```
//! </details> //! </details>
use std::{io::Write, iter, time::SystemTime}; use std::{borrow::Cow, iter, sync::Arc, time::SystemTime};
pub use attachment::Attachment; pub use attachment::Attachment;
pub use body::{Body, IntoBody, MaybeString}; 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 /// Something that can be formatted as an email message
trait EmailFormat { trait EmailFormat {
// Use a writer? // Use a writer?
fn format(&self, out: &mut Vec<u8>); fn format<'a>(&'a self, out: &mut impl Extend<Cow<'a, [u8]>>);
fn format_to_vec(&self) -> Vec<u8> {
struct Formatter(Vec<u8>);
impl<'a> Extend<Cow<'a, [u8]>> for Formatter {
fn extend<T: IntoIterator<Item = Cow<'a, [u8]>>>(&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 /// A builder for messages
@@ -454,7 +470,7 @@ impl MessageBuilder {
let body = body.into_body(maybe_encoding); let body = body.into_body(maybe_encoding);
self.headers.set(body.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]) /// Create message using mime body ([`MultiPart`][self::MultiPart])
@@ -489,7 +505,7 @@ pub struct Message {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
enum MessageBody { enum MessageBody {
Mime(Part), Mime(Part),
Raw(Vec<u8>), Raw(Arc<[u8]>),
} }
impl Message { impl Message {
@@ -515,9 +531,7 @@ impl Message {
/// Get message content formatted for SMTP /// Get message content formatted for SMTP
pub fn formatted(&self) -> Vec<u8> { pub fn formatted(&self) -> Vec<u8> {
let mut out = Vec::new(); self.format_to_vec()
self.format(&mut out);
out
} }
#[cfg(feature = "dkim")] #[cfg(feature = "dkim")]
@@ -593,15 +607,13 @@ impl Message {
} }
impl EmailFormat for Message { impl EmailFormat for Message {
fn format(&self, out: &mut Vec<u8>) { fn format<'a>(&'a self, out: &mut impl Extend<Cow<'a, [u8]>>) {
write!(out, "{}", self.headers) self.headers.format(out);
.expect("A Write implementation panicked while formatting headers");
match &self.body { match &self.body {
MessageBody::Mime(p) => p.format(out), MessageBody::Mime(p) => p.format(out),
MessageBody::Raw(r) => { MessageBody::Raw(r) => {
out.extend_from_slice(b"\r\n"); out.extend([Cow::Borrowed("\r\n".as_bytes()), Cow::Borrowed(r)]);
out.extend_from_slice(r)
} }
} }
} }