#![warn(clippy::return_self_not_must_use)]
#[cfg(all(
target_family = "wasm",
target_os = "unknown",
any(
feature = "smtp",
feature = "gmail",
feature = "protonbridge",
feature = "mailgun",
feature = "preview",
feature = "preview-axum",
feature = "preview-actix"
)
))]
compile_error!(
"missive features smtp, gmail, protonbridge, mailgun, preview, preview-axum, and preview-actix are native-only on wasm32-unknown-unknown. Use HTTP JSON providers such as resend, postmark, sendgrid, brevo, amazon_ses, mailtrap, mailjet, socketlabs, or jmap."
);
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
mod address;
mod attachment;
mod client;
mod config;
mod email;
mod error;
pub mod interceptor;
mod mailer;
pub mod providers;
#[cfg(feature = "local")]
mod storage;
#[cfg(feature = "local")]
pub mod testing;
#[cfg(any(
all(
feature = "preview",
not(all(target_family = "wasm", target_os = "unknown"))
),
all(
feature = "preview-axum",
not(all(target_family = "wasm", target_os = "unknown"))
),
all(
feature = "preview-actix",
not(all(target_family = "wasm", target_os = "unknown"))
)
))]
pub mod preview;
#[cfg(feature = "templates")]
mod template;
#[cfg(feature = "templates")]
pub use template::{EmailTemplate, EmailTemplateExt};
#[cfg(all(target_family = "wasm", target_os = "unknown"))]
use std::cell::RefCell;
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
use std::env;
use std::sync::Arc;
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
use parking_lot::RwLock;
pub use address::{Address, ToAddress};
pub use attachment::{Attachment, AttachmentType};
pub use client::EmailClient;
pub use config::*;
pub use email::{Email, PreparedEmail};
pub use error::MailError;
pub use interceptor::{Interceptor, InterceptorExt, WithInterceptor};
pub use mailer::{DeliveryResult, Mailer};
#[cfg(feature = "local")]
pub use storage::{MemoryStorage, Storage, StoredEmail};
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
static MAILER: RwLock<Option<Arc<dyn Mailer>>> = RwLock::new(None);
#[cfg(all(target_family = "wasm", target_os = "unknown"))]
thread_local! {
static MAILER: RefCell<Option<Arc<dyn Mailer>>> = RefCell::new(None);
}
#[cfg(feature = "local")]
#[deprecated(
since = "0.7.0",
note = "use LocalMailer::storage() and pass the storage to preview explicitly"
)]
pub fn local_storage() -> Option<Arc<MemoryStorage>> {
None
}
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
pub fn default_from() -> Option<Address> {
let email = env::var("EMAIL_FROM").ok()?;
match env::var("EMAIL_FROM_NAME").ok() {
Some(name) => Some(Address::with_name(name, email)),
None => Some(Address::new(email)),
}
}
#[cfg(all(target_family = "wasm", target_os = "unknown"))]
pub fn default_from() -> Option<Address> {
None
}
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
fn create_mailer_from_env() -> Result<Arc<dyn Mailer>, MailError> {
MailerConfig::from_env()?.into_mailer()
}
#[cfg(all(target_family = "wasm", target_os = "unknown"))]
fn create_mailer_from_env() -> Result<Arc<dyn Mailer>, MailError> {
Err(MailError::UnsupportedFeature(
"global environment auto-configuration is not supported on wasm32-unknown-unknown; use EmailClient::new, EmailClient::from_env_with, or configure_arc".into(),
))
}
fn get_mailer() -> Result<Arc<dyn Mailer>, MailError> {
if let Some(mailer) = configured_mailer() {
return Ok(mailer);
}
let mailer = create_mailer_from_env()?;
Ok(configure_if_missing(mailer))
}
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
fn configured_mailer() -> Option<Arc<dyn Mailer>> {
let guard = MAILER.read();
guard.as_ref().cloned()
}
#[cfg(all(target_family = "wasm", target_os = "unknown"))]
fn configured_mailer() -> Option<Arc<dyn Mailer>> {
MAILER.with(|slot| slot.borrow().as_ref().cloned())
}
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
fn configure_if_missing(mailer: Arc<dyn Mailer>) -> Arc<dyn Mailer> {
let mut guard = MAILER.write();
if guard.is_none() {
*guard = Some(Arc::clone(&mailer));
}
guard.as_ref().map_or(mailer, Arc::clone)
}
#[cfg(all(target_family = "wasm", target_os = "unknown"))]
fn configure_if_missing(mailer: Arc<dyn Mailer>) -> Arc<dyn Mailer> {
MAILER.with(|slot| {
let mut slot = slot.borrow_mut();
if slot.is_none() {
*slot = Some(Arc::clone(&mailer));
}
slot.as_ref().map_or(mailer, Arc::clone)
})
}
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
fn set_configured_mailer(mailer: Arc<dyn Mailer>) {
let mut guard = MAILER.write();
*guard = Some(mailer);
}
#[cfg(all(target_family = "wasm", target_os = "unknown"))]
fn set_configured_mailer(mailer: Arc<dyn Mailer>) {
MAILER.with(|slot| {
*slot.borrow_mut() = Some(mailer);
});
}
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
fn clear_configured_mailer() {
let mut guard = MAILER.write();
*guard = None;
}
#[cfg(all(target_family = "wasm", target_os = "unknown"))]
fn clear_configured_mailer() {
MAILER.with(|slot| {
*slot.borrow_mut() = None;
});
}
pub fn is_configured() -> bool {
MailerConfig::from_env().is_ok()
}
pub fn init() -> Result<(), MailError> {
if !is_configured() {
return Err(MailError::NotConfigured);
}
let _ = get_mailer()?;
Ok(())
}
pub async fn deliver(email: &Email) -> Result<DeliveryResult, MailError> {
let mailer = get_mailer()?;
EmailClient::new(mailer)
.with_optional_default_from(default_from())
.deliver(email.clone())
.await
}
pub async fn deliver_with<M: Mailer>(
email: &Email,
mailer: &M,
) -> Result<DeliveryResult, MailError> {
EmailClient::new(mailer)
.with_optional_default_from(default_from())
.deliver(email.clone())
.await
}
pub async fn deliver_many(emails: &[Email]) -> Result<Vec<DeliveryResult>, MailError> {
let mailer = get_mailer()?;
EmailClient::new(mailer)
.with_optional_default_from(default_from())
.deliver_many(emails.to_vec())
.await
}
pub fn configure<M: Mailer + 'static>(mailer: M) {
set_configured_mailer(Arc::new(mailer));
}
pub fn configure_arc(mailer: Arc<dyn Mailer>) {
set_configured_mailer(mailer);
}
pub fn reset() {
clear_configured_mailer();
}
pub fn mailer() -> Option<Arc<dyn Mailer>> {
configured_mailer()
}
pub mod prelude {
pub use crate::Address;
pub use crate::Attachment;
pub use crate::DeliveryResult;
pub use crate::Email;
pub use crate::EmailClient;
pub use crate::MailError;
pub use crate::Mailer;
pub use crate::PreparedEmail;
pub use crate::ToAddress;
pub use crate::{default_from, deliver, deliver_many, deliver_with, is_configured};
#[cfg(feature = "local")]
pub use crate::Storage;
}