1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
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,
}
|