mirror of
https://github.com/pimalaya/himalaya.git
synced 2026-06-17 13:17:55 +08:00
203 lines
5.9 KiB
Rust
203 lines
5.9 KiB
Rust
mod config;
|
|
mod editor;
|
|
mod email;
|
|
mod imap;
|
|
mod mailbox;
|
|
mod smtp;
|
|
mod table;
|
|
|
|
use clap::{App, AppSettings, Arg, SubCommand};
|
|
use std::{fmt, process::exit, result};
|
|
|
|
use crate::config::Config;
|
|
use crate::imap::ImapConnector;
|
|
use crate::table::DisplayTable;
|
|
|
|
#[derive(Debug)]
|
|
pub enum Error {
|
|
ConfigError(config::Error),
|
|
ImapError(imap::Error),
|
|
EditorError(editor::Error),
|
|
}
|
|
|
|
impl fmt::Display for Error {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
match self {
|
|
Error::ConfigError(err) => err.fmt(f),
|
|
Error::ImapError(err) => err.fmt(f),
|
|
Error::EditorError(err) => err.fmt(f),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<config::Error> for Error {
|
|
fn from(err: config::Error) -> Error {
|
|
Error::ConfigError(err)
|
|
}
|
|
}
|
|
|
|
impl From<imap::Error> for Error {
|
|
fn from(err: imap::Error) -> Error {
|
|
Error::ImapError(err)
|
|
}
|
|
}
|
|
|
|
impl From<editor::Error> for Error {
|
|
fn from(err: editor::Error) -> Error {
|
|
Error::EditorError(err)
|
|
}
|
|
}
|
|
|
|
// Result wrapper
|
|
|
|
type Result<T> = result::Result<T, Error>;
|
|
|
|
// Run
|
|
|
|
fn mailbox_arg() -> Arg<'static, 'static> {
|
|
Arg::with_name("mailbox")
|
|
.short("m")
|
|
.long("mailbox")
|
|
.help("Name of the targeted mailbox")
|
|
.value_name("STRING")
|
|
.default_value("INBOX")
|
|
}
|
|
|
|
fn uid_arg() -> Arg<'static, 'static> {
|
|
Arg::with_name("uid")
|
|
.help("UID of the targeted email")
|
|
.value_name("UID")
|
|
.required(true)
|
|
}
|
|
|
|
fn run() -> Result<()> {
|
|
let matches = App::new("Himalaya")
|
|
.version("0.1.0")
|
|
.about("📫 Minimalist CLI email client")
|
|
.author("soywod <clement.douin@posteo.net>")
|
|
.setting(AppSettings::ArgRequiredElseHelp)
|
|
.subcommand(SubCommand::with_name("list").about("Lists all available mailboxes"))
|
|
.subcommand(
|
|
SubCommand::with_name("search")
|
|
.about("Lists emails matching the given IMAP query")
|
|
.arg(mailbox_arg())
|
|
.arg(
|
|
Arg::with_name("query")
|
|
.help("IMAP query (see https://tools.ietf.org/html/rfc3501#section-6.4.4)")
|
|
.value_name("QUERY")
|
|
.multiple(true)
|
|
.required(true),
|
|
),
|
|
)
|
|
.subcommand(
|
|
SubCommand::with_name("read")
|
|
.about("Reads an email by its UID")
|
|
.arg(uid_arg())
|
|
.arg(mailbox_arg())
|
|
.arg(
|
|
Arg::with_name("mime-type")
|
|
.help("MIME type to use")
|
|
.short("t")
|
|
.long("mime-type")
|
|
.value_name("STRING")
|
|
.possible_values(&["text/plain", "text/html"])
|
|
.default_value("text/plain"),
|
|
),
|
|
)
|
|
.subcommand(SubCommand::with_name("write").about("Writes a new email"))
|
|
.subcommand(
|
|
SubCommand::with_name("forward")
|
|
.about("Forwards an email by its UID")
|
|
.arg(uid_arg())
|
|
.arg(mailbox_arg()),
|
|
)
|
|
.subcommand(
|
|
SubCommand::with_name("reply")
|
|
.about("Replies to an email by its UID")
|
|
.arg(uid_arg())
|
|
.arg(mailbox_arg())
|
|
.arg(
|
|
Arg::with_name("reply all")
|
|
.help("Replies to all recipients")
|
|
.short("a")
|
|
.long("all"),
|
|
),
|
|
)
|
|
.get_matches();
|
|
|
|
if let Some(_) = matches.subcommand_matches("list") {
|
|
let config = Config::new_from_file()?;
|
|
let mboxes = ImapConnector::new(config.imap)?
|
|
.list_mailboxes()?
|
|
.to_table();
|
|
|
|
println!("{}", mboxes);
|
|
}
|
|
|
|
if let Some(matches) = matches.subcommand_matches("search") {
|
|
let config = Config::new_from_file()?;
|
|
let mbox = matches.value_of("mailbox").unwrap();
|
|
|
|
if let Some(matches) = matches.values_of("query") {
|
|
let query = matches
|
|
.fold((false, vec![]), |(escape, mut cmds), cmd| {
|
|
match (cmd, escape) {
|
|
// Next command is an arg and needs to be escaped
|
|
("subject", _) | ("body", _) | ("text", _) => {
|
|
cmds.push(cmd.to_string());
|
|
(true, cmds)
|
|
}
|
|
// Escaped arg commands
|
|
(_, true) => {
|
|
cmds.push(format!("\"{}\"", cmd));
|
|
(false, cmds)
|
|
}
|
|
// Regular commands
|
|
(_, false) => {
|
|
cmds.push(cmd.to_string());
|
|
(false, cmds)
|
|
}
|
|
}
|
|
})
|
|
.1
|
|
.join(" ");
|
|
|
|
let emails = ImapConnector::new(config.imap)?
|
|
.read_emails(&mbox, &query)?
|
|
.to_table();
|
|
|
|
println!("{}", emails);
|
|
}
|
|
}
|
|
|
|
if let Some(matches) = matches.subcommand_matches("read") {
|
|
let config = Config::new_from_file()?;
|
|
let mbox = matches.value_of("mailbox").unwrap();
|
|
let uid = matches.value_of("uid").unwrap();
|
|
let mime = matches.value_of("mime-type").unwrap();
|
|
let body = ImapConnector::new(config.imap)?.read_email_body(&mbox, &uid, &mime)?;
|
|
|
|
println!("{}", body);
|
|
}
|
|
|
|
if let Some(_) = matches.subcommand_matches("write") {
|
|
let config = Config::new_from_file()?;
|
|
let draft = editor::open_with_new_template()?;
|
|
|
|
println!("Sending ...");
|
|
smtp::send(&config, draft.as_bytes());
|
|
println!("Done!");
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
// Main
|
|
|
|
fn main() {
|
|
if let Err(err) = run() {
|
|
eprintln!("Error {}", err);
|
|
exit(1);
|
|
}
|
|
}
|