use std::ops::{Deref, DerefMut};
use anyhow::{Result, anyhow};
use io_gmail::v1::client::{GmailClientStd as Inner, GmailClientStdConnectOptions};
use pimalaya_config::secret::SecretResolver;
use secrecy::{ExposeSecret, SecretString};
use crate::{
account::context::Account,
config::{AccountConfig, Config, GmailAuthConfig, GmailConfig, ProxyConfig},
email::mailbox::{MailboxIndex, MailboxRole},
};
pub struct GmailClient {
inner: Inner,
label_index: Option<MailboxIndex>,
}
impl GmailClient {
pub fn new(config: GmailConfig) -> Result<Self> {
let mut resolver = SecretResolver::new();
let token = gmail_token(config.auth, &mut resolver)?;
let options = GmailClientStdConnectOptions {
tls: config.tls.into_tls(config.alpn),
proxy: ProxyConfig::resolve(config.proxy, &mut resolver)?,
user_id: config.user_id,
};
let inner = Inner::connect(token.expose_secret(), options)?;
Ok(Self {
inner,
label_index: None,
})
}
pub fn resolve_mailbox_id(&mut self, mailbox: &str) -> Result<String> {
self.label_index()?.resolve(mailbox)
}
pub fn role_mailbox_id(&mut self, role: &MailboxRole) -> Result<Option<String>> {
self.label_index()?.with_role(role)
}
fn label_index(&mut self) -> Result<&MailboxIndex> {
if self.label_index.is_none() {
let labels = self.list_mailboxes(false)?;
self.label_index = Some(MailboxIndex(labels));
}
Ok(self.label_index.get_or_insert_default())
}
}
impl Deref for GmailClient {
type Target = Inner;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl DerefMut for GmailClient {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
pub fn build_gmail_client(
config: Config,
name: String,
mut account_config: AccountConfig,
) -> Result<(Account, GmailClient)> {
let gmail_config = account_config
.gmail
.take()
.ok_or_else(|| anyhow!("Gmail config is missing for account `{name}`"))?;
let account = Account::from(config).merge(Account::from(account_config));
let client = GmailClient::new(gmail_config)?;
Ok((account, client))
}
pub fn gmail_token(config: GmailAuthConfig, resolver: &mut SecretResolver) -> Result<SecretString> {
Ok(resolver.resolve(config.token)?)
}