dust-mail 0.4.0

A multi protocol email client
use std::{collections::HashMap, fmt::Display, result};

use crate::error::{err, Error, ErrorKind, Result};

use super::{address::Address, content::Content, incoming::types::flag::Flag, parser, Headers};

#[derive(Debug)]
pub struct MessageBuilder {
    pub(crate) from: Option<Address>,
    pub(crate) to: Option<Address>,
    pub(crate) cc: Option<Address>,
    pub(crate) bcc: Option<Address>,
    pub(crate) flags: Vec<Flag>,
    pub(crate) id: Option<String>,
    pub(crate) sent: Option<i64>,
    pub(crate) subject: Option<String>,
    pub(crate) headers: Option<Headers>,
    pub(crate) content: Content,
}

#[cfg(feature = "maildir")]
impl TryFrom<maildir::MailEntry> for MessageBuilder {
    type Error = Error;

    fn try_from(mut mail_entry: maildir::MailEntry) -> result::Result<Self, Self::Error> {
        let parsed = mail_entry.parsed()?;

        let mut builder = parser::message::from_parsed_mail(parsed)?;

        if mail_entry.is_seen() {
            builder = builder.flag(Flag::Read);
        }

        if mail_entry.is_flagged() {
            builder = builder.flag(Flag::Flagged);
        }

        if mail_entry.is_draft() {
            builder = builder.flag(Flag::Draft);
        }

        if mail_entry.is_trashed() {
            builder = builder.flag(Flag::Deleted);
        }

        if mail_entry.is_replied() {
            builder = builder.flag(Flag::Answered);
        }

        Ok(builder)
    }
}

impl TryFrom<&[u8]> for MessageBuilder {
    type Error = Error;

    fn try_from(bytes: &[u8]) -> result::Result<Self, Self::Error> {
        parser::message::from_rfc822(bytes)
    }
}

impl MessageBuilder {
    pub fn new() -> Self {
        Self {
            flags: Vec::new(),
            from: None,
            bcc: None,
            cc: None,
            to: None,
            id: None,
            sent: None,
            subject: None,
            content: Content::default(),
            headers: None,
        }
    }

    pub fn flags<F: IntoIterator<Item = Flag>>(mut self, flags: F) -> Self {
        let mut iter = flags.into_iter();

        while let Some(flag) = iter.next() {
            self.flags.push(flag)
        }

        self
    }

    pub fn flag(mut self, flag: Flag) -> Self {
        self.flags.push(flag);

        self
    }

    pub fn senders<C: Into<Address>>(mut self, sender: C) -> Self {
        self.from = Some(sender.into());

        self
    }

    pub fn recipients<C: Into<Address>>(mut self, recipient: C) -> Self {
        self.to = Some(recipient.into());

        self
    }

    pub fn cc<C: Into<Address>>(mut self, cc: C) -> Self {
        self.cc = Some(cc.into());

        self
    }

    pub fn bcc<C: Into<Address>>(mut self, bcc: C) -> Self {
        self.bcc = Some(bcc.into());

        self
    }

    pub fn id<I: Display>(mut self, id: I) -> Self {
        self.id = Some(id.to_string());

        self
    }

    pub fn sent(mut self, sent: i64) -> Self {
        self.sent = Some(sent);

        self
    }

    pub fn subject<S: Display>(mut self, subject: S) -> Self {
        self.subject = Some(subject.to_string());

        self
    }

    pub fn headers(mut self, headers: Headers) -> Self {
        self.headers = Some(headers);

        self
    }

    pub fn header<H: Into<String>, V: Display>(mut self, header: H, value: V) -> Self {
        if let None = self.headers {
            self.headers = Some(HashMap::new());
        }

        if let Some(headers) = self.headers.as_mut() {
            headers.insert(header.into(), value.to_string());
        }

        self
    }

    pub fn html<H: Into<String>>(mut self, html: H) -> Self {
        self.content.set_html(html);

        self
    }

    pub fn text<H: Into<String>>(mut self, text: H) -> Self {
        self.content.set_text(text);

        self
    }

    pub fn build<T: TryFrom<Self>>(self) -> Result<T> {
        match self.try_into() {
            Ok(message) => Ok(message),
            Err(_err) => err!(ErrorKind::InvalidMessage, "Could not build a valid message"),
        }
    }
}