1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
//! Create a dynamic mailer trait object depending on runtime mailer configuration.
//!
//! Microsoft Outlook and SMTP mailer variants are available.
//!
//! Example:
//!
//! ```no_run
//! // Outlook configuration, e.g. from command line arguments or environment variables.
//! let mailer_configuration = MailerConfiguration::Outlook {
//!     tenant: "<Microsoft Identity service tenant>",
//!     app_guid: "<OAuth2 app GUID>",
//!     secret: "<OAuth2 app secret>"
//! };
//!
//! // Alternative: SMTP configuration, e.g. from command line arguments or environment variables.
//! let mailer_configuration = MailerConfiguration::Outlook {
//!     host: "smtp.example.com",
//!     port: 465,
//!     invalid_certs: SmtpInvalidCertsPolicy::Deny,
//!     user: "<username>",
//!     password: "<password>"
//! };
//!
//! // Create a `Box<dyn Mailer>`.
//! // The implementation is `Send` and `Sync` and may be store e.g. as part of your server state.
//! let mailer = new_mailer(mailer_configuration).await?;
//!
//! // Build a message using the re-exported `mail_builder::MessageBuilder'.
//! // For blazingly fast rendering of beautiful HTML mail, I recommend combining `askama` with `mrml`.
//! let message = MessageBuilder::new()
//!     .from(("From Name", "from@example.com"))
//!     .to("to@example.com")
//!     .subject("Subject")
//!     .text_body("Mail body");
//!
//! // Send the message using the implementation-agnostic `dyn Mailer`.
//! mailer.send_mail(&message).await?;
//! ```

use std::sync::Arc;

pub use secrecy::Secret;

#[cfg(feature = "tracing")]
use tracing::instrument;

pub use async_mailer_core::mail_send::mail_builder::MessageBuilder;
pub use async_mailer_core::mail_send::smtp::message::Message;
pub use async_mailer_core::Mailer;

#[cfg(feature = "outlook")]
pub use async_mailer_outlook::*;

#[cfg(feature = "smtp")]
pub use async_mailer_smtp::*;

/// Mailer configuration helper, which can be used with clap.
#[derive(Clone, Debug)]
pub enum MailerConfiguration {
    #[cfg(feature = "outlook")]
    Outlook {
        /// Microsoft Active Directory tenant
        tenant: String,

        /// Outlook OAuth 2.0 application ID
        app_guid: String,

        /// Outlook OAuth 2.0 client secret
        secret: Secret<String>,
    },

    #[cfg(feature = "smtp")]
    Smtp {
        /// SMTP host
        host: String,

        /// SMTP port
        port: u16,

        /// Allow or deny invalid (self-signed) certificates. Set to 'deny' except on local test environments
        invalid_certs: SmtpInvalidCertsPolicy,

        /// SMTP user
        user: String,

        /// SMTP password
        password: Secret<String>,
    },
}

/// Create a new dynamic mailer trait object depending on the provided [`MailerConfiguration`].
#[cfg_attr(feature = "tracing", instrument)]
pub async fn new_mailer(
    mailer_configuration: MailerConfiguration,
) -> Result<Arc<dyn Mailer>, MailerError> {
    match mailer_configuration {
        // Outlook mailer - used in production
        #[cfg(feature = "outlook")]
        MailerConfiguration::Outlook {
            tenant,
            app_guid,
            secret,
        } => Ok(Arc::new(
            OutlookMailer::new(tenant, app_guid, secret).await?,
        )),

        // SMTP mailer - used during development and testing
        #[cfg(feature = "smtp")]
        MailerConfiguration::Smtp {
            host,
            port,
            invalid_certs,
            user,
            password,
        } => Ok(Arc::new(SmtpMailer::new(
            host,
            port,
            invalid_certs,
            user,
            password,
        ))),
    }
}