aboutsummaryrefslogtreecommitdiffstats
path: root/src/cli.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/cli.rs')
-rw-r--r--src/cli.rs133
1 files changed, 133 insertions, 0 deletions
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<PathBuf>,
+
+ /// Account name from the config (default: $POSTA_ACCOUNT or `default_account`)
+ #[arg(long, short = 'a', global = true, value_name = "NAME")]
+ pub account: Option<String>,
+
+ /// 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<String>,
+ #[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<String>,
+ #[arg(long)]
+ cc: Vec<String>,
+ #[arg(long)]
+ subject: String,
+ #[arg(long)]
+ body: Option<String>,
+ #[arg(long)]
+ body_file: Option<PathBuf>,
+ /// 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<u32>,
+ #[arg(long, default_value = "INBOX")]
+ folder: String,
+ },
+ /// Move messages to another folder
+ Move {
+ #[arg(required = true)]
+ uids: Vec<u32>,
+ #[arg(long)]
+ to: String,
+ #[arg(long, default_value = "INBOX")]
+ folder: String,
+ },
+ /// Move messages to the trash folder
+ Delete {
+ #[arg(required = true)]
+ uids: Vec<u32>,
+ #[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,
+}