use std::ffi::OsStr;
use std::fmt::{self, Display, Formatter};
use std::str::FromStr;
use async_std::io::{self, Cursor, Read};
use async_std::pin::Pin;
use async_std::prelude::*;
use async_std::task::{Context, Poll};
use fast_chemail::is_valid_email;
use pin_project::pin_project;
use crate::error::EmailResult;
use crate::error::Error;
#[derive(PartialEq, Eq, Clone, Debug)]
#[cfg_attr(
feature = "serde-impls",
derive(serde_derive::Serialize, serde_derive::Deserialize)
)]
pub struct EmailAddress(String);
impl EmailAddress {
pub fn new(address: String) -> EmailResult<EmailAddress> {
if !is_valid_email(&address) && !address.ends_with("localhost") {
return Err(Error::InvalidEmailAddress);
}
Ok(EmailAddress(address))
}
}
impl FromStr for EmailAddress {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
EmailAddress::new(s.to_string())
}
}
impl Display for EmailAddress {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_str(&self.0)
}
}
impl AsRef<str> for EmailAddress {
fn as_ref(&self) -> &str {
&self.0
}
}
impl AsRef<OsStr> for EmailAddress {
fn as_ref(&self) -> &OsStr {
&self.0.as_ref()
}
}
#[derive(PartialEq, Eq, Clone, Debug)]
#[cfg_attr(
feature = "serde-impls",
derive(serde_derive::Serialize, serde_derive::Deserialize)
)]
pub struct Envelope {
forward_path: Vec<EmailAddress>,
reverse_path: Option<EmailAddress>,
}
impl Envelope {
pub fn new(from: Option<EmailAddress>, to: Vec<EmailAddress>) -> EmailResult<Envelope> {
if to.is_empty() {
return Err(Error::MissingTo);
}
Ok(Envelope {
forward_path: to,
reverse_path: from,
})
}
pub fn to(&self) -> &[EmailAddress] {
self.forward_path.as_slice()
}
pub fn from(&self) -> Option<&EmailAddress> {
self.reverse_path.as_ref()
}
}
#[pin_project(project = MessageProj)]
#[allow(missing_debug_implementations)]
pub enum Message {
Reader(#[pin] Box<dyn Read + Send + Sync>),
Bytes(#[pin] Cursor<Vec<u8>>),
}
impl Read for Message {
#[allow(unsafe_code)]
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
match self.project() {
MessageProj::Reader(mut rdr) => {
let r: Pin<&mut _> = unsafe { Pin::new_unchecked(&mut **rdr) };
r.poll_read(cx, buf)
}
MessageProj::Bytes(rdr) => {
let _: Pin<&mut _> = rdr;
rdr.poll_read(cx, buf)
}
}
}
}
#[allow(missing_debug_implementations)]
pub struct SendableEmail {
envelope: Envelope,
message_id: String,
message: Message,
}
impl SendableEmail {
pub fn new<S: AsRef<str>, T: AsRef<[u8]>>(
envelope: Envelope,
message_id: S,
message: T,
) -> SendableEmail {
SendableEmail {
envelope,
message_id: message_id.as_ref().into(),
message: Message::Bytes(Cursor::new(message.as_ref().to_vec())),
}
}
pub fn new_with_reader(
envelope: Envelope,
message_id: String,
message: Box<dyn Read + Send + Sync>,
) -> SendableEmail {
SendableEmail {
envelope,
message_id,
message: Message::Reader(message),
}
}
pub fn envelope(&self) -> &Envelope {
&self.envelope
}
pub fn message_id(&self) -> &str {
&self.message_id
}
pub fn message(self) -> Message {
self.message
}
pub async fn message_to_string(mut self) -> Result<String, io::Error> {
let mut message_content = String::new();
self.message.read_to_string(&mut message_content).await?;
Ok(message_content)
}
}