mirror of
https://github.com/soywod/himalaya.git
synced 2024-11-22 02:50:19 +00:00
commit
f66679318d
47 changed files with 1405 additions and 652 deletions
105
.github/workflows/deployment.yml
vendored
105
.github/workflows/deployment.yml
vendored
|
@ -1,105 +0,0 @@
|
|||
name: deployment
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
|
||||
jobs:
|
||||
create_release:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
steps:
|
||||
- name: Create release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: ${{ github.ref }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
deploy_linux_macos_windows_github:
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: create_release
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
os_name: linux
|
||||
- os: macos-latest
|
||||
os_name: macos
|
||||
- os: windows-latest
|
||||
os_name: windows
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Install rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
- name: Check project
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: check
|
||||
- name: Builds release
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --release
|
||||
- name: Compress executable (unix)
|
||||
if: matrix.os_name == 'linux' || matrix.os_name == 'macos'
|
||||
run: tar czf himalaya.tar.gz -C target/release himalaya
|
||||
- name: Compress executable (windows)
|
||||
if: matrix.os_name == 'windows'
|
||||
run: tar czf himalaya.tar.gz -C target/release himalaya.exe
|
||||
- name: Upload release asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ needs.create_release.outputs.upload_url }}
|
||||
asset_path: himalaya.tar.gz
|
||||
asset_name: himalaya-${{ matrix.os_name }}.tar.gz
|
||||
asset_content_type: application/gzip
|
||||
deploy_musl_github:
|
||||
runs-on: ubuntu-latest
|
||||
needs: create_release
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Build release
|
||||
run: |
|
||||
docker run -v "${PWD}:/volume" --rm -t clux/muslrust:stable cargo build --release
|
||||
- name: Compress executable
|
||||
run: tar czf himalaya.tar.gz -C target/x86_64-unknown-linux-musl/release himalaya
|
||||
- name: Upload release asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ needs.create_release.outputs.upload_url }}
|
||||
asset_path: himalaya.tar.gz
|
||||
asset_name: himalaya-musl.tar.gz
|
||||
asset_content_type: application/gzip
|
||||
deploy_crates:
|
||||
runs-on: ubuntu-latest
|
||||
needs: create_release
|
||||
environment: deployment
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Install rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
- uses: katyo/publish-crates@v1
|
||||
with:
|
||||
registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
38
.github/workflows/nix.yml
vendored
38
.github/workflows/nix.yml
vendored
|
@ -1,38 +0,0 @@
|
|||
name: nix
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
nix-build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkouts code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Caches Nix store
|
||||
uses: actions/cache@v3
|
||||
id: nix-cache
|
||||
with:
|
||||
path: /tmp/nix-cache
|
||||
key: nix-${{ hashFiles('**/flake.*') }}
|
||||
|
||||
- name: Installs Nix
|
||||
uses: cachix/install-nix-action@v18
|
||||
with:
|
||||
extra_nix_config: |
|
||||
experimental-features = nix-command flakes
|
||||
|
||||
- name: Imports Nix store cache
|
||||
if: ${{ steps.nix-cache.outputs.cache-hit == 'true' }}
|
||||
run: nix-store --import < /tmp/nix-cache
|
||||
|
||||
- name: Builds the project
|
||||
run: nix build
|
||||
|
||||
- name: Exports Nix store cache
|
||||
if: ${{ steps.nix-cache.outputs.cache-hit != 'true' }}
|
||||
run: nix-store --export $(find /nix/store -maxdepth 1 -name '*-*') > /tmp/nix-cache
|
150
.github/workflows/release.yml
vendored
Normal file
150
.github/workflows/release.yml
vendored
Normal file
|
@ -0,0 +1,150 @@
|
|||
name: release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
|
||||
jobs:
|
||||
create_release:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
steps:
|
||||
- name: Create release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: ${{ github.ref }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
|
||||
deploy_github:
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: create_release
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- target: linux
|
||||
os: ubuntu-latest
|
||||
- target: macos
|
||||
os: macos-latest
|
||||
- target: musl
|
||||
os: ubuntu-latest
|
||||
# TODO: put back when nix package .#windows is fixed
|
||||
# - target: windows
|
||||
# os: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Install Nix
|
||||
uses: cachix/install-nix-action@v20
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
extra_nix_config: |
|
||||
experimental-features = nix-command flakes
|
||||
- uses: cachix/cachix-action@v12
|
||||
with:
|
||||
name: soywod
|
||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||
- name: Build release
|
||||
run: nix build .#${{ matrix.target }}
|
||||
- name: Compress executable
|
||||
run: |
|
||||
mkdir -p {man,completions}
|
||||
cp result/bin/himalaya* .
|
||||
nix run .#${{ matrix.target }} man ./man
|
||||
nix run .#${{ matrix.target }} completion bash > ./completions/himalaya.bash
|
||||
nix run .#${{ matrix.target }} completion elvish > ./completions/himalaya.elvish
|
||||
nix run .#${{ matrix.target }} completion fish > ./completions/himalaya.fish
|
||||
nix run .#${{ matrix.target }} completion powershell > ./completions/himalaya.powershell
|
||||
nix run .#${{ matrix.target }} completion zsh > ./completions/himalaya.zsh
|
||||
tar -czf himalaya.tar.gz himalaya* man completions
|
||||
zip -r himalaya.zip himalaya* man completions
|
||||
- name: Upload tar.gz release asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ needs.create_release.outputs.upload_url }}
|
||||
asset_path: himalaya.tar.gz
|
||||
asset_name: himalaya-${{ matrix.target }}.tar.gz
|
||||
asset_content_type: application/gzip
|
||||
- name: Upload zip release asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ needs.create_release.outputs.upload_url }}
|
||||
asset_path: himalaya.zip
|
||||
asset_name: himalaya-${{ matrix.target }}.zip
|
||||
asset_content_type: application/zip
|
||||
|
||||
# TODO: remove me when nix package .#windows is fixed
|
||||
deploy_windows_github:
|
||||
runs-on: windows-latest
|
||||
needs: create_release
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Install rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
command: check
|
||||
- name: Builds release
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --release
|
||||
- name: Compress executable
|
||||
run: |
|
||||
mkdir man
|
||||
mkdir completions
|
||||
copy target/release/himalaya.exe .
|
||||
./himalaya.exe man ./man
|
||||
./himalaya.exe completion bash > ./completions/himalaya.bash
|
||||
./himalaya.exe completion elvish > ./completions/himalaya.elvish
|
||||
./himalaya.exe completion fish > ./completions/himalaya.fish
|
||||
./himalaya.exe completion powershell > ./completions/himalaya.powershell
|
||||
./himalaya.exe completion zsh > ./completions/himalaya.zsh
|
||||
tar -czf himalaya.tar.gz himalaya.exe man completions
|
||||
zip -r himalaya.zip himalaya.exe man completions
|
||||
- name: Upload tar.gz release asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ needs.create_release.outputs.upload_url }}
|
||||
asset_path: himalaya.tar.gz
|
||||
asset_name: himalaya-windows.tar.gz
|
||||
asset_content_type: application/gzip
|
||||
- name: Upload zip release asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ needs.create_release.outputs.upload_url }}
|
||||
asset_path: himalaya.zip
|
||||
asset_name: himalaya-windows.zip
|
||||
asset_content_type: application/zip
|
||||
|
||||
deploy_crates:
|
||||
runs-on: ubuntu-latest
|
||||
needs: create_release
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Install Nix
|
||||
uses: cachix/install-nix-action@v20
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
extra_nix_config: |
|
||||
experimental-features = nix-command flakes
|
||||
- name: Publish library to crates.io
|
||||
env:
|
||||
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
||||
run: nix develop -c cargo publish --no-verify --token ${CARGO_REGISTRY_TOKEN}
|
36
.github/workflows/tests.yml
vendored
36
.github/workflows/tests.yml
vendored
|
@ -3,36 +3,22 @@ name: tests
|
|||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install libnotmuch
|
||||
run: sudo apt-get install -y libnotmuch-dev
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Start GreenMail testing server
|
||||
run: |
|
||||
docker run \
|
||||
--rm \
|
||||
-d \
|
||||
-e GREENMAIL_OPTS='-Dgreenmail.setup.test.all -Dgreenmail.hostname=0.0.0.0 -Dgreenmail.auth.disabled -Dgreenmail.verbose' \
|
||||
-p 3025:3025 \
|
||||
-p 3110:3110 \
|
||||
-p 3143:3143 \
|
||||
-p 3465:3465 \
|
||||
-p 3993:3993 \
|
||||
-p 3995:3995 \
|
||||
greenmail/standalone:1.6.11
|
||||
- name: Install rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
uses: actions/checkout@v2
|
||||
- name: Install Nix
|
||||
uses: cachix/install-nix-action@v20
|
||||
with:
|
||||
toolchain: stable
|
||||
- name: Run tests
|
||||
uses: actions-rs/cargo@v1
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
extra_nix_config: |
|
||||
experimental-features = nix-command flakes
|
||||
- uses: cachix/cachix-action@v12
|
||||
with:
|
||||
command: test
|
||||
args: --all-features
|
||||
name: soywod
|
||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||
- name: Test
|
||||
run: nix run .#test
|
||||
|
|
60
CHANGELOG.md
60
CHANGELOG.md
|
@ -7,6 +7,56 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.7.2] - 2023-05-01
|
||||
|
||||
### Added
|
||||
|
||||
- Added `create` and `delete` folder commands [sourcehut#54].
|
||||
- Added generated completions and man pages to releases
|
||||
[sourcehut#43].
|
||||
- Added new account config option `sync-folders-strategy` which allows
|
||||
to choose a folders synchronization strategy [sourcehut#59]:
|
||||
|
||||
- `sync-folders-strategy = "all"`: synchronize all existing folders
|
||||
for the current account
|
||||
- `sync-folders-strategy.include = ["folder1", "folder2", …]`:
|
||||
synchronize only the given folders for the current account
|
||||
- `sync-folders-strategy.exclude = ["folder1", "folder2", …]`:
|
||||
synchronizes all folders except the given ones for the current
|
||||
account
|
||||
|
||||
Also added new `account sync` arguments that override the account
|
||||
config option:
|
||||
|
||||
- `-A|--all-folders`: include all folders to the synchronization.
|
||||
- `-F|--include-folder`: include given folders to the
|
||||
synchronization. They can be repeated `-F folder1 folder2` or `-F
|
||||
folder1 -F folder2`.
|
||||
- `-x|--exclude-folder`: exclude given folders from the
|
||||
synchronization. They can be repeated `-x folder1 folder2` or `-x
|
||||
folder1 -F folder2`.
|
||||
|
||||
- Added cargo features `native-tls` (default), `rustls-tls` and
|
||||
`rustls-native-certs`.
|
||||
|
||||
### Changed
|
||||
|
||||
- Made global options truly global, which means they can be used
|
||||
everywhere (not only *before* commands but also *after*)
|
||||
[sourcehut#60].
|
||||
- Replaced reply all `-a` argument with `-A` because it conflicted
|
||||
with the global option `-a|--account`.
|
||||
- Replaced `himalaya-lib` by `pimalaya-email`.
|
||||
- Renamed feature `vendored` to `native-tls-vendored`.
|
||||
- Removed the `develop` branch, all the development is now done on the
|
||||
`master` branch.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed config deserialization issue with `email-hooks` and
|
||||
`email-reading-format`.
|
||||
- Fixed flags case sensitivity.
|
||||
|
||||
## [0.7.1] - 2023-02-14
|
||||
|
||||
### Added
|
||||
|
@ -17,7 +67,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
### Changed
|
||||
|
||||
- Changed the location of the
|
||||
[documentation](https://pimalaya.org/himalaya/docs/).
|
||||
[documentation](https://pimalaya.org/himalaya/).
|
||||
|
||||
### Fixed
|
||||
|
||||
|
@ -498,7 +548,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Password from command [#22]
|
||||
- Set up README [#20]
|
||||
|
||||
[Unreleased]: https://github.com/soywod/himalaya/compare/v0.7.1...develop
|
||||
[Unreleased]: https://github.com/soywod/himalaya/compare/v0.7.2...develop
|
||||
[0.7.2]: https://github.com/soywod/himalaya/compare/v0.7.1...v0.7.2
|
||||
[0.7.1]: https://github.com/soywod/himalaya/compare/v0.7.0...v0.7.1
|
||||
[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
|
||||
|
@ -664,3 +715,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
[#356]: https://github.com/soywod/himalaya/issues/356
|
||||
[#419]: https://github.com/soywod/himalaya/issues/419
|
||||
[#430]: https://github.com/soywod/himalaya/issues/430
|
||||
|
||||
[sourcehut#43]: https://todo.sr.ht/~soywod/pimalaya/43
|
||||
[sourcehut#54]: https://todo.sr.ht/~soywod/pimalaya/54
|
||||
[sourcehut#59]: https://todo.sr.ht/~soywod/pimalaya/59
|
||||
[sourcehut#60]: https://todo.sr.ht/~soywod/pimalaya/60
|
||||
|
|
374
Cargo.lock
generated
374
Cargo.lock
generated
|
@ -94,12 +94,24 @@ version = "0.13.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24a6904aef64d73cf10ab17ebace7befb918b82164785cb89907993be7f83813"
|
||||
|
||||
[[package]]
|
||||
name = "bufstream"
|
||||
version = "0.1.4"
|
||||
|
@ -136,7 +148,7 @@ version = "0.1.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18e9079d1a12a2cc2bffb5db039c43661836ead4082120d5844f02555aca2d46"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.13.1",
|
||||
"encoding_rs",
|
||||
]
|
||||
|
||||
|
@ -180,7 +192,7 @@ version = "4.1.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"clap_lex",
|
||||
"is-terminal",
|
||||
"strsim 0.10.0",
|
||||
|
@ -221,7 +233,7 @@ version = "0.0.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -500,11 +512,11 @@ checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
|||
|
||||
[[package]]
|
||||
name = "email-encoding"
|
||||
version = "0.1.3"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34dd14c63662e0206599796cd5e1ad0268ab2b9d19b868d6050d688eba2bbf98"
|
||||
checksum = "dbfb21b9878cf7a348dcb8559109aabc0ec40d69924bd706fa5149846c4fef75"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.21.0",
|
||||
"memchr 2.5.0",
|
||||
]
|
||||
|
||||
|
@ -765,10 +777,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "himalaya"
|
||||
version = "0.7.1"
|
||||
version = "0.7.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"atty",
|
||||
"chrono",
|
||||
"clap",
|
||||
"clap_complete",
|
||||
"clap_mangen",
|
||||
|
@ -778,10 +791,10 @@ dependencies = [
|
|||
"email_address",
|
||||
"env_logger",
|
||||
"erased-serde",
|
||||
"himalaya-lib",
|
||||
"indicatif",
|
||||
"log",
|
||||
"once_cell",
|
||||
"pimalaya-email",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"shellexpand",
|
||||
|
@ -794,42 +807,6 @@ dependencies = [
|
|||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "himalaya-lib"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6aa84cdd1cec7bd25e319f0decd7d6ec5d765fb983da7a0dea10d797f7e73a8"
|
||||
dependencies = [
|
||||
"ammonia",
|
||||
"chrono",
|
||||
"convert_case",
|
||||
"dirs",
|
||||
"html-escape",
|
||||
"imap",
|
||||
"imap-proto",
|
||||
"lettre",
|
||||
"log",
|
||||
"maildir",
|
||||
"mailparse",
|
||||
"md5",
|
||||
"mime-msg-builder",
|
||||
"native-tls",
|
||||
"notmuch",
|
||||
"ouroboros",
|
||||
"proc-lock",
|
||||
"rayon",
|
||||
"regex",
|
||||
"rfc2047-decoder",
|
||||
"rusqlite",
|
||||
"serde",
|
||||
"shellexpand",
|
||||
"thiserror",
|
||||
"tree_magic",
|
||||
"urlencoding",
|
||||
"utf7-imap",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hostname"
|
||||
version = "0.3.1"
|
||||
|
@ -906,17 +883,6 @@ version = "1.0.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
|
||||
dependencies = [
|
||||
"matches",
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.3.0"
|
||||
|
@ -933,7 +899,7 @@ version = "3.0.0-alpha.9"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1c2ff52273d9cd791687b4510d8a0047277e985a348e411c94fe84e193e7a76"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.13.1",
|
||||
"bufstream",
|
||||
"chrono",
|
||||
"imap-proto",
|
||||
|
@ -942,6 +908,7 @@ dependencies = [
|
|||
"nom 7.1.1",
|
||||
"ouroboros",
|
||||
"regex",
|
||||
"rustls-connector",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1029,25 +996,29 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||
|
||||
[[package]]
|
||||
name = "lettre"
|
||||
version = "0.10.1"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2eabca5e0b4d0e98e7f2243fb5b7520b6af2b65d8f87bcc86f2c75185a6ff243"
|
||||
checksum = "dd84a055407850bcf4791baa77cb4818d37cbb79ad4e60b9b659727b920d2c65"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.21.0",
|
||||
"email-encoding",
|
||||
"email_address",
|
||||
"fastrand",
|
||||
"futures-util",
|
||||
"hostname",
|
||||
"httpdate",
|
||||
"idna 0.2.3",
|
||||
"idna",
|
||||
"mime",
|
||||
"native-tls",
|
||||
"nom 7.1.1",
|
||||
"once_cell",
|
||||
"quoted_printable",
|
||||
"rustls",
|
||||
"rustls-pemfile",
|
||||
"serde",
|
||||
"socket2",
|
||||
"tokio",
|
||||
"webpki-roots",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1058,9 +1029,9 @@ checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
|
|||
|
||||
[[package]]
|
||||
name = "libsqlite3-sys"
|
||||
version = "0.25.2"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29f835d03d717946d28b1d1ed632eb6f0e24a299388ee623d0c23118d3e8a7fa"
|
||||
checksum = "afc22eff61b133b115c6e8c74e818c628d6d5e7a502afea6f64dee076dd94326"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
|
@ -1116,6 +1087,15 @@ version = "0.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
|
||||
|
||||
[[package]]
|
||||
name = "mail-parser"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a2e03aa1d18528b45d0e79e46790f38cfeece6cce3af17a85912677272f36cd"
|
||||
dependencies = [
|
||||
"encoding_rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "maildir"
|
||||
version = "0.6.3"
|
||||
|
@ -1164,12 +1144,6 @@ version = "0.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4"
|
||||
|
||||
[[package]]
|
||||
name = "matches"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
|
||||
|
||||
[[package]]
|
||||
name = "md5"
|
||||
version = "0.7.0"
|
||||
|
@ -1238,6 +1212,18 @@ version = "0.2.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.11"
|
||||
|
@ -1281,6 +1267,15 @@ dependencies = [
|
|||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom8"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8"
|
||||
dependencies = [
|
||||
"memchr 2.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "notmuch"
|
||||
version = "0.8.0"
|
||||
|
@ -1338,7 +1333,7 @@ version = "0.10.43"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "020433887e44c27ff16365eaa2d380547a94544ad509aff6eb5b6e3e0b27b376"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"cfg-if 1.0.0",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
|
@ -1364,6 +1359,15 @@ version = "0.1.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-src"
|
||||
version = "111.25.0+1.1.1t"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3173cd3626c43e3854b1b727422a276e568d9ec5fe8cec197822cf52cfb743d6"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.78"
|
||||
|
@ -1373,6 +1377,7 @@ dependencies = [
|
|||
"autocfg",
|
||||
"cc",
|
||||
"libc",
|
||||
"openssl-src",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
@ -1507,6 +1512,46 @@ dependencies = [
|
|||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pimalaya-email"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bab7e9b0747644aecff24024c88348146bd74bba78fa5ecca10e7e5938d6873a"
|
||||
dependencies = [
|
||||
"ammonia",
|
||||
"chrono",
|
||||
"convert_case",
|
||||
"dirs",
|
||||
"html-escape",
|
||||
"imap",
|
||||
"imap-proto",
|
||||
"lettre",
|
||||
"log",
|
||||
"mail-parser",
|
||||
"maildir",
|
||||
"mailparse",
|
||||
"md5",
|
||||
"mime-msg-builder",
|
||||
"native-tls",
|
||||
"notmuch",
|
||||
"once_cell",
|
||||
"ouroboros",
|
||||
"proc-lock",
|
||||
"rayon",
|
||||
"regex",
|
||||
"rfc2047-decoder",
|
||||
"rusqlite",
|
||||
"rustls",
|
||||
"rustls-native-certs",
|
||||
"shellexpand",
|
||||
"thiserror",
|
||||
"tree_magic",
|
||||
"urlencoding",
|
||||
"utf7-imap",
|
||||
"uuid",
|
||||
"webpki-roots",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.9"
|
||||
|
@ -1702,7 +1747,7 @@ version = "0.2.16"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1748,7 +1793,7 @@ version = "0.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11347d014ae34e1d367aaf9191c37bf071b161e2e1c09c8559c7717e87030e11"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.13.1",
|
||||
"charset",
|
||||
"chumsky 0.8.0",
|
||||
"memchr 2.5.0",
|
||||
|
@ -1756,6 +1801,21 @@ dependencies = [
|
|||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.16.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"spin",
|
||||
"untrusted",
|
||||
"web-sys",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roff"
|
||||
version = "0.2.1"
|
||||
|
@ -1764,11 +1824,11 @@ checksum = "b833d8d034ea094b1ea68aa6d5c740e0d04bad9d16568d08ba6f76823a114316"
|
|||
|
||||
[[package]]
|
||||
name = "rusqlite"
|
||||
version = "0.28.0"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01e213bc3ecb39ac32e81e51ebe31fd888a940515173e3a18a35f8c6e896422a"
|
||||
checksum = "549b9d036d571d42e6e85d1c1425e2ac83491075078ca9a15be021c56b1641f2"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 2.2.1",
|
||||
"fallible-iterator",
|
||||
"fallible-streaming-iterator",
|
||||
"hashlink",
|
||||
|
@ -1782,7 +1842,7 @@ version = "0.36.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3807b5d10909833d3e9acd1eb5fb988f79376ff10fce42937de71a449c4c588"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"errno",
|
||||
"io-lifetimes",
|
||||
"libc",
|
||||
|
@ -1790,6 +1850,51 @@ dependencies = [
|
|||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.20.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f"
|
||||
dependencies = [
|
||||
"log",
|
||||
"ring",
|
||||
"sct",
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-connector"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c6a18f8d10f71bce9bca6eaeb80429460e652f3bcf0381f0c5f8954abf7b3b8"
|
||||
dependencies = [
|
||||
"log",
|
||||
"rustls",
|
||||
"rustls-native-certs",
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-native-certs"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50"
|
||||
dependencies = [
|
||||
"openssl-probe",
|
||||
"rustls-pemfile",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b"
|
||||
dependencies = [
|
||||
"base64 0.21.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.11"
|
||||
|
@ -1818,13 +1923,23 @@ version = "1.0.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898"
|
||||
|
||||
[[package]]
|
||||
name = "sct"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"core-foundation",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
|
@ -1843,18 +1958,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.148"
|
||||
version = "1.0.152"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e53f64bb4ba0191d6d0676e1b141ca55047d83b74f5607e6d8eb88126c52c2dc"
|
||||
checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.148"
|
||||
version = "1.0.152"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a55492425aa53521babf6137309e7d34c20bbfbbfcfe2c7f3a047fd1f6b92c0c"
|
||||
checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -1872,6 +1987,15 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shellexpand"
|
||||
version = "2.1.2"
|
||||
|
@ -1912,6 +2036,12 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "stacker"
|
||||
version = "0.1.15"
|
||||
|
@ -2074,12 +2204,51 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.9"
|
||||
name = "tokio"
|
||||
version = "1.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
|
||||
checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"libc",
|
||||
"mio",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7afcae9e3f0fe2c370fd4657108972cbb2fa9db1b9f84849cefd80741b01cb6"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.19.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e6a7712b49e1775fb9a7b998de6635b299237f48b404dde71704f2e0e7f37e5"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"nom8",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2122,6 +2291,12 @@ version = "0.1.10"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.3.1"
|
||||
|
@ -2129,7 +2304,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna 0.3.0",
|
||||
"idna",
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
|
@ -2151,7 +2326,7 @@ version = "0.3.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e326365261fc2761f0809dfb6032810534a0427bbd8f0edf546f6afeef89f5d"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.13.1",
|
||||
"encoding_rs",
|
||||
"regex",
|
||||
]
|
||||
|
@ -2249,6 +2424,35 @@ version = "0.2.83"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.22.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87"
|
||||
dependencies = [
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
|
|
38
Cargo.toml
38
Cargo.toml
|
@ -1,25 +1,36 @@
|
|||
[package]
|
||||
name = "himalaya"
|
||||
description = "Command-line interface for email management."
|
||||
version = "0.7.1"
|
||||
description = "CLI to manage your emails."
|
||||
version = "0.7.2"
|
||||
authors = ["soywod <clement.douin@posteo.net>"]
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
categories = ["command-line-interface", "command-line-utilities", "email"]
|
||||
keywords = ["cli", "mail", "email", "client", "imap"]
|
||||
homepage = "https://github.com/soywod/himalaya"
|
||||
documentation = "https://github.com/soywod/himalaya/wiki"
|
||||
homepage = "https://pimalaya.org/himalaya/"
|
||||
documentation = "https://pimalaya.org/himalaya/"
|
||||
repository = "https://github.com/soywod/himalaya"
|
||||
|
||||
[package.metadata.deb]
|
||||
priority = "optional"
|
||||
section = "mail"
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
||||
[features]
|
||||
imap-backend = ["himalaya-lib/imap-backend"]
|
||||
smtp-sender = ["himalaya-lib/smtp-sender"]
|
||||
notmuch-backend = ["himalaya-lib/notmuch-backend"]
|
||||
default = ["imap-backend", "smtp-sender"]
|
||||
default = ["rustls-tls", "rustls-native-certs", "imap-backend", "smtp-sender"]
|
||||
|
||||
# rustls
|
||||
rustls-tls = ["pimalaya-email/rustls-tls"]
|
||||
rustls-native-certs = ["pimalaya-email/rustls-native-certs"]
|
||||
|
||||
# native tls
|
||||
native-tls = ["pimalaya-email/native-tls"]
|
||||
native-tls-vendored = ["pimalaya-email/native-tls-vendored"]
|
||||
|
||||
# backends
|
||||
imap-backend = ["pimalaya-email/imap-backend"]
|
||||
notmuch-backend = ["pimalaya-email/notmuch-backend"]
|
||||
|
||||
# senders
|
||||
smtp-sender = ["pimalaya-email/smtp-sender"]
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.3"
|
||||
|
@ -27,6 +38,7 @@ tempfile = "3.3"
|
|||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
atty = "0.2"
|
||||
chrono = "0.4.23"
|
||||
clap = "4.0"
|
||||
clap_complete = "4.0"
|
||||
clap_mangen = "0.2"
|
||||
|
@ -36,7 +48,7 @@ dialoguer = "0.10.2"
|
|||
email_address = "0.2.4"
|
||||
env_logger = "0.8"
|
||||
erased-serde = "0.3"
|
||||
himalaya-lib = "0.6.0"
|
||||
pimalaya-email = "0.7.1"
|
||||
indicatif = "0.17"
|
||||
log = "0.4"
|
||||
once_cell = "1.16.0"
|
||||
|
@ -45,7 +57,7 @@ serde_json = "1.0"
|
|||
shellexpand = "2.1"
|
||||
termcolor = "1.1"
|
||||
terminal_size = "0.1"
|
||||
toml = "0.5"
|
||||
toml = "0.7.2"
|
||||
unicode-width = "0.1"
|
||||
url = "2.2"
|
||||
uuid = { version = "0.8", features = ["v4"] }
|
||||
|
|
43
README.md
43
README.md
|
@ -1,8 +1,9 @@
|
|||
# 📫 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)
|
||||
|
||||
Himalaya is a CLI based on the
|
||||
[himalaya-lib](https://git.sr.ht/~soywod/himalaya-lib) that allows you
|
||||
to manipulate your emails using commands in your console.
|
||||
https://pimalaya.org/himalaya/
|
||||
|
||||
CLI to manage your emails, based on the
|
||||
[pimalaya-email](https://sr.ht/~soywod/pimalaya/) library.
|
||||
|
||||
![image](https://user-images.githubusercontent.com/10437171/138774902-7b9de5a3-93eb-44b0-8cfb-6d2e11e3b1aa.png)
|
||||
|
||||
|
@ -25,19 +26,19 @@ production before the `v1.0.0`.*
|
|||
- JSON output
|
||||
- …
|
||||
|
||||
[Folder listing]: https://pimalaya.org/himalaya/docs/cli/usage/folders/list.html
|
||||
[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
|
||||
[Folder listing]: https://pimalaya.org/himalaya/cli/usage/folders/list.html
|
||||
[Envelopes listing]: https://pimalaya.org/himalaya/cli/usage/envelopes/list.html
|
||||
[searching]: https://pimalaya.org/himalaya/cli/usage/envelopes/search.html
|
||||
[sorting]: https://pimalaya.org/himalaya/cli/usage/envelopes/sort.html
|
||||
[Email composition]: https://pimalaya.org/himalaya/cli/usage/emails/write.html
|
||||
[copy]: https://pimalaya.org/himalaya/cli/usage/emails/copy.html
|
||||
[move]: https://pimalaya.org/himalaya/cli/usage/emails/move.html
|
||||
[delete]: https://pimalaya.org/himalaya/cli/usage/emails/delete.html
|
||||
[Multi-accounting]: https://pimalaya.org/himalaya/cli/configuration.html
|
||||
[Account listing]: https://pimalaya.org/himalaya/cli/usage/accounts/list.html
|
||||
[Account synchronization]: https://pimalaya.org/himalaya/cli/usage/accounts/synchronize.html
|
||||
[real-time notifications]: https://pimalaya.org/himalaya/cli/usage/notifications.html
|
||||
[Completions]: https://pimalaya.org/himalaya/cli/tips/completion.html
|
||||
|
||||
## Installation
|
||||
|
||||
|
@ -60,6 +61,9 @@ $ yay -S himalaya-git
|
|||
# Homebrew
|
||||
$ brew install himalaya
|
||||
|
||||
# Scoop
|
||||
$ scoop install himalaya
|
||||
|
||||
# Cargo
|
||||
$ cargo install himalaya
|
||||
|
||||
|
@ -68,7 +72,7 @@ $ nix-env -i himalaya
|
|||
```
|
||||
|
||||
*See the
|
||||
[documentation](https://pimalaya.org/himalaya/docs/cli/installation.html)
|
||||
[documentation](https://pimalaya.org/himalaya/cli/installation.html)
|
||||
for other installation methods.*
|
||||
|
||||
</td>
|
||||
|
@ -78,11 +82,12 @@ for other installation methods.*
|
|||
## Configuration
|
||||
|
||||
Please read the
|
||||
[documentation](https://pimalaya.org/himalaya/docs/cli/configuration.html).
|
||||
[documentation](https://pimalaya.org/himalaya/cli/configuration.html).
|
||||
|
||||
## Contributing
|
||||
|
||||
If you find a **bug**, please send an email at
|
||||
If you find a **bug** that [does not exist
|
||||
yet](https://todo.sr.ht/~soywod/pimalaya), please send an email at
|
||||
[~soywod/pimalaya@todo.sr.ht](mailto:~soywod/pimalaya@todo.sr.ht).
|
||||
|
||||
If you have a **question**, please send an email at
|
||||
|
|
52
config.sample.toml
Normal file
52
config.sample.toml
Normal file
|
@ -0,0 +1,52 @@
|
|||
display-name = "Display NAME"
|
||||
signature-delim = "~~"
|
||||
signature = "~/.signature"
|
||||
downloads-dir = "~/downloads"
|
||||
folder-listing-page-size = 12
|
||||
email-listing-page-size = 12
|
||||
email-reading-headers = ["From", "To"]
|
||||
email-reading-verify-cmd = "gpg --verify -q"
|
||||
email-reading-decrypt-cmd = "gpg -dq"
|
||||
email-writing-sign-cmd = "gpg -o - -saq"
|
||||
email-writing-encrypt-cmd = "gpg -o - -eqar <recipient>"
|
||||
|
||||
[example]
|
||||
default = true
|
||||
display-name = "Display NAME (gmail)"
|
||||
email = "display.name@gmail.local"
|
||||
|
||||
backend = "imap"
|
||||
imap-host = "imap.gmail.com"
|
||||
imap-login = "display.name@gmail.local"
|
||||
imap-passwd-cmd = "pass show gmail"
|
||||
imap-port = 993
|
||||
imap-ssl = true
|
||||
imap-starttls = false
|
||||
imap-notify-cmd = """📫 "<sender>" "<subject>""""
|
||||
imap-notify-query = "NOT SEEN"
|
||||
imap-watch-cmds = ["echo \"received server changes!\""]
|
||||
|
||||
sender = "smtp"
|
||||
smtp-host = "smtp.gmail.com"
|
||||
smtp-login = "display.name@gmail.local"
|
||||
smtp-passwd-cmd = "pass show piana/gmail"
|
||||
smtp-port = 465
|
||||
smtp-ssl = true
|
||||
smtp-starttls = false
|
||||
|
||||
sync = true
|
||||
sync-dir = "/tmp/sync/gmail"
|
||||
sync-folders-strategy.only = ["INBOX"]
|
||||
|
||||
[example.folder-aliases]
|
||||
inbox = "INBOX"
|
||||
drafts = "[Gmail]/Drafts"
|
||||
sent = "[Gmail]/Sent Mail"
|
||||
trash = "[Gmail]/Trash"
|
||||
|
||||
[example.email-hooks]
|
||||
pre-send = "echo $1"
|
||||
|
||||
[example.email-reading-format]
|
||||
type = "fixed"
|
||||
width = 64
|
152
flake.lock
152
flake.lock
|
@ -1,28 +1,36 @@
|
|||
{
|
||||
"nodes": {
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"fenix": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"rust-analyzer-src": "rust-analyzer-src"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1673956053,
|
||||
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
|
||||
"lastModified": 1682835640,
|
||||
"narHash": "sha256-rAYEOd4nZFLjDlrF9KNlcopPKNVtr1svSXcEValVRMY=",
|
||||
"owner": "nix-community",
|
||||
"repo": "fenix",
|
||||
"rev": "006b429d3c493f4c5b1743a94f71ad961c7693ab",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"owner": "nix-community",
|
||||
"repo": "fenix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1659877975,
|
||||
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
|
||||
"lastModified": 1681202837,
|
||||
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
|
||||
"rev": "cfacdce06f30d2b68473a46042957675eebb3401",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -31,16 +39,38 @@
|
|||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gitignore": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1660459072,
|
||||
"narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"rev": "a20de23b925fd8264fd7fad6454652e142fd7f73",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"naersk": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1671096816,
|
||||
"narHash": "sha256-ezQCsNgmpUHdZANDCILm3RvtO1xH8uujk/+EqNvzIOg=",
|
||||
"lastModified": 1679567394,
|
||||
"narHash": "sha256-ZvLuzPeARDLiQUt6zSZFGOs+HZmE+3g4QURc8mkBsfM=",
|
||||
"owner": "nix-community",
|
||||
"repo": "naersk",
|
||||
"rev": "d998160d6a076cfe8f9741e56aeec7e267e3e114",
|
||||
"rev": "88cd22380154a2c36799fe8098888f0f59861a15",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -51,88 +81,58 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1675698036,
|
||||
"narHash": "sha256-BgsQkQewdlQi8gapJN4phpxkI/FCE/2sORBaFcYbp/A=",
|
||||
"owner": "NixOS",
|
||||
"lastModified": 1682669017,
|
||||
"narHash": "sha256-Vi+p4y3wnl0/4gcwTdmCO398kKlDaUrNROtf3GOD2NY=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "1046c7b92e908a1202c0f1ba3fc21d19e1cf1b62",
|
||||
"rev": "7449971a3ecf857b4a554cf79b1d9dcc1a4647d8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1664356419,
|
||||
"narHash": "sha256-PD0hM9YWp2lepAJk7edh8g1VtzJip5rals1fpoQUlY0=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "46e8398474ac3b1b7bb198bf9097fc213bbf59b1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"nixpkgs_3": {
|
||||
"locked": {
|
||||
"lastModified": 1665296151,
|
||||
"narHash": "sha256-uOB0oxqxN9K7XGF1hcnY+PQnlQJ+3bP2vCn/+Ru/bbc=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "14ccaaedd95a488dd7ae142757884d8e125b3363",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-22.11",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"naersk": "naersk",
|
||||
"nixpkgs": "nixpkgs_2",
|
||||
"rust-overlay": "rust-overlay",
|
||||
"utils": "utils"
|
||||
}
|
||||
},
|
||||
"rust-overlay": {
|
||||
"inputs": {
|
||||
"fenix": "fenix",
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs_3"
|
||||
},
|
||||
"gitignore": "gitignore",
|
||||
"naersk": "naersk",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
},
|
||||
"rust-analyzer-src": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1675823425,
|
||||
"narHash": "sha256-o/uLXQdq3OrRAv4BZVVY0VmhMmQBLWw6Y4o+p6ZiaR4=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "02e1abbdcbc2d516193ff8a7add71f44cd976ba0",
|
||||
"lastModified": 1682792082,
|
||||
"narHash": "sha256-1nuP2rqipsdB8IJ3N5ws3FQm4dX3mKIueIrCUSu1bWw=",
|
||||
"owner": "rust-lang",
|
||||
"repo": "rust-analyzer",
|
||||
"rev": "7bcb4c2ef23e151a639ff918fbb8ab9d521eabb9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"owner": "rust-lang",
|
||||
"ref": "nightly",
|
||||
"repo": "rust-analyzer",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"utils": {
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1667395993,
|
||||
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
|
|
171
flake.nix
171
flake.nix
|
@ -1,65 +1,140 @@
|
|||
{
|
||||
description = "Command-line interface for email management.";
|
||||
description = "CLI to manage your emails.";
|
||||
|
||||
inputs = {
|
||||
utils.url = "github:numtide/flake-utils";
|
||||
rust-overlay.url = "github:oxalica/rust-overlay";
|
||||
naersk.url = "github:nix-community/naersk";
|
||||
flake-compat = {
|
||||
url = "github:edolstra/flake-compat";
|
||||
flake = false;
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-22.11";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
gitignore = {
|
||||
url = "github:hercules-ci/gitignore.nix";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
fenix = {
|
||||
url = "github:nix-community/fenix";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
naersk = {
|
||||
url = "github:nix-community/naersk";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, utils, rust-overlay, naersk, ... }:
|
||||
utils.lib.eachDefaultSystem
|
||||
(system:
|
||||
outputs = { self, nixpkgs, flake-utils, gitignore, fenix, naersk }:
|
||||
let
|
||||
inherit (gitignore.lib) gitignoreSource;
|
||||
|
||||
mkToolchain = buildPlatform:
|
||||
fenix.packages.${buildPlatform}.minimal.toolchain;
|
||||
|
||||
mkToolchainWithTarget = buildPlatform: targetPlatform:
|
||||
with fenix.packages.${buildPlatform}; combine [
|
||||
stable.rustc
|
||||
stable.cargo
|
||||
targets.${targetPlatform}.stable.rust-std
|
||||
];
|
||||
|
||||
mkDevShells = buildPlatform:
|
||||
let
|
||||
name = "himalaya";
|
||||
overlays = [ (import rust-overlay) ];
|
||||
pkgs = import nixpkgs { inherit system overlays; };
|
||||
pkgs = import nixpkgs { system = buildPlatform; };
|
||||
rust-toolchain = fenix.packages.${buildPlatform}.fromToolchainFile {
|
||||
file = ./rust-toolchain.toml;
|
||||
sha256 = "eMJethw5ZLrJHmoN2/l0bIyQjoTX1NsvalWSscTixpI=";
|
||||
};
|
||||
in
|
||||
rec {
|
||||
# nix build
|
||||
defaultPackage = packages.${name};
|
||||
packages = {
|
||||
${name} = naersk.lib.${system}.buildPackage {
|
||||
pname = name;
|
||||
root = ./.;
|
||||
nativeBuildInputs = with pkgs; [ openssl.dev pkg-config ];
|
||||
overrideMain = _: {
|
||||
postInstall = ''
|
||||
mkdir -p $out/share/applications/
|
||||
cp assets/himalaya.desktop $out/share/applications/
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# nix run
|
||||
defaultApp = apps.${name};
|
||||
apps.${name} = utils.lib.mkApp {
|
||||
inherit name;
|
||||
drv = packages.${name};
|
||||
};
|
||||
|
||||
# nix develop
|
||||
devShell = pkgs.mkShell {
|
||||
inputsFrom = builtins.attrValues self.packages.${system};
|
||||
nativeBuildInputs = with pkgs; [
|
||||
# Nix LSP + formatter
|
||||
{
|
||||
default = pkgs.mkShell {
|
||||
buildInputs = with pkgs; [
|
||||
# Nix env
|
||||
rnix-lsp
|
||||
nixpkgs-fmt
|
||||
|
||||
# Rust env
|
||||
(rust-bin.fromRustupToolchainFile ./rust-toolchain.toml)
|
||||
cargo-watch
|
||||
rust-analyzer
|
||||
rust-toolchain
|
||||
|
||||
# Notmuch
|
||||
# notmuch
|
||||
notmuch
|
||||
];
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
mkPackage = pkgs: buildPlatform: targetPlatform: package:
|
||||
let
|
||||
toolchain =
|
||||
if isNull targetPlatform
|
||||
then mkToolchain buildPlatform
|
||||
else mkToolchainWithTarget buildPlatform targetPlatform;
|
||||
naersk' = naersk.lib.${buildPlatform}.override {
|
||||
cargo = toolchain;
|
||||
rustc = toolchain;
|
||||
};
|
||||
package' = {
|
||||
name = "himalaya";
|
||||
src = gitignoreSource ./.;
|
||||
overrideMain = _: {
|
||||
postInstall = ''
|
||||
mkdir -p $out/share/applications/
|
||||
cp assets/himalaya.desktop $out/share/applications/
|
||||
'';
|
||||
};
|
||||
} // pkgs.lib.optionalAttrs (!isNull targetPlatform) {
|
||||
CARGO_BUILD_TARGET = targetPlatform;
|
||||
} // package;
|
||||
in
|
||||
naersk'.buildPackage package';
|
||||
|
||||
mkPackages = buildPlatform:
|
||||
let
|
||||
pkgs = import nixpkgs { system = buildPlatform; };
|
||||
mkPackageWithTarget = mkPackage pkgs buildPlatform;
|
||||
defaultPackage = mkPackage pkgs buildPlatform null { };
|
||||
in
|
||||
{
|
||||
default = defaultPackage;
|
||||
linux = defaultPackage;
|
||||
macos = defaultPackage;
|
||||
musl = mkPackageWithTarget "x86_64-unknown-linux-musl" (with pkgs.pkgsStatic; {
|
||||
CARGO_BUILD_RUSTFLAGS = "-C target-feature=+crt-static";
|
||||
SQLITE3_STATIC = 1;
|
||||
SQLITE3_LIB_DIR = "${sqlite.out}/lib";
|
||||
hardeningDisable = [ "all" ];
|
||||
});
|
||||
# FIXME: package does not build, assembler messages: unknown
|
||||
# pseudo-op…
|
||||
windows = mkPackageWithTarget "x86_64-pc-windows-gnu" {
|
||||
strictDeps = true;
|
||||
depsBuildBuild = with pkgs.pkgsCross.mingwW64; [
|
||||
stdenv.cc
|
||||
windows.pthreads
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
mkApp = drv: flake-utils.lib.mkApp {
|
||||
inherit drv;
|
||||
name = "himalaya";
|
||||
};
|
||||
|
||||
mkApps = buildPlatform: {
|
||||
default = mkApp self.packages.${buildPlatform}.default;
|
||||
linux = mkApp self.packages.${buildPlatform}.linux;
|
||||
macos = mkApp self.packages.${buildPlatform}.macos;
|
||||
musl = mkApp self.packages.${buildPlatform}.musl;
|
||||
windows =
|
||||
let
|
||||
pkgs = import nixpkgs { system = buildPlatform; };
|
||||
wine = pkgs.wine.override { wineBuild = "wine64"; };
|
||||
himalaya = self.packages.${buildPlatform}.windows;
|
||||
app = pkgs.writeShellScriptBin "himalaya" ''
|
||||
export WINEPREFIX="$(mktemp -d)"
|
||||
${wine}/bin/wine64 ${himalaya}/bin/himalaya.exe $@
|
||||
'';
|
||||
in
|
||||
mkApp app;
|
||||
};
|
||||
|
||||
in
|
||||
flake-utils.lib.eachDefaultSystem (system: {
|
||||
devShells = mkDevShells system;
|
||||
packages = mkPackages system;
|
||||
apps = mkApps system;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,2 +1,11 @@
|
|||
[toolchain]
|
||||
channel = "stable"
|
||||
profile = "default"
|
||||
components = [
|
||||
"cargo",
|
||||
"clippy",
|
||||
"rust-analyzer",
|
||||
"rust-std",
|
||||
"rustc",
|
||||
"rustfmt",
|
||||
]
|
||||
|
|
7
src/cache/args.rs
vendored
7
src/cache/args.rs
vendored
|
@ -8,8 +8,13 @@ const ARG_DISABLE_CACHE: &str = "disable-cache";
|
|||
/// the user to disable any sort of cache.
|
||||
pub fn arg() -> Arg {
|
||||
Arg::new(ARG_DISABLE_CACHE)
|
||||
.long("disable-cache")
|
||||
.help("Disable any sort of cache")
|
||||
.long_help(
|
||||
"Disable any sort of cache. The action depends on
|
||||
the command it applies on.",
|
||||
)
|
||||
.long("disable-cache")
|
||||
.global(true)
|
||||
.action(ArgAction::SetTrue)
|
||||
}
|
||||
|
||||
|
|
|
@ -8,9 +8,10 @@ const ARG_CONFIG: &str = "config";
|
|||
/// user to customize the config file path.
|
||||
pub fn arg() -> Arg {
|
||||
Arg::new(ARG_CONFIG)
|
||||
.help("Set a custom configuration file path")
|
||||
.long("config")
|
||||
.short('c')
|
||||
.help("Forces a specific config file path")
|
||||
.global(true)
|
||||
.value_name("PATH")
|
||||
}
|
||||
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use dirs::{config_dir, home_dir};
|
||||
use himalaya_lib::{AccountConfig, BackendConfig, EmailHooks, EmailTextPlainFormat};
|
||||
use log::{debug, trace};
|
||||
use pimalaya_email::{AccountConfig, BackendConfig, EmailHooks, EmailTextPlainFormat};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{collections::HashMap, fs, path::PathBuf};
|
||||
use toml;
|
||||
|
@ -17,7 +17,7 @@ use crate::{
|
|||
};
|
||||
|
||||
/// Represents the user config file.
|
||||
#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct DeserializedConfig {
|
||||
#[serde(alias = "name")]
|
||||
|
@ -31,12 +31,8 @@ pub struct DeserializedConfig {
|
|||
|
||||
pub email_listing_page_size: Option<usize>,
|
||||
pub email_reading_headers: Option<Vec<String>>,
|
||||
#[serde(
|
||||
default,
|
||||
with = "EmailTextPlainFormatOptionDef",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub email_reading_format: Option<EmailTextPlainFormat>,
|
||||
#[serde(default, with = "EmailTextPlainFormatDef")]
|
||||
pub email_reading_format: EmailTextPlainFormat,
|
||||
pub email_reading_verify_cmd: Option<String>,
|
||||
pub email_reading_decrypt_cmd: Option<String>,
|
||||
pub email_writing_headers: Option<Vec<String>>,
|
||||
|
@ -44,10 +40,10 @@ pub struct DeserializedConfig {
|
|||
pub email_writing_encrypt_cmd: Option<String>,
|
||||
#[serde(
|
||||
default,
|
||||
with = "EmailHooksOptionDef",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
with = "EmailHooksDef",
|
||||
skip_serializing_if = "EmailHooks::is_empty"
|
||||
)]
|
||||
pub email_hooks: Option<EmailHooks>,
|
||||
pub email_hooks: EmailHooks,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub accounts: HashMap<String, DeserializedAccountConfig>,
|
||||
|
@ -123,12 +119,12 @@ impl DeserializedConfig {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use himalaya_lib::{EmailSender, MaildirConfig, SendmailConfig, SmtpConfig};
|
||||
use pimalaya_email::{EmailSender, MaildirConfig, SendmailConfig, SmtpConfig};
|
||||
|
||||
#[cfg(feature = "imap-backend")]
|
||||
use himalaya_lib::ImapConfig;
|
||||
use pimalaya_email::ImapConfig;
|
||||
#[cfg(feature = "notmuch-backend")]
|
||||
use himalaya_lib::NotmuchConfig;
|
||||
use pimalaya_email::NotmuchConfig;
|
||||
|
||||
use std::io::Write;
|
||||
use tempfile::NamedTempFile;
|
||||
|
@ -163,10 +159,11 @@ mod tests {
|
|||
fn account_missing_backend_field() {
|
||||
let config = make_config("[account]");
|
||||
|
||||
assert_eq!(
|
||||
config.unwrap_err().root_cause().to_string(),
|
||||
"missing field `backend` at line 1 column 1"
|
||||
);
|
||||
assert!(config
|
||||
.unwrap_err()
|
||||
.root_cause()
|
||||
.to_string()
|
||||
.contains("missing field `backend`"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -180,7 +177,7 @@ mod tests {
|
|||
.unwrap_err()
|
||||
.root_cause()
|
||||
.to_string()
|
||||
.starts_with("unknown variant `bad`"));
|
||||
.contains("unknown variant `bad`"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -190,10 +187,11 @@ mod tests {
|
|||
backend = \"none\"",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
config.unwrap_err().root_cause().to_string(),
|
||||
"missing field `email` at line 1 column 1"
|
||||
);
|
||||
assert!(config
|
||||
.unwrap_err()
|
||||
.root_cause()
|
||||
.to_string()
|
||||
.contains("missing field `email`"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -205,10 +203,11 @@ mod tests {
|
|||
backend = \"imap\"",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
config.unwrap_err().root_cause().to_string(),
|
||||
"missing field `imap-host` at line 1 column 1"
|
||||
);
|
||||
assert!(config
|
||||
.unwrap_err()
|
||||
.root_cause()
|
||||
.to_string()
|
||||
.contains("missing field `imap-host`"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -221,10 +220,11 @@ mod tests {
|
|||
imap-host = \"localhost\"",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
config.unwrap_err().root_cause().to_string(),
|
||||
"missing field `imap-port` at line 1 column 1"
|
||||
);
|
||||
assert!(config
|
||||
.unwrap_err()
|
||||
.root_cause()
|
||||
.to_string()
|
||||
.contains("missing field `imap-port`"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -238,10 +238,11 @@ mod tests {
|
|||
imap-port = 993",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
config.unwrap_err().root_cause().to_string(),
|
||||
"missing field `imap-login` at line 1 column 1"
|
||||
);
|
||||
assert!(config
|
||||
.unwrap_err()
|
||||
.root_cause()
|
||||
.to_string()
|
||||
.contains("missing field `imap-login`"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -256,10 +257,11 @@ mod tests {
|
|||
imap-login = \"login\"",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
config.unwrap_err().root_cause().to_string(),
|
||||
"missing field `imap-passwd-cmd` at line 1 column 1"
|
||||
);
|
||||
assert!(config
|
||||
.unwrap_err()
|
||||
.root_cause()
|
||||
.to_string()
|
||||
.contains("missing field `imap-passwd-cmd`"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -271,10 +273,11 @@ mod tests {
|
|||
backend = \"maildir\"",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
config.unwrap_err().root_cause().to_string(),
|
||||
"missing field `maildir-root-dir` at line 1 column 1"
|
||||
);
|
||||
assert!(config
|
||||
.unwrap_err()
|
||||
.root_cause()
|
||||
.to_string()
|
||||
.contains("missing field `maildir-root-dir`"));
|
||||
}
|
||||
|
||||
#[cfg(feature = "notmuch-backend")]
|
||||
|
@ -287,10 +290,11 @@ mod tests {
|
|||
backend = \"notmuch\"",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
config.unwrap_err().root_cause().to_string(),
|
||||
"missing field `notmuch-db-path` at line 1 column 1"
|
||||
);
|
||||
assert!(config
|
||||
.unwrap_err()
|
||||
.root_cause()
|
||||
.to_string()
|
||||
.contains("missing field `notmuch-db-path`"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -301,10 +305,11 @@ mod tests {
|
|||
backend = \"none\"",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
config.unwrap_err().root_cause().to_string(),
|
||||
"missing field `sender` at line 1 column 1"
|
||||
);
|
||||
assert!(config
|
||||
.unwrap_err()
|
||||
.root_cause()
|
||||
.to_string()
|
||||
.contains("missing field `sender`"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -316,10 +321,11 @@ mod tests {
|
|||
sender = \"bad\"",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
config.unwrap_err().root_cause().to_string(),
|
||||
"unknown variant `bad`, expected one of `none`, `smtp`, `sendmail` at line 1 column 1",
|
||||
);
|
||||
assert!(config
|
||||
.unwrap_err()
|
||||
.root_cause()
|
||||
.to_string()
|
||||
.contains("unknown variant `bad`, expected one of `none`, `smtp`, `sendmail`"),);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -331,10 +337,11 @@ mod tests {
|
|||
sender = \"smtp\"",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
config.unwrap_err().root_cause().to_string(),
|
||||
"missing field `smtp-host` at line 1 column 1"
|
||||
);
|
||||
assert!(config
|
||||
.unwrap_err()
|
||||
.root_cause()
|
||||
.to_string()
|
||||
.contains("missing field `smtp-host`"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -347,10 +354,11 @@ mod tests {
|
|||
smtp-host = \"localhost\"",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
config.unwrap_err().root_cause().to_string(),
|
||||
"missing field `smtp-port` at line 1 column 1"
|
||||
);
|
||||
assert!(config
|
||||
.unwrap_err()
|
||||
.root_cause()
|
||||
.to_string()
|
||||
.contains("missing field `smtp-port`"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -364,10 +372,11 @@ mod tests {
|
|||
smtp-port = 25",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
config.unwrap_err().root_cause().to_string(),
|
||||
"missing field `smtp-login` at line 1 column 1"
|
||||
);
|
||||
assert!(config
|
||||
.unwrap_err()
|
||||
.root_cause()
|
||||
.to_string()
|
||||
.contains("missing field `smtp-login`"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -382,10 +391,11 @@ mod tests {
|
|||
smtp-login = \"login\"",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
config.unwrap_err().root_cause().to_string(),
|
||||
"missing field `smtp-passwd-cmd` at line 1 column 1"
|
||||
);
|
||||
assert!(config
|
||||
.unwrap_err()
|
||||
.root_cause()
|
||||
.to_string()
|
||||
.contains("missing field `smtp-passwd-cmd`"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -397,10 +407,11 @@ mod tests {
|
|||
sender = \"sendmail\"",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
config.unwrap_err().root_cause().to_string(),
|
||||
"missing field `sendmail-cmd` at line 1 column 1"
|
||||
);
|
||||
assert!(config
|
||||
.unwrap_err()
|
||||
.root_cause()
|
||||
.to_string()
|
||||
.contains("missing field `sendmail-cmd`"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use himalaya_lib::{
|
||||
EmailHooks, EmailSender, EmailTextPlainFormat, MaildirConfig, SendmailConfig, SmtpConfig,
|
||||
use pimalaya_email::{
|
||||
folder::sync::Strategy as SyncFoldersStrategy, EmailHooks, EmailSender, EmailTextPlainFormat,
|
||||
MaildirConfig, SendmailConfig, SmtpConfig,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{collections::HashSet, path::PathBuf};
|
||||
|
||||
#[cfg(feature = "imap-backend")]
|
||||
use himalaya_lib::ImapConfig;
|
||||
use pimalaya_email::ImapConfig;
|
||||
|
||||
#[cfg(feature = "notmuch-backend")]
|
||||
use himalaya_lib::NotmuchConfig;
|
||||
use pimalaya_email::NotmuchConfig;
|
||||
|
||||
#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "SmtpConfig")]
|
||||
struct SmtpConfigDef {
|
||||
#[serde(rename = "smtp-host")]
|
||||
|
@ -31,7 +31,7 @@ struct SmtpConfigDef {
|
|||
}
|
||||
|
||||
#[cfg(feature = "imap-backend")]
|
||||
#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "ImapConfig")]
|
||||
pub struct ImapConfigDef {
|
||||
#[serde(rename = "imap-host")]
|
||||
|
@ -56,41 +56,39 @@ pub struct ImapConfigDef {
|
|||
pub watch_cmds: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "MaildirConfig")]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "MaildirConfig", rename_all = "kebab-case")]
|
||||
pub struct MaildirConfigDef {
|
||||
#[serde(rename = "maildir-root-dir")]
|
||||
pub root_dir: PathBuf,
|
||||
}
|
||||
|
||||
#[cfg(feature = "notmuch-backend")]
|
||||
#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "NotmuchConfig")]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "NotmuchConfig", rename_all = "kebab-case")]
|
||||
pub struct NotmuchConfigDef {
|
||||
#[serde(rename = "notmuch-db-path")]
|
||||
pub db_path: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "Option<EmailTextPlainFormat>")]
|
||||
pub enum EmailTextPlainFormatOptionDef {
|
||||
#[serde(with = "EmailTextPlainFormatDef")]
|
||||
Some(EmailTextPlainFormat),
|
||||
#[default]
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "EmailTextPlainFormat", rename_all = "snake_case")]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(
|
||||
remote = "EmailTextPlainFormat",
|
||||
tag = "type",
|
||||
content = "width",
|
||||
rename_all = "kebab-case"
|
||||
)]
|
||||
pub enum EmailTextPlainFormatDef {
|
||||
#[default]
|
||||
Auto,
|
||||
Flowed,
|
||||
Fixed(usize),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "EmailSender", tag = "sender", rename_all = "snake_case")]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "EmailSender", tag = "sender", rename_all = "kebab-case")]
|
||||
pub enum EmailSenderDef {
|
||||
#[default]
|
||||
None,
|
||||
#[serde(with = "SmtpConfigDef")]
|
||||
Smtp(SmtpConfig),
|
||||
|
@ -98,27 +96,30 @@ pub enum EmailSenderDef {
|
|||
Sendmail(SendmailConfig),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "SendmailConfig")]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "SendmailConfig", rename_all = "kebab-case")]
|
||||
pub struct SendmailConfigDef {
|
||||
#[serde(rename = "sendmail-cmd")]
|
||||
cmd: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "Option<EmailHooks>")]
|
||||
pub enum EmailHooksOptionDef {
|
||||
#[serde(with = "EmailHooksDef")]
|
||||
Some(EmailHooks),
|
||||
#[default]
|
||||
None,
|
||||
}
|
||||
|
||||
/// Represents the email hooks. Useful for doing extra email
|
||||
/// processing before or after sending it.
|
||||
#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "EmailHooks")]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "EmailHooks", rename_all = "kebab-case")]
|
||||
pub struct EmailHooksDef {
|
||||
/// Represents the hook called just before sending an email.
|
||||
pub pre_send: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "SyncFoldersStrategy", rename_all = "kebab-case")]
|
||||
pub enum SyncFoldersStrategyDef {
|
||||
#[default]
|
||||
All,
|
||||
#[serde(alias = "only")]
|
||||
Include(HashSet<String>),
|
||||
#[serde(alias = "except")]
|
||||
#[serde(alias = "ignore")]
|
||||
Exclude(HashSet<String>),
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::account::{
|
|||
};
|
||||
use anyhow::Result;
|
||||
use dialoguer::{Input, Select};
|
||||
use himalaya_lib::ImapConfig;
|
||||
use pimalaya_email::ImapConfig;
|
||||
|
||||
#[cfg(feature = "imap-backend")]
|
||||
pub(crate) fn configure(base: DeserializedBaseAccountConfig) -> Result<DeserializedAccountConfig> {
|
||||
|
|
|
@ -5,7 +5,7 @@ use crate::account::{
|
|||
use anyhow::Result;
|
||||
use dialoguer::Input;
|
||||
use dirs::home_dir;
|
||||
use himalaya_lib::MaildirConfig;
|
||||
use pimalaya_email::MaildirConfig;
|
||||
|
||||
pub(crate) fn configure(base: DeserializedBaseAccountConfig) -> Result<DeserializedAccountConfig> {
|
||||
let input = if let Some(home) = home_dir() {
|
||||
|
|
|
@ -32,7 +32,6 @@ const SECURITY_PROTOCOLS: &[&str] = &["SSL/TLS", "STARTTLS", "None"];
|
|||
static THEME: Lazy<ColorfulTheme> = Lazy::new(ColorfulTheme::default);
|
||||
|
||||
pub(crate) fn wizard() -> Result<DeserializedConfig> {
|
||||
trace!(">> wizard");
|
||||
println!("Himalaya couldn't find an already existing configuration file.");
|
||||
|
||||
match Confirm::new()
|
||||
|
@ -111,7 +110,7 @@ pub(crate) fn wizard() -> Result<DeserializedConfig> {
|
|||
// Serialize config to file
|
||||
println!("\nWriting the configuration to {path:?}...");
|
||||
fs::create_dir_all(path.parent().unwrap())?;
|
||||
fs::write(path, toml::to_vec(&config)?)?;
|
||||
fs::write(path, toml::to_string(&config)?)?;
|
||||
|
||||
trace!("<< wizard");
|
||||
Ok(config)
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::account::{
|
|||
};
|
||||
use anyhow::Result;
|
||||
use dialoguer::Input;
|
||||
use himalaya_lib::{NotmuchBackend, NotmuchConfig};
|
||||
use pimalaya_email::{NotmuchBackend, NotmuchConfig};
|
||||
|
||||
pub(crate) fn configure(base: DeserializedBaseAccountConfig) -> Result<DeserializedAccountConfig> {
|
||||
let db_path = match NotmuchBackend::get_default_db_path() {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use super::THEME;
|
||||
use anyhow::Result;
|
||||
use dialoguer::Input;
|
||||
use himalaya_lib::{EmailSender, SendmailConfig};
|
||||
use pimalaya_email::{EmailSender, SendmailConfig};
|
||||
|
||||
pub(crate) fn configure() -> Result<EmailSender> {
|
||||
Ok(EmailSender::Sendmail(SendmailConfig {
|
||||
|
|
|
@ -2,7 +2,7 @@ use super::{SECURITY_PROTOCOLS, THEME};
|
|||
use crate::account::DeserializedBaseAccountConfig;
|
||||
use anyhow::Result;
|
||||
use dialoguer::{Input, Select};
|
||||
use himalaya_lib::{EmailSender, SmtpConfig};
|
||||
use pimalaya_email::{EmailSender, SmtpConfig};
|
||||
|
||||
pub(crate) fn configure(base: &DeserializedBaseAccountConfig) -> Result<EmailSender> {
|
||||
let mut smtp_config = SmtpConfig {
|
||||
|
|
|
@ -3,8 +3,10 @@
|
|||
use anyhow::Result;
|
||||
use clap::{Arg, ArgAction, ArgMatches, Command};
|
||||
use log::info;
|
||||
use pimalaya_email::folder::sync::Strategy as SyncFoldersStrategy;
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::ui::table;
|
||||
use crate::{folder, ui::table};
|
||||
|
||||
const ARG_ACCOUNT: &str = "account";
|
||||
const ARG_DRY_RUN: &str = "dry-run";
|
||||
|
@ -20,7 +22,7 @@ pub enum Cmd {
|
|||
/// Represents the list accounts command.
|
||||
List(table::args::MaxTableWidth),
|
||||
/// Represents the sync account command.
|
||||
Sync(DryRun),
|
||||
Sync(Option<SyncFoldersStrategy>, DryRun),
|
||||
}
|
||||
|
||||
/// Represents the account command matcher.
|
||||
|
@ -29,7 +31,22 @@ pub fn matches(m: &ArgMatches) -> Result<Option<Cmd>> {
|
|||
if let Some(m) = m.subcommand_matches(CMD_SYNC) {
|
||||
info!("sync account subcommand matched");
|
||||
let dry_run = parse_dry_run_arg(m);
|
||||
Some(Cmd::Sync(dry_run))
|
||||
let include = folder::args::parse_include_arg(m);
|
||||
let exclude = folder::args::parse_exclude_arg(m);
|
||||
let folders_strategy = if let Some(folder) = folder::args::parse_source_arg(m) {
|
||||
Some(SyncFoldersStrategy::Include(HashSet::from_iter([
|
||||
folder.to_owned()
|
||||
])))
|
||||
} else if !include.is_empty() {
|
||||
Some(SyncFoldersStrategy::Include(include.to_owned()))
|
||||
} else if !exclude.is_empty() {
|
||||
Some(SyncFoldersStrategy::Exclude(exclude))
|
||||
} else if folder::args::parse_all_arg(m) {
|
||||
Some(SyncFoldersStrategy::All)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Some(Cmd::Sync(folders_strategy, dry_run))
|
||||
} else if let Some(m) = m.subcommand_matches(CMD_LIST) {
|
||||
info!("list accounts subcommand matched");
|
||||
let max_table_width = table::args::parse_max_width(m);
|
||||
|
@ -55,6 +72,13 @@ pub fn subcmd() -> Command {
|
|||
.arg(table::args::max_width()),
|
||||
Command::new(CMD_SYNC)
|
||||
.about("Synchronize the given account locally")
|
||||
.arg(folder::args::all_arg("Synchronize all folders"))
|
||||
.arg(folder::args::include_arg(
|
||||
"Synchronize only the given folders",
|
||||
))
|
||||
.arg(folder::args::exclude_arg(
|
||||
"Synchronize all folders except the given ones",
|
||||
))
|
||||
.arg(dry_run()),
|
||||
])
|
||||
}
|
||||
|
@ -63,9 +87,10 @@ pub fn subcmd() -> Command {
|
|||
/// the user to select a different account than the default one.
|
||||
pub fn arg() -> Arg {
|
||||
Arg::new(ARG_ACCOUNT)
|
||||
.help("Set the account")
|
||||
.long("account")
|
||||
.short('a')
|
||||
.help("Select a specific account by name")
|
||||
.global(true)
|
||||
.value_name("STRING")
|
||||
}
|
||||
|
||||
|
|
|
@ -3,24 +3,24 @@
|
|||
//! This module contains the raw deserialized representation of an
|
||||
//! account in the accounts section of the user configuration file.
|
||||
|
||||
use himalaya_lib::{
|
||||
AccountConfig, BackendConfig, EmailHooks, EmailSender, EmailTextPlainFormat, MaildirConfig,
|
||||
use pimalaya_email::{
|
||||
folder::sync::Strategy as SyncFoldersStrategy, AccountConfig, BackendConfig, EmailHooks,
|
||||
EmailSender, EmailTextPlainFormat, MaildirConfig,
|
||||
};
|
||||
|
||||
#[cfg(feature = "imap-backend")]
|
||||
use himalaya_lib::ImapConfig;
|
||||
|
||||
#[cfg(feature = "notmuch-backend")]
|
||||
use himalaya_lib::NotmuchConfig;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{collections::HashMap, path::PathBuf};
|
||||
|
||||
#[cfg(feature = "imap-backend")]
|
||||
use pimalaya_email::ImapConfig;
|
||||
|
||||
#[cfg(feature = "notmuch-backend")]
|
||||
use pimalaya_email::NotmuchConfig;
|
||||
|
||||
use crate::config::{prelude::*, DeserializedConfig};
|
||||
|
||||
/// Represents all existing kind of account config.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(tag = "backend", rename_all = "snake_case")]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(tag = "backend", rename_all = "kebab-case")]
|
||||
pub enum DeserializedAccountConfig {
|
||||
None(DeserializedBaseAccountConfig),
|
||||
Maildir(DeserializedMaildirAccountConfig),
|
||||
|
@ -70,7 +70,7 @@ impl DeserializedAccountConfig {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct DeserializedBaseAccountConfig {
|
||||
pub email: String,
|
||||
|
@ -85,12 +85,8 @@ pub struct DeserializedBaseAccountConfig {
|
|||
|
||||
pub email_listing_page_size: Option<usize>,
|
||||
pub email_reading_headers: Option<Vec<String>>,
|
||||
#[serde(
|
||||
default,
|
||||
with = "EmailTextPlainFormatOptionDef",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub email_reading_format: Option<EmailTextPlainFormat>,
|
||||
#[serde(default, with = "EmailTextPlainFormatDef")]
|
||||
pub email_reading_format: EmailTextPlainFormat,
|
||||
pub email_reading_verify_cmd: Option<String>,
|
||||
pub email_reading_decrypt_cmd: Option<String>,
|
||||
pub email_writing_headers: Option<Vec<String>>,
|
||||
|
@ -100,14 +96,16 @@ pub struct DeserializedBaseAccountConfig {
|
|||
pub email_sender: EmailSender,
|
||||
#[serde(
|
||||
default,
|
||||
with = "EmailHooksOptionDef",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
with = "EmailHooksDef",
|
||||
skip_serializing_if = "EmailHooks::is_empty"
|
||||
)]
|
||||
pub email_hooks: Option<EmailHooks>,
|
||||
pub email_hooks: EmailHooks,
|
||||
|
||||
#[serde(default)]
|
||||
pub sync: bool,
|
||||
pub sync_dir: Option<PathBuf>,
|
||||
#[serde(default, with = "SyncFoldersStrategyDef")]
|
||||
pub sync_folders_strategy: SyncFoldersStrategy,
|
||||
}
|
||||
|
||||
impl DeserializedBaseAccountConfig {
|
||||
|
@ -159,12 +157,7 @@ impl DeserializedBaseAccountConfig {
|
|||
.as_ref()
|
||||
.map(ToOwned::to_owned)
|
||||
.or_else(|| config.email_reading_headers.as_ref().map(ToOwned::to_owned)),
|
||||
email_reading_format: self
|
||||
.email_reading_format
|
||||
.as_ref()
|
||||
.map(ToOwned::to_owned)
|
||||
.or_else(|| config.email_reading_format.as_ref().map(ToOwned::to_owned))
|
||||
.unwrap_or_default(),
|
||||
email_reading_format: self.email_reading_format.clone(),
|
||||
email_reading_verify_cmd: self
|
||||
.email_reading_verify_cmd
|
||||
.as_ref()
|
||||
|
@ -212,26 +205,16 @@ impl DeserializedBaseAccountConfig {
|
|||
.or_else(|| config.email_writing_headers.as_ref().map(ToOwned::to_owned)),
|
||||
email_sender: self.email_sender.to_owned(),
|
||||
email_hooks: EmailHooks {
|
||||
pre_send: self
|
||||
.email_hooks
|
||||
.as_ref()
|
||||
.map(ToOwned::to_owned)
|
||||
.map(|hook| hook.pre_send)
|
||||
.or_else(|| {
|
||||
config
|
||||
.email_hooks
|
||||
.as_ref()
|
||||
.map(|hook| hook.pre_send.as_ref().map(ToOwned::to_owned))
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
pre_send: self.email_hooks.pre_send.clone(),
|
||||
},
|
||||
sync: self.sync,
|
||||
sync_dir: self.sync_dir.clone(),
|
||||
sync_folders_strategy: self.sync_folders_strategy.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[cfg(feature = "imap-backend")]
|
||||
pub struct DeserializedImapAccountConfig {
|
||||
#[serde(flatten)]
|
||||
|
@ -240,7 +223,7 @@ pub struct DeserializedImapAccountConfig {
|
|||
pub backend: ImapConfig,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
pub struct DeserializedMaildirAccountConfig {
|
||||
#[serde(flatten)]
|
||||
pub base: DeserializedBaseAccountConfig,
|
||||
|
@ -248,7 +231,7 @@ pub struct DeserializedMaildirAccountConfig {
|
|||
pub backend: MaildirConfig,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[cfg(feature = "notmuch-backend")]
|
||||
pub struct DeserializedNotmuchAccountConfig {
|
||||
#[serde(flatten)]
|
||||
|
|
|
@ -3,9 +3,12 @@
|
|||
//! This module gathers all account actions triggered by the CLI.
|
||||
|
||||
use anyhow::Result;
|
||||
use himalaya_lib::{AccountConfig, Backend, BackendSyncBuilder, BackendSyncProgressEvent};
|
||||
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
|
||||
use log::{info, trace};
|
||||
use pimalaya_email::{
|
||||
folder::sync::Strategy as SyncFoldersStrategy, AccountConfig, Backend, BackendSyncBuilder,
|
||||
BackendSyncProgressEvent,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
config::DeserializedConfig,
|
||||
|
@ -43,15 +46,17 @@ pub fn sync<P: Printer>(
|
|||
account_config: &AccountConfig,
|
||||
printer: &mut P,
|
||||
backend: &dyn Backend,
|
||||
folder: &Option<String>,
|
||||
folders_strategy: Option<SyncFoldersStrategy>,
|
||||
dry_run: bool,
|
||||
) -> Result<()> {
|
||||
info!("entering the sync accounts handler");
|
||||
trace!("dry run: {}", dry_run);
|
||||
trace!("dry run: {dry_run}");
|
||||
trace!("folders strategy: {folders_strategy:#?}");
|
||||
|
||||
let mut sync_builder = BackendSyncBuilder::new(account_config);
|
||||
if let Some(folder) = folder {
|
||||
sync_builder = sync_builder.only_folder(folder);
|
||||
|
||||
if let Some(strategy) = folders_strategy {
|
||||
sync_builder = sync_builder.folders_strategy(strategy);
|
||||
}
|
||||
|
||||
if dry_run {
|
||||
|
@ -221,7 +226,7 @@ pub fn sync<P: Printer>(
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use himalaya_lib::{AccountConfig, ImapConfig};
|
||||
use pimalaya_email::{AccountConfig, ImapConfig};
|
||||
use std::{collections::HashMap, fmt::Debug, io};
|
||||
use termcolor::ColorSpec;
|
||||
|
||||
|
|
|
@ -298,9 +298,9 @@ pub fn parse_criteria_arg(matches: &ArgMatches) -> String {
|
|||
/// Represents the email reply all argument.
|
||||
pub fn reply_all_flag() -> Arg {
|
||||
Arg::new(ARG_REPLY_ALL)
|
||||
.help("Includes all recipients")
|
||||
.help("Include all recipients")
|
||||
.long("all")
|
||||
.short('a')
|
||||
.short('A')
|
||||
.action(ArgAction::SetTrue)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use anyhow::{anyhow, Context, Result};
|
||||
use atty::Stream;
|
||||
use himalaya_lib::{
|
||||
use log::{debug, trace};
|
||||
use pimalaya_email::{
|
||||
AccountConfig, Backend, Email, Flag, Flags, Sender, ShowTextPartsStrategy, Tpl, TplBuilder,
|
||||
};
|
||||
use log::{debug, trace};
|
||||
use std::{
|
||||
fs,
|
||||
io::{self, BufRead},
|
||||
|
@ -14,6 +14,7 @@ use uuid::Uuid;
|
|||
use crate::{
|
||||
printer::{PrintTableOpts, Printer},
|
||||
ui::editor,
|
||||
Envelopes,
|
||||
};
|
||||
|
||||
pub fn attachments<P: Printer, B: Backend + ?Sized>(
|
||||
|
@ -132,10 +133,10 @@ pub fn list<P: Printer, B: Backend + ?Sized>(
|
|||
let folder = config.folder_alias(folder)?;
|
||||
let page_size = page_size.unwrap_or(config.email_listing_page_size());
|
||||
debug!("page size: {}", page_size);
|
||||
let msgs = backend.list_envelopes(&folder, page_size, page)?;
|
||||
trace!("envelopes: {:?}", msgs);
|
||||
let envelopes: Envelopes = backend.list_envelopes(&folder, page_size, page)?.into();
|
||||
trace!("envelopes: {:?}", envelopes);
|
||||
printer.print_table(
|
||||
Box::new(msgs),
|
||||
Box::new(envelopes),
|
||||
PrintTableOpts {
|
||||
format: &config.email_reading_format,
|
||||
max_width,
|
||||
|
@ -291,7 +292,9 @@ pub fn search<P: Printer, B: Backend + ?Sized>(
|
|||
) -> Result<()> {
|
||||
let folder = config.folder_alias(folder)?;
|
||||
let page_size = page_size.unwrap_or(config.email_listing_page_size());
|
||||
let envelopes = backend.search_envelopes(&folder, &query, "", page_size, page)?;
|
||||
let envelopes: Envelopes = backend
|
||||
.search_envelopes(&folder, &query, "", page_size, page)?
|
||||
.into();
|
||||
let opts = PrintTableOpts {
|
||||
format: &config.email_reading_format,
|
||||
max_width,
|
||||
|
@ -313,7 +316,9 @@ pub fn sort<P: Printer, B: Backend + ?Sized>(
|
|||
) -> Result<()> {
|
||||
let folder = config.folder_alias(folder)?;
|
||||
let page_size = page_size.unwrap_or(config.email_listing_page_size());
|
||||
let envelopes = backend.search_envelopes(&folder, &query, &sort, page_size, page)?;
|
||||
let envelopes: Envelopes = backend
|
||||
.search_envelopes(&folder, &query, &sort, page_size, page)?
|
||||
.into();
|
||||
let opts = PrintTableOpts {
|
||||
format: &config.email_reading_format,
|
||||
max_width,
|
||||
|
@ -329,7 +334,7 @@ pub fn send<P: Printer, B: Backend + ?Sized, S: Sender + ?Sized>(
|
|||
sender: &mut S,
|
||||
raw_email: String,
|
||||
) -> Result<()> {
|
||||
let folder = config.folder_alias("sent")?;
|
||||
let folder = config.sent_folder_alias()?;
|
||||
let is_tty = atty::is(Stream::Stdin);
|
||||
let is_json = printer.is_json();
|
||||
let raw_email = if is_tty || is_json {
|
||||
|
@ -344,7 +349,11 @@ pub fn send<P: Printer, B: Backend + ?Sized, S: Sender + ?Sized>(
|
|||
};
|
||||
trace!("raw email: {:?}", raw_email);
|
||||
sender.send(raw_email.as_bytes())?;
|
||||
backend.add_email(&folder, raw_email.as_bytes(), &Flags::default())?;
|
||||
backend.add_email(
|
||||
&folder,
|
||||
raw_email.as_bytes(),
|
||||
&Flags::from_iter([Flag::Seen]),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,45 @@
|
|||
use himalaya_lib::{Envelope, Flag};
|
||||
use chrono::{DateTime, Local};
|
||||
use serde::{Serialize, Serializer};
|
||||
|
||||
use crate::ui::{Cell, Row, Table};
|
||||
use crate::{
|
||||
ui::{Cell, Row, Table},
|
||||
Flag, Flags,
|
||||
};
|
||||
|
||||
fn date<S: Serializer>(date: &DateTime<Local>, s: S) -> Result<S::Ok, S::Error> {
|
||||
s.serialize_str(&date.to_rfc3339())
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize)]
|
||||
pub struct Mailbox {
|
||||
pub name: Option<String>,
|
||||
pub addr: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize)]
|
||||
pub struct Envelope {
|
||||
pub id: String,
|
||||
pub flags: Flags,
|
||||
pub subject: String,
|
||||
pub from: Mailbox,
|
||||
#[serde(serialize_with = "date")]
|
||||
pub date: DateTime<Local>,
|
||||
}
|
||||
|
||||
impl From<&pimalaya_email::Envelope> for Envelope {
|
||||
fn from(envelope: &pimalaya_email::Envelope) -> Self {
|
||||
Envelope {
|
||||
id: envelope.id.clone(),
|
||||
flags: envelope.flags.clone().into(),
|
||||
subject: envelope.subject.clone(),
|
||||
from: Mailbox {
|
||||
name: envelope.from.name.clone(),
|
||||
addr: envelope.from.addr.clone(),
|
||||
},
|
||||
date: envelope.date.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Table for Envelope {
|
||||
fn head() -> Row {
|
||||
|
@ -14,15 +53,29 @@ impl Table for Envelope {
|
|||
|
||||
fn row(&self) -> Row {
|
||||
let id = self.id.to_string();
|
||||
let flags = self.flags.to_symbols_string();
|
||||
let unseen = !self.flags.contains(&Flag::Seen);
|
||||
let flags = {
|
||||
let mut flags = String::new();
|
||||
flags.push_str(if !unseen { " " } else { "✷" });
|
||||
flags.push_str(if self.flags.contains(&Flag::Answered) {
|
||||
"↵"
|
||||
} else {
|
||||
" "
|
||||
});
|
||||
flags.push_str(if self.flags.contains(&Flag::Flagged) {
|
||||
"⚑"
|
||||
} else {
|
||||
" "
|
||||
});
|
||||
flags
|
||||
};
|
||||
let subject = &self.subject;
|
||||
let sender = if let Some(name) = &self.from.name {
|
||||
name
|
||||
} else {
|
||||
&self.from.addr
|
||||
};
|
||||
let date = self.date.format("%d/%m/%Y %H:%M").to_string();
|
||||
let date = self.date.to_rfc3339();
|
||||
|
||||
Row::new()
|
||||
.cell(Cell::new(id).bold_if(unseen).red())
|
||||
|
|
|
@ -1,11 +1,32 @@
|
|||
use std::ops;
|
||||
|
||||
use anyhow::Result;
|
||||
use himalaya_lib::Envelopes;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
printer::{PrintTable, PrintTableOpts, WriteColor},
|
||||
ui::Table,
|
||||
Envelope,
|
||||
};
|
||||
|
||||
/// Represents the list of envelopes.
|
||||
#[derive(Clone, Debug, Default, Serialize)]
|
||||
pub struct Envelopes(Vec<Envelope>);
|
||||
|
||||
impl ops::Deref for Envelopes {
|
||||
type Target = Vec<Envelope>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<pimalaya_email::Envelopes> for Envelopes {
|
||||
fn from(envelopes: pimalaya_email::Envelopes) -> Self {
|
||||
Envelopes(envelopes.iter().map(Envelope::from).collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintTable for Envelopes {
|
||||
fn print_table(&self, writer: &mut dyn WriteColor, opts: PrintTableOpts) -> Result<()> {
|
||||
writeln!(writer)?;
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
|
||||
use anyhow::Result;
|
||||
use clap::{Arg, ArgMatches, Command};
|
||||
use himalaya_lib::{Flag, Flags};
|
||||
use log::{debug, info};
|
||||
use pimalaya_email::{Flag, Flags};
|
||||
|
||||
use crate::email;
|
||||
|
||||
|
@ -83,7 +83,11 @@ pub fn flags_arg() -> Arg {
|
|||
Arg::new(ARG_FLAGS)
|
||||
.value_name("FLAGS")
|
||||
.help("The flags")
|
||||
.long_help("The list of flags. It can be one of: seen, answered, flagged, deleted, draft, recent. Other flags are considered custom.")
|
||||
.long_help(
|
||||
"The list of flags.
|
||||
It can be one of: seen, answered, flagged, deleted, or draft.
|
||||
Other flags are considered custom.",
|
||||
)
|
||||
.num_args(1..)
|
||||
.required(true)
|
||||
.last(true)
|
||||
|
|
25
src/domain/flag/flag.rs
Normal file
25
src/domain/flag/flag.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
use serde::Serialize;
|
||||
|
||||
/// Represents the flag variants.
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize)]
|
||||
pub enum Flag {
|
||||
Seen,
|
||||
Answered,
|
||||
Flagged,
|
||||
Deleted,
|
||||
Draft,
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
impl From<&pimalaya_email::Flag> for Flag {
|
||||
fn from(flag: &pimalaya_email::Flag) -> Self {
|
||||
match flag {
|
||||
pimalaya_email::Flag::Seen => Flag::Seen,
|
||||
pimalaya_email::Flag::Answered => Flag::Answered,
|
||||
pimalaya_email::Flag::Flagged => Flag::Flagged,
|
||||
pimalaya_email::Flag::Deleted => Flag::Deleted,
|
||||
pimalaya_email::Flag::Draft => Flag::Draft,
|
||||
pimalaya_email::Flag::Custom(flag) => Flag::Custom(flag.clone()),
|
||||
}
|
||||
}
|
||||
}
|
21
src/domain/flag/flags.rs
Normal file
21
src/domain/flag/flags.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
use serde::Serialize;
|
||||
use std::{collections::HashSet, ops};
|
||||
|
||||
use crate::Flag;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize)]
|
||||
pub struct Flags(pub HashSet<Flag>);
|
||||
|
||||
impl ops::Deref for Flags {
|
||||
type Target = HashSet<Flag>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<pimalaya_email::Flags> for Flags {
|
||||
fn from(flags: pimalaya_email::Flags) -> Self {
|
||||
Flags(flags.iter().map(Flag::from).collect())
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
use anyhow::Result;
|
||||
use himalaya_lib::{Backend, Flags};
|
||||
use pimalaya_email::{Backend, Flags};
|
||||
|
||||
use crate::printer::Printer;
|
||||
|
||||
|
|
|
@ -1,2 +1,8 @@
|
|||
pub mod args;
|
||||
pub mod handlers;
|
||||
|
||||
pub mod flag;
|
||||
pub use flag::*;
|
||||
|
||||
pub mod flags;
|
||||
pub use flags::*;
|
||||
|
|
|
@ -3,14 +3,21 @@
|
|||
//! This module provides subcommands, arguments and a command matcher
|
||||
//! related to the folder domain.
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::{self, Arg, ArgMatches, Command};
|
||||
use clap::{self, Arg, ArgAction, ArgMatches, Command};
|
||||
use log::{debug, info};
|
||||
|
||||
use crate::ui::table;
|
||||
|
||||
const ARG_ALL: &str = "all";
|
||||
const ARG_EXCLUDE: &str = "exclude";
|
||||
const ARG_INCLUDE: &str = "include";
|
||||
const ARG_SOURCE: &str = "source";
|
||||
const ARG_TARGET: &str = "target";
|
||||
const CMD_CREATE: &str = "create";
|
||||
const CMD_DELETE: &str = "delete";
|
||||
const CMD_EXPUNGE: &str = "expunge";
|
||||
const CMD_FOLDERS: &str = "folders";
|
||||
const CMD_LIST: &str = "list";
|
||||
|
@ -18,8 +25,10 @@ const CMD_LIST: &str = "list";
|
|||
/// Represents the folder commands.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Cmd {
|
||||
Create,
|
||||
List(table::args::MaxTableWidth),
|
||||
Expunge,
|
||||
Delete,
|
||||
}
|
||||
|
||||
/// Represents the folder command matcher.
|
||||
|
@ -28,10 +37,16 @@ pub fn matches(m: &ArgMatches) -> Result<Option<Cmd>> {
|
|||
if let Some(_) = m.subcommand_matches(CMD_EXPUNGE) {
|
||||
info!("expunge folder subcommand matched");
|
||||
Some(Cmd::Expunge)
|
||||
} else if let Some(_) = m.subcommand_matches(CMD_CREATE) {
|
||||
debug!("create folder command matched");
|
||||
Some(Cmd::Create)
|
||||
} 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 if let Some(_) = m.subcommand_matches(CMD_DELETE) {
|
||||
debug!("delete folder command matched");
|
||||
Some(Cmd::Delete)
|
||||
} else {
|
||||
info!("no folder subcommand matched, falling back to subcommand list");
|
||||
Some(Cmd::List(None))
|
||||
|
@ -49,18 +64,25 @@ pub fn subcmd() -> Command {
|
|||
.about("Manage folders")
|
||||
.subcommands([
|
||||
Command::new(CMD_EXPUNGE).about("Delete emails marked for deletion"),
|
||||
Command::new(CMD_CREATE)
|
||||
.aliases(["add", "new"])
|
||||
.about("Create a new folder"),
|
||||
Command::new(CMD_LIST)
|
||||
.about("List folders")
|
||||
.arg(table::args::max_width()),
|
||||
Command::new(CMD_DELETE)
|
||||
.aliases(["remove", "rm"])
|
||||
.about("Delete a folder with all its emails"),
|
||||
])
|
||||
}
|
||||
|
||||
/// Represents the source folder argument.
|
||||
pub fn source_arg() -> Arg {
|
||||
Arg::new(ARG_SOURCE)
|
||||
.help("Set the source folder")
|
||||
.long("folder")
|
||||
.short('f')
|
||||
.help("Specifies the source folder")
|
||||
.global(true)
|
||||
.value_name("SOURCE")
|
||||
}
|
||||
|
||||
|
@ -69,6 +91,70 @@ pub fn parse_source_arg(matches: &ArgMatches) -> Option<&str> {
|
|||
matches.get_one::<String>(ARG_SOURCE).map(String::as_str)
|
||||
}
|
||||
|
||||
/// Represents the all folders argument.
|
||||
pub fn all_arg(help: &'static str) -> Arg {
|
||||
Arg::new(ARG_ALL)
|
||||
.help(help)
|
||||
.long("all-folders")
|
||||
.alias("all")
|
||||
.short('A')
|
||||
.action(ArgAction::SetTrue)
|
||||
.conflicts_with(ARG_SOURCE)
|
||||
.conflicts_with(ARG_INCLUDE)
|
||||
.conflicts_with(ARG_EXCLUDE)
|
||||
}
|
||||
|
||||
/// Represents the all folders argument parser.
|
||||
pub fn parse_all_arg(m: &ArgMatches) -> bool {
|
||||
m.get_flag(ARG_ALL)
|
||||
}
|
||||
|
||||
/// Represents the folders to include argument.
|
||||
pub fn include_arg(help: &'static str) -> Arg {
|
||||
Arg::new(ARG_INCLUDE)
|
||||
.help(help)
|
||||
.long("include-folder")
|
||||
.alias("only")
|
||||
.short('F')
|
||||
.value_name("FOLDER")
|
||||
.num_args(1..)
|
||||
.action(ArgAction::Append)
|
||||
.conflicts_with(ARG_SOURCE)
|
||||
.conflicts_with(ARG_ALL)
|
||||
.conflicts_with(ARG_EXCLUDE)
|
||||
}
|
||||
|
||||
/// Represents the folders to include argument parser.
|
||||
pub fn parse_include_arg(m: &ArgMatches) -> HashSet<String> {
|
||||
m.get_many::<String>(ARG_INCLUDE)
|
||||
.unwrap_or_default()
|
||||
.map(ToOwned::to_owned)
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Represents the folders to exclude argument.
|
||||
pub fn exclude_arg(help: &'static str) -> Arg {
|
||||
Arg::new(ARG_EXCLUDE)
|
||||
.help(help)
|
||||
.long("exclude-folder")
|
||||
.alias("except")
|
||||
.short('x')
|
||||
.value_name("FOLDER")
|
||||
.num_args(1..)
|
||||
.action(ArgAction::Append)
|
||||
.conflicts_with(ARG_SOURCE)
|
||||
.conflicts_with(ARG_ALL)
|
||||
.conflicts_with(ARG_INCLUDE)
|
||||
}
|
||||
|
||||
/// Represents the folders to exclude argument parser.
|
||||
pub fn parse_exclude_arg(m: &ArgMatches) -> HashSet<String> {
|
||||
m.get_many::<String>(ARG_EXCLUDE)
|
||||
.unwrap_or_default()
|
||||
.map(ToOwned::to_owned)
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Represents the target folder argument.
|
||||
pub fn target_arg() -> Arg {
|
||||
Arg::new(ARG_TARGET)
|
||||
|
|
|
@ -1,7 +1,24 @@
|
|||
use himalaya_lib::folder::Folder;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::ui::{Cell, Row, Table};
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize)]
|
||||
pub struct Folder {
|
||||
pub delim: String,
|
||||
pub name: String,
|
||||
pub desc: String,
|
||||
}
|
||||
|
||||
impl From<&pimalaya_email::Folder> for Folder {
|
||||
fn from(folder: &pimalaya_email::Folder) -> Self {
|
||||
Folder {
|
||||
delim: folder.delim.clone(),
|
||||
name: folder.name.clone(),
|
||||
desc: folder.desc.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Table for Folder {
|
||||
fn head() -> Row {
|
||||
Row::new()
|
||||
|
|
|
@ -1,11 +1,31 @@
|
|||
use std::ops;
|
||||
|
||||
use anyhow::Result;
|
||||
use himalaya_lib::folder::Folders;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
printer::{PrintTable, PrintTableOpts, WriteColor},
|
||||
ui::Table,
|
||||
Folder,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize)]
|
||||
pub struct Folders(Vec<Folder>);
|
||||
|
||||
impl ops::Deref for Folders {
|
||||
type Target = Vec<Folder>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<pimalaya_email::Folders> for Folders {
|
||||
fn from(folders: pimalaya_email::Folders) -> Self {
|
||||
Folders(folders.iter().map(Folder::from).collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintTable for Folders {
|
||||
fn print_table(&self, writer: &mut dyn WriteColor, opts: PrintTableOpts) -> Result<()> {
|
||||
writeln!(writer)?;
|
||||
|
|
|
@ -3,26 +3,31 @@
|
|||
//! This module gathers all folder actions triggered by the CLI.
|
||||
|
||||
use anyhow::Result;
|
||||
use himalaya_lib::{AccountConfig, Backend};
|
||||
use dialoguer::Confirm;
|
||||
use pimalaya_email::{AccountConfig, Backend};
|
||||
use std::process;
|
||||
|
||||
use crate::printer::{PrintTableOpts, Printer};
|
||||
use crate::{
|
||||
printer::{PrintTableOpts, Printer},
|
||||
Folders,
|
||||
};
|
||||
|
||||
pub fn expunge<P: Printer, B: Backend + ?Sized>(
|
||||
folder: &str,
|
||||
printer: &mut P,
|
||||
backend: &mut B,
|
||||
folder: &str,
|
||||
) -> Result<()> {
|
||||
backend.expunge_folder(folder)?;
|
||||
printer.print(format!("Folder {folder} successfully expunged!"))
|
||||
}
|
||||
|
||||
pub fn list<P: Printer, B: Backend + ?Sized>(
|
||||
max_width: Option<usize>,
|
||||
config: &AccountConfig,
|
||||
printer: &mut P,
|
||||
backend: &mut B,
|
||||
max_width: Option<usize>,
|
||||
) -> Result<()> {
|
||||
let folders = backend.list_folders()?;
|
||||
let folders: Folders = backend.list_folders()?.into();
|
||||
printer.print_table(
|
||||
// TODO: remove Box
|
||||
Box::new(folders),
|
||||
|
@ -33,9 +38,36 @@ pub fn list<P: Printer, B: Backend + ?Sized>(
|
|||
)
|
||||
}
|
||||
|
||||
pub fn create<P: Printer, B: Backend + ?Sized>(
|
||||
printer: &mut P,
|
||||
backend: &mut B,
|
||||
folder: &str,
|
||||
) -> Result<()> {
|
||||
backend.add_folder(folder)?;
|
||||
printer.print("Folder successfully created!")
|
||||
}
|
||||
|
||||
pub fn delete<P: Printer, B: Backend + ?Sized>(
|
||||
printer: &mut P,
|
||||
backend: &mut B,
|
||||
folder: &str,
|
||||
) -> Result<()> {
|
||||
if let Some(false) | None = Confirm::new()
|
||||
.with_prompt(format!("Confirm deletion of folder {folder}?"))
|
||||
.default(false)
|
||||
.report(false)
|
||||
.interact_opt()?
|
||||
{
|
||||
process::exit(0);
|
||||
};
|
||||
|
||||
backend.delete_folder(folder)?;
|
||||
printer.print("Folder successfully deleted!")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use himalaya_lib::{
|
||||
use pimalaya_email::{
|
||||
backend, AccountConfig, Backend, Emails, Envelope, Envelopes, Flags, Folder, Folders,
|
||||
};
|
||||
use std::{any::Any, fmt::Debug, io};
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
//! This module gathers all IMAP handlers triggered by the CLI.
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use himalaya_lib::ImapBackend;
|
||||
use pimalaya_email::ImapBackend;
|
||||
|
||||
pub fn notify(imap: &ImapBackend, folder: &str, keepalive: u64) -> Result<()> {
|
||||
imap.notify(keepalive, folder).context("cannot imap notify")
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use anyhow::{anyhow, Result};
|
||||
use atty::Stream;
|
||||
use himalaya_lib::{AccountConfig, Backend, CompilerBuilder, Email, Flags, Sender, Tpl};
|
||||
use pimalaya_email::{AccountConfig, Backend, CompilerBuilder, Email, Flags, Sender, Tpl};
|
||||
use std::io::{stdin, BufRead};
|
||||
|
||||
use crate::printer::Printer;
|
||||
|
|
43
src/main.rs
43
src/main.rs
|
@ -1,4 +1,4 @@
|
|||
use anyhow::Result;
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use clap::Command;
|
||||
use std::{borrow::Cow, env};
|
||||
use url::Url;
|
||||
|
@ -10,7 +10,7 @@ use himalaya::{
|
|||
printer::StdoutPrinter,
|
||||
tpl,
|
||||
};
|
||||
use himalaya_lib::{
|
||||
use pimalaya_email::{
|
||||
BackendBuilder, BackendConfig, ImapBackend, SenderBuilder, DEFAULT_INBOX_FOLDER,
|
||||
};
|
||||
|
||||
|
@ -77,8 +77,7 @@ fn main() -> Result<()> {
|
|||
_ => (),
|
||||
}
|
||||
|
||||
// checks completion command before configs
|
||||
// https://github.com/soywod/himalaya/issues/115
|
||||
// also checks man command before configs
|
||||
match man::args::matches(&m)? {
|
||||
Some(man::args::Cmd::GenerateAll(dir)) => {
|
||||
return man::handlers::generate(dir, create_app());
|
||||
|
@ -123,11 +122,7 @@ fn main() -> Result<()> {
|
|||
Some(account::args::Cmd::List(max_width)) => {
|
||||
return account::handlers::list(max_width, &account_config, &config, &mut printer);
|
||||
}
|
||||
Some(account::args::Cmd::Sync(dry_run)) => {
|
||||
let folder = match folder {
|
||||
Some(folder) => Some(account_config.folder_alias(folder)?),
|
||||
None => None,
|
||||
};
|
||||
Some(account::args::Cmd::Sync(folders_strategy, dry_run)) => {
|
||||
let backend = BackendBuilder::new()
|
||||
.sessions_pool_size(8)
|
||||
.disable_cache(true)
|
||||
|
@ -136,7 +131,7 @@ fn main() -> Result<()> {
|
|||
&account_config,
|
||||
&mut printer,
|
||||
backend.as_ref(),
|
||||
&folder,
|
||||
folders_strategy,
|
||||
dry_run,
|
||||
)?;
|
||||
backend.close()?;
|
||||
|
@ -147,24 +142,44 @@ fn main() -> Result<()> {
|
|||
|
||||
// checks folder commands
|
||||
match folder::args::matches(&m)? {
|
||||
Some(folder::args::Cmd::Expunge) => {
|
||||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||
Some(folder::args::Cmd::Create) => {
|
||||
let folder = folder
|
||||
.ok_or_else(|| anyhow!("the folder argument is missing"))
|
||||
.context("cannot create folder")?;
|
||||
let folder = account_config.folder_alias(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());
|
||||
return folder::handlers::create(&mut printer, backend.as_mut(), &folder);
|
||||
}
|
||||
Some(folder::args::Cmd::List(max_width)) => {
|
||||
let mut backend = BackendBuilder::new()
|
||||
.disable_cache(disable_cache)
|
||||
.build(&account_config, &backend_config)?;
|
||||
return folder::handlers::list(
|
||||
max_width,
|
||||
&account_config,
|
||||
&mut printer,
|
||||
backend.as_mut(),
|
||||
max_width,
|
||||
);
|
||||
}
|
||||
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(&mut printer, backend.as_mut(), &folder);
|
||||
}
|
||||
Some(folder::args::Cmd::Delete) => {
|
||||
let folder = folder
|
||||
.ok_or_else(|| anyhow!("the folder argument is missing"))
|
||||
.context("cannot delete folder")?;
|
||||
let folder = account_config.folder_alias(folder)?;
|
||||
let mut backend = BackendBuilder::new()
|
||||
.disable_cache(disable_cache)
|
||||
.build(&account_config, &backend_config)?;
|
||||
return folder::handlers::delete(&mut printer, backend.as_mut(), &folder);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
|
|
|
@ -30,11 +30,14 @@ pub fn matches(m: &ArgMatches) -> Result<Option<Cmd>> {
|
|||
/// Man subcommands.
|
||||
pub fn subcmd() -> Command {
|
||||
Command::new(CMD_MAN)
|
||||
.about("Generates all man pages to the specified directory.")
|
||||
.about("Generate all man pages to the given directory")
|
||||
.arg(
|
||||
Arg::new(ARG_DIR)
|
||||
.help("Directory where to generate man files")
|
||||
.long_help("Represents the directory where all man files of all commands and subcommands should be generated in.")
|
||||
.help("Directory to generate man files in")
|
||||
.long_help(
|
||||
"Represents the directory where all man files of
|
||||
all commands and subcommands should be generated in.",
|
||||
)
|
||||
.required(true),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -11,22 +11,23 @@ pub(crate) const ARG_OUTPUT: &str = "output";
|
|||
pub fn args() -> Vec<Arg> {
|
||||
vec![
|
||||
Arg::new(ARG_OUTPUT)
|
||||
.help("Defines the output format")
|
||||
.help("Set the output format")
|
||||
.long("output")
|
||||
.short('o')
|
||||
.global(true)
|
||||
.value_name("FMT")
|
||||
.value_parser(["plain", "json"])
|
||||
.default_value("plain"),
|
||||
Arg::new(ARG_COLOR)
|
||||
.help("Controls when to use colors.")
|
||||
.help("Control when to use colors.")
|
||||
.long_help(
|
||||
"
|
||||
This flag controls when to use colors. The default setting is 'auto', which
|
||||
means himalaya will try to guess when to use colors. For example, if himalaya is
|
||||
printing to a terminal, then it will use colors, but if it is redirected to a
|
||||
file or a pipe, then it will suppress color output. himalaya will suppress color
|
||||
output in some other circumstances as well. For example, if the TERM
|
||||
environment variable is not set or set to 'dumb', then himalaya will not use
|
||||
"This flag controls when to use colors. The default
|
||||
setting is 'auto', which means himalaya will try to guess when to use
|
||||
colors. For example, if himalaya is printing to a terminal, then it
|
||||
will use colors, but if it is redirected to a file or a pipe, then it
|
||||
will suppress color output. himalaya will suppress color output in
|
||||
some other circumstances as well. For example, if the TERM environment
|
||||
variable is not set or set to 'dumb', then himalaya will not use
|
||||
colors.
|
||||
|
||||
The possible values for this flag are:
|
||||
|
@ -34,11 +35,11 @@ The possible values for this flag are:
|
|||
never Colors will never be used.
|
||||
auto The default. himalaya tries to be smart.
|
||||
always Colors will always be used regardless of where output is sent.
|
||||
ansi Like 'always', but emits ANSI escapes (even in a Windows console).
|
||||
",
|
||||
ansi Like 'always', but emits ANSI escapes (even in a Windows console).",
|
||||
)
|
||||
.long("color")
|
||||
.short('C')
|
||||
.global(true)
|
||||
.value_parser(["never", "auto", "always", "ansi"])
|
||||
.default_value("auto")
|
||||
.value_name("WHEN"),
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use anyhow::{Context, Result};
|
||||
use himalaya_lib::Tpl;
|
||||
use pimalaya_email::Tpl;
|
||||
|
||||
use crate::printer::WriteColor;
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use anyhow::Result;
|
||||
use himalaya_lib::EmailTextPlainFormat;
|
||||
use pimalaya_email::EmailTextPlainFormat;
|
||||
use std::io;
|
||||
use termcolor::{self, StandardStream};
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use anyhow::{Context, Result};
|
||||
use himalaya_lib::{
|
||||
use log::debug;
|
||||
use pimalaya_email::{
|
||||
email::{local_draft_path, remove_local_draft},
|
||||
AccountConfig, Backend, CompilerBuilder, Flag, Flags, Sender, Tpl,
|
||||
};
|
||||
use log::debug;
|
||||
use std::{env, fs, process::Command};
|
||||
|
||||
use crate::{
|
||||
|
@ -81,7 +81,7 @@ pub fn edit_tpl_with_editor<P: Printer, B: Backend + ?Sized, S: Sender + ?Sized>
|
|||
sender.send(&email)?;
|
||||
let sent_folder = config.sent_folder_alias()?;
|
||||
printer.print_log(format!("Adding email to the {} folder…", sent_folder))?;
|
||||
backend.add_email(&sent_folder, &email, &Flags::default())?;
|
||||
backend.add_email(&sent_folder, &email, &Flags::from_iter([Flag::Seen]))?;
|
||||
remove_local_draft()?;
|
||||
printer.print("Done!")?;
|
||||
break;
|
||||
|
@ -101,7 +101,11 @@ pub fn edit_tpl_with_editor<P: Printer, B: Backend + ?Sized, S: Sender + ?Sized>
|
|||
.some_pgp_sign_cmd(config.email_writing_sign_cmd.as_ref())
|
||||
.some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.as_ref()),
|
||||
)?;
|
||||
backend.add_email(&draft_folder, &email, &Flags::from_iter([Flag::Draft]))?;
|
||||
backend.add_email(
|
||||
&draft_folder,
|
||||
&email,
|
||||
&Flags::from_iter([Flag::Seen, Flag::Draft]),
|
||||
)?;
|
||||
remove_local_draft()?;
|
||||
printer.print(format!("Email successfully saved to {}", draft_folder))?;
|
||||
break;
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
//! [builder design pattern]: https://refactoring.guru/design-patterns/builder
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use himalaya_lib::EmailTextPlainFormat;
|
||||
use log::trace;
|
||||
use pimalaya_email::EmailTextPlainFormat;
|
||||
use termcolor::{Color, ColorSpec};
|
||||
use terminal_size;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
|
Loading…
Reference in a new issue