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;
/// A [`Message`][super::Message] or [`SinglePart`][super::SinglePart] body that has already been encoded.
#[derive(Debug, Clone)]
pub struct Body {
buf: Vec<u8>,
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<u8>, encoding: ContentTransferEncoding) -> Self {
Self { buf, encoding }
Self {
buf: buf.into(),
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 {
ContentTransferEncoding::SevenBit
| ContentTransferEncoding::EightBit
@@ -133,7 +136,16 @@ impl Body {
/// Consumes `Body` and returns the inner `Vec<u8>`
#[inline]
#[deprecated(
note = "The inner memory is not stored into `Vec<u8>` anymore. Consider using `into_inner`"
)]
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
}
}

View File

@@ -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<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.
// comes from `http` crate
#[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;
@@ -28,7 +28,7 @@ impl 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 {
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<u8>,
body: Arc<[u8]>,
}
impl SinglePart {
@@ -138,24 +138,18 @@ impl SinglePart {
/// Get message content formatted for sending
pub fn formatted(&self) -> Vec<u8> {
let mut out = Vec::new();
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");
self.format_to_vec()
}
}
impl EmailFormat for SinglePart {
fn format(&self, out: &mut Vec<u8>) {
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<Cow<'a, [u8]>>) {
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<u8> {
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<u8>) {
fn format_body<'a>(&'a self, out: &mut impl Extend<Cow<'a, [u8]>>) {
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<u8>) {
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<Cow<'a, [u8]>>) {
self.headers.format(out);
out.extend([Cow::Borrowed("\r\n".as_bytes())]);
self.format_body(out);
}
}

View File

@@ -198,7 +198,7 @@
//! ```
//! </details>
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<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
@@ -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<u8>),
Raw(Arc<[u8]>),
}
impl Message {
@@ -515,9 +531,7 @@ impl Message {
/// Get message content formatted for SMTP
pub fn formatted(&self) -> Vec<u8> {
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<u8>) {
write!(out, "{}", self.headers)
.expect("A Write implementation panicked while formatting headers");
fn format<'a>(&'a self, out: &mut impl Extend<Cow<'a, [u8]>>) {
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)]);
}
}
}