From d9738b4be2add82ff955f061c79316b0dc91768f Mon Sep 17 00:00:00 2001 From: Elvis Claros Castro Date: Sat, 26 Sep 2026 18:47:40 -0300 Subject: posta: generic IMAP/SMTP mail CLI with JSON output --- src/cli.rs | 133 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 src/cli.rs (limited to 'src/cli.rs') diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 0000000..920f7bf --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,133 @@ +//! Command-line interface definition. + +use std::path::PathBuf; + +use clap::{Parser, Subcommand, ValueEnum}; + +#[derive(Parser)] +#[command( + name = "posta", + version, + about = "A tiny, generic IMAP/SMTP mail CLI", + long_about = "posta — manage any IMAP/SMTP mailbox from your terminal.\n\ + Accounts live in a TOML config file; every command prints JSON \ + by default so it plays nicely with jq and scripts." +)] +pub struct Cli { + /// Config file (default: $POSTA_CONFIG or ~/.config/posta/config.toml) + #[arg(long, global = true, value_name = "FILE")] + pub config: Option, + + /// Account name from the config (default: $POSTA_ACCOUNT or `default_account`) + #[arg(long, short = 'a', global = true, value_name = "NAME")] + pub account: Option, + + /// Output format + #[arg(long, global = true, value_enum, default_value_t = Format::Json)] + pub format: Format, + + #[command(subcommand)] + pub cmd: Cmd, +} + +#[derive(Clone, Copy, PartialEq, Eq, ValueEnum)] +pub enum Format { + Json, + Table, +} + +#[derive(Subcommand)] +pub enum Cmd { + /// List the accounts defined in the config file + Accounts, + /// List mailbox folders + Folders, + /// Message and unread counts for a folder + Status { + #[arg(long, default_value = "INBOX")] + folder: String, + }, + /// List messages, newest first + List { + #[arg(long, default_value = "INBOX")] + folder: String, + /// Only unread messages (shorthand for --query UNSEEN) + #[arg(long)] + unread: bool, + /// Raw IMAP SEARCH query, e.g. 'FROM bank SINCE 1-Jul-2026' + #[arg(long)] + query: Option, + #[arg(long, default_value_t = 20)] + max: usize, + }, + /// Show a message by UID + Get { + uid: u32, + #[arg(long, default_value = "INBOX")] + folder: String, + /// Print the raw RFC 822 source + #[arg(long)] + raw: bool, + /// Prefer the text/html part over text/plain + #[arg(long)] + html: bool, + /// Mark the message as read after fetching it + #[arg(long)] + mark_read: bool, + }, + /// Send a message (body from --body, --body-file, or stdin) + Send { + #[arg(long, required = true)] + to: Vec, + #[arg(long)] + cc: Vec, + #[arg(long)] + subject: String, + #[arg(long)] + body: Option, + #[arg(long)] + body_file: Option, + /// Skip saving a copy to the sent folder + #[arg(long)] + no_record: bool, + }, + /// Add or remove message flags + Mark { + #[arg(value_enum)] + state: MarkState, + #[arg(required = true)] + uids: Vec, + #[arg(long, default_value = "INBOX")] + folder: String, + }, + /// Move messages to another folder + Move { + #[arg(required = true)] + uids: Vec, + #[arg(long)] + to: String, + #[arg(long, default_value = "INBOX")] + folder: String, + }, + /// Move messages to the trash folder + Delete { + #[arg(required = true)] + uids: Vec, + #[arg(long, default_value = "INBOX")] + folder: String, + }, + /// Run an IMAP SEARCH query and print matching UIDs + Search { + query: String, + #[arg(long, default_value = "INBOX")] + folder: String, + }, +} + +#[derive(Clone, Copy, ValueEnum)] +pub enum MarkState { + Read, + Unread, + Flagged, + Unflagged, +} -- cgit v1.2.3