use clap::{Args, Parser};
use colored::*;
use std::process::exit;
use vomit::Options;
const VERSION: &str = env!("CARGO_PKG_VERSION");
const AUTHORS: &str = env!("CARGO_PKG_AUTHORS");
#[derive(Parser)]
#[clap(name = "vmt", version = VERSION, author = AUTHORS, max_term_width = 80)]
struct Opts {
#[command(flatten)]
target: Target,
#[arg(short, long)]
multiple: bool,
#[arg(short, long)]
list: bool,
#[arg(short, long)]
empty: bool,
#[arg(short, long)]
unread: bool,
#[arg(long = "am")]
marker: bool,
#[clap(short, long, value_parser)]
config: Option<String>,
#[clap(short, long, value_parser)]
account: Option<String>,
#[clap(value_parser)]
pub path: Option<String>,
}
#[derive(Args)]
#[group(multiple = false)]
struct Target {
#[arg(short = 'B', long)]
mailbox: bool,
#[arg(short = 'A', long)]
attachment: bool,
#[arg(short = 'P', long)]
part: bool,
}
const E_NON_UTF8: &str = "path contains non-UTF8 character";
const E_KEEP_TMP: &str = "error persisting tmp file";
fn pick_part(path: Option<&str>, o: &Options) -> Result<(), vomit::Error> {
for part in vomit::pick_part(path, o)? {
let (_, path) = part.as_tmp_file()?.keep().expect(E_KEEP_TMP);
println!("{}", path.to_str().expect(E_NON_UTF8));
}
Ok(())
}
fn pick_attachment(path: Option<&str>, o: &Options) -> Result<(), vomit::Error> {
for attachment in vomit::pick_attachment(path, o)? {
let (_, path) = attachment.as_tmp_file()?.keep().expect(E_KEEP_TMP);
println!("{}", path.to_str().expect(E_NON_UTF8));
}
Ok(())
}
fn pick_mail(path: Option<&str>, o: &Options) -> Result<(), vomit::Error> {
for mail in vomit::pick_mail(path, o)? {
println!("{}", mail.path.to_str().expect(E_NON_UTF8));
}
Ok(())
}
fn pick_mailbox(path: Option<&str>, o: &Options) -> Result<(), vomit::Error> {
for m in vomit::pick_mailbox(path, o)? {
println!("{}", m.path.to_str().expect(E_NON_UTF8));
}
Ok(())
}
fn ls_part(path: Option<&str>, o: &Options) -> Result<(), vomit::Error> {
for p in vomit::ls_part(path, o)? {
println!(
"{:<7.7} {:>10.10} {:<30.30} {}",
p.index(),
p.human_size().green(),
p.mimetype().bright_blue(),
p.name.as_deref().unwrap_or_default().bright_white(),
);
}
Ok(())
}
fn ls_attachment(path: Option<&str>, o: &Options) -> Result<(), vomit::Error> {
for a in vomit::ls_attachment(path, o)? {
println!(
"{:>10.10} {} {}",
a.part.human_size().green(),
a.name,
format!("({})", a.mimetype()).bright_blue(),
);
}
Ok(())
}
fn ls_mail(path: Option<&str>, o: &Options) -> Result<(), vomit::Error> {
for m in vomit::ls_mail(path, o)? {
let date = match m.date {
None => format!("{:<16.16}", "<no date>").white().dimmed(),
Some(d) => format!("{:<16.16}", d.format("%Y-%m-%d %H:%M")).bright_blue(),
};
println!(
"{} {:<30.30} {}",
date,
m.from.green(),
if m.unread {
m.subject.bold()
} else {
m.subject.normal()
}
);
}
Ok(())
}
fn ls_mailbox(path: Option<&str>, o: &Options) -> Result<(), vomit::Error> {
for m in vomit::ls_mailbox(path, o)? {
let total = format!("{:>5} ", m.total).bright_blue();
let unread = m
.unread
.map(|u| format!("{:>5} ", u))
.unwrap_or_default()
.green();
println!(
"{}{}{}",
total,
unread,
m.name
.unwrap_or_else(|| m.path.to_string_lossy().into_owned()),
);
}
Ok(())
}
fn main() {
let opts: Opts = Opts::parse();
let o = vomit::Options {
account: opts.account,
multiple: opts.multiple,
date: true,
attachment_marker: opts.marker || opts.target.attachment,
attachments_only: opts.target.attachment,
empty: opts.empty,
mailbox_unread: opts.unread,
config: opts.config,
};
let path = opts.path.as_deref();
let r = if opts.list {
if opts.target.part {
ls_part(path, &o)
} else if opts.target.attachment {
ls_attachment(path, &o)
} else if opts.target.mailbox {
ls_mailbox(path, &o)
} else {
ls_mail(path, &o)
}
} else if opts.target.part {
pick_part(path, &o)
} else if opts.target.attachment {
pick_attachment(path, &o)
} else if opts.target.mailbox {
pick_mailbox(path, &o)
} else {
pick_mail(path, &o)
};
if let Err(e) = r {
eprintln!("error: {}", e);
exit(1);
}
}