lots of updates, gonna probably ditch tui-rs
This commit is contained in:
parent
6b82137124
commit
a1e9576d07
13 changed files with 224 additions and 407 deletions
5
.github/workflows/ci.yml
vendored
5
.github/workflows/ci.yml
vendored
|
@ -1,4 +1,7 @@
|
||||||
on: [push]
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
name: workflow
|
name: workflow
|
||||||
|
|
||||||
|
|
273
Cargo.lock
generated
273
Cargo.lock
generated
|
@ -203,15 +203,6 @@ dependencies = [
|
||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bitpacking"
|
|
||||||
version = "0.8.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3744aff20a3437a99ebc0bb7733e9e60c7bf590478c9b897e95b38d57e5acb68"
|
|
||||||
dependencies = [
|
|
||||||
"crunchy",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitvec"
|
name = "bitvec"
|
||||||
version = "0.19.5"
|
version = "0.19.5"
|
||||||
|
@ -385,12 +376,6 @@ dependencies = [
|
||||||
"jobserver",
|
"jobserver",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "census"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5927edd8345aef08578bcbb4aea7314f340d80c7f4931f99fbeb40b99d8f5060"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "0.1.10"
|
version = "0.1.10"
|
||||||
|
@ -486,15 +471,6 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "combine"
|
|
||||||
version = "4.5.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cc4369b5e4c0cddf64ad8981c0111e7df4f7078f4d6ba98fb31f2e17c4c57b7e"
|
|
||||||
dependencies = [
|
|
||||||
"memchr",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "commoncrypto"
|
name = "commoncrypto"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
@ -583,20 +559,6 @@ dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam"
|
|
||||||
version = "0.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fd01a6eb3daaafa260f6fc94c3a6c36390abc2080e38e3e34ced87393fb77d80"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if 1.0.0",
|
|
||||||
"crossbeam-channel",
|
|
||||||
"crossbeam-deque",
|
|
||||||
"crossbeam-epoch",
|
|
||||||
"crossbeam-queue",
|
|
||||||
"crossbeam-utils 0.8.3",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-channel"
|
name = "crossbeam-channel"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
|
@ -607,30 +569,6 @@ dependencies = [
|
||||||
"crossbeam-utils 0.8.3",
|
"crossbeam-utils 0.8.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam-deque"
|
|
||||||
version = "0.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if 1.0.0",
|
|
||||||
"crossbeam-epoch",
|
|
||||||
"crossbeam-utils 0.8.3",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam-epoch"
|
|
||||||
version = "0.9.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2584f639eb95fea8c798496315b297cf81b9b58b6d30ab066a75455333cf4b12"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if 1.0.0",
|
|
||||||
"crossbeam-utils 0.8.3",
|
|
||||||
"lazy_static",
|
|
||||||
"memoffset",
|
|
||||||
"scopeguard",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-queue"
|
name = "crossbeam-queue"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
|
@ -951,17 +889,6 @@ dependencies = [
|
||||||
"termcolor",
|
"termcolor",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fail"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3be3c61c59fdc91f5dbc3ea31ee8623122ce80057058be560654c5d410d181a6"
|
|
||||||
dependencies = [
|
|
||||||
"lazy_static",
|
|
||||||
"log",
|
|
||||||
"rand 0.7.3",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastrand"
|
name = "fastrand"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
|
@ -1071,16 +998,6 @@ version = "0.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0e04cda45add94e71c2990de778ae13059897d77b773130a9bc225e2970c413e"
|
checksum = "0e04cda45add94e71c2990de778ae13059897d77b773130a9bc225e2970c413e"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fs2"
|
|
||||||
version = "0.4.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "funty"
|
name = "funty"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
@ -1133,7 +1050,6 @@ dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-task",
|
"futures-task",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"num_cpus",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1534,12 +1450,6 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "htmlescape"
|
|
||||||
version = "0.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e9025058dae765dee5070ec375f591e2ba14638c63feff74f13805a72e523163"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "humantime"
|
name = "humantime"
|
||||||
version = "2.1.0"
|
version = "2.1.0"
|
||||||
|
@ -1694,7 +1604,7 @@ dependencies = [
|
||||||
"petgraph",
|
"petgraph",
|
||||||
"pico-args",
|
"pico-args",
|
||||||
"regex",
|
"regex",
|
||||||
"regex-syntax 0.6.23",
|
"regex-syntax",
|
||||||
"string_cache",
|
"string_cache",
|
||||||
"term",
|
"term",
|
||||||
"tiny-keccak",
|
"tiny-keccak",
|
||||||
|
@ -1722,12 +1632,6 @@ version = "1.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "levenshtein_automata"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f44db4199cdb049b494a92d105acbfa43c25b3925e33803923ba9580b7bc9e1a"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lexical-core"
|
name = "lexical-core"
|
||||||
version = "0.7.5"
|
version = "0.7.5"
|
||||||
|
@ -1826,15 +1730,6 @@ dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lru"
|
|
||||||
version = "0.6.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1f374d42cdfc1d7dbf3d3dec28afab2eb97ffbf43a3234d795b5986dbf4b90ba"
|
|
||||||
dependencies = [
|
|
||||||
"hashbrown 0.9.1",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mac-notification-sys"
|
name = "mac-notification-sys"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
|
@ -1885,25 +1780,6 @@ version = "2.3.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
|
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "memmap"
|
|
||||||
version = "0.7.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "memoffset"
|
|
||||||
version = "0.6.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.4.4"
|
version = "0.4.4"
|
||||||
|
@ -1937,15 +1813,6 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "murmurhash32"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d736ff882f0e85fe9689fb23db229616c4c00aee2b3ac282f666d8f20eb25d4a"
|
|
||||||
dependencies = [
|
|
||||||
"byteorder",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nb-connect"
|
name = "nb-connect"
|
||||||
version = "1.0.3"
|
version = "1.0.3"
|
||||||
|
@ -2167,7 +2034,6 @@ dependencies = [
|
||||||
"shellexpand",
|
"shellexpand",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"structopt",
|
"structopt",
|
||||||
"tantivy",
|
|
||||||
"tokio 1.3.0",
|
"tokio 1.3.0",
|
||||||
"tokio-rustls",
|
"tokio-rustls",
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
|
@ -2204,6 +2070,10 @@ dependencies = [
|
||||||
name = "panorama-smtp"
|
name = "panorama-smtp"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "panorama-tui"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking"
|
name = "parking"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
|
@ -2552,31 +2422,6 @@ dependencies = [
|
||||||
"rand_core 0.5.1",
|
"rand_core 0.5.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rayon"
|
|
||||||
version = "1.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
"crossbeam-deque",
|
|
||||||
"either",
|
|
||||||
"rayon-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rayon-core"
|
|
||||||
version = "1.9.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a"
|
|
||||||
dependencies = [
|
|
||||||
"crossbeam-channel",
|
|
||||||
"crossbeam-deque",
|
|
||||||
"crossbeam-utils 0.8.3",
|
|
||||||
"lazy_static",
|
|
||||||
"num_cpus",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.1.57"
|
version = "0.1.57"
|
||||||
|
@ -2621,15 +2466,9 @@ checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
"regex-syntax 0.6.23",
|
"regex-syntax",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex-syntax"
|
|
||||||
version = "0.4.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8e931c58b93d86f080c734bfd2bce7dd0079ae2331235818133c8be7f422e20e"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.6.23"
|
version = "0.6.23"
|
||||||
|
@ -2681,16 +2520,6 @@ dependencies = [
|
||||||
"crossbeam-utils 0.8.3",
|
"crossbeam-utils 0.8.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rust-stemmers"
|
|
||||||
version = "1.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e46a2036019fdb888131db7a4c847a1063a7493f971ed94ea82c67eada63ca54"
|
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
"serde_derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-hash"
|
name = "rustc-hash"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
@ -2931,12 +2760,6 @@ version = "1.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
|
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "snap"
|
|
||||||
version = "1.0.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "dc725476a1398f0480d56cd0ad381f6f32acf2642704456f8f59a35df464b59a"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "socket2"
|
name = "socket2"
|
||||||
version = "0.3.19"
|
version = "0.3.19"
|
||||||
|
@ -3050,12 +2873,6 @@ dependencies = [
|
||||||
"tokio-rustls",
|
"tokio-rustls",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "stable_deref_trait"
|
|
||||||
version = "1.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "static_assertions"
|
name = "static_assertions"
|
||||||
version = "0.3.4"
|
version = "0.3.4"
|
||||||
|
@ -3188,68 +3005,6 @@ dependencies = [
|
||||||
"unicode-xid 0.0.4",
|
"unicode-xid 0.0.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tantivy"
|
|
||||||
version = "0.14.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "edca90bddda472f39fdc74a031d61d52b08b1de97f2a704afae726a8004abb0d"
|
|
||||||
dependencies = [
|
|
||||||
"base64 0.13.0",
|
|
||||||
"bitpacking",
|
|
||||||
"byteorder",
|
|
||||||
"census",
|
|
||||||
"chrono",
|
|
||||||
"crc32fast",
|
|
||||||
"crossbeam",
|
|
||||||
"downcast-rs",
|
|
||||||
"fail",
|
|
||||||
"fnv",
|
|
||||||
"fs2",
|
|
||||||
"futures 0.3.13",
|
|
||||||
"htmlescape",
|
|
||||||
"levenshtein_automata",
|
|
||||||
"log",
|
|
||||||
"lru",
|
|
||||||
"memmap",
|
|
||||||
"murmurhash32",
|
|
||||||
"num_cpus",
|
|
||||||
"once_cell",
|
|
||||||
"rayon",
|
|
||||||
"regex",
|
|
||||||
"rust-stemmers",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"smallvec",
|
|
||||||
"snap",
|
|
||||||
"stable_deref_trait",
|
|
||||||
"tantivy-fst",
|
|
||||||
"tantivy-query-grammar",
|
|
||||||
"tempfile",
|
|
||||||
"thiserror",
|
|
||||||
"uuid",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tantivy-fst"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cb20cdc0d83e9184560bdde9cd60142dbb4af2e0f770e88fce45770495224205"
|
|
||||||
dependencies = [
|
|
||||||
"byteorder",
|
|
||||||
"regex-syntax 0.4.2",
|
|
||||||
"utf8-ranges",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tantivy-query-grammar"
|
|
||||||
version = "0.14.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "70864085b31ecd5af8f53a76506440ece1c426d187f3d72f4b722e238d2ce19a"
|
|
||||||
dependencies = [
|
|
||||||
"combine",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tap"
|
name = "tap"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
@ -3562,28 +3317,12 @@ dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "utf8-ranges"
|
|
||||||
version = "1.0.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b4ae116fef2b7fea257ed6440d3cfcff7f190865f170cdad00bb6465bf18ecba"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8parse"
|
name = "utf8parse"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8772a4ccbb4e89959023bc5b7cb8623a795caa7092d99f3aa9501b9484d4557d"
|
checksum = "8772a4ccbb4e89959023bc5b7cb8623a795caa7092d99f3aa9501b9484d4557d"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "uuid"
|
|
||||||
version = "0.8.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
|
||||||
dependencies = [
|
|
||||||
"getrandom 0.2.2",
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vcpkg"
|
name = "vcpkg"
|
||||||
version = "0.2.11"
|
version = "0.2.11"
|
||||||
|
|
|
@ -12,6 +12,7 @@ license = "GPL-3.0-or-later"
|
||||||
members = [
|
members = [
|
||||||
"imap",
|
"imap",
|
||||||
"smtp",
|
"smtp",
|
||||||
|
"tui",
|
||||||
]
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
@ -46,7 +47,7 @@ sha2 = "0.9.3"
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
shellexpand = "2.1.0"
|
shellexpand = "2.1.0"
|
||||||
mailparse = "0.13.2"
|
mailparse = "0.13.2"
|
||||||
tantivy = "0.14.0"
|
# tantivy = "0.14.0"
|
||||||
|
|
||||||
[dependencies.panorama-imap]
|
[dependencies.panorama-imap]
|
||||||
path = "imap"
|
path = "imap"
|
||||||
|
|
|
@ -36,7 +36,6 @@ pub fn parse_capability(s: impl AsRef<str>) -> ParseResult<Capability> {
|
||||||
|
|
||||||
pub fn parse_streamed_response(s: impl AsRef<str>) -> ParseResult<(Response, usize)> {
|
pub fn parse_streamed_response(s: impl AsRef<str>) -> ParseResult<(Response, usize)> {
|
||||||
let s = s.as_ref();
|
let s = s.as_ref();
|
||||||
let len = s.len();
|
|
||||||
let mut pairs = match Rfc3501::parse(Rule::streamed_response, s) {
|
let mut pairs = match Rfc3501::parse(Rule::streamed_response, s) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate anyhow;
|
extern crate anyhow;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
extern crate async_trait;
|
||||||
|
#[macro_use]
|
||||||
extern crate crossterm;
|
extern crate crossterm;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate format_bytes;
|
extern crate format_bytes;
|
||||||
|
|
|
@ -44,6 +44,7 @@ pub enum MailCommand {
|
||||||
|
|
||||||
/// Main entrypoint for the mail listener.
|
/// Main entrypoint for the mail listener.
|
||||||
pub async fn run_mail(
|
pub async fn run_mail(
|
||||||
|
mail_store: MailStore,
|
||||||
mut config_watcher: ConfigWatcher,
|
mut config_watcher: ConfigWatcher,
|
||||||
ui2mail_rx: UnboundedReceiver<MailCommand>,
|
ui2mail_rx: UnboundedReceiver<MailCommand>,
|
||||||
mail2ui_tx: UnboundedSender<MailEvent>,
|
mail2ui_tx: UnboundedSender<MailEvent>,
|
||||||
|
@ -67,7 +68,6 @@ pub async fn run_mail(
|
||||||
conn.abort();
|
conn.abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mail_store = MailStore::new(config.clone()).await?;
|
|
||||||
for (acct_name, acct) in config.mail_accounts.clone().into_iter() {
|
for (acct_name, acct) in config.mail_accounts.clone().into_iter() {
|
||||||
let mail2ui_tx = mail2ui_tx.clone();
|
let mail2ui_tx = mail2ui_tx.clone();
|
||||||
let mail_store = mail_store.clone();
|
let mail_store = mail_store.clone();
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
//! Package for managing the offline storage of emails
|
//! Module for managing the offline storage of emails
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::mem;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -11,21 +13,34 @@ use sqlx::{
|
||||||
sqlite::{Sqlite, SqlitePool},
|
sqlite::{Sqlite, SqlitePool},
|
||||||
Error as SqlxError, Row,
|
Error as SqlxError, Row,
|
||||||
};
|
};
|
||||||
use tokio::{fs, sync::broadcast};
|
use tokio::{
|
||||||
|
fs,
|
||||||
|
sync::{broadcast, RwLock},
|
||||||
|
task::JoinHandle,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::config::Config;
|
use crate::config::{Config, ConfigWatcher};
|
||||||
|
|
||||||
|
use super::MailEvent;
|
||||||
|
|
||||||
static MIGRATOR: Migrator = sqlx::migrate!();
|
static MIGRATOR: Migrator = sqlx::migrate!();
|
||||||
|
|
||||||
/// Manages email storage on disk, for both database and caches
|
/// Manages email storage on disk, for both database and caches
|
||||||
///
|
///
|
||||||
/// This struct is clone-safe: cloning it will just return a reference to the same data structure
|
/// This struct is clone-safe: cloning it will just return a reference to the same data structure
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct MailStore {
|
pub struct MailStore {
|
||||||
config: Config,
|
config: Arc<RwLock<Option<Config>>>,
|
||||||
mail_dir: PathBuf,
|
inner: Arc<RwLock<Option<MailStoreInner>>>,
|
||||||
|
handle: Arc<JoinHandle<()>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
/// This is associated with a particular config. When the config is updated, this gets replaced
|
||||||
|
struct MailStoreInner {
|
||||||
pool: SqlitePool,
|
pool: SqlitePool,
|
||||||
// email_events: broadcast::Sender<EmailUpdateInfo>,
|
mail_dir: PathBuf,
|
||||||
|
accounts: HashMap<String, Arc<AccountRef>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -33,7 +48,33 @@ pub struct EmailUpdateInfo {}
|
||||||
|
|
||||||
impl MailStore {
|
impl MailStore {
|
||||||
/// Creates a new MailStore
|
/// Creates a new MailStore
|
||||||
pub async fn new(config: Config) -> Result<Self> {
|
pub fn new(mut config_watcher: ConfigWatcher) -> Self {
|
||||||
|
let config = Arc::new(RwLock::new(None));
|
||||||
|
let config2 = config.clone();
|
||||||
|
|
||||||
|
let listener = async move {
|
||||||
|
while let Ok(()) = config_watcher.changed().await {
|
||||||
|
let new_config = config_watcher.borrow().clone();
|
||||||
|
let mut write = config2.write().await;
|
||||||
|
|
||||||
|
// drop old config
|
||||||
|
if let Some(old_config) = write.take() {
|
||||||
|
mem::drop(old_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
*write = Some(new_config);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let handle = tokio::spawn(listener);
|
||||||
|
|
||||||
|
MailStore {
|
||||||
|
config,
|
||||||
|
inner: Arc::new(RwLock::new(None)),
|
||||||
|
handle: Arc::new(handle),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn init_with_config(&self, config: Config) -> Result<()> {
|
||||||
let data_dir = config.data_dir.to_string_lossy();
|
let data_dir = config.data_dir.to_string_lossy();
|
||||||
let data_dir = PathBuf::from(shellexpand::tilde(data_dir.as_ref()).as_ref());
|
let data_dir = PathBuf::from(shellexpand::tilde(data_dir.as_ref()).as_ref());
|
||||||
|
|
||||||
|
@ -63,21 +104,27 @@ impl MailStore {
|
||||||
MIGRATOR.run(&pool).await?;
|
MIGRATOR.run(&pool).await?;
|
||||||
debug!("run migrations : {:?}", MIGRATOR);
|
debug!("run migrations : {:?}", MIGRATOR);
|
||||||
|
|
||||||
|
let accounts = config
|
||||||
|
.mail_accounts
|
||||||
|
.keys()
|
||||||
|
.map(|acct| {
|
||||||
|
let folders = RwLock::new(Vec::new());
|
||||||
|
(acct.to_owned(), Arc::new(AccountRef { folders }))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
// let (new_email_tx, new_email_rx) = broadcast::channel(100);
|
// let (new_email_tx, new_email_rx) = broadcast::channel(100);
|
||||||
|
{
|
||||||
Ok(MailStore {
|
let mut write = self.inner.write().await;
|
||||||
config,
|
*write = Some(MailStoreInner {
|
||||||
mail_dir,
|
mail_dir,
|
||||||
pool,
|
pool,
|
||||||
// email_events: new_email_tx,
|
accounts,
|
||||||
})
|
});
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// /// Subscribes to the email updates
|
|
||||||
// pub fn subscribe(&self) -> broadcast::Receiver<EmailUpdateInfo> {
|
|
||||||
// self.email_events.subscribe()
|
|
||||||
// }
|
|
||||||
|
|
||||||
/// Given a UID and optional message-id try to identify a particular message
|
/// Given a UID and optional message-id try to identify a particular message
|
||||||
pub async fn try_identify_email(
|
pub async fn try_identify_email(
|
||||||
&self,
|
&self,
|
||||||
|
@ -87,6 +134,11 @@ impl MailStore {
|
||||||
uidvalidity: u32,
|
uidvalidity: u32,
|
||||||
message_id: Option<&str>,
|
message_id: Option<&str>,
|
||||||
) -> Result<Option<u32>> {
|
) -> Result<Option<u32>> {
|
||||||
|
let read = self.inner.read().await;
|
||||||
|
let inner = match &*read {
|
||||||
|
Some(v) => v,
|
||||||
|
None => return Ok(None),
|
||||||
|
};
|
||||||
let existing: Option<(u32,)> = into_opt(
|
let existing: Option<(u32,)> = into_opt(
|
||||||
sqlx::query_as(
|
sqlx::query_as(
|
||||||
r#"
|
r#"
|
||||||
|
@ -99,9 +151,10 @@ impl MailStore {
|
||||||
.bind(folder.as_ref())
|
.bind(folder.as_ref())
|
||||||
.bind(uid)
|
.bind(uid)
|
||||||
.bind(uidvalidity)
|
.bind(uidvalidity)
|
||||||
.fetch_one(&self.pool)
|
.fetch_one(&inner.pool)
|
||||||
.await,
|
.await,
|
||||||
)?;
|
)?;
|
||||||
|
mem::drop(inner);
|
||||||
|
|
||||||
if let Some(existing) = existing {
|
if let Some(existing) = existing {
|
||||||
let rowid = existing.0;
|
let rowid = existing.0;
|
||||||
|
@ -149,7 +202,12 @@ impl MailStore {
|
||||||
hasher.update(body.as_bytes());
|
hasher.update(body.as_bytes());
|
||||||
let hash = hasher.finalize();
|
let hash = hasher.finalize();
|
||||||
let filename = format!("{}.mail", hex::encode(hash));
|
let filename = format!("{}.mail", hex::encode(hash));
|
||||||
let path = self.mail_dir.join(&filename);
|
let path = {
|
||||||
|
match &*self.inner.read().await {
|
||||||
|
Some(inner) => inner.mail_dir.join(&filename),
|
||||||
|
None => return Ok(()),
|
||||||
|
}
|
||||||
|
};
|
||||||
fs::write(path, &body)
|
fs::write(path, &body)
|
||||||
.await
|
.await
|
||||||
.context("error writing email to file")?;
|
.context("error writing email to file")?;
|
||||||
|
@ -171,6 +229,11 @@ impl MailStore {
|
||||||
|
|
||||||
debug!("message-id: {:?}", message_id);
|
debug!("message-id: {:?}", message_id);
|
||||||
|
|
||||||
|
let read = self.inner.read().await;
|
||||||
|
let inner = match &*read {
|
||||||
|
Some(v) => v,
|
||||||
|
None => return Ok(()),
|
||||||
|
};
|
||||||
let existing = into_opt(
|
let existing = into_opt(
|
||||||
sqlx::query(
|
sqlx::query(
|
||||||
r#"
|
r#"
|
||||||
|
@ -183,7 +246,7 @@ impl MailStore {
|
||||||
.bind(folder.as_ref())
|
.bind(folder.as_ref())
|
||||||
.bind(uid)
|
.bind(uid)
|
||||||
.bind(uidvalidity)
|
.bind(uidvalidity)
|
||||||
.fetch_one(&self.pool)
|
.fetch_one(&inner.pool)
|
||||||
.await,
|
.await,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
@ -204,11 +267,12 @@ impl MailStore {
|
||||||
.bind(uidvalidity)
|
.bind(uidvalidity)
|
||||||
.bind(filename)
|
.bind(filename)
|
||||||
.bind(internaldate.to_rfc3339())
|
.bind(internaldate.to_rfc3339())
|
||||||
.execute(&self.pool)
|
.execute(&inner.pool)
|
||||||
.await
|
.await
|
||||||
.context("error inserting email into db")?
|
.context("error inserting email into db")?
|
||||||
.last_insert_rowid();
|
.last_insert_rowid();
|
||||||
}
|
}
|
||||||
|
mem::drop(inner);
|
||||||
|
|
||||||
// self.email_events
|
// self.email_events
|
||||||
// .send(EmailUpdateInfo {})
|
// .send(EmailUpdateInfo {})
|
||||||
|
@ -216,6 +280,34 @@ impl MailStore {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Event handerl
|
||||||
|
pub fn handle_mail_event(&self, evt: MailEvent) {
|
||||||
|
debug!("TODO: handle {:?}", evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a map of the accounts that are currently being tracked as well as a reference to the
|
||||||
|
/// account handles themselves
|
||||||
|
pub async fn list_accounts(&self) -> HashMap<String, Arc<AccountRef>> {
|
||||||
|
let read = self.inner.read().await;
|
||||||
|
let inner = match &*read {
|
||||||
|
Some(v) => v,
|
||||||
|
None => return HashMap::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
inner.accounts.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct AccountRef {
|
||||||
|
folders: RwLock<Vec<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AccountRef {
|
||||||
|
pub async fn folders(&self) -> Vec<String> {
|
||||||
|
self.folders.read().await.clone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn into_opt<T>(res: Result<T, SqlxError>) -> Result<Option<T>> {
|
fn into_opt<T>(res: Result<T, SqlxError>) -> Result<Option<T>> {
|
||||||
|
|
10
src/main.rs
10
src/main.rs
|
@ -6,7 +6,7 @@ use fern::colors::{Color, ColoredLevelConfig};
|
||||||
use futures::future::TryFutureExt;
|
use futures::future::TryFutureExt;
|
||||||
use panorama::{
|
use panorama::{
|
||||||
config::{spawn_config_watcher_system, ConfigWatcher},
|
config::{spawn_config_watcher_system, ConfigWatcher},
|
||||||
mail::{self, MailEvent},
|
mail::{self, MailEvent, MailStore},
|
||||||
report_err,
|
report_err,
|
||||||
ui::{self, UiParams},
|
ui::{self, UiParams},
|
||||||
};
|
};
|
||||||
|
@ -50,6 +50,7 @@ fn main() -> Result<()> {
|
||||||
async fn run(opt: Opt) -> Result<()> {
|
async fn run(opt: Opt) -> Result<()> {
|
||||||
let _xdg = BaseDirectories::new()?;
|
let _xdg = BaseDirectories::new()?;
|
||||||
let (_config_thread, config_update) = spawn_config_watcher_system()?;
|
let (_config_thread, config_update) = spawn_config_watcher_system()?;
|
||||||
|
let mail_store = MailStore::new(config_update.clone());
|
||||||
|
|
||||||
// used to notify the runtime that the process should exit
|
// used to notify the runtime that the process should exit
|
||||||
let (exit_tx, mut exit_rx) = mpsc::channel::<()>(1);
|
let (exit_tx, mut exit_rx) = mpsc::channel::<()>(1);
|
||||||
|
@ -64,15 +65,16 @@ async fn run(opt: Opt) -> Result<()> {
|
||||||
let (ui2vm_tx, _ui2vm_rx) = mpsc::unbounded_channel();
|
let (ui2vm_tx, _ui2vm_rx) = mpsc::unbounded_channel();
|
||||||
|
|
||||||
let config_update2 = config_update.clone();
|
let config_update2 = config_update.clone();
|
||||||
|
let mail_store2 = mail_store.clone();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
mail::run_mail(config_update2, ui2mail_rx, mail2ui_tx)
|
mail::run_mail(mail_store2, config_update2, ui2mail_rx, mail2ui_tx)
|
||||||
.unwrap_or_else(report_err)
|
.unwrap_or_else(report_err)
|
||||||
.await;
|
.await;
|
||||||
});
|
});
|
||||||
|
|
||||||
if !opt.headless {
|
if !opt.headless {
|
||||||
let config_update2 = config_update.clone();
|
let config_update2 = config_update.clone();
|
||||||
run_ui(config_update2, exit_tx, mail2ui_rx, ui2vm_tx);
|
run_ui(config_update2, mail_store.clone(), exit_tx, mail2ui_rx, ui2vm_tx);
|
||||||
}
|
}
|
||||||
|
|
||||||
exit_rx.recv().await;
|
exit_rx.recv().await;
|
||||||
|
@ -87,6 +89,7 @@ async fn run(opt: Opt) -> Result<()> {
|
||||||
// Spawns the entire UI in a different thread, since it must be thread-local
|
// Spawns the entire UI in a different thread, since it must be thread-local
|
||||||
fn run_ui(
|
fn run_ui(
|
||||||
config_update: ConfigWatcher,
|
config_update: ConfigWatcher,
|
||||||
|
mail_store: MailStore,
|
||||||
exit_tx: mpsc::Sender<()>,
|
exit_tx: mpsc::Sender<()>,
|
||||||
mail2ui_rx: mpsc::UnboundedReceiver<MailEvent>,
|
mail2ui_rx: mpsc::UnboundedReceiver<MailEvent>,
|
||||||
_ui2vm_tx: mpsc::UnboundedSender<()>,
|
_ui2vm_tx: mpsc::UnboundedSender<()>,
|
||||||
|
@ -102,6 +105,7 @@ fn run_ui(
|
||||||
let localset = LocalSet::new();
|
let localset = LocalSet::new();
|
||||||
let params = UiParams {
|
let params = UiParams {
|
||||||
config_update,
|
config_update,
|
||||||
|
mail_store,
|
||||||
stdout,
|
stdout,
|
||||||
exit_tx,
|
exit_tx,
|
||||||
mail2ui_rx,
|
mail2ui_rx,
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::rc::Rc;
|
||||||
use std::sync::{
|
use std::sync::{
|
||||||
atomic::{AtomicI8, AtomicU32, Ordering},
|
atomic::{AtomicI8, AtomicU32, Ordering},
|
||||||
Arc,
|
Arc,
|
||||||
|
@ -47,110 +48,83 @@ impl HandlesInput for MailView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
impl Window for MailView {
|
impl Window for MailView {
|
||||||
fn name(&self) -> String {
|
fn name(&self) -> String {
|
||||||
String::from("email")
|
String::from("email")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(&self, f: FrameType, area: Rect, ui: &UI) {
|
async fn draw(&self) {
|
||||||
let chunks = Layout::default()
|
// let chunks = Layout::default()
|
||||||
.direction(Direction::Horizontal)
|
// .direction(Direction::Horizontal)
|
||||||
.margin(0)
|
// .margin(0)
|
||||||
.constraints([Constraint::Length(20), Constraint::Max(5000)])
|
// .constraints([Constraint::Length(20), Constraint::Max(5000)])
|
||||||
.split(area);
|
// .split(area);
|
||||||
|
|
||||||
let accts = self.mail_store.iter_accts();
|
// let accts = self.mail_store.list_accounts().await;
|
||||||
|
|
||||||
// folder list
|
// // folder list
|
||||||
let mut items = vec![];
|
// let mut items = vec![];
|
||||||
for acct in accts.iter() {
|
// for (acct_name, acct_ref) in accts.iter() {
|
||||||
let result = self.mail_store.folders_of(acct);
|
// let folders = acct_ref.folders().await;
|
||||||
if let Some(folders) = result {
|
|
||||||
items.push(ListItem::new(acct.to_owned()));
|
|
||||||
for folder in folders {
|
|
||||||
items.push(ListItem::new(format!(" {}", folder)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let dirlist = List::new(items)
|
// items.push(ListItem::new(acct_name.to_owned()));
|
||||||
.block(Block::default().borders(Borders::NONE).title(Span::styled(
|
// for folder in folders {
|
||||||
"hellosu",
|
// items.push(ListItem::new(format!(" {}", folder)));
|
||||||
Style::default().add_modifier(Modifier::BOLD),
|
// }
|
||||||
)))
|
// }
|
||||||
.style(Style::default().fg(Color::White))
|
|
||||||
.highlight_style(Style::default().add_modifier(Modifier::ITALIC))
|
|
||||||
.highlight_symbol(">>");
|
|
||||||
|
|
||||||
// message list table
|
// let dirlist = List::new(items)
|
||||||
// let mut metas = self
|
// .block(Block::default().borders(Borders::NONE).title(Span::styled(
|
||||||
// .message_uids
|
// "hellosu",
|
||||||
// .iter()
|
// Style::default().add_modifier(Modifier::BOLD),
|
||||||
// .filter_map(|id| self.message_map.get(id))
|
// )))
|
||||||
// .collect::<Vec<_>>();
|
// .style(Style::default().fg(Color::White))
|
||||||
// metas.sort_by_key(|m| m.date);
|
// .highlight_style(Style::default().add_modifier(Modifier::ITALIC))
|
||||||
// let rows = metas
|
// .highlight_symbol(">>");
|
||||||
// .iter()
|
|
||||||
// .rev()
|
// let mut rows = vec![];
|
||||||
// .map(|meta| {
|
// for acct in accts.iter() {
|
||||||
// let mut row = Row::new(vec![
|
// // TODO: messages
|
||||||
// String::from(if meta.unread { "\u{2b24}" } else { "" }),
|
// let result: Option<Vec<EmailMetadata>> = None; // self.mail_store.messages_of(acct);
|
||||||
// meta.uid.map(|u| u.to_string()).unwrap_or_default(),
|
// if let Some(messages) = result {
|
||||||
// meta.date.map(|d| humanize_timestamp(d)).unwrap_or_default(),
|
// for meta in messages {
|
||||||
// meta.from.clone(),
|
// let mut row = Row::new(vec![
|
||||||
// meta.subject.clone(),
|
// String::from(if meta.unread { "\u{2b24}" } else { "" }),
|
||||||
// ]);
|
// meta.uid.map(|u| u.to_string()).unwrap_or_default(),
|
||||||
// if meta.unread {
|
// meta.date.map(|d| humanize_timestamp(d)).unwrap_or_default(),
|
||||||
// row = row.style(
|
// meta.from.clone(),
|
||||||
// Style::default()
|
// meta.subject.clone(),
|
||||||
// .fg(Color::LightCyan)
|
// ]);
|
||||||
// .add_modifier(Modifier::BOLD),
|
// if meta.unread {
|
||||||
// );
|
// row = row.style(
|
||||||
|
// Style::default()
|
||||||
|
// .fg(Color::LightCyan)
|
||||||
|
// .add_modifier(Modifier::BOLD),
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// rows.push(row);
|
||||||
// }
|
// }
|
||||||
// row
|
// }
|
||||||
// })
|
// }
|
||||||
// .collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let mut rows = vec![];
|
// let table = Table::new(rows)
|
||||||
for acct in accts.iter() {
|
// .style(Style::default().fg(Color::White))
|
||||||
let result = self.mail_store.messages_of(acct);
|
// .widths(&[
|
||||||
if let Some(messages) = result {
|
// Constraint::Length(1),
|
||||||
for meta in messages {
|
// Constraint::Max(3),
|
||||||
let mut row = Row::new(vec![
|
// Constraint::Min(20),
|
||||||
String::from(if meta.unread { "\u{2b24}" } else { "" }),
|
// Constraint::Min(35),
|
||||||
meta.uid.map(|u| u.to_string()).unwrap_or_default(),
|
// Constraint::Max(5000),
|
||||||
meta.date.map(|d| humanize_timestamp(d)).unwrap_or_default(),
|
// ])
|
||||||
meta.from.clone(),
|
// .header(
|
||||||
meta.subject.clone(),
|
// Row::new(vec!["", "UID", "Date", "From", "Subject"])
|
||||||
]);
|
// .style(Style::default().add_modifier(Modifier::BOLD)),
|
||||||
if meta.unread {
|
// )
|
||||||
row = row.style(
|
// .highlight_style(Style::default().bg(Color::DarkGray));
|
||||||
Style::default()
|
|
||||||
.fg(Color::LightCyan)
|
|
||||||
.add_modifier(Modifier::BOLD),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
rows.push(row);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let table = Table::new(rows)
|
|
||||||
.style(Style::default().fg(Color::White))
|
|
||||||
.widths(&[
|
|
||||||
Constraint::Length(1),
|
|
||||||
Constraint::Max(3),
|
|
||||||
Constraint::Min(20),
|
|
||||||
Constraint::Min(35),
|
|
||||||
Constraint::Max(5000),
|
|
||||||
])
|
|
||||||
.header(
|
|
||||||
Row::new(vec!["", "UID", "Date", "From", "Subject"])
|
|
||||||
.style(Style::default().add_modifier(Modifier::BOLD)),
|
|
||||||
)
|
|
||||||
.highlight_style(Style::default().bg(Color::DarkGray));
|
|
||||||
|
|
||||||
f.render_widget(dirlist, chunks[0]);
|
// f.render_widget(dirlist, chunks[0]);
|
||||||
f.render_widget(table, chunks[1]);
|
// f.render_widget(table, chunks[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
mod colon_prompt;
|
mod colon_prompt;
|
||||||
mod input;
|
mod input;
|
||||||
mod keybinds;
|
mod keybinds;
|
||||||
mod mail_store;
|
|
||||||
mod mail_view;
|
mod mail_view;
|
||||||
mod messages;
|
mod messages;
|
||||||
mod windows;
|
mod windows;
|
||||||
|
@ -39,11 +38,10 @@ use tui::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::config::ConfigWatcher;
|
use crate::config::ConfigWatcher;
|
||||||
use crate::mail::{EmailMetadata, MailEvent};
|
use crate::mail::{EmailMetadata, MailEvent, MailStore};
|
||||||
|
|
||||||
use self::colon_prompt::ColonPrompt;
|
use self::colon_prompt::ColonPrompt;
|
||||||
use self::input::{BaseInputHandler, HandlesInput, InputResult};
|
use self::input::{BaseInputHandler, HandlesInput, InputResult};
|
||||||
use self::mail_store::MailStore;
|
|
||||||
use self::mail_view::MailView;
|
use self::mail_view::MailView;
|
||||||
pub(crate) use self::messages::*;
|
pub(crate) use self::messages::*;
|
||||||
use self::windows::*;
|
use self::windows::*;
|
||||||
|
@ -56,6 +54,9 @@ pub struct UiParams {
|
||||||
/// Config updates
|
/// Config updates
|
||||||
pub config_update: ConfigWatcher,
|
pub config_update: ConfigWatcher,
|
||||||
|
|
||||||
|
/// Mail store
|
||||||
|
pub mail_store: MailStore,
|
||||||
|
|
||||||
/// Handle to the screen
|
/// Handle to the screen
|
||||||
pub stdout: Stdout,
|
pub stdout: Stdout,
|
||||||
|
|
||||||
|
@ -81,8 +82,7 @@ pub async fn run_ui2(params: UiParams) -> Result<()> {
|
||||||
|
|
||||||
let should_exit = Arc::new(AtomicBool::new(false));
|
let should_exit = Arc::new(AtomicBool::new(false));
|
||||||
|
|
||||||
let mail_store = MailStore::default();
|
let mail_store = params.mail_store;
|
||||||
|
|
||||||
let mut ui = UI {
|
let mut ui = UI {
|
||||||
should_exit: should_exit.clone(),
|
should_exit: should_exit.clone(),
|
||||||
window_layout: WindowLayout::default(),
|
window_layout: WindowLayout::default(),
|
||||||
|
@ -172,7 +172,7 @@ impl UI {
|
||||||
let visible = self.window_layout.visible_windows(chunks[0]);
|
let visible = self.window_layout.visible_windows(chunks[0]);
|
||||||
for (layout_id, area) in visible.into_iter() {
|
for (layout_id, area) in visible.into_iter() {
|
||||||
if let Some(window) = self.windows.get(&layout_id) {
|
if let Some(window) = self.windows.get(&layout_id) {
|
||||||
window.draw(f, area, self);
|
window.draw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,19 @@
|
||||||
use std::collections::{HashMap, HashSet, VecDeque};
|
use std::collections::{HashMap, HashSet, VecDeque};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use futures::future::Future;
|
||||||
use tui::layout::Rect;
|
use tui::layout::Rect;
|
||||||
|
|
||||||
use super::{FrameType, HandlesInput, UI};
|
use super::{FrameType, HandlesInput, UI};
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
pub trait Window: HandlesInput {
|
pub trait Window: HandlesInput {
|
||||||
// Return some kind of name
|
/// Return some kind of name
|
||||||
fn name(&self) -> String;
|
fn name(&self) -> String;
|
||||||
|
|
||||||
// Main draw function
|
/// Main draw function
|
||||||
fn draw(&self, f: FrameType, area: Rect, ui: &UI);
|
async fn draw(&self);
|
||||||
|
// async fn draw(&self, f: FrameType, area: Rect, ui: Rc<UI>);
|
||||||
/// Draw function, except the window is not the actively focused one
|
|
||||||
///
|
|
||||||
/// By default, this just calls the regular draw function but the window may choose to perform
|
|
||||||
/// a less intensive draw if it's known to not be active
|
|
||||||
fn draw_inactive(&mut self, f: FrameType, area: Rect, ui: &UI) {
|
|
||||||
self.draw(f, area, ui);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Update function
|
/// Update function
|
||||||
fn update(&mut self) {}
|
fn update(&mut self) {}
|
||||||
|
|
7
tui/Cargo.toml
Normal file
7
tui/Cargo.toml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
[package]
|
||||||
|
name = "panorama-tui"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Michael Zhang <mail@mzhang.io>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
0
tui/src/lib.rs
Normal file
0
tui/src/lib.rs
Normal file
Loading…
Reference in a new issue