mirror of
https://github.com/pimalaya/himalaya.git
synced 2026-06-15 11:27:53 +08:00
docs: fix shared-API command names + plural hidden aliases
* docs: fix shared-API command names, document search, draft and read semantics
The shared commands are singular since 3a1a981b but the README examples
still used the plural forms. Also document the query DSL limits (SORT
capability required, see #698), the save-as-draft recipe, and the
missing Reading messages section.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(cli): accept plural shared commands as hidden aliases
Naming rule from #701: singular public API with hidden plural aliases,
plural struct fields (the mailbox.alias config key is now backed by an
`aliases` field, `aliases` kept as config alternative).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Refs: #701 #703
This commit is contained in:
@@ -192,17 +192,19 @@ Accounts can be (re)configured later with `himalaya account configure <name>`. T
|
||||
Backend-agnostic commands operate on the account's first configured backend, or the one selected with `-b/--backend`:
|
||||
|
||||
```
|
||||
himalaya mailboxes list
|
||||
himalaya envelopes list -m INBOX --page 2
|
||||
himalaya envelopes search from alice and after 2026-01-01 order by date desc
|
||||
himalaya flags add -m INBOX --flag seen 1:3,5
|
||||
himalaya messages copy --from INBOX --to Archives 42
|
||||
himalaya attachments download -m INBOX 42
|
||||
himalaya mailbox list
|
||||
himalaya envelope list -m INBOX --page 2
|
||||
himalaya envelope search from alice and after 2026-01-01 order by date desc
|
||||
himalaya flag add -m INBOX --flag seen 1:3,5
|
||||
himalaya message copy --from INBOX --to Archives 42
|
||||
himalaya attachment download -m INBOX 42
|
||||
```
|
||||
|
||||
When the `inbox` alias is configured under `[mailbox.alias]`, `-m/--mailbox` becomes optional: shared commands fall back to that id. With `[mailbox.alias] inbox = "INBOX"`, the calls above shorten to `envelopes list --page 2`, `flags add --flag seen 1:3,5`, etc.
|
||||
When the `inbox` alias is configured under `[mailbox.alias]`, `-m/--mailbox` becomes optional: shared commands fall back to that id. With `[mailbox.alias] inbox = "INBOX"`, the calls above shorten to `envelope list --page 2`, `flag add --flag seen 1:3,5`, etc.
|
||||
|
||||
`envelopes list` is plain pagination, ordered by date descending. To filter or sort, use `envelopes search` with a trailing query covering `date`, `after`, `from`, `to`, `subject`, `body`, `flag` conditions (combined with `and`, `or`, `not`, grouped with parens) and an `order by date|from|to|subject [asc|desc]` sort chain. Date clauses target the `Date:` header (sent-at) on every backend.
|
||||
`envelope list` is plain pagination, ordered by date descending. To filter or sort, use `envelope search` with a trailing query covering `date`, `after`, `from`, `to`, `subject`, `body`, `flag` conditions (combined with `and`, `or`, `not`, grouped with parens) and an `order by date|from|to|subject [asc|desc]` sort chain. Date clauses target the `Date:` header (sent-at) on every backend. The full grammar lives in `himalaya envelope search --help`, which is the source of truth for the query DSL.
|
||||
|
||||
The query DSL is himalaya's own and compiles to each backend's native search: provider-specific operators (Gmail's `in:`/`label:` syntax, `X-GM-RAW`, …) are not supported. On IMAP the search currently runs server-side as `UID SORT`, so it requires the `SORT` capability — servers without it (notably Gmail) reject the command for now (see [#698](https://github.com/pimalaya/himalaya/issues/698)).
|
||||
|
||||
The shared surface is a strict least-common-denominator subset across IMAP, JMAP and Maildir. Operations that do not generalize (mailbox roles, attribute flags, JMAP-specific queries…) live under the protocol-specific subcommands.
|
||||
|
||||
@@ -211,9 +213,9 @@ The shared surface is a strict least-common-denominator subset across IMAP, JMAP
|
||||
Each backend exposes its full native API under its own subgroup:
|
||||
|
||||
```
|
||||
himalaya imap mailboxes select INBOX
|
||||
himalaya imap mailboxes status INBOX
|
||||
himalaya imap mailboxes subscribe INBOX
|
||||
himalaya imap mailbox select INBOX
|
||||
himalaya imap mailbox status INBOX
|
||||
himalaya imap mailbox subscribe INBOX
|
||||
|
||||
himalaya jmap mailboxes query --role drafts
|
||||
himalaya jmap identity get
|
||||
@@ -229,25 +231,42 @@ The `-b/--backend` flag is only consumed by the shared commands; protocol subcom
|
||||
|
||||
### Composing messages
|
||||
|
||||
The built-in `messages compose` / `reply` / `forward` commands cover simple cases via CLI flags:
|
||||
The built-in `message compose` / `reply` / `forward` commands cover simple cases via CLI flags:
|
||||
|
||||
```
|
||||
himalaya messages compose --from me@example.org --to you@example.org \
|
||||
himalaya message compose --from me@example.org --to you@example.org \
|
||||
--subject "Hello" --body "Hi!" --send
|
||||
```
|
||||
|
||||
For richer composition (multipart MIME, MML directives, signing/encryption, editor-driven workflows), chain a standalone composer such as [mml](https://github.com/pimalaya/mml) into `messages send` / `messages add` through a tempfile or bash/zsh process substitution:
|
||||
For richer composition (multipart MIME, MML directives, signing/encryption, editor-driven workflows), chain a standalone composer such as [mml](https://github.com/pimalaya/mml) into `message send` / `message add` through a tempfile or bash/zsh process substitution:
|
||||
|
||||
```sh
|
||||
# Explicit tempfile, works in plain POSIX sh
|
||||
mml compose /tmp/draft.eml && himalaya messages send /tmp/draft.eml
|
||||
mml compose /tmp/draft.eml && himalaya message send /tmp/draft.eml
|
||||
|
||||
# Bash / zsh process substitution, single command, no tempfile
|
||||
mml compose >(himalaya messages send)
|
||||
himalaya messages read 42 | mml reply >(himalaya messages send)
|
||||
mml compose >(himalaya message send)
|
||||
himalaya message read 42 | mml reply >(himalaya message send)
|
||||
```
|
||||
|
||||
The path-arg or process-substitution forms keep the composer's stdout connected to the terminal, so any `$EDITOR` it spawns sees a real tty. The bare-pipe form (`mml compose | himalaya messages send`) hangs because the editor inherits a pipe on its stdout.
|
||||
The path-arg or process-substitution forms keep the composer's stdout connected to the terminal, so any `$EDITOR` it spawns sees a real tty. The bare-pipe form (`mml compose | himalaya message send`) hangs because the editor inherits a pipe on its stdout.
|
||||
|
||||
A prepared RFC 5322 file can also be staged as a draft instead of sent right away — handy for "compose, review in another client, then send" workflows:
|
||||
|
||||
```sh
|
||||
himalaya message add -m drafts --flag draft < message.eml # save as draft
|
||||
himalaya message send --save sent < message.eml # send + keep a copy
|
||||
```
|
||||
|
||||
Both `-m`/`--save` values are resolved through the account's `[mailbox.alias]` map.
|
||||
|
||||
### Reading messages
|
||||
|
||||
`himalaya message read <ID>` renders headers and text bodies; `--raw` dumps the original RFC 5322 bytes; `--json` emits the parsed message. A few behaviours worth knowing, especially when scripting:
|
||||
|
||||
- Reading is side-effect-free: messages are fetched with `BODY.PEEK`, so `message read` never sets `\Seen`. Mark explicitly with `flag add --flag seen <ID>`.
|
||||
- Ids are per-mailbox (IMAP UID, JMAP email id or Maildir filename id): the same message gets a new id when copied or moved. The `message-id` field exposed in `--json` envelope output is the stable cross-mailbox key.
|
||||
- Every command accepts `--json`; envelope listings serialize as `{"envelopes": [{"id", "message-id", "flags": [{"raw", "iana"}], "subject", "from": [{"name", "email"}], "to", "date", "size", "has-attachment"}]}`.
|
||||
|
||||
### Re-using sessions
|
||||
|
||||
@@ -314,7 +333,7 @@ Himalaya CLI is one of several front-ends to the Pimalaya libraries:
|
||||
Use `--log-level <level>` (alias `--log`) where `<level>` is one of `off`, `error`, `warn`, `info`, `debug`, `trace`:
|
||||
|
||||
```
|
||||
himalaya --log trace mailboxes list
|
||||
himalaya --log trace mailbox list
|
||||
```
|
||||
|
||||
The `RUST_LOG` environment variable is consulted when `--log` is not passed, and supports per-target filters (see the [`env_logger` documentation](https://docs.rs/env_logger/latest/env_logger/#enabling-logging)). `RUST_BACKTRACE=1` enables full error backtraces.
|
||||
@@ -322,13 +341,13 @@ Himalaya CLI is one of several front-ends to the Pimalaya libraries:
|
||||
Logs are written to `stderr`, so they can be redirected easily to a file:
|
||||
|
||||
```
|
||||
himalaya --log trace mailboxes list 2>/tmp/himalaya.log
|
||||
himalaya --log trace mailbox list 2>/tmp/himalaya.log
|
||||
```
|
||||
|
||||
You can also send logs straight to a file via `--log-file <path>`:
|
||||
|
||||
```
|
||||
himalaya --log trace --log-file /tmp/himalaya.log mailboxes list
|
||||
himalaya --log trace --log-file /tmp/himalaya.log mailbox list
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
@@ -360,7 +360,7 @@ impl From<Config> for Account {
|
||||
mailboxes_list_table: config.mailbox.list.table,
|
||||
attachments_list_table: config.attachment.list.table,
|
||||
|
||||
mailbox_alias: lowercase_alias_keys(config.mailbox.alias),
|
||||
mailbox_alias: lowercase_alias_keys(config.mailbox.aliases),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -380,7 +380,7 @@ impl From<AccountConfig> for Account {
|
||||
mailboxes_list_table: config.mailbox.list.table,
|
||||
attachments_list_table: config.attachment.list.table,
|
||||
|
||||
mailbox_alias: lowercase_alias_keys(config.mailbox.alias),
|
||||
mailbox_alias: lowercase_alias_keys(config.mailbox.aliases),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -391,13 +391,13 @@ mod tests {
|
||||
use crate::config::MailboxConfig;
|
||||
|
||||
fn account_with_aliases(pairs: &[(&str, &str)]) -> Account {
|
||||
let alias = pairs
|
||||
let aliases = pairs
|
||||
.iter()
|
||||
.map(|(k, v)| ((*k).to_string(), (*v).to_string()))
|
||||
.collect();
|
||||
let config = Config {
|
||||
mailbox: MailboxConfig {
|
||||
alias,
|
||||
aliases,
|
||||
..MailboxConfig::default()
|
||||
},
|
||||
..Config::default()
|
||||
|
||||
+5
-5
@@ -71,15 +71,15 @@ pub struct Cli {
|
||||
pub enum Command {
|
||||
// --- Shared API
|
||||
//
|
||||
#[command(subcommand, visible_alias = "mbox")]
|
||||
#[command(subcommand, visible_alias = "mbox", alias = "mailboxes")]
|
||||
Mailbox(MailboxCommand),
|
||||
#[command(subcommand)]
|
||||
#[command(subcommand, alias = "envelopes")]
|
||||
Envelope(EnvelopeCommand),
|
||||
#[command(subcommand)]
|
||||
#[command(subcommand, alias = "flags")]
|
||||
Flag(FlagCommand),
|
||||
#[command(subcommand, visible_alias = "msg")]
|
||||
#[command(subcommand, visible_alias = "msg", alias = "messages")]
|
||||
Message(MessageCommand),
|
||||
#[command(subcommand)]
|
||||
#[command(subcommand, alias = "attachments")]
|
||||
Attachment(AttachmentCommand),
|
||||
|
||||
// --- Protocol-specific APIs
|
||||
|
||||
+2
-2
@@ -133,8 +133,8 @@ pub struct EnvelopeConfig {
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
|
||||
pub struct MailboxConfig {
|
||||
#[serde(default, alias = "aliases")]
|
||||
pub alias: HashMap<String, String>,
|
||||
#[serde(default, rename = "alias", alias = "aliases")]
|
||||
pub aliases: HashMap<String, String>,
|
||||
|
||||
#[serde(default)]
|
||||
pub list: MailboxListConfig,
|
||||
|
||||
Reference in New Issue
Block a user