prepare v0.7.1

Those commits have been stashed then applied due to merge issue:

add ability to sync specific folders f7585eb
add expunge command 1c0b7fb
update readme links to documentation e1c8cf5
fix other doc typos 9c27165
reword title of the project 1eaac7d
reword title of the project bis a7419d6
fix broken links in changelog 26b0311
prepare v0.7.1 2b5e58e
This commit is contained in:
Clément DOUIN 2023-02-14 16:47:02 +01:00
parent 694173b534
commit 509b09d533
No known key found for this signature in database
GPG key ID: 353E4A18EE0FAB72
15 changed files with 397 additions and 322 deletions

View file

@ -7,41 +7,65 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
## [0.7.1] - 2023-02-14
### Added
- Added command `folders expunge` that deletes all emails marked for
deletion.
### Changed
- Changed the location of the
[documentation](https://pimalaya.org/himalaya/docs/).
### Fixed
- Fixed broken links in README.md.
### Removed
- Removed the `maildir-backend` cargo feature, it is now included by
default.
- Removed issues section on GitHub, now issues need to be opened by
sending an email at
[~soywod/pimalaya@todo.sr.ht](mailto:~soywod/pimalaya@todo.sr.ht).
## [0.7.0] - 2023-02-08 ## [0.7.0] - 2023-02-08
### Added ### Added
* Added offline support with the `account sync` command to synchronize - Added offline support with the `account sync` command to synchronize
a backend to a local Maildir backend [#342]. a backend to a local Maildir backend [#342].
* Added the flag `--disable-cache` to not use the local Maildir - Added the flag `--disable-cache` to not use the local Maildir
backend. backend.
* Added the email composer (from its own - Added the email composer (from its own
[repository](https://git.sr.ht/~soywod/mime-msg-builder)) [#341]. [repository](https://git.sr.ht/~soywod/mime-msg-builder)) [#341].
* Added Musl builds to releases [#356]. - Added Musl builds to releases [#356].
* Added `himalaya man` command to generate man page [#419]. - Added `himalaya man` command to generate man page [#419].
### Changed ### Changed
* Made commands `read`, `attachments`, `flags`, `copy`, `move`, - Made commands `read`, `attachments`, `flags`, `copy`, `move`,
`delete` accept multiple ids. `delete` accept multiple ids.
* Flipped arguments `ids` and `folder` for commands `copy` and `move` - Flipped arguments `ids` and `folder` for commands `copy` and `move`
in order the folder not to be considered as an id. in order the folder not to be considered as an id.
### Fixed ### Fixed
* Fixed missing folder aliases [#430]. - Fixed missing folder aliases [#430].
### Removed ### Removed
* Removed the `-a|--attachment` argument from `write`, `reply` and - Removed the `-a|--attachment` argument from `write`, `reply` and
`forward` commands. Instead you can attach documents directly from `forward` commands. Instead you can attach documents directly from
the template using the syntax `<#part the template using the syntax `<#part
filename=/path/to/you/document.ext>`. filename=/path/to/you/document.ext>`.
* Removed the `-e|--encrypt` flag from `write`, `reply` and `forward` - Removed the `-e|--encrypt` flag from `write`, `reply` and `forward`
commands. Instead you can encrypt and sign parts directly from the commands. Instead you can encrypt and sign parts directly from the
template using the syntax `<#part type=text/plain encrypt=command template using the syntax `<#part type=text/plain encrypt=command
sign=command>Hello!<#/part>`. sign=command>Hello!<#/part>`.
* Removed the `-l|--log-level` option, use instead the `RUST_LOG` - Removed the `-l|--log-level` option, use instead the `RUST_LOG`
environment variable (see the environment variable (see the
[wiki](https://github.com/soywod/himalaya/wiki/Tips:debug-and-logs)) [wiki](https://github.com/soywod/himalaya/wiki/Tips:debug-and-logs))
@ -49,434 +73,434 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added ### Added
* Added `-s|--sanitize` flag for the `read` command. - Added `-s|--sanitize` flag for the `read` command.
### Changed ### Changed
* Changed the behaviour of the `-t|--mime-type` argument of the `read` - Changed the behaviour of the `-t|--mime-type` argument of the `read`
command. It is less strict now: if no part is found for the given command. It is less strict now: if no part is found for the given
MIME type, it will fallback to the other one. For example, giving MIME type, it will fallback to the other one. For example, giving
`-t html` will show in priority HTML parts, but if none of them are `-t html` will show in priority HTML parts, but if none of them are
found it will show plain parts instead (and vice versa). found it will show plain parts instead (and vice versa).
* Sanitization is not done by default when using the `read` command, - Sanitization is not done by default when using the `read` command,
the flag `-s|--sanitize` needs to be explicitly provided. the flag `-s|--sanitize` needs to be explicitly provided.
### Fixed ### Fixed
* Fixed empty text bodies when reading html part on plain text email - Fixed empty text bodies when reading html part on plain text email
[#352]. [#352].
## [0.6.0] - 2022-10-10 ## [0.6.0] - 2022-10-10
### Changed ### Changed
* Separated the CLI from the lib module [#340]. - Separated the CLI from the lib module [#340].
The source code has been splitted into subrepositories: The source code has been splitted into subrepositories:
* The email logic has been extracted from the CLI and placed in a - The email logic has been extracted from the CLI and placed in a
lib on [sourcehut](https://git.sr.ht/~soywod/himalaya-lib) lib on [sourcehut](https://git.sr.ht/~soywod/himalaya-lib)
* The vim plugin is now in a dedicated repository on - The vim plugin is now in a dedicated repository on
[sourcehut](https://git.sr.ht/~soywod/himalaya-vim) as well [sourcehut](https://git.sr.ht/~soywod/himalaya-vim) as well
* This repository only contains the CLI source code (it was not - This repository only contains the CLI source code (it was not
possible to move it to sourcehut because of cross platform builds) possible to move it to sourcehut because of cross platform builds)
* [**BREAKING**] Renamed `-m|--mailbox` to `-f|--folder` - [**BREAKING**] Renamed `-m|--mailbox` to `-f|--folder`
* [**BREAKING**] Refactored config system [#344]. - [**BREAKING**] Refactored config system [#344].
The configuration has been rethought in order to be more intuitive The configuration has been rethought in order to be more intuitive
and structured. Here are the breaking changes for the global config: and structured. Here are the breaking changes for the global config:
* `name` becomes `display-name` and is not mandatory anymore - `name` becomes `display-name` and is not mandatory anymore
* `signature-delimiter` becomes `signature-delim` - `signature-delimiter` becomes `signature-delim`
* `default-page-size` has been moved to `folder-listing-page-size` - `default-page-size` has been moved to `folder-listing-page-size`
and `email-listing-page-size` and `email-listing-page-size`
* `notify-cmd`, `notify-query` and `watch-cmds` have been removed - `notify-cmd`, `notify-query` and `watch-cmds` have been removed
from the global config (available in account config only) from the global config (available in account config only)
* `folder-aliases` has been added to the global config (previously - `folder-aliases` has been added to the global config (previously
known as `mailboxes` from the account config) known as `mailboxes` from the account config)
* `email-reading-headers`, `email-reading-format`, - `email-reading-headers`, `email-reading-format`,
`email-reading-decrypt-cmd`, `email-writing-encrypt-cmd` and `email-reading-decrypt-cmd`, `email-writing-encrypt-cmd` and
`email-hooks` have been added `email-hooks` have been added
The account config inherits the same breaking changes from the The account config inherits the same breaking changes from the
global config, plus: global config, plus:
* `imap-*` requires `backend = "imap"` - `imap-*` requires `backend = "imap"`
* `maildir-*` requires `backend = "maildir"` - `maildir-*` requires `backend = "maildir"`
* `notmuch-*` requires `backend = "notmuch"` - `notmuch-*` requires `backend = "notmuch"`
* `smtp-*` requires `sender = "smtp"` - `smtp-*` requires `sender = "smtp"`
* `sendmail-*` requires `sender = "sendmail"` - `sendmail-*` requires `sender = "sendmail"`
* `pgp-encrypt-cmd` becomes `email-writing-encrypt-cmd` - `pgp-encrypt-cmd` becomes `email-writing-encrypt-cmd`
* `pgp-decrypt-cmd` becomes `email-reading-decrypt-cmd` - `pgp-decrypt-cmd` becomes `email-reading-decrypt-cmd`
* `mailboxes` becomes `folder-aliases` - `mailboxes` becomes `folder-aliases`
* `hooks` becomes `email-hooks` - `hooks` becomes `email-hooks`
* `maildir-dir` becomes `maildir-root-dir` - `maildir-dir` becomes `maildir-root-dir`
* `notmuch-database-dir` becomes `notmuch-db-path` - `notmuch-database-dir` becomes `notmuch-db-path`
## [0.5.10] - 2022-03-20 ## [0.5.10] - 2022-03-20
### Fixed ### Fixed
* Flag commands [#334] - Flag commands [#334]
* Windows build [#346] - Windows build [#346]
## [0.5.9] - 2022-03-12 ## [0.5.9] - 2022-03-12
### Added ### Added
* SMTP pre-send hook [#178] - SMTP pre-send hook [#178]
* Customize headers to show at the top of a read message [#338] - Customize headers to show at the top of a read message [#338]
### Changed ### Changed
* Improve `attachments` command [#281] - Improve `attachments` command [#281]
### Fixed ### Fixed
* `In-Reply-To` not set properly when replying to a message [#323] - `In-Reply-To` not set properly when replying to a message [#323]
* `Cc` missing or invalid when replying to a message [#324] - `Cc` missing or invalid when replying to a message [#324]
* Notmuch backend hangs [#329] - Notmuch backend hangs [#329]
* Maildir e2e tests [#335] - Maildir e2e tests [#335]
* JSON API for listings [#331] - JSON API for listings [#331]
## [0.5.8] - 2022-03-04 ## [0.5.8] - 2022-03-04
### Added ### Added
* Flowed format support [#206] - Flowed format support [#206]
* List accounts command [#244] - List accounts command [#244]
* One cargo feature per backend [#318] - One cargo feature per backend [#318]
### Changed ### Changed
* Vim doc about mailbox pickers [#298] - Vim doc about mailbox pickers [#298]
### Fixed ### Fixed
* Some emojis break the table layout [#300] - Some emojis break the table layout [#300]
* Bad sender and date in reply and forward template [#321] - Bad sender and date in reply and forward template [#321]
## [0.5.7] - 2022-03-01 ## [0.5.7] - 2022-03-01
### Added ### Added
* Notmuch support [#57] - Notmuch support [#57]
### Fixed ### Fixed
* Build failure due to `imap` version [#303] - Build failure due to `imap` version [#303]
* No tilde expansion in `maildir-dir` [#305] - No tilde expansion in `maildir-dir` [#305]
* Unknown command SORT [#308] - Unknown command SORT [#308]
### Changed ### Changed
* [**BREAKING**] Replace `inbox-folder`, `sent-folder` and `draft-folder` by a generic hashmap `mailboxes` - [**BREAKING**] Replace `inbox-folder`, `sent-folder` and `draft-folder` by a generic hashmap `mailboxes`
* Display short envelopes id for `maildir` and `notmuch` backends [#309] - Display short envelopes id for `maildir` and `notmuch` backends [#309]
## [0.5.6] - 2022-02-22 ## [0.5.6] - 2022-02-22
### Added ### Added
* Sort command [#34] - Sort command [#34]
* Maildir support [#43] - Maildir support [#43]
### Fixed ### Fixed
* Suffix to downloaded attachments with same name [#204] - Suffix to downloaded attachments with same name [#204]
## [0.5.5] - 2022-02-08 ## [0.5.5] - 2022-02-08
### Added ### Added
* [Contributing guide](https://github.com/soywod/himalaya/blob/master/CONTRIBUTING.md) [#256] - [Contributing guide](https://github.com/soywod/himalaya/blob/master/CONTRIBUTING.md) [#256]
* Notify query config option [#289] - Notify query config option [#289]
* End-to-end encryption [#54] - End-to-end encryption [#54]
### Fixed ### Fixed
* Multiple recipients issue [#288] - Multiple recipients issue [#288]
* Cannot parse address [#227] - Cannot parse address [#227]
## [0.5.4] - 2022-02-05 ## [0.5.4] - 2022-02-05
### Fixed ### Fixed
* Add attachments with save and send commands [#47] [#259] - Add attachments with save and send commands [#47] [#259]
* Invalid sequence set [#276] - Invalid sequence set [#276]
## [0.5.3] - 2022-02-03 ## [0.5.3] - 2022-02-03
### Added ### Added
* Activate rust-imap logs when trace mode is enabled - Activate rust-imap logs when trace mode is enabled
* Set up cargo deployment - Set up cargo deployment
## [0.5.2] - 2022-02-02 ## [0.5.2] - 2022-02-02
### Fixed ### Fixed
* Blur in list msg screenshot [#181] - Blur in list msg screenshot [#181]
* Make inbox, sent and drafts folders customizable [#172] - Make inbox, sent and drafts folders customizable [#172]
* Vim plugin get focused msg id [#268] - Vim plugin get focused msg id [#268]
* Nix run issue [#272] - Nix run issue [#272]
* Range not displayed when fetch fails [#276] - Range not displayed when fetch fails [#276]
* Blank lines and spaces in `text/plain` parts [#280] - Blank lines and spaces in `text/plain` parts [#280]
* Watch command [#271] - Watch command [#271]
* Mailbox telescope.nvim preview [#249] - Mailbox telescope.nvim preview [#249]
### Removed ### Removed
* The wiki git submodule [#273] - The wiki git submodule [#273]
## [0.5.1] - 2021-10-24 ## [0.5.1] - 2021-10-24
### Added ### Added
* Disable color feature [#185] - Disable color feature [#185]
* `--max-width|-w` argument to restrict listing table width [#220] - `--max-width|-w` argument to restrict listing table width [#220]
### Fixed ### Fixed
* Error when receiving notification from `notify` command [#228] - Error when receiving notification from `notify` command [#228]
### Changed ### Changed
* Remove error when empty subject [#229] - Remove error when empty subject [#229]
* Vim plugin does not render anymore the msg by itself, it uses the one available from the CLI [#220] - Vim plugin does not render anymore the msg by itself, it uses the one available from the CLI [#220]
## [0.5.0] - 2021-10-10 ## [0.5.0] - 2021-10-10
### Added ### Added
* Mailto support [#162] - Mailto support [#162]
* Remove previous signature when replying/forwarding a message [#193] - Remove previous signature when replying/forwarding a message [#193]
* Config option `signature-delimiter` to customize the signature delimiter (default to `-- \n`) [[#114](https://github.com/soywod/himalaya/pull/114)] - Config option `signature-delimiter` to customize the signature delimiter (default to `-- \n`) [[#114](https://github.com/soywod/himalaya/pull/114)]
* Expand tilde and env vars for `downloads-dir` and `signature` [#102] - Expand tilde and env vars for `downloads-dir` and `signature` [#102]
### Changed ### Changed
* [**BREAKING**] Folder structure, message management, JSON API and Vim plugin [#199] - [**BREAKING**] Folder structure, message management, JSON API and Vim plugin [#199]
* Pagination for list and search cmd starts from 1 instead of 0 [#186] - Pagination for list and search cmd starts from 1 instead of 0 [#186]
* Errors management with `anyhow` [#152] - Errors management with `anyhow` [#152]
### Fixed ### Fixed
* Panic on flags command [#190] - Panic on flags command [#190]
* Make more use of serde [#153] - Make more use of serde [#153]
* Write message vim plugin [#196] - Write message vim plugin [#196]
* Invalid encoding when sending message [#205] - Invalid encoding when sending message [#205]
* Pagination reset current account [#215] - Pagination reset current account [#215]
* New/reply/forward from Vim plugin since Tpl refactor [#176] - New/reply/forward from Vim plugin since Tpl refactor [#176]
## [0.4.0] - 2021-06-03 ## [0.4.0] - 2021-06-03
### Added ### Added
* Add ability to change account in with the Vim plugin [#91] - Add ability to change account in with the Vim plugin [#91]
* Add possibility to make Himalaya default email app [#160] [[#161](https://github.com/soywod/himalaya/pull/161)] - Add possibility to make Himalaya default email app [#160] [[#161](https://github.com/soywod/himalaya/pull/161)]
### Changed ### Changed
* [**BREAKING**] Short version of reply `--all` arg is now `-A` to - [**BREAKING**] Short version of reply `--all` arg is now `-A` to
avoid conflicts with `--attachment|-a` avoid conflicts with `--attachment|-a`
* Template management [#80] - Template management [#80]
### Fixed ### Fixed
* `\Seen` flag when moving a message - `\Seen` flag when moving a message
* Attachments arg for reply and forward commands [#109] - Attachments arg for reply and forward commands [#109]
* Vim doc [#117] - Vim doc [#117]
### Removed ### Removed
* `Content-Type` from templates [#146] - `Content-Type` from templates [#146]
## [0.3.2] - 2021-05-08 ## [0.3.2] - 2021-05-08
### Added ### Added
* Mailbox attributes [#134] - Mailbox attributes [#134]
* Wiki entry about new messages counter [#121] - Wiki entry about new messages counter [#121]
* Copy/move/delete a message in vim [#95] - Copy/move/delete a message in vim [#95]
### Changed ### Changed
* Get signature from file [#135] - Get signature from file [#135]
* [**BREAKING**] Split `idle` command into two commands: - [**BREAKING**] Split `idle` command into two commands:
* `notify`: Runs `notify-cmd` when a new message arrives to the server - `notify`: Runs `notify-cmd` when a new message arrives to the server
* `watch`: Runs `watch-cmds` when any change occurs on the server - `watch`: Runs `watch-cmds` when any change occurs on the server
### Removed ### Removed
* `.exe` extension from release binaries [#144] - `.exe` extension from release binaries [#144]
## [0.3.1] - 2021-05-04 ## [0.3.1] - 2021-05-04
### Added ### Added
* Send message via stdin [#78] - Send message via stdin [#78]
### Fixed ### Fixed
* Table with subject containing `\r`, `\n` or `\t` [#141] - Table with subject containing `\r`, `\n` or `\t` [#141]
* Overflow panic when shrink column [#138] - Overflow panic when shrink column [#138]
* Vim plugin empty mailbox message [#136] - Vim plugin empty mailbox message [#136]
## [0.3.0] - 2021-04-28 ## [0.3.0] - 2021-04-28
### Fixed ### Fixed
* IDLE mode after network interruption [#123] - IDLE mode after network interruption [#123]
* Output redirected to `stderr` [#130] - Output redirected to `stderr` [#130]
* Refactor table system [#132] - Refactor table system [#132]
* Editon file format on Linux [#133] - Editon file format on Linux [#133]
* Show email address when name not available [#131] - Show email address when name not available [#131]
### Removed ### Removed
* `--log-level|-l` arg (replaced by default `RUST_LOG` env var from `env_logger`) [#130] - `--log-level|-l` arg (replaced by default `RUST_LOG` env var from `env_logger`) [#130]
## [0.2.7] - 2021-04-24 ## [0.2.7] - 2021-04-24
### Added ### Added
* Default page size to config [#96] - Default page size to config [#96]
* Custom config path [#86] - Custom config path [#86]
* Setting idle-hook-cmds - Setting idle-hook-cmds
### Changed ### Changed
* Plain logger with `env_logger` [#126] - Plain logger with `env_logger` [#126]
* Refresh email list on load buffer [#125] - Refresh email list on load buffer [#125]
### Fixed ### Fixed
* Improve config compatibility on Windows [[#111](https://github.com/soywod/himalaya/pull/111)] - Improve config compatibility on Windows [[#111](https://github.com/soywod/himalaya/pull/111)]
* Vim table containing emoji [#122] - Vim table containing emoji [#122]
## [0.2.6] - 2021-04-17 ## [0.2.6] - 2021-04-17
### Added ### Added
* Insecure TLS option [#84] [#103](https://github.com/soywod/himalaya/pull/103) [[#105](https://github.com/soywod/himalaya/pull/105)] - Insecure TLS option [#84] [#103](https://github.com/soywod/himalaya/pull/103) [[#105](https://github.com/soywod/himalaya/pull/105)]
* Completion subcommands [[#99](https://github.com/soywod/himalaya/pull/99)] - Completion subcommands [[#99](https://github.com/soywod/himalaya/pull/99)]
* Vim flags to enable telescope preview and to choose picker [[#97](https://github.com/soywod/himalaya/pull/97)] - Vim flags to enable telescope preview and to choose picker [[#97](https://github.com/soywod/himalaya/pull/97)]
### Changed ### Changed
* Make `install.sh` POSIX compliant [[#53](https://github.com/soywod/himalaya/pull/53)] - Make `install.sh` POSIX compliant [[#53](https://github.com/soywod/himalaya/pull/53)]
### Fixed ### Fixed
* SMTP port [#87] - SMTP port [#87]
* Save msg upon error [#59] - Save msg upon error [#59]
* Answered flag not set [#50] - Answered flag not set [#50]
* Panic when downloads-dir does not exist [#100] - Panic when downloads-dir does not exist [#100]
* Idle mode incorrect new message notification [#48] - Idle mode incorrect new message notification [#48]
## [0.2.5] - 2021-04-12 ## [0.2.5] - 2021-04-12
### Fixed ### Fixed
* Expunge mbox after `move` and `delete` cmd [#83] - Expunge mbox after `move` and `delete` cmd [#83]
* JSON output [#89] - JSON output [#89]
## [0.2.4] - 2021-04-09 ## [0.2.4] - 2021-04-09
### Added ### Added
* Wiki entry for Gmail users [#58] - Wiki entry for Gmail users [#58]
* Info logs for copy/move/delete cmd + silent mode [#74] - Info logs for copy/move/delete cmd + silent mode [#74]
* `--raw` arg for `read` cmd [#79] - `--raw` arg for `read` cmd [#79]
### Changed ### Changed
* Refactor output system + log levels [#74] - Refactor output system + log levels [#74]
## [0.2.3] - 2021-04-08 ## [0.2.3] - 2021-04-08
### Added ### Added
* Telescope support [#61] - Telescope support [#61]
### Fixed ### Fixed
* Unicode chars breaks the view [#71] - Unicode chars breaks the view [#71]
* Copy/move incomplete (missing parts) [#75] - Copy/move incomplete (missing parts) [#75]
## [0.2.2] - 2021-04-04 ## [0.2.2] - 2021-04-04
### Added ### Added
* `w` alias for `write` cmd - `w` alias for `write` cmd
### Fixed ### Fixed
* `attachments` cmd logs - `attachments` cmd logs
* Page size arg `search` cmd - Page size arg `search` cmd
## [0.2.1] - 2021-04-04 ## [0.2.1] - 2021-04-04
### Added ### Added
* IDLE support [#29] - IDLE support [#29]
* Improve choice after editing msg [#30] - Improve choice after editing msg [#30]
* Flags management [#41] - Flags management [#41]
* Copy feature [#35] - Copy feature [#35]
* Move feature [#31] - Move feature [#31]
* Delete feature [#36] - Delete feature [#36]
* Signature support [#33] - Signature support [#33]
* Add attachment(s) to a message (CLI) [#37] - Add attachment(s) to a message (CLI) [#37]
### Changed ### Changed
* Errors management with `error_chain` [#39] - Errors management with `error_chain` [#39]
### Fixed ### Fixed
* Missing `FLAGS` column in messages table [#40] - Missing `FLAGS` column in messages table [#40]
* Subtract with overflow if next page empty [#38] - Subtract with overflow if next page empty [#38]
## [0.2.0] - 2021-03-10 ## [0.2.0] - 2021-03-10
### Added ### Added
* STARTTLS support [#32] - STARTTLS support [#32]
* Flags [#25] - Flags [#25]
### Changed ### Changed
* JSON support [#18] - JSON support [#18]
## [0.1.0] - 2021-01-17 ## [0.1.0] - 2021-01-17
### Added ### Added
* Parse TOML config [#1] - Parse TOML config [#1]
* Populate Config struct from TOML [#2] - Populate Config struct from TOML [#2]
* Set up IMAP connection [#3] - Set up IMAP connection [#3]
* List new emails [#6] - List new emails [#6]
* Set up CLI arg parser [#15] - Set up CLI arg parser [#15]
* List mailboxes command [#5] - List mailboxes command [#5]
* Text and HTML previews [#12] [#13] - Text and HTML previews [#12] [#13]
* Set up SMTP connection [#4] - Set up SMTP connection [#4]
* Write new email [#8] - Write new email [#8]
* Write new email [#8] - Write new email [#8]
* Reply, reply all and forward [#9] [#10] [#11] - Reply, reply all and forward [#9] [#10] [#11]
* Download attachments [#14] - Download attachments [#14]
* Merge `Email` with `Msg` [#21] - Merge `Email` with `Msg` [#21]
* List command with pagination [#19] - List command with pagination [#19]
* Icon in table when attachment is present [#16] - Icon in table when attachment is present [#16]
* Multi-account [#17] - Multi-account [#17]
* Password from command [#22] - Password from command [#22]
* Set up README [#20] - Set up README [#20]
[Unreleased]: https://github.com/soywod/himalaya/compare/v0.7.0...HEAD [Unreleased]: https://github.com/soywod/himalaya/compare/v0.7.1...develop
[0.7.0]: https://github.com/soywod/himalaya/compare/v0.6.2...v0.7.0 [0.7.1]: https://github.com/soywod/himalaya/compare/v0.7.0...v0.7.1
[0.6.2]: https://github.com/soywod/himalaya/compare/v0.6.1...v0.6.2 [0.7.0]: https://github.com/soywod/himalaya/compare/v0.6.1...v0.7.0
[0.6.1]: https://github.com/soywod/himalaya/compare/v0.6.0...v0.6.1 [0.6.1]: https://github.com/soywod/himalaya/compare/v0.6.0...v0.6.1
[0.6.0]: https://github.com/soywod/himalaya/compare/v0.5.10...v0.6.0 [0.6.0]: https://github.com/soywod/himalaya/compare/v0.5.10...v0.6.0
[0.5.10]: https://github.com/soywod/himalaya/compare/v0.5.9...v0.5.10 [0.5.10]: https://github.com/soywod/himalaya/compare/v0.5.9...v0.5.10

View file

@ -44,5 +44,6 @@ email at
If you want to **discuss** about the project, feel free to join the If you want to **discuss** about the project, feel free to join the
[Matrix](https://matrix.org/) workspace [Matrix](https://matrix.org/) workspace
[#pimalaya](https://matrix.to/#/#pimalaya:matrix.org) or contact me [#pimalaya.himalaya](https://matrix.to/#/#pimalaya.himalaya:matrix.org)
directly [@soywod](https://matrix.to/#/@soywod:matrix.org). or contact me directly
[@soywod](https://matrix.to/#/@soywod:matrix.org).

16
Cargo.lock generated
View file

@ -765,7 +765,7 @@ dependencies = [
[[package]] [[package]]
name = "himalaya" name = "himalaya"
version = "0.7.0" version = "0.7.1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"atty", "atty",
@ -796,9 +796,9 @@ dependencies = [
[[package]] [[package]]
name = "himalaya-lib" name = "himalaya-lib"
version = "0.5.1" version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d96448bba297a565c27dc18404da17d465e79df2feffa632b249281c8c0b5b68" checksum = "d6aa84cdd1cec7bd25e319f0decd7d6ec5d765fb983da7a0dea10d797f7e73a8"
dependencies = [ dependencies = [
"ammonia", "ammonia",
"chrono", "chrono",
@ -1124,6 +1124,7 @@ checksum = "5d2d08d52a925272eda99f8fe9e91237b1cb958804ee0628cc398ebd1bbc426f"
dependencies = [ dependencies = [
"gethostname", "gethostname",
"mailparse", "mailparse",
"memmap2",
] ]
[[package]] [[package]]
@ -1190,6 +1191,15 @@ version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "memmap2"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b182332558b18d807c4ce1ca8ca983b34c3ee32765e47b3f0f69b90355cc1dc"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "memoffset" name = "memoffset"
version = "0.7.1" version = "0.7.1"

View file

@ -1,7 +1,7 @@
[package] [package]
name = "himalaya" name = "himalaya"
description = "Command-line interface for email management." description = "Command-line interface for email management."
version = "0.7.0" version = "0.7.1"
authors = ["soywod <clement.douin@posteo.net>"] authors = ["soywod <clement.douin@posteo.net>"]
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
@ -17,9 +17,9 @@ section = "mail"
[features] [features]
imap-backend = ["himalaya-lib/imap-backend"] imap-backend = ["himalaya-lib/imap-backend"]
maildir-backend = ["himalaya-lib/maildir-backend"] smtp-sender = ["himalaya-lib/smtp-sender"]
notmuch-backend = ["himalaya-lib/notmuch-backend"] notmuch-backend = ["himalaya-lib/notmuch-backend"]
default = ["imap-backend", "maildir-backend"] default = ["imap-backend", "smtp-sender"]
[dev-dependencies] [dev-dependencies]
tempfile = "3.3" tempfile = "3.3"
@ -36,7 +36,7 @@ dialoguer = "0.10.2"
email_address = "0.2.4" email_address = "0.2.4"
env_logger = "0.8" env_logger = "0.8"
erased-serde = "0.3" erased-serde = "0.3"
himalaya-lib = "0.5" himalaya-lib = "0.6.0"
indicatif = "0.17" indicatif = "0.17"
log = "0.4" log = "0.4"
once_cell = "1.16.0" once_cell = "1.16.0"

106
README.md
View file

@ -1,30 +1,43 @@
# 📫 Himalaya [![GitHub release](https://img.shields.io/github/v/release/soywod/himalaya?color=success&style=flat-square)](https://github.com/soywod/himalaya/releases/latest) [![Matrix](https://img.shields.io/matrix/himalaya.email.client:matrix.org?color=success&label=chat&style=flat-square)](https://matrix.to/#/#himalaya.email.client:matrix.org) # 📫 Himalaya [![GitHub release](https://img.shields.io/github/v/release/soywod/himalaya?color=success)](https://github.com/soywod/himalaya/releases/latest) [![Matrix](https://img.shields.io/matrix/pimalaya.himalaya:matrix.org?color=success&label=chat)](https://matrix.to/#/#pimalaya.himalaya:matrix.org)
Command-line interface for email management based on the Himalaya is a CLI based on the
[himalaya-lib](https://git.sr.ht/~soywod/himalaya-lib). [himalaya-lib](https://git.sr.ht/~soywod/himalaya-lib) that allows you
to manipulate your emails using commands in your console.
![image](https://user-images.githubusercontent.com/10437171/138774902-7b9de5a3-93eb-44b0-8cfb-6d2e11e3b1aa.png) ![image](https://user-images.githubusercontent.com/10437171/138774902-7b9de5a3-93eb-44b0-8cfb-6d2e11e3b1aa.png)
*Warning: the project is under active development, do not use in *Disclaimer: the project is under active development, do not use in
production before the `v1.0.0`.* production before the `v1.0.0`.*
## Features ## Features
- Folder listing - [Folder listing]
- Email listing and searching - [Envelopes listing], [searching] and [sorting]
- Email composition based on `$EDITOR` - [Email composition] based on `$EDITOR`
- Email manipulation (copy/move/delete) - Email manipulation ([copy]/[move]/[delete])
- Multi-accounting - [Multi-accounting]
- Account listing - [Account listing]
- [Account synchronization] for offline usage
- IMAP, Maildir and Notmuch support - IMAP, Maildir and Notmuch support
- IMAP IDLE mode for real-time notifications - IMAP IDLE mode for [real-time notifications]
- PGP end-to-end encryption - PGP end-to-end encryption
- Completions for various shells - [Completions] for various shells
- JSON output - JSON output
- … - …
*Note: see the [wiki](https://github.com/soywod/himalaya/wiki) for all [Folder listing]: https://pimalaya.org/himalaya/docs/cli/usage/folders/list.html
the features.* [Envelopes listing]: https://pimalaya.org/himalaya/docs/cli/usage/envelopes/list.html
[searching]: https://pimalaya.org/himalaya/docs/cli/usage/envelopes/search.html
[sorting]: https://pimalaya.org/himalaya/docs/cli/usage/envelopes/sort.html
[Email composition]: https://pimalaya.org/himalaya/docs/cli/usage/emails/write.html
[copy]: https://pimalaya.org/himalaya/docs/cli/usage/emails/copy.html
[move]: https://pimalaya.org/himalaya/docs/cli/usage/emails/move.html
[delete]: https://pimalaya.org/himalaya/docs/cli/usage/emails/delete.html
[Multi-accounting]: https://pimalaya.org/himalaya/docs/cli/configuration.html
[Account listing]: https://pimalaya.org/himalaya/docs/cli/usage/accounts/list.html
[Account synchronization]: https://pimalaya.org/himalaya/docs/cli/usage/accounts/synchronize.html
[real-time notifications]: https://pimalaya.org/himalaya/docs/cli/usage/notifications.html
[Completions]: https://pimalaya.org/himalaya/docs/cli/tips/completion.html
## Installation ## Installation
@ -37,7 +50,7 @@ the features.*
</td> </td>
<td width="50%"> <td width="50%">
```shell ```bash
# Arch Linux (official) # Arch Linux (official)
$ pacman -S himalaya $ pacman -S himalaya
@ -54,9 +67,9 @@ $ cargo install himalaya
$ nix-env -i himalaya $ nix-env -i himalaya
``` ```
*Note: see the *See the
[wiki](https://github.com/soywod/himalaya/wiki/Installation) for other [documentation](https://pimalaya.org/himalaya/docs/cli/installation.html)
installation methods.* for other installation methods.*
</td> </td>
</tr> </tr>
@ -64,49 +77,8 @@ installation methods.*
## Configuration ## Configuration
```toml Please read the
# ~/.config/himalaya/config.toml [documentation](https://pimalaya.org/himalaya/docs/cli/configuration.html).
display-name = "Test"
downloads-dir = "~/downloads"
signature = "Regards,"
[gmail]
default = true
email = "test@gmail.com"
backend = "imap"
imap-host = "imap.gmail.com"
imap-port = 993
imap-login = "test@gmail.com"
imap-passwd-cmd = "security find-internet-password -gs gmail -w"
sender = "smtp"
smtp-host = "smtp.gmail.com"
smtp-port = 465
smtp-login = "test@gmail.com"
smtp-passwd-cmd = "security find-internet-password -gs gmail -w"
[gmail.folder-aliases]
inbox = "INBOX"
sent = "[Gmail]/Sent"
drafts = "[Gmail]/Drafts"
[local]
email = "test@localhost"
signature-delim = "~~\n"
signature = "Regards,"
backend = "maildir"
maildir-root-dir = "~/emails"
sender = "sendmail"
sendmail-cmd = "msmtp --read-envelope-from --read-recipients"
```
*Note: see the
[wiki](https://github.com/soywod/himalaya/wiki/Configuration) for all
the options.*
## Contributing ## Contributing
@ -132,7 +104,7 @@ email at
If you want to **discuss** about the project, feel free to join the If you want to **discuss** about the project, feel free to join the
[Matrix](https://matrix.org/) workspace [Matrix](https://matrix.org/) workspace
[#pimalaya](https://matrix.to/#/#pimalaya:matrix.org) or contact me [#pimalaya.himalaya](https://matrix.to/#/#pimalaya.himalaya:matrix.org) or contact me
directly [@soywod](https://matrix.to/#/@soywod:matrix.org). directly [@soywod](https://matrix.to/#/@soywod:matrix.org).
## Credits ## Credits
@ -163,8 +135,8 @@ European Commission in September, 2022.
## Sponsoring ## Sponsoring
[![GitHub](https://img.shields.io/badge/-GitHub%20Sponsors-fafbfc?logo=GitHub%20Sponsors&style=flat-square)](https://github.com/sponsors/soywod) [![GitHub](https://img.shields.io/badge/-GitHub%20Sponsors-fafbfc?logo=GitHub%20Sponsors)](https://github.com/sponsors/soywod)
[![PayPal](https://img.shields.io/badge/-PayPal-0079c1?logo=PayPal&logoColor=ffffff&style=flat-square)](https://www.paypal.com/paypalme/soywod) [![PayPal](https://img.shields.io/badge/-PayPal-0079c1?logo=PayPal&logoColor=ffffff)](https://www.paypal.com/paypalme/soywod)
[![Ko-fi](https://img.shields.io/badge/-Ko--fi-ff5e5a?logo=Ko-fi&logoColor=ffffff&style=flat-square)](https://ko-fi.com/soywod) [![Ko-fi](https://img.shields.io/badge/-Ko--fi-ff5e5a?logo=Ko-fi&logoColor=ffffff)](https://ko-fi.com/soywod)
[![Buy Me a Coffee](https://img.shields.io/badge/-Buy%20Me%20a%20Coffee-ffdd00?logo=Buy%20Me%20A%20Coffee&logoColor=000000&style=flat-square)](https://www.buymeacoffee.com/soywod) [![Buy Me a Coffee](https://img.shields.io/badge/-Buy%20Me%20a%20Coffee-ffdd00?logo=Buy%20Me%20A%20Coffee&logoColor=000000)](https://www.buymeacoffee.com/soywod)
[![Liberapay](https://img.shields.io/badge/-Liberapay-f6c915?logo=Liberapay&logoColor=222222&style=flat-square)](https://liberapay.com/soywod) [![Liberapay](https://img.shields.io/badge/-Liberapay-f6c915?logo=Liberapay&logoColor=222222)](https://liberapay.com/soywod)

View file

@ -31,14 +31,22 @@ pub struct DeserializedConfig {
pub email_listing_page_size: Option<usize>, pub email_listing_page_size: Option<usize>,
pub email_reading_headers: Option<Vec<String>>, pub email_reading_headers: Option<Vec<String>>,
#[serde(default, with = "EmailTextPlainFormatOptionDef", skip_serializing_if = "Option::is_none")] #[serde(
default,
with = "EmailTextPlainFormatOptionDef",
skip_serializing_if = "Option::is_none"
)]
pub email_reading_format: Option<EmailTextPlainFormat>, pub email_reading_format: Option<EmailTextPlainFormat>,
pub email_reading_verify_cmd: Option<String>, pub email_reading_verify_cmd: Option<String>,
pub email_reading_decrypt_cmd: Option<String>, pub email_reading_decrypt_cmd: Option<String>,
pub email_writing_headers: Option<Vec<String>>, pub email_writing_headers: Option<Vec<String>>,
pub email_writing_sign_cmd: Option<String>, pub email_writing_sign_cmd: Option<String>,
pub email_writing_encrypt_cmd: Option<String>, pub email_writing_encrypt_cmd: Option<String>,
#[serde(default, with = "EmailHooksOptionDef", skip_serializing_if = "Option::is_none")] #[serde(
default,
with = "EmailHooksOptionDef",
skip_serializing_if = "Option::is_none"
)]
pub email_hooks: Option<EmailHooks>, pub email_hooks: Option<EmailHooks>,
#[serde(flatten)] #[serde(flatten)]
@ -115,12 +123,10 @@ impl DeserializedConfig {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use himalaya_lib::{EmailSender, SendmailConfig, SmtpConfig}; use himalaya_lib::{EmailSender, MaildirConfig, SendmailConfig, SmtpConfig};
#[cfg(feature = "imap-backend")] #[cfg(feature = "imap-backend")]
use himalaya_lib::ImapConfig; use himalaya_lib::ImapConfig;
#[cfg(feature = "maildir-backend")]
use himalaya_lib::MaildirConfig;
#[cfg(feature = "notmuch-backend")] #[cfg(feature = "notmuch-backend")]
use himalaya_lib::NotmuchConfig; use himalaya_lib::NotmuchConfig;
@ -131,7 +137,6 @@ mod tests {
#[cfg(feature = "imap-backend")] #[cfg(feature = "imap-backend")]
use crate::account::DeserializedImapAccountConfig; use crate::account::DeserializedImapAccountConfig;
#[cfg(feature = "maildir-backend")]
use crate::account::DeserializedMaildirAccountConfig; use crate::account::DeserializedMaildirAccountConfig;
#[cfg(feature = "notmuch-backend")] #[cfg(feature = "notmuch-backend")]
use crate::account::DeserializedNotmuchAccountConfig; use crate::account::DeserializedNotmuchAccountConfig;

View file

@ -1,13 +1,13 @@
use himalaya_lib::{EmailHooks, EmailSender, EmailTextPlainFormat, SendmailConfig, SmtpConfig};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::path::PathBuf; use std::path::PathBuf;
use himalaya_lib::{
EmailHooks, EmailSender, EmailTextPlainFormat, MaildirConfig, SendmailConfig, SmtpConfig,
};
#[cfg(feature = "imap-backend")] #[cfg(feature = "imap-backend")]
use himalaya_lib::ImapConfig; use himalaya_lib::ImapConfig;
#[cfg(feature = "maildir-backend")]
use himalaya_lib::MaildirConfig;
#[cfg(feature = "notmuch-backend")] #[cfg(feature = "notmuch-backend")]
use himalaya_lib::NotmuchConfig; use himalaya_lib::NotmuchConfig;
@ -56,7 +56,6 @@ pub struct ImapConfigDef {
pub watch_cmds: Option<Vec<String>>, pub watch_cmds: Option<Vec<String>>,
} }
#[cfg(feature = "maildir-backend")]
#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)]
#[serde(remote = "MaildirConfig")] #[serde(remote = "MaildirConfig")]
pub struct MaildirConfigDef { pub struct MaildirConfigDef {

View file

@ -7,7 +7,6 @@ use dialoguer::Input;
use dirs::home_dir; use dirs::home_dir;
use himalaya_lib::MaildirConfig; use himalaya_lib::MaildirConfig;
#[cfg(feature = "maildir-backend")]
pub(crate) fn configure(base: DeserializedBaseAccountConfig) -> Result<DeserializedAccountConfig> { pub(crate) fn configure(base: DeserializedBaseAccountConfig) -> Result<DeserializedAccountConfig> {
let input = if let Some(home) = home_dir() { let input = if let Some(home) = home_dir() {
Input::with_theme(&*THEME) Input::with_theme(&*THEME)

View file

@ -1,6 +1,5 @@
#[cfg(feature = "imap-backend")] #[cfg(feature = "imap-backend")]
mod imap; mod imap;
#[cfg(feature = "maildir-backend")]
mod maildir; mod maildir;
#[cfg(feature = "notmuch-backend")] #[cfg(feature = "notmuch-backend")]
mod notmuch; mod notmuch;
@ -18,10 +17,9 @@ use once_cell::sync::Lazy;
use std::{fs, process}; use std::{fs, process};
const BACKENDS: &[&str] = &[ const BACKENDS: &[&str] = &[
"Maildir",
#[cfg(feature = "imap-backend")] #[cfg(feature = "imap-backend")]
"IMAP", "IMAP",
#[cfg(feature = "maildir-backend")]
"Maildir",
#[cfg(feature = "notmuch-backend")] #[cfg(feature = "notmuch-backend")]
"Notmuch", "Notmuch",
]; ];
@ -102,10 +100,9 @@ pub(crate) fn wizard() -> Result<DeserializedConfig> {
match default { match default {
Some(DeserializedAccountConfig::None(default)) => default.default = Some(true), Some(DeserializedAccountConfig::None(default)) => default.default = Some(true),
Some(DeserializedAccountConfig::Maildir(default)) => default.base.default = Some(true),
#[cfg(feature = "imap-backend")] #[cfg(feature = "imap-backend")]
Some(DeserializedAccountConfig::Imap(default)) => default.base.default = Some(true), Some(DeserializedAccountConfig::Imap(default)) => default.base.default = Some(true),
#[cfg(feature = "maildir-backend")]
Some(DeserializedAccountConfig::Maildir(default)) => default.base.default = Some(true),
#[cfg(feature = "notmuch-backend")] #[cfg(feature = "notmuch-backend")]
Some(DeserializedAccountConfig::Notmuch(default)) => default.base.default = Some(true), Some(DeserializedAccountConfig::Notmuch(default)) => default.base.default = Some(true),
_ => {} _ => {}
@ -141,10 +138,9 @@ fn configure_account() -> Result<Option<DeserializedAccountConfig>> {
.interact_opt()?; .interact_opt()?;
match backend { match backend {
Some(idx) if BACKENDS[idx] == "Maildir" => Ok(Some(maildir::configure(base)?)),
#[cfg(feature = "imap-backend")] #[cfg(feature = "imap-backend")]
Some(idx) if BACKENDS[idx] == "IMAP" => Ok(Some(imap::configure(base)?)), Some(idx) if BACKENDS[idx] == "IMAP" => Ok(Some(imap::configure(base)?)),
#[cfg(feature = "maildir-backend")]
Some(idx) if BACKENDS[idx] == "Maildir" => Ok(Some(maildir::configure(base)?)),
#[cfg(feature = "notmuch-backend")] #[cfg(feature = "notmuch-backend")]
Some(idx) if BACKENDS[idx] == "Notmuch" => Ok(Some(notmuch::configure(base)?)), Some(idx) if BACKENDS[idx] == "Notmuch" => Ok(Some(notmuch::configure(base)?)),
_ => Ok(None), _ => Ok(None),

View file

@ -40,14 +40,13 @@ impl From<Iter<'_, String, DeserializedAccountConfig>> for Accounts {
fn from(map: Iter<'_, String, DeserializedAccountConfig>) -> Self { fn from(map: Iter<'_, String, DeserializedAccountConfig>) -> Self {
let mut accounts: Vec<_> = map let mut accounts: Vec<_> = map
.map(|(name, account)| match account { .map(|(name, account)| match account {
DeserializedAccountConfig::Maildir(config) => {
Account::new(name, "maildir", config.base.default.unwrap_or_default())
}
#[cfg(feature = "imap-backend")] #[cfg(feature = "imap-backend")]
DeserializedAccountConfig::Imap(config) => { DeserializedAccountConfig::Imap(config) => {
Account::new(name, "imap", config.base.default.unwrap_or_default()) Account::new(name, "imap", config.base.default.unwrap_or_default())
} }
#[cfg(feature = "maildir-backend")]
DeserializedAccountConfig::Maildir(config) => {
Account::new(name, "maildir", config.base.default.unwrap_or_default())
}
#[cfg(feature = "notmuch-backend")] #[cfg(feature = "notmuch-backend")]
DeserializedAccountConfig::Notmuch(config) => { DeserializedAccountConfig::Notmuch(config) => {
Account::new(name, "notmuch", config.base.default.unwrap_or_default()) Account::new(name, "notmuch", config.base.default.unwrap_or_default())

View file

@ -3,14 +3,13 @@
//! This module contains the raw deserialized representation of an //! This module contains the raw deserialized representation of an
//! account in the accounts section of the user configuration file. //! account in the accounts section of the user configuration file.
use himalaya_lib::{AccountConfig, BackendConfig, EmailHooks, EmailSender, EmailTextPlainFormat}; use himalaya_lib::{
AccountConfig, BackendConfig, EmailHooks, EmailSender, EmailTextPlainFormat, MaildirConfig,
};
#[cfg(feature = "imap-backend")] #[cfg(feature = "imap-backend")]
use himalaya_lib::ImapConfig; use himalaya_lib::ImapConfig;
#[cfg(feature = "maildir-backend")]
use himalaya_lib::MaildirConfig;
#[cfg(feature = "notmuch-backend")] #[cfg(feature = "notmuch-backend")]
use himalaya_lib::NotmuchConfig; use himalaya_lib::NotmuchConfig;
@ -24,10 +23,9 @@ use crate::config::{prelude::*, DeserializedConfig};
#[serde(tag = "backend", rename_all = "snake_case")] #[serde(tag = "backend", rename_all = "snake_case")]
pub enum DeserializedAccountConfig { pub enum DeserializedAccountConfig {
None(DeserializedBaseAccountConfig), None(DeserializedBaseAccountConfig),
Maildir(DeserializedMaildirAccountConfig),
#[cfg(feature = "imap-backend")] #[cfg(feature = "imap-backend")]
Imap(DeserializedImapAccountConfig), Imap(DeserializedImapAccountConfig),
#[cfg(feature = "maildir-backend")]
Maildir(DeserializedMaildirAccountConfig),
#[cfg(feature = "notmuch-backend")] #[cfg(feature = "notmuch-backend")]
Notmuch(DeserializedNotmuchAccountConfig), Notmuch(DeserializedNotmuchAccountConfig),
} }
@ -43,16 +41,15 @@ impl DeserializedAccountConfig {
config.to_account_config(name, global_config), config.to_account_config(name, global_config),
BackendConfig::None, BackendConfig::None,
), ),
DeserializedAccountConfig::Maildir(config) => (
config.base.to_account_config(name, global_config),
BackendConfig::Maildir(config.backend.clone()),
),
#[cfg(feature = "imap-backend")] #[cfg(feature = "imap-backend")]
DeserializedAccountConfig::Imap(config) => ( DeserializedAccountConfig::Imap(config) => (
config.base.to_account_config(name, global_config), config.base.to_account_config(name, global_config),
BackendConfig::Imap(config.backend.clone()), BackendConfig::Imap(config.backend.clone()),
), ),
#[cfg(feature = "maildir-backend")]
DeserializedAccountConfig::Maildir(config) => (
config.base.to_account_config(name, global_config),
BackendConfig::Maildir(config.backend.clone()),
),
#[cfg(feature = "notmuch-backend")] #[cfg(feature = "notmuch-backend")]
DeserializedAccountConfig::Notmuch(config) => ( DeserializedAccountConfig::Notmuch(config) => (
config.base.to_account_config(name, global_config), config.base.to_account_config(name, global_config),
@ -64,10 +61,9 @@ impl DeserializedAccountConfig {
pub fn is_default(&self) -> bool { pub fn is_default(&self) -> bool {
match self { match self {
DeserializedAccountConfig::None(config) => config.default.unwrap_or_default(), DeserializedAccountConfig::None(config) => config.default.unwrap_or_default(),
DeserializedAccountConfig::Maildir(config) => config.base.default.unwrap_or_default(),
#[cfg(feature = "imap-backend")] #[cfg(feature = "imap-backend")]
DeserializedAccountConfig::Imap(config) => config.base.default.unwrap_or_default(), DeserializedAccountConfig::Imap(config) => config.base.default.unwrap_or_default(),
#[cfg(feature = "maildir-backend")]
DeserializedAccountConfig::Maildir(config) => config.base.default.unwrap_or_default(),
#[cfg(feature = "notmuch-backend")] #[cfg(feature = "notmuch-backend")]
DeserializedAccountConfig::Notmuch(config) => config.base.default.unwrap_or_default(), DeserializedAccountConfig::Notmuch(config) => config.base.default.unwrap_or_default(),
} }
@ -89,7 +85,11 @@ pub struct DeserializedBaseAccountConfig {
pub email_listing_page_size: Option<usize>, pub email_listing_page_size: Option<usize>,
pub email_reading_headers: Option<Vec<String>>, pub email_reading_headers: Option<Vec<String>>,
#[serde(default, with = "EmailTextPlainFormatOptionDef", skip_serializing_if = "Option::is_none")] #[serde(
default,
with = "EmailTextPlainFormatOptionDef",
skip_serializing_if = "Option::is_none"
)]
pub email_reading_format: Option<EmailTextPlainFormat>, pub email_reading_format: Option<EmailTextPlainFormat>,
pub email_reading_verify_cmd: Option<String>, pub email_reading_verify_cmd: Option<String>,
pub email_reading_decrypt_cmd: Option<String>, pub email_reading_decrypt_cmd: Option<String>,
@ -98,7 +98,11 @@ pub struct DeserializedBaseAccountConfig {
pub email_writing_encrypt_cmd: Option<String>, pub email_writing_encrypt_cmd: Option<String>,
#[serde(flatten, with = "EmailSenderDef")] #[serde(flatten, with = "EmailSenderDef")]
pub email_sender: EmailSender, pub email_sender: EmailSender,
#[serde(default, with = "EmailHooksOptionDef", skip_serializing_if = "Option::is_none")] #[serde(
default,
with = "EmailHooksOptionDef",
skip_serializing_if = "Option::is_none"
)]
pub email_hooks: Option<EmailHooks>, pub email_hooks: Option<EmailHooks>,
#[serde(default)] #[serde(default)]
@ -237,7 +241,6 @@ pub struct DeserializedImapAccountConfig {
} }
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] #[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
#[cfg(feature = "maildir-backend")]
pub struct DeserializedMaildirAccountConfig { pub struct DeserializedMaildirAccountConfig {
#[serde(flatten)] #[serde(flatten)]
pub base: DeserializedBaseAccountConfig, pub base: DeserializedBaseAccountConfig,

View file

@ -43,12 +43,16 @@ pub fn sync<P: Printer>(
account_config: &AccountConfig, account_config: &AccountConfig,
printer: &mut P, printer: &mut P,
backend: &dyn Backend, backend: &dyn Backend,
folder: &Option<String>,
dry_run: bool, dry_run: bool,
) -> Result<()> { ) -> Result<()> {
info!("entering the sync accounts handler"); info!("entering the sync accounts handler");
trace!("dry run: {}", dry_run); trace!("dry run: {}", dry_run);
let sync_builder = BackendSyncBuilder::new(account_config); let mut sync_builder = BackendSyncBuilder::new(account_config);
if let Some(folder) = folder {
sync_builder = sync_builder.only_folder(folder);
}
if dry_run { if dry_run {
let report = sync_builder.dry_run(true).sync(backend)?; let report = sync_builder.dry_run(true).sync(backend)?;

View file

@ -5,26 +5,37 @@
use anyhow::Result; use anyhow::Result;
use clap::{self, Arg, ArgMatches, Command}; use clap::{self, Arg, ArgMatches, Command};
use log::debug; use log::{debug, info};
use crate::ui::table; use crate::ui::table;
const ARG_SOURCE: &str = "source"; const ARG_SOURCE: &str = "source";
const ARG_TARGET: &str = "target"; const ARG_TARGET: &str = "target";
const CMD_EXPUNGE: &str = "expunge";
const CMD_FOLDERS: &str = "folders"; const CMD_FOLDERS: &str = "folders";
const CMD_LIST: &str = "list";
/// Represents the folder commands. /// Represents the folder commands.
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub enum Cmd { pub enum Cmd {
List(table::args::MaxTableWidth), List(table::args::MaxTableWidth),
Expunge,
} }
/// Represents the folder command matcher. /// Represents the folder command matcher.
pub fn matches(m: &ArgMatches) -> Result<Option<Cmd>> { pub fn matches(m: &ArgMatches) -> Result<Option<Cmd>> {
let cmd = if let Some(m) = m.subcommand_matches(CMD_FOLDERS) { let cmd = if let Some(m) = m.subcommand_matches(CMD_FOLDERS) {
debug!("folders command matched"); if let Some(_) = m.subcommand_matches(CMD_EXPUNGE) {
let max_table_width = table::args::parse_max_width(m); info!("expunge folder subcommand matched");
Some(Cmd::List(max_table_width)) Some(Cmd::Expunge)
} else if let Some(m) = m.subcommand_matches(CMD_LIST) {
debug!("list folders command matched");
let max_table_width = table::args::parse_max_width(m);
Some(Cmd::List(max_table_width))
} else {
info!("no folder subcommand matched, falling back to subcommand list");
Some(Cmd::List(None))
}
} else { } else {
None None
}; };
@ -35,8 +46,13 @@ pub fn matches(m: &ArgMatches) -> Result<Option<Cmd>> {
/// Represents the folder subcommand. /// Represents the folder subcommand.
pub fn subcmd() -> Command { pub fn subcmd() -> Command {
Command::new(CMD_FOLDERS) Command::new(CMD_FOLDERS)
.about("Lists folders") .about("Manage folders")
.arg(table::args::max_width()) .subcommands([
Command::new(CMD_EXPUNGE).about("Delete emails marked for deletion"),
Command::new(CMD_LIST)
.about("List folders")
.arg(table::args::max_width()),
])
} }
/// Represents the source folder argument. /// Represents the source folder argument.
@ -46,12 +62,11 @@ pub fn source_arg() -> Arg {
.short('f') .short('f')
.help("Specifies the source folder") .help("Specifies the source folder")
.value_name("SOURCE") .value_name("SOURCE")
.default_value("inbox")
} }
/// Represents the source folder argument parser. /// Represents the source folder argument parser.
pub fn parse_source_arg(matches: &ArgMatches) -> &str { pub fn parse_source_arg(matches: &ArgMatches) -> Option<&str> {
matches.get_one::<String>(ARG_SOURCE).unwrap().as_str() matches.get_one::<String>(ARG_SOURCE).map(String::as_str)
} }
/// Represents the target folder argument. /// Represents the target folder argument.
@ -82,7 +97,7 @@ mod tests {
let arg = Command::new("himalaya") let arg = Command::new("himalaya")
.subcommand(subcmd()) .subcommand(subcmd())
.get_matches_from(&["himalaya", "folders", "--max-width", "20"]); .get_matches_from(&["himalaya", "folders", "list", "--max-width", "20"]);
assert_eq!(Some(Cmd::List(Some(20))), matches(&arg).unwrap()); assert_eq!(Some(Cmd::List(Some(20))), matches(&arg).unwrap());
} }
@ -97,10 +112,7 @@ mod tests {
} }
let app = get_matches_from![]; let app = get_matches_from![];
assert_eq!( assert_eq!(None, app.get_one::<String>(ARG_SOURCE).map(String::as_str));
Some("inbox"),
app.get_one::<String>(ARG_SOURCE).map(String::as_str)
);
let app = get_matches_from!["-f", "SOURCE"]; let app = get_matches_from!["-f", "SOURCE"];
assert_eq!( assert_eq!(

View file

@ -7,6 +7,15 @@ use himalaya_lib::{AccountConfig, Backend};
use crate::printer::{PrintTableOpts, Printer}; use crate::printer::{PrintTableOpts, Printer};
pub fn expunge<P: Printer, B: Backend + ?Sized>(
folder: &str,
printer: &mut P,
backend: &mut B,
) -> Result<()> {
backend.expunge_folder(folder)?;
printer.print(format!("Folder {folder} successfully expunged!"))
}
pub fn list<P: Printer, B: Backend + ?Sized>( pub fn list<P: Printer, B: Backend + ?Sized>(
max_width: Option<usize>, max_width: Option<usize>,
config: &AccountConfig, config: &AccountConfig,
@ -123,6 +132,9 @@ mod tests {
}, },
])) ]))
} }
fn expunge_folder(&self, _: &str) -> backend::Result<()> {
unimplemented!();
}
fn purge_folder(&self, _: &str) -> backend::Result<()> { fn purge_folder(&self, _: &str) -> backend::Result<()> {
unimplemented!(); unimplemented!();
} }

View file

@ -10,7 +10,9 @@ use himalaya::{
printer::StdoutPrinter, printer::StdoutPrinter,
tpl, tpl,
}; };
use himalaya_lib::{BackendBuilder, BackendConfig, ImapBackend, SenderBuilder}; use himalaya_lib::{
BackendBuilder, BackendConfig, ImapBackend, SenderBuilder, DEFAULT_INBOX_FOLDER,
};
#[cfg(feature = "imap-backend")] #[cfg(feature = "imap-backend")]
use himalaya::imap; use himalaya::imap;
@ -87,11 +89,13 @@ fn main() -> Result<()> {
// inits config // inits config
let config = DeserializedConfig::from_opt_path(config::args::parse_arg(&m))?; let config = DeserializedConfig::from_opt_path(config::args::parse_arg(&m))?;
let (account_config, backend_config) = config.to_configs(account::args::parse_arg(&m))?; let (account_config, backend_config) = config.to_configs(account::args::parse_arg(&m))?;
let folder = account_config.folder_alias(folder::args::parse_source_arg(&m))?; let folder = folder::args::parse_source_arg(&m);
// checks IMAP commands // checks IMAP commands
#[cfg(feature = "imap-backend")] #[cfg(feature = "imap-backend")]
if let BackendConfig::Imap(imap_config) = &backend_config { if let BackendConfig::Imap(imap_config) = &backend_config {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
// FIXME: find a way to downcast `backend` instead of // FIXME: find a way to downcast `backend` instead of
// recreating an instance. // recreating an instance.
match imap::args::matches(&m)? { match imap::args::matches(&m)? {
@ -120,11 +124,21 @@ fn main() -> Result<()> {
return account::handlers::list(max_width, &account_config, &config, &mut printer); return account::handlers::list(max_width, &account_config, &config, &mut printer);
} }
Some(account::args::Cmd::Sync(dry_run)) => { Some(account::args::Cmd::Sync(dry_run)) => {
let folder = match folder {
Some(folder) => Some(account_config.folder_alias(folder)?),
None => None,
};
let backend = BackendBuilder::new() let backend = BackendBuilder::new()
.sessions_pool_size(16) .sessions_pool_size(8)
.disable_cache(true) .disable_cache(true)
.build(&account_config, &backend_config)?; .build(&account_config, &backend_config)?;
account::handlers::sync(&account_config, &mut printer, backend.as_ref(), dry_run)?; account::handlers::sync(
&account_config,
&mut printer,
backend.as_ref(),
&folder,
dry_run,
)?;
backend.close()?; backend.close()?;
return Ok(()); return Ok(());
} }
@ -133,6 +147,13 @@ fn main() -> Result<()> {
// checks folder commands // checks folder commands
match folder::args::matches(&m)? { match folder::args::matches(&m)? {
Some(folder::args::Cmd::Expunge) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new()
.disable_cache(disable_cache)
.build(&account_config, &backend_config)?;
return folder::handlers::expunge(&folder, &mut printer, backend.as_mut());
}
Some(folder::args::Cmd::List(max_width)) => { Some(folder::args::Cmd::List(max_width)) => {
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .disable_cache(disable_cache)
@ -150,6 +171,7 @@ fn main() -> Result<()> {
// checks email commands // checks email commands
match email::args::matches(&m)? { match email::args::matches(&m)? {
Some(email::args::Cmd::Attachments(ids)) => { Some(email::args::Cmd::Attachments(ids)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .disable_cache(disable_cache)
.build(&account_config, &backend_config)?; .build(&account_config, &backend_config)?;
@ -162,6 +184,7 @@ fn main() -> Result<()> {
); );
} }
Some(email::args::Cmd::Copy(ids, to_folder)) => { Some(email::args::Cmd::Copy(ids, to_folder)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .disable_cache(disable_cache)
.build(&account_config, &backend_config)?; .build(&account_config, &backend_config)?;
@ -175,6 +198,7 @@ fn main() -> Result<()> {
); );
} }
Some(email::args::Cmd::Delete(ids)) => { Some(email::args::Cmd::Delete(ids)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .disable_cache(disable_cache)
.build(&account_config, &backend_config)?; .build(&account_config, &backend_config)?;
@ -187,6 +211,7 @@ fn main() -> Result<()> {
); );
} }
Some(email::args::Cmd::Forward(id, headers, body)) => { Some(email::args::Cmd::Forward(id, headers, body)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .disable_cache(disable_cache)
.build(&account_config, &backend_config)?; .build(&account_config, &backend_config)?;
@ -202,6 +227,7 @@ fn main() -> Result<()> {
); );
} }
Some(email::args::Cmd::List(max_width, page_size, page)) => { Some(email::args::Cmd::List(max_width, page_size, page)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .disable_cache(disable_cache)
.build(&account_config, &backend_config)?; .build(&account_config, &backend_config)?;
@ -216,6 +242,7 @@ fn main() -> Result<()> {
); );
} }
Some(email::args::Cmd::Move(ids, to_folder)) => { Some(email::args::Cmd::Move(ids, to_folder)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .disable_cache(disable_cache)
.build(&account_config, &backend_config)?; .build(&account_config, &backend_config)?;
@ -229,6 +256,7 @@ fn main() -> Result<()> {
); );
} }
Some(email::args::Cmd::Read(ids, text_mime, sanitize, raw, headers)) => { Some(email::args::Cmd::Read(ids, text_mime, sanitize, raw, headers)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .disable_cache(disable_cache)
.build(&account_config, &backend_config)?; .build(&account_config, &backend_config)?;
@ -245,6 +273,7 @@ fn main() -> Result<()> {
); );
} }
Some(email::args::Cmd::Reply(id, all, headers, body)) => { Some(email::args::Cmd::Reply(id, all, headers, body)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .disable_cache(disable_cache)
.build(&account_config, &backend_config)?; .build(&account_config, &backend_config)?;
@ -261,6 +290,7 @@ fn main() -> Result<()> {
); );
} }
Some(email::args::Cmd::Save(raw_email)) => { Some(email::args::Cmd::Save(raw_email)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .disable_cache(disable_cache)
.build(&account_config, &backend_config)?; .build(&account_config, &backend_config)?;
@ -273,6 +303,7 @@ fn main() -> Result<()> {
); );
} }
Some(email::args::Cmd::Search(query, max_width, page_size, page)) => { Some(email::args::Cmd::Search(query, max_width, page_size, page)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .disable_cache(disable_cache)
.build(&account_config, &backend_config)?; .build(&account_config, &backend_config)?;
@ -288,6 +319,7 @@ fn main() -> Result<()> {
); );
} }
Some(email::args::Cmd::Sort(criteria, query, max_width, page_size, page)) => { Some(email::args::Cmd::Sort(criteria, query, max_width, page_size, page)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .disable_cache(disable_cache)
.build(&account_config, &backend_config)?; .build(&account_config, &backend_config)?;
@ -317,18 +349,21 @@ fn main() -> Result<()> {
} }
Some(email::args::Cmd::Flag(m)) => match m { Some(email::args::Cmd::Flag(m)) => match m {
Some(flag::args::Cmd::Set(ids, ref flags)) => { Some(flag::args::Cmd::Set(ids, ref flags)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .disable_cache(disable_cache)
.build(&account_config, &backend_config)?; .build(&account_config, &backend_config)?;
return flag::handlers::set(&mut printer, backend.as_mut(), &folder, ids, flags); return flag::handlers::set(&mut printer, backend.as_mut(), &folder, ids, flags);
} }
Some(flag::args::Cmd::Add(ids, ref flags)) => { Some(flag::args::Cmd::Add(ids, ref flags)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .disable_cache(disable_cache)
.build(&account_config, &backend_config)?; .build(&account_config, &backend_config)?;
return flag::handlers::add(&mut printer, backend.as_mut(), &folder, ids, flags); return flag::handlers::add(&mut printer, backend.as_mut(), &folder, ids, flags);
} }
Some(flag::args::Cmd::Remove(ids, ref flags)) => { Some(flag::args::Cmd::Remove(ids, ref flags)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .disable_cache(disable_cache)
.build(&account_config, &backend_config)?; .build(&account_config, &backend_config)?;
@ -338,6 +373,7 @@ fn main() -> Result<()> {
}, },
Some(email::args::Cmd::Tpl(m)) => match m { Some(email::args::Cmd::Tpl(m)) => match m {
Some(tpl::args::Cmd::Forward(id, headers, body)) => { Some(tpl::args::Cmd::Forward(id, headers, body)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .disable_cache(disable_cache)
.build(&account_config, &backend_config)?; .build(&account_config, &backend_config)?;
@ -355,6 +391,7 @@ fn main() -> Result<()> {
return tpl::handlers::write(&account_config, &mut printer, headers, body); return tpl::handlers::write(&account_config, &mut printer, headers, body);
} }
Some(tpl::args::Cmd::Reply(id, all, headers, body)) => { Some(tpl::args::Cmd::Reply(id, all, headers, body)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .disable_cache(disable_cache)
.build(&account_config, &backend_config)?; .build(&account_config, &backend_config)?;
@ -370,6 +407,7 @@ fn main() -> Result<()> {
); );
} }
Some(tpl::args::Cmd::Save(tpl)) => { Some(tpl::args::Cmd::Save(tpl)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .disable_cache(disable_cache)
.build(&account_config, &backend_config)?; .build(&account_config, &backend_config)?;
@ -382,6 +420,7 @@ fn main() -> Result<()> {
); );
} }
Some(tpl::args::Cmd::Send(tpl)) => { Some(tpl::args::Cmd::Send(tpl)) => {
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
let mut backend = BackendBuilder::new() let mut backend = BackendBuilder::new()
.disable_cache(disable_cache) .disable_cache(disable_cache)
.build(&account_config, &backend_config)?; .build(&account_config, &backend_config)?;