himalaya 0.6.1

Command-line interface for email management.
Documentation
use anyhow::{Context, Result};
use std::env;
use url::Url;

use himalaya::{
    account, compl,
    config::{self, DeserializedConfig},
    email, flag, folder,
    output::{self, OutputFmt},
    printer::StdoutPrinter,
    tpl,
};
use himalaya_lib::{BackendBuilder, BackendConfig, ImapBackend, SenderBuilder};

#[cfg(feature = "imap-backend")]
use himalaya::imap;

fn create_app<'a>() -> clap::App<'a, 'a> {
    let app = clap::App::new(env!("CARGO_PKG_NAME"))
        .version(env!("CARGO_PKG_VERSION"))
        .about(env!("CARGO_PKG_DESCRIPTION"))
        .author(env!("CARGO_PKG_AUTHORS"))
        .global_setting(clap::AppSettings::GlobalVersion)
        .arg(&config::args::arg())
        .arg(&account::args::arg())
        .args(&output::args::args())
        .arg(folder::args::source_arg())
        .subcommands(compl::args::subcmds())
        .subcommands(account::args::subcmds())
        .subcommands(folder::args::subcmds())
        .subcommands(email::args::subcmds());

    #[cfg(feature = "imap-backend")]
    let app = app.subcommands(imap::args::subcmds());

    app
}

