himalaya 1.0.0-beta

CLI to manage emails
use anyhow::{Context, Result};
use email::{
    account::config::AccountConfig,
    email::utils::{local_draft_path, remove_local_draft},
    flag::{Flag, Flags},
    folder::DRAFTS,
};
use log::debug;
use mml::MmlCompilerBuilder;
use process::SingleCmd;
use std::{env, fs};

use crate::{
    backend::Backend,
    printer::Printer,
    ui::choice::{self, PostEditChoice, PreEditChoice},
};

pub async fn open_with_tpl(tpl: String) -> Result<String> {
    let path = local_draft_path();

    debug!("create draft");
    fs::write(&path, tpl.as_bytes()).context(format!("cannot write local draft at {:?}", path))?;

    debug!("open editor");
    let editor = env::var("EDITOR").context("cannot get editor from env var")?;
    SingleCmd::from(format!("{editor} {}", &path.to_string_lossy()))
        .with_output_piped(false)
        .run()
        .await
        .context("cannot launch editor")?;

    debug!("read draft");
    let content =
        fs::read_to_string(&path).context(format!("cannot read local draft at {:?}", path))?;

    Ok(content)
}

pub async fn open_with_local_draft() -> Result<String> {
    let path = local_draft_path();
    let content =
        fs::read_to_string(&path).context(format!("cannot read local draft at {:?}", path))?;
    open_with_tpl(content).await
}

pub async fn edit_tpl_with_editor<P: Printer>(
    #[allow(unused)] config: &AccountConfig,
    printer: &mut P,
    backend: &Backend,
    mut tpl: String,
) -> Result<()> {
    let draft = local_draft_path();
    if draft.exists() {
        loop {
            match choice::pre_edit() {
                Ok(choice) => match choice {
                    PreEditChoice::Edit => {
                        tpl = open_with_local_draft().await?;
                        break;
                    }
                    PreEditChoice::Discard => {
                        tpl = open_with_tpl(tpl).await?;
                        break;
                    }
                    PreEditChoice::Quit => return Ok(()),
                },
                Err(err) => {
                    println!("{}", err);
                    continue;
                }
            }
        }
    } else {
        tpl = open_with_tpl(tpl).await?;
    }

    loop {
        match choice::post_edit() {
            Ok(PostEditChoice::Send) => {
                printer.print_log("Sending email…")?;

                #[allow(unused_mut)]
                let mut compiler = MmlCompilerBuilder::new();

                #[cfg(feature = "pgp")]
                compiler.set_some_pgp(config.pgp.clone());

                let email = compiler.build(tpl.as_str())?.compile().await?.into_vec()?;

                backend.send_raw_message(&email).await?;

                remove_local_draft()?;
                printer.print("Done!")?;
                break;
            }
            Ok(PostEditChoice::Edit) => {
                tpl = open_with_tpl(tpl).await?;
                continue;
            }
            Ok(PostEditChoice::LocalDraft) => {
                printer.print("Email successfully saved locally")?;
                break;
            }
            Ok(PostEditChoice::RemoteDraft) => {
                #[allow(unused_mut)]
                let mut compiler = MmlCompilerBuilder::new();

                #[cfg(feature = "pgp")]
                compiler.set_some_pgp(config.pgp.clone());

                let email = compiler.build(tpl.as_str())?.compile().await?.into_vec()?;

                backend
                    .add_raw_message_with_flags(
                        DRAFTS,
                        &email,
                        &Flags::from_iter([Flag::Seen, Flag::Draft]),
                    )
                    .await?;
                remove_local_draft()?;
                printer.print("Email successfully saved to drafts")?;
                break;
            }
            Ok(PostEditChoice::Discard) => {
                remove_local_draft()?;
                break;
            }
            Err(err) => {
                println!("{}", err);
                continue;
            }
        }
    }

    Ok(())
}