use core::sync::atomic::AtomicBool;
use alloc::{string::String, sync::Arc, vec::Vec};
use std::sync::mpsc::Sender;
use thiserror::Error;
#[cfg(feature = "imap")]
use crate::imap::client::{ImapClientError, ImapClientStd};
#[cfg(feature = "jmap")]
use crate::jmap::client::{JmapClientError, JmapClientStd};
#[cfg(feature = "m2dir")]
use crate::m2dir::client::{M2dirClient, M2dirClientError};
#[cfg(feature = "maildir")]
use crate::maildir::client::{MaildirClient, MaildirClientError};
#[cfg(feature = "search")]
use crate::search::query::SearchEmailsQuery;
#[cfg(feature = "smtp")]
use crate::smtp::client::{SmtpClientError, SmtpClientStd};
use crate::{
envelope::event::WatchEvent,
envelope::types::{Envelope, EnvelopeDiff},
flag::types::{Flag, FlagOp},
mailbox::types::{Mailbox, MailboxDiff},
};
#[cfg(feature = "imap")]
use io_imap::types::core::{IString, NString};
#[cfg(feature = "smtp")]
#[cfg(any(
feature = "rustls-ring",
feature = "rustls-aws",
feature = "native-tls"
))]
use io_smtp::rfc5321::types::ehlo_domain::EhloDomain;
#[cfg(feature = "imap")]
#[cfg(any(
feature = "rustls-ring",
feature = "rustls-aws",
feature = "native-tls"
))]
use pimalaya_stream::sasl::Sasl as ImapSasl;
#[cfg(feature = "jmap")]
#[cfg(any(
feature = "rustls-ring",
feature = "rustls-aws",
feature = "native-tls"
))]
use secrecy::SecretString;
#[cfg(any(feature = "imap", feature = "jmap", feature = "smtp"))]
#[cfg(any(
feature = "rustls-ring",
feature = "rustls-aws",
feature = "native-tls"
))]
use {pimalaya_stream::tls::Tls, url::Url};
#[derive(Debug, Error)]
pub enum EmailClientStdError {
#[cfg(feature = "imap")]
#[error(transparent)]
Imap(#[from] ImapClientError),
#[cfg(feature = "jmap")]
#[error(transparent)]
Jmap(#[from] JmapClientError),
#[cfg(feature = "smtp")]
#[error(transparent)]
Smtp(#[from] SmtpClientError),
#[cfg(feature = "maildir")]
#[error(transparent)]
Maildir(#[from] MaildirClientError),
#[cfg(feature = "m2dir")]
#[error(transparent)]
M2dir(#[from] M2dirClientError),
#[error("No backend supporting this operation is registered")]
NoBackendRegistered,
#[error("Registered backend does not support this operation")]
UnsupportedOperation,
}
#[derive(Default)]
pub struct EmailClientStd {
#[cfg(feature = "imap")]
pub imap: Option<ImapClientStd>,
#[cfg(feature = "jmap")]
pub jmap: Option<JmapClientStd>,
#[cfg(feature = "smtp")]
pub smtp: Option<SmtpClientStd>,
#[cfg(feature = "maildir")]
pub maildir: Option<MaildirClient>,
#[cfg(feature = "m2dir")]
pub m2dir: Option<M2dirClient>,
}
impl EmailClientStd {
pub fn new() -> Self {
Self::default()
}
#[cfg(feature = "imap")]
pub fn with_imap(mut self, client: ImapClientStd) -> Self {
self.imap = Some(client);
self
}
#[cfg(feature = "jmap")]
pub fn with_jmap(mut self, client: JmapClientStd) -> Self {
self.jmap = Some(client);
self
}
#[cfg(feature = "smtp")]
pub fn with_smtp(mut self, client: SmtpClientStd) -> Self {
self.smtp = Some(client);
self
}
#[cfg(feature = "maildir")]
pub fn with_maildir(mut self, client: MaildirClient) -> Self {
self.maildir = Some(client);
self
}
#[cfg(feature = "m2dir")]
pub fn with_m2dir(mut self, client: M2dirClient) -> Self {
self.m2dir = Some(client);
self
}
#[cfg(feature = "imap")]
#[cfg(any(
feature = "rustls-ring",
feature = "rustls-aws",
feature = "native-tls"
))]
pub fn connect_imap(
self,
url: &Url,
tls: &Tls,
starttls: bool,
sasl: Option<impl Into<ImapSasl>>,
auto_id: Option<Vec<(IString<'static>, NString<'static>)>>,
) -> Result<Self, EmailClientStdError> {
Ok(self.with_imap(ImapClientStd::connect(url, tls, starttls, sasl, auto_id)?))
}
#[cfg(feature = "jmap")]
#[cfg(any(
feature = "rustls-ring",
feature = "rustls-aws",
feature = "native-tls"
))]
pub fn connect_jmap(
self,
url: &Url,
tls: &Tls,
http_auth: SecretString,
) -> Result<Self, EmailClientStdError> {
Ok(self.with_jmap(JmapClientStd::connect(url, tls, http_auth)?))
}
#[cfg(feature = "smtp")]
#[cfg(any(
feature = "rustls-ring",
feature = "rustls-aws",
feature = "native-tls"
))]
pub fn connect_smtp(
self,
url: &Url,
tls: &Tls,
starttls: bool,
domain: EhloDomain<'_>,
sasl: Option<impl Into<pimalaya_stream::sasl::Sasl>>,
) -> Result<Self, EmailClientStdError> {
Ok(self.with_smtp(SmtpClientStd::connect(url, tls, starttls, domain, sasl)?))
}
pub fn ping(&mut self) -> Result<(), EmailClientStdError> {
#[cfg(feature = "imap")]
if let Some(c) = self.imap.as_mut() {
c.ping()?;
}
#[cfg(feature = "smtp")]
if let Some(c) = self.smtp.as_mut() {
c.ping()?;
}
Ok(())
}
pub fn list_mailboxes(
&mut self,
with_counts: bool,
) -> Result<Vec<Mailbox>, EmailClientStdError> {
#[cfg(feature = "maildir")]
if let Some(c) = &self.maildir {
return Ok(c.list_mailboxes(with_counts)?);
}
#[cfg(feature = "m2dir")]
if let Some(c) = &self.m2dir {
return Ok(c.list_mailboxes(with_counts)?);
}
#[cfg(feature = "jmap")]
if let Some(c) = self.jmap.as_mut() {
return Ok(c.list_mailboxes(with_counts)?);
}
#[cfg(feature = "imap")]
if let Some(c) = self.imap.as_mut() {
return Ok(c.list_mailboxes(with_counts)?);
}
Err(EmailClientStdError::NoBackendRegistered)
}
pub fn list_envelopes(
&mut self,
mailbox: &str,
page: Option<u32>,
page_size: Option<u32>,
with_attachment: bool,
) -> Result<Vec<Envelope>, EmailClientStdError> {
#[cfg(feature = "maildir")]
if let Some(c) = &self.maildir {
return Ok(c.list_envelopes(mailbox, page, page_size, with_attachment)?);
}
#[cfg(feature = "m2dir")]
if let Some(c) = &self.m2dir {
return Ok(c.list_envelopes(mailbox, page, page_size, with_attachment)?);
}
#[cfg(feature = "jmap")]
if let Some(c) = self.jmap.as_mut() {
return Ok(c.list_envelopes(mailbox, page, page_size)?);
}
#[cfg(feature = "imap")]
if let Some(c) = self.imap.as_mut() {
return Ok(c.list_envelopes(mailbox, page, page_size, with_attachment)?);
}
let _ = (mailbox, page, page_size, with_attachment);
Err(EmailClientStdError::NoBackendRegistered)
}
#[cfg(feature = "search")]
pub fn search_envelopes(
&mut self,
mailbox: &str,
query: Option<&SearchEmailsQuery>,
page: Option<u32>,
page_size: Option<u32>,
with_attachment: bool,
) -> Result<Vec<Envelope>, EmailClientStdError> {
#[cfg(feature = "maildir")]
if let Some(c) = &self.maildir {
return Ok(c.search_envelopes(mailbox, query, page, page_size, with_attachment)?);
}
#[cfg(feature = "m2dir")]
if let Some(c) = &self.m2dir {
return Ok(c.search_envelopes(mailbox, query, page, page_size, with_attachment)?);
}
#[cfg(feature = "jmap")]
if let Some(c) = self.jmap.as_mut() {
return Ok(c.search_envelopes(mailbox, query, page, page_size)?);
}
#[cfg(feature = "imap")]
if let Some(c) = self.imap.as_mut() {
return Ok(c.search_envelopes(mailbox, query, page, page_size, with_attachment)?);
}
let _ = (mailbox, query, page, page_size, with_attachment);
Err(EmailClientStdError::NoBackendRegistered)
}
pub fn store_flags(
&mut self,
mailbox: &str,
ids: &[&str],
flags: &[Flag],
op: FlagOp,
) -> Result<(), EmailClientStdError> {
#[cfg(feature = "maildir")]
if let Some(c) = &self.maildir {
return Ok(c.store_flags(mailbox, ids, flags, op)?);
}
#[cfg(feature = "m2dir")]
if let Some(c) = &self.m2dir {
return Ok(c.store_flags(mailbox, ids, flags, op)?);
}
#[cfg(feature = "jmap")]
if let Some(c) = self.jmap.as_mut() {
return Ok(c.store_flags(mailbox, ids, flags, op)?);
}
#[cfg(feature = "imap")]
if let Some(c) = self.imap.as_mut() {
return Ok(c.store_flags(mailbox, ids, flags, op)?);
}
let _ = (mailbox, ids, flags, op);
Err(EmailClientStdError::NoBackendRegistered)
}
pub fn get_message(&mut self, mailbox: &str, id: &str) -> Result<Vec<u8>, EmailClientStdError> {
#[cfg(feature = "maildir")]
if let Some(c) = &self.maildir {
return Ok(c.get_message(mailbox, id)?);
}
#[cfg(feature = "m2dir")]
if let Some(c) = &self.m2dir {
return Ok(c.get_message(mailbox, id)?);
}
#[cfg(feature = "jmap")]
if let Some(c) = self.jmap.as_mut() {
return Ok(c.get_message(mailbox, id)?);
}
#[cfg(feature = "imap")]
if let Some(c) = self.imap.as_mut() {
return Ok(c.get_message(mailbox, id)?);
}
let _ = (mailbox, id);
Err(EmailClientStdError::NoBackendRegistered)
}
pub fn add_message(
&mut self,
mailbox: &str,
flags: &[Flag],
raw: Vec<u8>,
) -> Result<String, EmailClientStdError> {
#[cfg(feature = "maildir")]
if let Some(c) = &self.maildir {
return Ok(c.add_message(mailbox, flags, raw)?);
}
#[cfg(feature = "m2dir")]
if let Some(c) = &self.m2dir {
return Ok(c.add_message(mailbox, flags, raw)?);
}
#[cfg(feature = "jmap")]
if let Some(c) = self.jmap.as_mut() {
return Ok(c.add_message(mailbox, flags, raw)?);
}
#[cfg(feature = "imap")]
if let Some(c) = self.imap.as_mut() {
return Ok(c.add_message(mailbox, flags, raw)?);
}
let _ = (mailbox, flags, raw);
Err(EmailClientStdError::NoBackendRegistered)
}
pub fn create_mailbox(&mut self, name: &str) -> Result<(), EmailClientStdError> {
#[cfg(feature = "maildir")]
if let Some(c) = &self.maildir {
return Ok(c.create_mailbox(name)?);
}
#[cfg(feature = "m2dir")]
if let Some(c) = &self.m2dir {
return Ok(c.create_mailbox(name)?);
}
#[cfg(feature = "jmap")]
if let Some(c) = self.jmap.as_mut() {
return Ok(c.create_mailbox(name)?);
}
#[cfg(feature = "imap")]
if let Some(c) = self.imap.as_mut() {
return Ok(c.create_mailbox(name)?);
}
let _ = name;
Err(EmailClientStdError::NoBackendRegistered)
}
pub fn delete_mailbox(&mut self, name: &str) -> Result<(), EmailClientStdError> {
#[cfg(feature = "maildir")]
if let Some(c) = &self.maildir {
return Ok(c.delete_mailbox(name)?);
}
#[cfg(feature = "m2dir")]
if let Some(c) = &self.m2dir {
return Ok(c.delete_mailbox(name)?);
}
#[cfg(feature = "jmap")]
if let Some(c) = self.jmap.as_mut() {
return Ok(c.delete_mailbox(name)?);
}
#[cfg(feature = "imap")]
if let Some(c) = self.imap.as_mut() {
return Ok(c.delete_mailbox(name)?);
}
let _ = name;
Err(EmailClientStdError::NoBackendRegistered)
}
pub fn delete_message(&mut self, mailbox: &str, id: &str) -> Result<(), EmailClientStdError> {
#[cfg(feature = "maildir")]
if let Some(c) = &self.maildir {
return Ok(c.delete_message(mailbox, id)?);
}
#[cfg(feature = "m2dir")]
if let Some(c) = &self.m2dir {
return Ok(c.delete_message(mailbox, id)?);
}
#[cfg(feature = "jmap")]
if let Some(c) = self.jmap.as_mut() {
return Ok(c.delete_message(mailbox, id)?);
}
#[cfg(feature = "imap")]
if let Some(c) = self.imap.as_mut() {
return Ok(c.delete_message(mailbox, id)?);
}
let _ = (mailbox, id);
Err(EmailClientStdError::NoBackendRegistered)
}
pub fn copy_messages(
&mut self,
from: &str,
to: &str,
ids: &[&str],
) -> Result<(), EmailClientStdError> {
#[cfg(feature = "maildir")]
if let Some(c) = &self.maildir {
return Ok(c.copy_messages(from, to, ids)?);
}
#[cfg(feature = "m2dir")]
if let Some(c) = &self.m2dir {
return Ok(c.copy_messages(from, to, ids)?);
}
#[cfg(feature = "jmap")]
if let Some(c) = self.jmap.as_mut() {
return Ok(c.copy_messages(from, to, ids)?);
}
#[cfg(feature = "imap")]
if let Some(c) = self.imap.as_mut() {
return Ok(c.copy_messages(from, to, ids)?);
}
let _ = (from, to, ids);
Err(EmailClientStdError::NoBackendRegistered)
}
pub fn move_messages(
&mut self,
from: &str,
to: &str,
ids: &[&str],
) -> Result<(), EmailClientStdError> {
#[cfg(feature = "maildir")]
if let Some(c) = &self.maildir {
return Ok(c.move_messages(from, to, ids)?);
}
#[cfg(feature = "m2dir")]
if let Some(c) = &self.m2dir {
return Ok(c.move_messages(from, to, ids)?);
}
#[cfg(feature = "jmap")]
if let Some(c) = self.jmap.as_mut() {
return Ok(c.move_messages(from, to, ids)?);
}
#[cfg(feature = "imap")]
if let Some(c) = self.imap.as_mut() {
return Ok(c.move_messages(from, to, ids)?);
}
let _ = (from, to, ids);
Err(EmailClientStdError::NoBackendRegistered)
}
pub fn diff_envelopes(
&mut self,
mailbox: &str,
state: Option<&[u8]>,
) -> Result<EnvelopeDiff, EmailClientStdError> {
#[cfg(feature = "jmap")]
if let Some(c) = self.jmap.as_mut() {
return Ok(c.diff_envelopes(mailbox, state)?);
}
#[cfg(feature = "imap")]
if let Some(c) = self.imap.as_mut() {
return Ok(c.diff_envelopes(mailbox, state)?);
}
let _ = (mailbox, state);
Err(EmailClientStdError::UnsupportedOperation)
}
pub fn diff_mailboxes(
&mut self,
state: Option<&[u8]>,
) -> Result<MailboxDiff, EmailClientStdError> {
#[cfg(feature = "jmap")]
if let Some(c) = self.jmap.as_mut() {
return Ok(c.diff_mailboxes(state)?);
}
let _ = state;
Err(EmailClientStdError::UnsupportedOperation)
}
#[cfg(any(feature = "imap", feature = "jmap"))]
pub fn watch_mailbox(
&mut self,
mailbox: &str,
shutdown: Arc<AtomicBool>,
tx: Sender<WatchEvent>,
) -> Result<(), EmailClientStdError> {
#[cfg(feature = "jmap")]
if let Some(c) = self.jmap.as_mut() {
return Ok(c.watch_mailbox(mailbox, shutdown, tx)?);
}
#[cfg(feature = "imap")]
if let Some(c) = self.imap.as_mut() {
return Ok(c.watch_mailbox(mailbox, shutdown, tx)?);
}
let _ = (mailbox, shutdown, tx);
Err(EmailClientStdError::NoBackendRegistered)
}
#[cfg(any(feature = "jmap", feature = "smtp"))]
pub fn send_message(&mut self, raw: Vec<u8>) -> Result<(), EmailClientStdError> {
#[cfg(feature = "jmap")]
if let Some(c) = self.jmap.as_mut() {
return Ok(c.send_message(raw)?);
}
#[cfg(feature = "smtp")]
if let Some(c) = self.smtp.as_mut() {
return Ok(c.send_message(raw)?);
}
let _ = raw;
Err(EmailClientStdError::NoBackendRegistered)
}
}