#[allow(clippy::single_match)]
fn main() -> Result<()> {
    let default_env_filter = env_logger::DEFAULT_FILTER_ENV;
    env_logger::init_from_env(env_logger::Env::default().filter_or(default_env_filter, "off"));

    // Check mailto command BEFORE app initialization.
    let raw_args: Vec<String> = env::args().collect();
    if raw_args.len() > 1 && raw_args[1].starts_with("mailto:") {
        let url = Url::parse(&raw_args[1])?;
        let config = DeserializedConfig::from_opt_path(None)?;
        let (account_config, backend_config) = config.to_configs(None)?;
        let mut backend = BackendBuilder::build(&account_config, &backend_config)?;
        let mut sender = SenderBuilder::build(&account_config)?;
        let mut printer = StdoutPrinter::from_fmt(OutputFmt::Plain);

        return email::handlers::mailto(
            &url,
            &account_config,
            &mut printer,
            backend.as_mut(),
            sender.as_mut(),
        );
    }

    let app = create_app();
    let m = app.get_matches();

    // Check completion command BEFORE entities and services initialization.
    // Related issue: https://github.com/soywod/himalaya/issues/115.
    match compl::args::matches(&m)? {
        Some(compl::args::Command::Generate(shell)) => {
            return compl::handlers::generate(create_app(), shell);
        }
        _ => (),
    }

    // Init entities and services.
    let config = DeserializedConfig::from_opt_path(config::args::parse_arg(&m))?;
    let (account_config, backend_config) = config.to_configs(account::args::parse_arg(&m))?;
    let folder = account_config.folder_alias(folder::args::parse_source_arg(&m))?;

    // Check IMAP commands.
    #[cfg(feature = "imap-backend")]
    if let BackendConfig::Imap(imap_config) = backend_config {
        // FIXME: find a way to downcast `backend` instead.
        let mut imap = ImapBackend::new(&account_config, imap_config);
        match imap::args::matches(&m)? {
            Some(imap::args::Command::Notify(keepalive)) => {
                return imap::handlers::notify(keepalive, &folder, &mut imap);
            }
            Some(imap::args::Command::Watch(keepalive)) => {
                return imap::handlers::watch(keepalive, &folder, &mut imap);
            }
            _ => (),
        }
    }

    let mut backend = BackendBuilder::build(&account_config, &backend_config)?;
    let mut sender = SenderBuilder::build(&account_config)?;
    let mut printer = StdoutPrinter::from_opt_str(m.value_of("output"))?;

    // Check account commands.
    match account::args::matches(&m)? {
        Some(account::args::Cmd::List(max_width)) => {
            return account::handlers::list(max_width, &account_config, &config, &mut printer);
        }
        _ => (),
    }

    // Check folder commands.
    match folder::args::matches(&m)? {
        Some(folder::args::Cmd::List(max_width)) => {
            return folder::handlers::list(
                max_width,
                &account_config,
                &mut printer,
                backend.as_mut(),
            );
        }
        _ => (),
    }

    // Check message commands.
    match email::args::matches(&m)? {
        Some(email::args::Cmd::Attachments(seq)) => {
            return email::handlers::attachments(
                seq,
                &folder,
                &account_config,
                &mut printer,
                backend.as_mut(),
            );
        }
        Some(email::args::Cmd::Copy(seq, mbox_dst)) => {
            return email::handlers::copy(seq, &folder, mbox_dst, &mut printer, backend.as_mut());
        }
        Some(email::args::Cmd::Delete(seq)) => {
            return email::handlers::delete(seq, &folder, &mut printer, backend.as_mut());
        }
        Some(email::args::Cmd::Forward(seq, attachment_paths, encrypt)) => {
            return email::handlers::forward(
                seq,
                attachment_paths,
                encrypt,
                &folder,
                &account_config,
                &mut printer,
                backend.as_mut(),
                sender.as_mut(),
            );
        }
        Some(email::args::Cmd::List(max_width, page_size, page)) => {
            return email::handlers::list(
                max_width,
                page_size,
                page,
                &folder,
                &account_config,
                &mut printer,
                backend.as_mut(),
            );
        }
        Some(email::args::Cmd::Move(seq, mbox_dst)) => {
            return email::handlers::move_(seq, &folder, mbox_dst, &mut printer, backend.as_mut());
        }
        Some(email::args::Cmd::Read(seq, text_mime, sanitize, raw, headers)) => {
            return email::handlers::read(
                seq,
                text_mime,
                sanitize,
                raw,
                headers,
                &folder,
                &account_config,
                &mut printer,
                backend.as_mut(),
            );
        }
        Some(email::args::Cmd::Reply(seq, all, attachment_paths, encrypt)) => {
            return email::handlers::reply(
                seq,
                all,
                attachment_paths,
                encrypt,
                &folder,
                &account_config,
                &mut printer,
                backend.as_mut(),
                sender.as_mut(),
            );
        }
        Some(email::args::Cmd::Save(raw_msg)) => {
            return email::handlers::save(&folder, raw_msg, &mut printer, backend.as_mut());
        }
        Some(email::args::Cmd::Search(query, max_width, page_size, page)) => {
            return email::handlers::search(
                query,
                max_width,
                page_size,
                page,
                &folder,
                &account_config,
                &mut printer,
                backend.as_mut(),
            );
        }
        Some(email::args::Cmd::Sort(criteria, query, max_width, page_size, page)) => {
            return email::handlers::sort(
                criteria,
                query,
                max_width,
                page_size,
                page,
                &folder,
                &account_config,
                &mut printer,
                backend.as_mut(),
            );
        }
        Some(email::args::Cmd::Send(raw_msg)) => {
            return email::handlers::send(
                raw_msg,
                &account_config,
                &mut printer,
                backend.as_mut(),
                sender.as_mut(),
            );
        }
        Some(email::args::Cmd::Write(tpl, atts, encrypt)) => {
            return email::handlers::write(
                tpl,
                atts,
                encrypt,
                &account_config,
                &mut printer,
                backend.as_mut(),
                sender.as_mut(),
            );
        }
        Some(email::args::Cmd::Flag(m)) => match m {
            Some(flag::args::Cmd::Set(seq_range, ref flags)) => {
                return flag::handlers::set(
                    seq_range,
                    flags,
                    &folder,
                    &mut printer,
                    backend.as_mut(),
                );
            }
            Some(flag::args::Cmd::Add(seq_range, ref flags)) => {
                return flag::handlers::add(
                    seq_range,
                    flags,
                    &folder,
                    &mut printer,
                    backend.as_mut(),
                );
            }
            Some(flag::args::Cmd::Del(seq_range, ref flags)) => {
                return flag::handlers::remove(
                    seq_range,
                    flags,
                    &folder,
                    &mut printer,
                    backend.as_mut(),
                );
            }
            _ => (),
        },
        Some(email::args::Cmd::Tpl(m)) => match m {
            Some(tpl::args::Cmd::New(tpl)) => {
                return tpl::handlers::new(tpl, &account_config, &mut printer);
            }
            Some(tpl::args::Cmd::Reply(seq, all, tpl)) => {
                return tpl::handlers::reply(
                    seq,
                    all,
                    tpl,
                    &folder,
                    &account_config,
                    &mut printer,
                    backend.as_mut(),
                );
            }
            Some(tpl::args::Cmd::Forward(seq, tpl)) => {
                return tpl::handlers::forward(
                    seq,
                    tpl,
                    &folder,
                    &account_config,
                    &mut printer,
                    backend.as_mut(),
                );
            }
            Some(tpl::args::Cmd::Save(atts, tpl)) => {
                return tpl::handlers::save(
                    &folder,
                    &account_config,
                    atts,
                    tpl,
                    &mut printer,
                    backend.as_mut(),
                );
            }
            Some(tpl::args::Cmd::Send(atts, tpl)) => {
                return tpl::handlers::send(
                    &folder,
                    atts,
                    tpl,
                    &mut printer,
                    backend.as_mut(),
                    sender.as_mut(),
                );
            }
            _ => (),
        },
        _ => (),
    }

    backend.as_mut().disconnect().context("cannot disconnect")
}