pimalaya-tui 0.2.0

Collection of crossterm widgets shared accross Pimalaya projects
use std::{
    fmt,
    path::{Path, PathBuf},
};

use super::config::*;
use crate::{
    terminal::{config::TomlConfig, print, prompt, wizard},
    Result,
};

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum BackendKind {
    #[cfg(feature = "imap")]
    Imap,
    #[cfg(feature = "maildir")]
    Maildir,
    #[cfg(feature = "notmuch")]
    Notmuch,
    None,
}

impl fmt::Display for BackendKind {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "{}",
            match self {
                #[cfg(feature = "imap")]
                Self::Imap => "IMAP",
                #[cfg(feature = "maildir")]
                Self::Maildir => "Maildir",
                #[cfg(feature = "notmuch")]
                Self::Notmuch => "Notmuch",
                Self::None => "None",
            }
        )
    }
}

const DEFAULT_BACKEND_KINDS: &[BackendKind] = &[
    #[cfg(feature = "imap")]
    BackendKind::Imap,
    #[cfg(feature = "maildir")]
    BackendKind::Maildir,
    #[cfg(feature = "notmuch")]
    BackendKind::Notmuch,
    BackendKind::None,
];

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum SendingBackendKind {
    #[cfg(feature = "smtp")]
    Smtp,
    #[cfg(feature = "sendmail")]
    Sendmail,
    None,
}

impl fmt::Display for SendingBackendKind {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "{}",
            match self {
                #[cfg(feature = "smtp")]
                Self::Smtp => "SMTP",
                #[cfg(feature = "sendmail")]
                Self::Sendmail => "Sendmail",
                Self::None => "None",
            }
        )
    }
}

const SEND_MESSAGE_BACKEND_KINDS: &[SendingBackendKind] = &[
    #[cfg(feature = "smtp")]
    SendingBackendKind::Smtp,
    #[cfg(feature = "sendmail")]
    SendingBackendKind::Sendmail,
    SendingBackendKind::None,
];

pub async fn edit(
    path: impl AsRef<Path>,
    mut config: HimalayaTomlConfig,
    account_name: Option<&str>,
    mut account_config: HimalayaTomlAccountConfig,
) -> Result<HimalayaTomlConfig> {
    match account_name.as_ref() {
        Some(name) => print::section(format!("Configuring your account {name}")),
        None => print::section("Configuring your default account"),
    };

    let default_email = Some(account_config.email.as_str()).filter(|email| !email.is_empty());
    let email = prompt::email("Email address:", default_email)?;

    account_config.email = email.to_string();

    let default =
        account_name.is_none() || prompt::bool("Should this account be the default one?", false)?;

    if default {
        config
            .accounts
            .iter_mut()
            .for_each(|(_, config)| config.default = None)
    }

    account_config.default = Some(default);

    let autoconfig_email = account_config.email.to_owned();
    let autoconfig =
        tokio::spawn(async move { email::autoconfig::from_addr(&autoconfig_email).await.ok() });

    let default_account_name = match account_name {
        Some(name) => name,
        None => email
            .domain()
            .split_once('.')
            .map(|domain| domain.0)
            .unwrap_or(email.domain()),
    };

    let account_name = prompt::text("Account name:", Some(default_account_name))?;

    let default_display_name = account_config
        .display_name
        .as_deref()
        .or(Some(email.local_part()));

    account_config.display_name = Some(prompt::text("Full display name:", default_display_name)?);

    let default_downloads_dir = Some(PathBuf::from("~/Downloads"));
    let default_downloads_dir = account_config
        .downloads_dir
        .as_deref()
        .or(default_downloads_dir.as_deref());

    account_config.downloads_dir =
        Some(prompt::path("Downloads directory:", default_downloads_dir)?);

    let autoconfig = autoconfig.await?;
    let autoconfig = autoconfig.as_ref();

    if let Some(config) = autoconfig {
        if config.is_gmail() {
            println!();
            print::warn("Warning: Google passwords cannot be used directly, see:");
            print::warn("https://github.com/pimalaya/himalaya?tab=readme-ov-file#configuration");
            println!();
        }
    }

    let backend = prompt::item("Default backend:", &*DEFAULT_BACKEND_KINDS, None)?;

    match backend {
        BackendKind::None => {
            account_config.backend = Some(Backend::None);
        }
        #[cfg(feature = "imap")]
        BackendKind::Imap => {
            let config = wizard::imap::start(&account_name, &email, autoconfig).await?;
            account_config.backend = Some(Backend::Imap(config));
        }
        #[cfg(feature = "maildir")]
        BackendKind::Maildir => {
            let config = wizard::maildir::start(&account_name)?;
            account_config.backend = Some(Backend::Maildir(config));
        }
        #[cfg(feature = "notmuch")]
        BackendKind::Notmuch => {
            let config = wizard::notmuch::start()?;
            account_config.backend = Some(Backend::Notmuch(config));
        }
    }

    let backend = prompt::item(
        "Backend for sending messages:",
        &*SEND_MESSAGE_BACKEND_KINDS,
        None,
    )?;

    match backend {
        SendingBackendKind::None => {
            account_config.message = Some(MessageConfig {
                send: Some(SendMessageConfig {
                    backend: Some(SendingBackend::None),
                    ..Default::default()
                }),
                ..Default::default()
            });
        }
        #[cfg(feature = "smtp")]
        SendingBackendKind::Smtp => {
            let config = wizard::smtp::start(&account_name, &email, autoconfig).await?;
            account_config.message = Some(MessageConfig {
                send: Some(SendMessageConfig {
                    backend: Some(SendingBackend::Smtp(config)),
                    ..Default::default()
                }),
                ..Default::default()
            });
        }
        #[cfg(feature = "sendmail")]
        SendingBackendKind::Sendmail => {
            let config = wizard::sendmail::start()?;
            account_config.message = Some(MessageConfig {
                send: Some(SendMessageConfig {
                    backend: Some(SendingBackend::Sendmail(config)),
                    ..Default::default()
                }),
                ..Default::default()
            });
        }
    };

    config.accounts.insert(account_name, account_config);
    config.write(path.as_ref())?;

    Ok(config)
}