29cc961
use config::mailbaby::{MailbabyAuthScheme, MailbabyChannel, MailbabyConfig};
use mailbaby::grpc;
use mailbaby::rest;
use mailbaby::{Auth, BatchResponse, Email, SendResponse};
use crate::error::ExportError;
#[derive(Clone)]
pub enum EmailClient {
Rest(rest::MailBabyClient),
Grpc(grpc::GrpcClient),
}
#[derive(Clone)]
pub struct AppEmail {
config: MailbabyConfig,
client: Option<EmailClient>,
}
impl AppEmail {
pub fn new(config: MailbabyConfig) -> Result<Self, ExportError> {
config.validate().map_err(ExportError::InvalidConfig)?;
if !config.enabled() {
return Ok(Self {
config,
client: None,
});
}
match config.channel() {
MailbabyChannel::Rest => {
let client = build_rest(&config)?;
Ok(Self {
config,
client: Some(EmailClient::Rest(client)),
})
}
MailbabyChannel::Grpc => Err(ExportError::InvalidConfig(
"grpc channel requires AppEmail::connect".to_string(),
)),
}
}
pub async fn connect(config: MailbabyConfig) -> Result<Self, ExportError> {
config.validate().map_err(ExportError::InvalidConfig)?;
if !config.enabled() {
return Ok(Self {
config,
client: None,
});
}
let client = match config.channel() {
MailbabyChannel::Rest => EmailClient::Rest(build_rest(&config)?),
MailbabyChannel::Grpc => EmailClient::Grpc(build_grpc(&config).await?),
};
Ok(Self {
config,
client: Some(client),
})
}
pub fn config(&self) -> &MailbabyConfig {
&self.config
}
pub fn enabled(&self) -> bool {
self.client.is_some()
}
pub fn channel(&self) -> MailbabyChannel {
self.config.channel()
}
pub fn client(&self) -> Result<&EmailClient, ExportError> {
self.client.as_ref().ok_or(ExportError::Disabled)
}
pub fn apply_defaults(&self, mut email: Email) -> Email {
if email.account.is_empty() {
email.account = self.config.account().unwrap_or_default().to_string();
}
if email.from.is_empty() {
email.from = self.config.from_address().unwrap_or_default().to_string();
}
if email.from_name.is_empty() {
email.from_name = self.config.from_name().unwrap_or_default().to_string();
}
if email.reply_to.is_empty() {
email.reply_to = self.config.reply_to().unwrap_or_default().to_string();
}
email
}
pub async fn send(&self, email: &Email) -> Result<SendResponse, ExportError> {
self.client()?.send(email).await
}
pub async fn send_queued(&self, email: &Email) -> Result<SendResponse, ExportError> {
self.client()?.send_queued(email).await
}
pub async fn send_batch(
&self,
emails: &[Email],
queue: bool,
) -> Result<BatchResponse, ExportError> {
self.client()?.send_batch(emails, queue).await
}
pub async fn ping(&self) -> Result<String, ExportError> {
self.client()?.ping().await
}
pub async fn live(&self) -> Result<(), ExportError> {
self.client()?.live().await
}
pub async fn ready(&self) -> Result<(), ExportError> {
self.client()?.ready().await
}
}
impl EmailClient {
pub async fn send(&self, email: &Email) -> Result<SendResponse, ExportError> {
match self {
EmailClient::Rest(client) => Ok(client.send(email).await?),
EmailClient::Grpc(client) => Ok(client.send(email).await?),
}
}
pub async fn send_queued(&self, email: &Email) -> Result<SendResponse, ExportError> {
match self {
EmailClient::Rest(client) => Ok(client.send_async(email).await?),
EmailClient::Grpc(client) => Ok(client.send_async(email).await?),
}
}
pub async fn send_batch(
&self,
emails: &[Email],
queue: bool,
) -> Result<BatchResponse, ExportError> {
match self {
EmailClient::Rest(client) => Ok(client.send_batch(emails, queue).await?),
EmailClient::Grpc(client) => Ok(client.send_batch(emails, queue).await?),
}
}
pub async fn ping(&self) -> Result<String, ExportError> {
match self {
EmailClient::Grpc(client) => Ok(client.ping().await?),
EmailClient::Rest(_) => Err(ExportError::Unsupported(
"ping is only available over the grpc channel".to_string(),
)),
}
}
pub async fn live(&self) -> Result<(), ExportError> {
match self {
EmailClient::Rest(client) => Ok(client.live().await?),
EmailClient::Grpc(_) => Err(ExportError::Unsupported(
"the live probe is only available over the rest channel".to_string(),
)),
}
}
pub async fn ready(&self) -> Result<(), ExportError> {
match self {
EmailClient::Rest(client) => Ok(client.ready().await?),
EmailClient::Grpc(_) => Err(ExportError::Unsupported(
"the ready probe is only available over the rest channel".to_string(),
)),
}
}
}
fn build_rest(config: &MailbabyConfig) -> Result<rest::MailBabyClient, ExportError> {
let mut builder = rest::MailBabyClient::builder(config.url()).timeout(config.request_timeout());
if let Some(auth) = build_auth(config)? {
builder = builder.auth(auth);
}
Ok(builder.build()?)
}
async fn build_grpc(config: &MailbabyConfig) -> Result<grpc::GrpcClient, ExportError> {
let mut builder = grpc::GrpcClient::builder(config.url()).timeout(config.request_timeout());
if let Some(auth) = build_auth(config)? {
builder = builder.auth(auth);
}
Ok(builder.build().await?)
}
fn build_auth(config: &MailbabyConfig) -> Result<Option<Auth>, ExportError> {
let Some(key) = config.api_key() else {
return Ok(None);
};
let auth = match config.auth_scheme() {
MailbabyAuthScheme::Header => {
let auth = Auth::api_key(key);
match config.api_key_header() {
Some(header) => auth.with_header(header),
None => auth,
}
}
MailbabyAuthScheme::Bearer => Auth::api_key(key).bearer(),
MailbabyAuthScheme::Query => Auth::api_key(key).query(),
};
Ok(Some(auth))
}