use std::sync::{Arc, OnceLock};
use crate::{MailConfig, MailError, Mailable};
tokio::task_local! {
pub(crate) static CURRENT_MAIL_CONFIG: Arc<MailConfig>;
}
pub(crate) fn scope_config<F: std::future::Future>(
config: Arc<MailConfig>,
f: F,
) -> impl std::future::Future<Output = F::Output> {
CURRENT_MAIL_CONFIG.scope(config, f)
}
static GLOBAL_MAIL_CONFIG: OnceLock<MailConfig> = OnceLock::new();
pub fn set_global_config(config: MailConfig) -> Result<(), MailError> {
GLOBAL_MAIL_CONFIG
.set(config)
.map_err(|_| MailError::NotConfigured)
}
pub fn global_config() -> Option<&'static MailConfig> {
GLOBAL_MAIL_CONFIG.get()
}
#[cfg(feature = "queue")]
tokio::task_local! {
pub(crate) static CURRENT_QUEUE: crate::queue_job::QueueRef;
}
#[cfg(feature = "queue")]
pub(crate) fn scope_queue<F: std::future::Future>(
queue: crate::queue_job::QueueRef,
f: F,
) -> impl std::future::Future<Output = F::Output> {
CURRENT_QUEUE.scope(queue, f)
}
pub struct Mail;
impl Mail {
pub async fn send(mailable: impl Mailable, to: &str) -> Result<(), MailError> {
let config = CURRENT_MAIL_CONFIG
.try_with(|c| c.clone())
.map_err(|_| MailError::NotConfigured)?;
Self::dispatch(&mailable, to, &config).await
}
pub async fn send_with(
mailable: impl Mailable,
to: &str,
config: &MailConfig,
) -> Result<(), MailError> {
Self::dispatch(&mailable, to, config).await
}
#[cfg(feature = "queue")]
pub async fn queue(mailable: impl Mailable, to: &str) -> Result<(), MailError> {
let queue = CURRENT_QUEUE
.try_with(|q| q.clone())
.map_err(|_| MailError::NotConfigured)?;
let job = crate::queue_job::SendMailJob {
to: to.to_string(),
subject: mailable.subject().to_string(),
body: mailable.body(),
html_body: mailable.html_body(),
};
queue
.dispatch(job)
.await
.map_err(|e| MailError::Http(e.to_string()))?;
Ok(())
}
#[cfg(feature = "queue")]
pub async fn later(
mailable: impl Mailable,
to: &str,
delay: std::time::Duration,
) -> Result<(), MailError> {
let queue = CURRENT_QUEUE
.try_with(|q| q.clone())
.map_err(|_| MailError::NotConfigured)?;
let job = crate::queue_job::SendMailJob {
to: to.to_string(),
subject: mailable.subject().to_string(),
body: mailable.body(),
html_body: mailable.html_body(),
};
queue
.dispatch_in(job, delay)
.await
.map_err(|e| MailError::Http(e.to_string()))?;
Ok(())
}
async fn dispatch(
mailable: &dyn Mailable,
to: &str,
config: &MailConfig,
) -> Result<(), MailError> {
match config.driver.as_str() {
"log" => crate::drivers::send_log(mailable, to, config).await,
#[cfg(feature = "smtp")]
"smtp" => crate::drivers::send_smtp(mailable, to, config).await,
#[cfg(feature = "postmark")]
"postmark" => crate::drivers::send_postmark(mailable, to, config).await,
#[cfg(feature = "resend")]
"resend" => crate::drivers::send_resend(mailable, to, config).await,
d => Err(MailError::UnknownDriver(d.to_string())),
}
}
}