#![forbid(unsafe_code)]
#![doc = include_str!("../README.md")]
use core::fmt;
use use_email_address::{AddressValidationError, EmailAddress};
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct EnvelopeAddress(EmailAddress);
impl EnvelopeAddress {
pub fn new(value: impl AsRef<str>) -> Result<Self, AddressValidationError> {
Ok(Self(EmailAddress::new(value)?))
}
#[must_use]
pub const fn from_email_address(address: EmailAddress) -> Self {
Self(address)
}
#[must_use]
pub const fn email_address(&self) -> &EmailAddress {
&self.0
}
}
impl From<EmailAddress> for EnvelopeAddress {
fn from(value: EmailAddress) -> Self {
Self(value)
}
}
impl fmt::Display for EnvelopeAddress {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "{}", self.0)
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum ReversePath {
Null,
Address(EnvelopeAddress),
}
impl ReversePath {
pub fn new(value: impl AsRef<str>) -> Result<Self, AddressValidationError> {
Ok(Self::Address(EnvelopeAddress::new(value)?))
}
#[must_use]
pub const fn null() -> Self {
Self::Null
}
}
impl fmt::Display for ReversePath {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Null => formatter.write_str("<>"),
Self::Address(address) => write!(formatter, "<{address}>"),
}
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct ForwardPath(EnvelopeAddress);
impl ForwardPath {
pub fn new(value: impl AsRef<str>) -> Result<Self, AddressValidationError> {
Ok(Self(EnvelopeAddress::new(value)?))
}
#[must_use]
pub const fn from_address(address: EnvelopeAddress) -> Self {
Self(address)
}
#[must_use]
pub const fn address(&self) -> &EnvelopeAddress {
&self.0
}
}
impl fmt::Display for ForwardPath {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "<{}>", self.0)
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct MailFromPath(ReversePath);
impl MailFromPath {
pub fn new(value: impl AsRef<str>) -> Result<Self, AddressValidationError> {
Ok(Self(ReversePath::new(value)?))
}
#[must_use]
pub const fn null() -> Self {
Self(ReversePath::Null)
}
#[must_use]
pub const fn reverse_path(&self) -> &ReversePath {
&self.0
}
}
impl fmt::Display for MailFromPath {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "{}", self.0)
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct RcptToPath(ForwardPath);
impl RcptToPath {
pub fn new(value: impl AsRef<str>) -> Result<Self, AddressValidationError> {
Ok(Self(ForwardPath::new(value)?))
}
#[must_use]
pub const fn from_forward_path(path: ForwardPath) -> Self {
Self(path)
}
#[must_use]
pub const fn forward_path(&self) -> &ForwardPath {
&self.0
}
}
impl fmt::Display for RcptToPath {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "{}", self.0)
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct EnvelopeSender(MailFromPath);
impl EnvelopeSender {
pub fn new(value: impl AsRef<str>) -> Result<Self, AddressValidationError> {
Ok(Self(MailFromPath::new(value)?))
}
#[must_use]
pub const fn null() -> Self {
Self(MailFromPath::null())
}
#[must_use]
pub const fn path(&self) -> &MailFromPath {
&self.0
}
}
impl fmt::Display for EnvelopeSender {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "{}", self.0)
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct EnvelopeRecipient(RcptToPath);
impl EnvelopeRecipient {
pub fn new(value: impl AsRef<str>) -> Result<Self, AddressValidationError> {
Ok(Self(RcptToPath::new(value)?))
}
#[must_use]
pub fn from_email_address(address: EmailAddress) -> Self {
Self(RcptToPath::from_forward_path(ForwardPath::from_address(
EnvelopeAddress::from(address),
)))
}
#[must_use]
pub const fn path(&self) -> &RcptToPath {
&self.0
}
}
impl fmt::Display for EnvelopeRecipient {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "{}", self.0)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Envelope {
sender: EnvelopeSender,
recipients: Vec<EnvelopeRecipient>,
}
impl Envelope {
#[must_use]
pub const fn new(sender: EnvelopeSender) -> Self {
Self {
sender,
recipients: Vec::new(),
}
}
#[must_use]
pub fn with_recipient(mut self, recipient: EnvelopeRecipient) -> Self {
self.recipients.push(recipient);
self
}
pub fn push_recipient(&mut self, recipient: EnvelopeRecipient) {
self.recipients.push(recipient);
}
#[must_use]
pub const fn sender(&self) -> &EnvelopeSender {
&self.sender
}
#[must_use]
pub fn recipients(&self) -> &[EnvelopeRecipient] {
&self.recipients
}
}
#[cfg(test)]
mod tests {
use super::{Envelope, EnvelopeRecipient, EnvelopeSender, ReversePath};
use use_email_address::AddressValidationError;
#[test]
fn models_envelope_identity() -> Result<(), AddressValidationError> {
let envelope = Envelope::new(EnvelopeSender::new("bounce@example.com")?)
.with_recipient(EnvelopeRecipient::new("jane@example.com")?);
assert_eq!(envelope.sender().to_string(), "<bounce@example.com>");
assert_eq!(envelope.recipients()[0].to_string(), "<jane@example.com>");
assert_eq!(ReversePath::null().to_string(), "<>");
Ok(())
}
}