himalaya 0.7.0

Command-line interface for email management.
Documentation
use anyhow::{Context, Result};
use himalaya_lib::{
    email::{local_draft_path, remove_local_draft},
    AccountConfig, Backend, CompilerBuilder, Flag, Flags, Sender, Tpl,
};
use log::debug;
use std::{env, fs, process::Command};

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

pub fn open_with_tpl(tpl: Tpl) -> Result<Tpl> {
    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");
    Command::new(env::var("EDITOR").context(r#"cannot find "$EDITOR" env var"#)?)
        .arg(&path)
        .status()
        .context("cannot launch editor")?;

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

    Ok(Tpl::from(content))
}

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

pub fn edit_tpl_with_editor<P: Printer, B: Backend + ?Sized, S: Sender + ?Sized>(
    config: &AccountConfig,
    printer: &mut P,
    backend: &mut B,
    sender: &mut S,
    mut tpl: Tpl,
) -> 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()?;
                        break;
                    }
                    PreEditChoice::Discard => {
                        tpl = open_with_tpl(tpl)?;
                        break;
                    }
                    PreEditChoice::Quit => return Ok(()),
                },
                Err(err) => {
                    println!("{}", err);
                    continue;
                }
            }
        }
    } else {
        tpl = open_with_tpl(tpl)?;
    }

    loop {
        match choice::post_edit() {
            Ok(PostEditChoice::Send) => {
                printer.print_log("Sending email…")?;
                let email = tpl.compile(
                    CompilerBuilder::default()
                        .some_pgp_sign_cmd(config.email_writing_sign_cmd.as_ref())
                        .some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.as_ref()),
                )?;
                sender.send(&email)?;
                let sent_folder = config.sent_folder_alias()?;
                printer.print_log(format!("Adding email to the {} folder…", sent_folder))?;
                backend.add_email(&sent_folder, &email, &Flags::default())?;
                remove_local_draft()?;
                printer.print("Done!")?;
                break;
            }
            Ok(PostEditChoice::Edit) => {
                tpl = open_with_tpl(tpl)?;
                continue;
            }
            Ok(PostEditChoice::LocalDraft) => {
                printer.print("Email successfully saved locally")?;
                break;
            }
            Ok(PostEditChoice::RemoteDraft) => {
                let draft_folder = config.folder_alias("drafts")?;
                let email = tpl.compile(
                    CompilerBuilder::default()
                        .some_pgp_sign_cmd(config.email_writing_sign_cmd.as_ref())
                        .some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.as_ref()),
                )?;
                backend.add_email(&draft_folder, &email, &Flags::from_iter([Flag::Draft]))?;
                remove_local_draft()?;
                printer.print(format!("Email successfully saved to {}", draft_folder))?;
                break;
            }
            Ok(PostEditChoice::Discard) => {
                remove_local_draft()?;
                break;
            }
            Err(err) => {
                println!("{}", err);
                continue;
            }
        }
    }

    Ok(())
}