diff --git a/.gitignore b/.gitignore index 0592392..00f02bd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target +target .DS_Store diff --git a/Cargo.lock b/Cargo.lock index c9aaf72..861e9f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,25 +4,25 @@ version = 3 [[package]] name = "ab_glyph" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af0ac006645f86f20f6c6fa4dcaef920bf803df819123626f9440e35835e7d80" +checksum = "20b228f2c198f98d4337ceb560333fb12cbb2f4948a953bf8c57d09deb219603" dependencies = [ "ab_glyph_rasterizer", - "owned_ttf_parser 0.12.1", + "owned_ttf_parser 0.13.2", ] [[package]] name = "ab_glyph_rasterizer" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9fe5e32de01730eb1f6b7f5b51c17e03e2325bf40a74f754f04f130043affff" +checksum = "a13739d7177fbd22bb0ed28badfff9f372f8bef46c863db4e1c6248f6b223b6e" [[package]] name = "addr2line" -version = "0.16.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61f2b7f93d2c7d2b08263acaa4a363b3e276806c68af6134c44f523bf1aacd" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" dependencies = [ "gimli", ] @@ -41,9 +41,9 @@ checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" [[package]] name = "ahash" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43bb833f0bf979d8475d38fbf09ed3b8a55e1885fe93ad3f93239fc6a4f17b98" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ "getrandom", "once_cell", @@ -110,9 +110,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "backtrace" -version = "0.3.61" +version = "0.3.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7a905d892734eea339e896738c14b9afce22b5318f64b951e70bf3844419b01" +checksum = "321629d8ba6513061f26707241fa9bc89524ff1cd7a915a97ef0c62c666ce1b6" dependencies = [ "addr2line", "cc", @@ -143,15 +143,15 @@ checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" [[package]] name = "bumpalo" -version = "3.7.1" +version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9df67f7bf9ef8498769f994239c45613ef0c5899415fb58e9add412d2c1a538" +checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c" [[package]] name = "bytemuck" -version = "1.7.2" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72957246c41db82b8ef88a5486143830adeb8227ef9837740bdec67724cf2c5b" +checksum = "439989e6b8c38d1b6570a384ef1e49c8848128f5a97f3914baef02920842712f" [[package]] name = "byteorder" @@ -171,9 +171,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.70" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0" +checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" [[package]] name = "cfg-if" @@ -205,7 +205,7 @@ dependencies = [ "libc", "num-integer", "num-traits", - "time", + "time 0.1.44", "winapi", ] @@ -228,8 +228,8 @@ dependencies = [ "bitflags", "block", "cocoa-foundation", - "core-foundation 0.9.1", - "core-graphics 0.22.2", + "core-foundation 0.9.2", + "core-graphics 0.22.3", "foreign-types", "libc", "objc", @@ -243,7 +243,7 @@ checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318" dependencies = [ "bitflags", "block", - "core-foundation 0.9.1", + "core-foundation 0.9.2", "core-graphics-types", "foreign-types", "libc", @@ -282,11 +282,11 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" +checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3" dependencies = [ - "core-foundation-sys 0.8.2", + "core-foundation-sys 0.8.3", "libc", ] @@ -298,9 +298,9 @@ checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" [[package]] name = "core-foundation-sys" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" [[package]] name = "core-graphics" @@ -316,12 +316,12 @@ dependencies = [ [[package]] name = "core-graphics" -version = "0.22.2" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "269f35f69b542b80e736a20a89a05215c0ce80c2c03c514abb2e318b78379d86" +checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" dependencies = [ "bitflags", - "core-foundation 0.9.1", + "core-foundation 0.9.2", "core-graphics-types", "foreign-types", "libc", @@ -334,7 +334,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" dependencies = [ "bitflags", - "core-foundation 0.9.1", + "core-foundation 0.9.2", "foreign-types", "libc", ] @@ -354,9 +354,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +checksum = "738c290dfaea84fc1ca15ad9c168d083b05a714e1efddd8edaab678dc28d2836" dependencies = [ "cfg-if 1.0.0", ] @@ -429,6 +429,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "cty" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" + [[package]] name = "darling" version = "0.10.2" @@ -485,6 +491,15 @@ dependencies = [ "syn", ] +[[package]] +name = "dirs" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309" +dependencies = [ + "dirs-sys", +] + [[package]] name = "dirs-next" version = "2.0.0" @@ -495,6 +510,17 @@ dependencies = [ "dirs-sys-next", ] +[[package]] +name = "dirs-sys" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "dirs-sys-next" version = "0.1.2" @@ -527,7 +553,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794" dependencies = [ - "libloading 0.7.0", + "libloading 0.7.2", ] [[package]] @@ -720,9 +746,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.25.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0a01e0497841a3b2db4f8afa483cce65f7e96a3498bd6c541734792aeac8fe7" +checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" [[package]] name = "gl_generator" @@ -760,19 +786,19 @@ dependencies = [ "android_glue", "cgl", "cocoa", - "core-foundation 0.9.1", + "core-foundation 0.9.2", "glutin_egl_sys", "glutin_emscripten_sys", "glutin_gles2_sys", "glutin_glx_sys", "glutin_wgl_sys", "lazy_static", - "libloading 0.7.0", + "libloading 0.7.2", "log", "objc", "osmesa-sys", "parking_lot", - "wayland-client", + "wayland-client 0.28.6", "wayland-egl", "winapi", "winit", @@ -898,9 +924,9 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "716d3d89f35ac6a34fd0eed635395f4c3b76fa889338a4632e5231a8684216bd" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if 1.0.0", ] @@ -911,6 +937,12 @@ version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + [[package]] name = "jni-sys" version = "0.3.0" @@ -946,9 +978,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.103" +version = "0.2.112" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6" +checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" [[package]] name = "libloading" @@ -962,9 +994,9 @@ dependencies = [ [[package]] name = "libloading" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f84d96438c15fcd6c3f244c8fce01d1e2b9c6b5623e9c711dc9286d8fc92d6a" +checksum = "afe203d669ec979b7128619bae5a63b7b42e9203c1b29146079ee05e2f604b52" dependencies = [ "cfg-if 1.0.0", "winapi", @@ -972,9 +1004,9 @@ dependencies = [ [[package]] name = "libsqlite3-sys" -version = "0.23.1" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abd5850c449b40bacb498b2bbdfaff648b1b055630073ba8db499caf2d0ea9f2" +checksum = "d2cafc7c74096c336d9d27145f7ebd4f4b6f95ba16aa5a282387267e6925cb58" dependencies = [ "cc", "pkg-config", @@ -1062,27 +1094,27 @@ dependencies = [ [[package]] name = "memmap2" -version = "0.2.3" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "723e3ebdcdc5c023db1df315364573789f8857c11b631a2fdfad7c00f5c046b4" +checksum = "00b6c2ebff6180198788f5db08d7ce3bc1d0b617176678831a7510825973e357" dependencies = [ "libc", ] [[package]] name = "memoffset" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" dependencies = [ "autocfg", ] [[package]] name = "minimal-lexical" -version = "0.1.3" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c835948974f68e0bd58636fc6c5b1fbff7b297e3046f11b3b3c18bbac012c6d" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" @@ -1105,9 +1137,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.7.13" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16" +checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" dependencies = [ "libc", "log", @@ -1118,9 +1150,9 @@ dependencies = [ [[package]] name = "mio-misc" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ddf05411bb159cdb5801bb10002afb66cb4572be656044315e363460ce69dc2" +checksum = "b47412f3a52115b936ff2a229b803498c7b4d332adeb87c2f1498c9da54c398c" dependencies = [ "crossbeam", "crossbeam-queue", @@ -1178,9 +1210,9 @@ dependencies = [ [[package]] name = "ndk-sys" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c44922cb3dbb1c70b5e5f443d63b64363a898564d739ba5198e3a9138442868d" +checksum = "e1bcdd74c20ad5d95aacd60ef9ba40fdf77f767051040541df557b7a9b2a2121" [[package]] name = "nix" @@ -1206,6 +1238,19 @@ dependencies = [ "libc", ] +[[package]] +name = "nix" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1e25ee6b412c2a1e3fcb6a4499a5c1bfe7f43e014bdce9a6b6666e5aa2d187" +dependencies = [ + "bitflags", + "cc", + "cfg-if 1.0.0", + "libc", + "memoffset", +] + [[package]] name = "nodrop" version = "0.1.14" @@ -1220,9 +1265,9 @@ checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" [[package]] name = "nom" -version = "7.0.0" +version = "7.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffd9d26838a953b4af82cbeb9f1592c6798916983959be223a7124e992742c1" +checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109" dependencies = [ "memchr", "minimal-lexical", @@ -1245,7 +1290,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bafe4179722c2894288ee77a9f044f02811c86af699344c498b0840c698a2465" dependencies = [ "arrayvec", - "itoa", + "itoa 0.4.8", ] [[package]] @@ -1352,18 +1397,18 @@ dependencies = [ [[package]] name = "object" -version = "0.26.2" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39f37e50073ccad23b6d09bcb5b263f4e76d3bb6038e4a3c08e52162ffa8abc2" +checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" +checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" [[package]] name = "osmesa-sys" @@ -1385,11 +1430,11 @@ dependencies = [ [[package]] name = "owned_ttf_parser" -version = "0.12.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60ac8dda2e5cc09bf6480e3b3feff9783db251710c922ae9369a429c51efdeb0" +checksum = "65ee3f72636e6f164cc41c9f9057f4e58c4e13507699ea7f5e5242b64b8198ee" dependencies = [ - "ttf-parser 0.12.3", + "ttf-parser 0.13.4", ] [[package]] @@ -1425,9 +1470,9 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "phf" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fc3db1018c4b59d7d582a739436478b6035138b6aecbce989fc91c3e98409f" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" dependencies = [ "phf_macros", "phf_shared", @@ -1475,21 +1520,21 @@ checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" [[package]] name = "pkg-config" -version = "0.3.20" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c9b1041b4387893b91ee6746cddfc28516aff326a3519fb2adf820932c5e6cb" +checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" [[package]] name = "plist" -version = "1.2.1" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a38d026d73eeaf2ade76309d0c65db5a35ecf649e3cec428db316243ea9d6711" +checksum = "bd39bc6cdc9355ad1dc5eeedefee696bb35c34caf21768741e81826c0bbd7225" dependencies = [ "base64", - "chrono", "indexmap", "line-wrap", "serde", + "time 0.3.5", "xml-rs", ] @@ -1509,43 +1554,15 @@ dependencies = [ name = "postsack" version = "0.2.0" dependencies = [ - "chrono", - "cocoa", - "crossbeam-channel", - "eframe", - "email-parser", - "emlx", - "eyre", - "flate2", - "image", - "lru", - "mbox-reader", - "num-format", - "objc", - "once_cell", - "rand", - "rayon", - "regex", - "rsql_builder", - "rusqlite", - "serde", - "serde_json", - "shellexpand", - "strum", - "strum_macros", - "thiserror", - "tinyfiledialogs", - "tracing", - "tracing-subscriber", - "treemap", - "walkdir", + "ps-core", + "ps-gui", ] [[package]] name = "ppv-lite86" -version = "0.2.10" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" +checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" [[package]] name = "proc-macro-crate" @@ -1574,18 +1591,114 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "proc-macro2" -version = "1.0.29" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d" +checksum = "2f84e92c0f7c9d58328b85a78557813e4bd845130db68d7184635344399423b1" dependencies = [ "unicode-xid", ] +[[package]] +name = "ps-core" +version = "0.2.0" +dependencies = [ + "chrono", + "crossbeam-channel", + "eyre", + "flate2", + "lru", + "once_cell", + "regex", + "rsql_builder", + "serde", + "serde_json", + "shellexpand", + "strum", + "strum_macros", + "thiserror", + "tracing", + "tracing-subscriber", + "treemap", +] + +[[package]] +name = "ps-database" +version = "0.2.0" +dependencies = [ + "chrono", + "eyre", + "ps-core", + "rsql_builder", + "rusqlite", + "serde", + "serde_json", + "thiserror", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "ps-gui" +version = "0.2.0" +dependencies = [ + "chrono", + "cocoa", + "crossbeam-channel", + "eframe", + "eyre", + "image", + "num-format", + "objc", + "once_cell", + "ps-core", + "ps-database", + "ps-importer", + "rand", + "shellexpand", + "thiserror", + "tinyfiledialogs", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "ps-importer" +version = "0.2.0" +dependencies = [ + "chrono", + "email-parser", + "emlx", + "eyre", + "flate2", + "mbox-reader", + "once_cell", + "ps-core", + "rand", + "rayon", + "regex", + "serde", + "serde_json", + "shellexpand", + "thiserror", + "tracing", + "tracing-subscriber", + "walkdir", +] + +[[package]] +name = "quick-xml" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8533f14c8382aaad0d592c812ac3b826162128b65662331e1127b45c3d18536b" +dependencies = [ + "memchr", +] + [[package]] name = "quote" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" dependencies = [ "proc-macro2", ] @@ -1632,11 +1745,21 @@ dependencies = [ [[package]] name = "raw-window-handle" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a441a7a6c80ad6473bd4b74ec1c9a4c951794285bf941c2126f607c72e48211" +checksum = "e28f55143d0548dad60bb4fbdc835a3d7ac6acc3324506450c5fdd6e42903a76" dependencies = [ "libc", + "raw-window-handle 0.4.2", +] + +[[package]] +name = "raw-window-handle" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba75eee94a9d5273a68c9e1e105d9cffe1ef700532325788389e5a83e2522b7" +dependencies = [ + "cty", ] [[package]] @@ -1712,9 +1835,9 @@ dependencies = [ [[package]] name = "rusqlite" -version = "0.26.1" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a82b0b91fad72160c56bf8da7a549b25d7c31109f52cc1437eac4c0ad2550a7" +checksum = "4ba4d3462c8b2e4d7f4fcfcf2b296dc6b65404fbbc7b63daa37fd485c149daf7" dependencies = [ "bitflags", "chrono", @@ -1751,9 +1874,9 @@ checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f" [[package]] name = "ryu" -version = "1.0.5" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" [[package]] name = "safemem" @@ -1784,18 +1907,18 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.130" +version = "1.0.131" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" +checksum = "b4ad69dfbd3e45369132cc64e6748c2d65cdfb001a2b1c232d128b4ad60561c1" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.130" +version = "1.0.131" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" +checksum = "b710a83c4e0dff6a3d511946b95274ad9ca9e5d3ae497b63fda866ac955358d2" dependencies = [ "proc-macro2", "quote", @@ -1804,20 +1927,20 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.70" +version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e277c495ac6cd1a01a58d0a0c574568b4d1ddf14f59965c6a58b8d96400b54f3" +checksum = "bcbd0344bc6533bc7ec56df11d42fb70f1b912351c0825ccb7211b59d8af7cf5" dependencies = [ - "itoa", + "itoa 1.0.1", "ryu", "serde", ] [[package]] name = "sharded-slab" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740223c51853f3145fe7c90360d2d4232f2b62e3449489c207eccde818979982" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" dependencies = [ "lazy_static", ] @@ -1867,36 +1990,37 @@ dependencies = [ "log", "memmap2 0.1.0", "nix 0.18.0", - "wayland-client", - "wayland-cursor", - "wayland-protocols", + "wayland-client 0.28.6", + "wayland-cursor 0.28.6", + "wayland-protocols 0.28.6", ] [[package]] name = "smithay-client-toolkit" -version = "0.14.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec783683499a2cfc85b6df3d04f83b1907b5cbd98a1aed44667dbdf1eac4e64c" +checksum = "210cf40de565aaaa085face1d860b17f6aee9f76f9d2816307ea2cc45eeb64f3" dependencies = [ "bitflags", "dlib 0.5.0", "lazy_static", "log", - "memmap2 0.2.3", - "nix 0.20.0", - "wayland-client", - "wayland-cursor", - "wayland-protocols", + "memmap2 0.3.1", + "nix 0.22.0", + "pkg-config", + "wayland-client 0.29.1", + "wayland-cursor 0.29.1", + "wayland-protocols 0.29.1", ] [[package]] name = "smithay-clipboard" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "986c5b4a7bd4f50d4c51f81f844745535cb488360f9cf63293780b109b9295f3" +checksum = "610b551bd25378bfd2b8e7a0fcbd83d427e8f2f6a40c47ae0f70688e9949dd55" dependencies = [ - "smithay-client-toolkit 0.14.0", - "wayland-client", + "smithay-client-toolkit 0.15.2", + "wayland-client 0.29.1", ] [[package]] @@ -1926,9 +2050,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.77" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5239bc68e0fef57495900cfea4e8dc75596d9a319d7e16b1e0a440d24e6fe0a0" +checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59" dependencies = [ "proc-macro2", "quote", @@ -1949,18 +2073,18 @@ checksum = "13de2d432b3eea016f6a010139c8b5a5bf050b5a05b8993d04033ca5232e44a9" [[package]] name = "thiserror" -version = "1.0.29" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "602eca064b2d83369e2b2f34b09c70b605402801927c65c11071ac911d299b88" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.29" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bad553cc2c78e8de258400763a647e80e6d1b31ee237275d756f6836d204494c" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" dependencies = [ "proc-macro2", "quote", @@ -1987,6 +2111,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "time" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41effe7cfa8af36f439fac33861b66b049edc6f9a32331e2312660529c1c24ad" +dependencies = [ + "itoa 0.4.8", + "libc", +] + [[package]] name = "timezone-abbreviations" version = "0.1.0" @@ -2060,9 +2194,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.0" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cf865b5ddc38e503a29c41c4843e616a73028ae18c637bc3eb2afaef4909c84" +checksum = "245da694cc7fc4729f3f418b304cb57789f1bed2a78c575407ab8a23f53cb4d3" dependencies = [ "ansi_term", "sharded-slab", @@ -2086,9 +2220,9 @@ checksum = "3e5d7cd7ab3e47dda6e56542f4bbf3824c15234958c6e1bd6aaa347e93499fdc" [[package]] name = "ttf-parser" -version = "0.12.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ae2f58a822f08abdaf668897e96a5656fe72f5a9ce66422423e8849384872e6" +checksum = "76dacc724328b3d5e2ed67f9e30cdb56893a34ab239032502cc8f19f8dae4bbc" [[package]] name = "unicode-segmentation" @@ -2208,9 +2342,25 @@ dependencies = [ "libc", "nix 0.20.0", "scoped-tls", - "wayland-commons", - "wayland-scanner", - "wayland-sys", + "wayland-commons 0.28.6", + "wayland-scanner 0.28.6", + "wayland-sys 0.28.6", +] + +[[package]] +name = "wayland-client" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9108ec1c37f4774d0c2937ba1a6c23d1786b2152c4a13bd9fdb20e42d16e8841" +dependencies = [ + "bitflags", + "downcast-rs", + "libc", + "nix 0.22.0", + "scoped-tls", + "wayland-commons 0.29.1", + "wayland-scanner 0.29.1", + "wayland-sys 0.29.1", ] [[package]] @@ -2222,7 +2372,19 @@ dependencies = [ "nix 0.20.0", "once_cell", "smallvec", - "wayland-sys", + "wayland-sys 0.28.6", +] + +[[package]] +name = "wayland-commons" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "265ef51b3b3e5c9ef098f10425c39624663f459c3821dcaacc4748be975f1beb" +dependencies = [ + "nix 0.22.0", + "once_cell", + "smallvec", + "wayland-sys 0.29.1", ] [[package]] @@ -2232,7 +2394,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be610084edd1586d45e7bdd275fe345c7c1873598caa464c4fb835dee70fa65a" dependencies = [ "nix 0.20.0", - "wayland-client", + "wayland-client 0.28.6", + "xcursor", +] + +[[package]] +name = "wayland-cursor" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c19bb6628daf4097e58b7911481e8371e13318d5a60894779901bd3267407a7" +dependencies = [ + "nix 0.22.0", + "wayland-client 0.29.1", "xcursor", ] @@ -2242,8 +2415,8 @@ version = "0.28.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99ba1ab1e18756b23982d36f08856d521d7df45015f404a2d7c4f0b2d2f66956" dependencies = [ - "wayland-client", - "wayland-sys", + "wayland-client 0.28.6", + "wayland-sys 0.28.6", ] [[package]] @@ -2253,9 +2426,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "286620ea4d803bacf61fa087a4242ee316693099ee5a140796aaba02b29f861f" dependencies = [ "bitflags", - "wayland-client", - "wayland-commons", - "wayland-scanner", + "wayland-client 0.28.6", + "wayland-commons 0.28.6", + "wayland-scanner 0.28.6", +] + +[[package]] +name = "wayland-protocols" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b3b6f1dc0193072ef4eadcb144da30d58c1f2895516c063804d213310703c8e" +dependencies = [ + "bitflags", + "wayland-client 0.29.1", + "wayland-commons 0.29.1", + "wayland-scanner 0.29.1", ] [[package]] @@ -2269,6 +2454,17 @@ dependencies = [ "xml-rs", ] +[[package]] +name = "wayland-scanner" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaaf2bc85e7b9143159af96bd23d954a5abe391c4376db712320643280fdc6f4" +dependencies = [ + "proc-macro2", + "quote", + "xml-rs", +] + [[package]] name = "wayland-sys" version = "0.28.6" @@ -2280,6 +2476,17 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "wayland-sys" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba9e06acb775b3007f8d3094438306979e572d1d3b844d7a71557a84b055d959" +dependencies = [ + "dlib 0.5.0", + "lazy_static", + "pkg-config", +] + [[package]] name = "web-sys" version = "0.3.55" @@ -2346,8 +2553,8 @@ checksum = "79610794594d5e86be473ef7763f604f2159cbac8c94debd00df8fb41e86c2f8" dependencies = [ "bitflags", "cocoa", - "core-foundation 0.9.1", - "core-graphics 0.22.2", + "core-foundation 0.9.2", + "core-graphics 0.22.3", "core-video-sys", "dispatch", "instant", @@ -2362,19 +2569,19 @@ dependencies = [ "objc", "parking_lot", "percent-encoding", - "raw-window-handle", + "raw-window-handle 0.3.4", "scopeguard", "smithay-client-toolkit 0.12.3", - "wayland-client", + "wayland-client 0.28.6", "winapi", "x11-dl", ] [[package]] name = "x11-clipboard" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b397ace6e980510de59a4fe3d4c758dffab231d6d747ce9fa1aba6b6035d5f32" +checksum = "473068b7b80ac86a18328824f1054e5e007898c47b5bbc281bd7abe32bc3653c" dependencies = [ "xcb", ] @@ -2392,12 +2599,13 @@ dependencies = [ [[package]] name = "xcb" -version = "0.9.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62056f63138b39116f82a540c983cc11f1c90cd70b3d492a70c25eaa50bd22a6" +checksum = "771e2b996df720cd1c6dd9ff90f62d91698fd3610cc078388d0564bdd6622a9c" dependencies = [ "libc", "log", + "quick-xml", ] [[package]] @@ -2411,9 +2619,12 @@ dependencies = [ [[package]] name = "xdg" -version = "2.2.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" +checksum = "3a23fe958c70412687039c86f578938b4a0bb50ec788e96bce4d6ab00ddd5803" +dependencies = [ + "dirs", +] [[package]] name = "xml-rs" diff --git a/Cargo.toml b/Cargo.toml index 35700b1..e9c6a94 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,66 +1,11 @@ -[package] -name = "postsack" -version = "0.2.0" -edition = "2021" -description = "Provides a high level visual overview of swaths of email" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[package.metadata.bundle] -name = "Postsack" -identifier = "com.stylemac.postsack" -icon = ["icons/Icon.icns", "icons/icon-win-256.png", "icons/icon-win-32.png", "icons/icon-win-16.png"] -version = "1.0.0" -copyright = "Copyright (c) Benedikt Terhechte (2021). All rights reserved." -category = "Developer Tool" -short_description = "Provides a high level visual overview of swaths of email" -osx_minimum_system_version = "10.14" - -[dependencies] -eyre = "0.6.5" -thiserror = "1.0.29" -tracing = "0.1.29" -tracing-subscriber = "0.3.0" -rusqlite = {version = "0.26.1", features = ["chrono", "trace", "serde_json", "bundled"]} -regex = "1.5.3" -flate2 = "1.0.22" -once_cell = "1.8.0" -email-parser = { git = "https://github.com/terhechte/email-parser", features = ["sender", "to", "in-reply-to", "date", "subject", "mime", "allow-duplicate-headers", "compatibility-fixes"]} -rayon = "1.5.1" -chrono = "0.4.19" -serde_json = "1.0.70" -serde = { version = "1.0.130", features = ["derive"]} -crossbeam-channel = "0.5.1" -eframe = { version = "0.15.0", optional = true} -rsql_builder = "0.1.2" -treemap = "0.3.2" -num-format = "0.4.0" -strum = "0.23.0" -strum_macros = "0.23.0" -lru = { version = "0.7.0", optional = true } -emlx = { git = "https://github.com/terhechte/emlx", features = []} -walkdir = "2.3.2" -mbox-reader = "0.2.0" -tinyfiledialogs = "3.0" -rand = "0.8.4" -shellexpand = "2.1.0" -image = { version = "0.23", default-features = false, features = ["png"] } - -[features] -default = ["gui"] -# Trace all SQL Queries -trace-sql = [] -gui = ["eframe", "lru"] - -[target."cfg(target_os = \"macos\")".dependencies.cocoa] -version = "0.24" -[target."cfg(target_os = \"macos\")".dependencies.objc] -version = "0.2.7" +[workspace] +members = [ + "ps-core", + "ps-database", + "ps-importer", + "ps-gui", + "postsack", +] [profile.dev] split-debuginfo = "unpacked" - -#[profile.release] -#lto = "fat" -#codegen-units = 1 -#panic = "abort" diff --git a/README.md b/README.md index 6917bf4..8e89eeb 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,10 @@ # Postsack +# What can go into core (web compatible!)? +- database only as trait +- importer only as trait + + ## Provides a high level visual overview of swaths of email ### Performance @@ -9,6 +14,7 @@ Update: It currently parses 632115 emails in ~56 seconds, so roughly `11.000` em ## Open Issues +- [ ] check the feature.lru to see if it compiles without LRU - [ ] build static linux binary via docker: Via Github Actions? - [ ] try to build a static windows binary: Via Github Actions? - [ ] try to build a macos binary: Via Github Actions? diff --git a/postsack/.gitignore b/postsack/.gitignore new file mode 100644 index 0000000..00f02bd --- /dev/null +++ b/postsack/.gitignore @@ -0,0 +1,3 @@ +/target +target +.DS_Store diff --git a/postsack/Cargo.lock b/postsack/Cargo.lock new file mode 100644 index 0000000..861e9f7 --- /dev/null +++ b/postsack/Cargo.lock @@ -0,0 +1,2633 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ab_glyph" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20b228f2c198f98d4337ceb560333fb12cbb2f4948a953bf8c57d09deb219603" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser 0.13.2", +] + +[[package]] +name = "ab_glyph_rasterizer" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13739d7177fbd22bb0ed28badfff9f372f8bef46c863db4e1c6248f6b223b6e" + +[[package]] +name = "addr2line" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "andrew" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c4afb09dd642feec8408e33f92f3ffc4052946f6b20f32fb99c1f58cd4fa7cf" +dependencies = [ + "bitflags", + "rusttype", + "walkdir", + "xdg", + "xml-rs", +] + +[[package]] +name = "android_glue" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "000444226fcff248f2bc4c7625be32c63caccfecc2723a2b9f78a7487a49c407" + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "arrayvec" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" +dependencies = [ + "nodrop", +] + +[[package]] +name = "atomic_refcell" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b5e5f48b927f04e952dedc932f31995a65a0bf65ec971c74436e51bf6e970d" + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "backtrace" +version = "0.3.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "321629d8ba6513061f26707241fa9bc89524ff1cd7a915a97ef0c62c666ce1b6" +dependencies = [ + "addr2line", + "cc", + "cfg-if 1.0.0", + "libc", + "miniz_oxide 0.4.4", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "bumpalo" +version = "3.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c" + +[[package]] +name = "bytemuck" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439989e6b8c38d1b6570a384ef1e49c8848128f5a97f3914baef02920842712f" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "calloop" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b036167e76041694579972c28cf4877b4f92da222560ddb49008937b6a6727c" +dependencies = [ + "log", + "nix 0.18.0", +] + +[[package]] +name = "cc" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cgl" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff" +dependencies = [ + "libc", +] + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time 0.1.44", + "winapi", +] + +[[package]] +name = "clipboard-win" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fdf5e01086b6be750428ba4a40619f847eb2e95756eee84b18e06e5f0b50342" +dependencies = [ + "lazy-bytes-cast", + "winapi", +] + +[[package]] +name = "cocoa" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63902e9223530efb4e26ccd0cf55ec30d592d3b42e21a28defc42a9586e832" +dependencies = [ + "bitflags", + "block", + "cocoa-foundation", + "core-foundation 0.9.2", + "core-graphics 0.22.3", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318" +dependencies = [ + "bitflags", + "block", + "core-foundation 0.9.2", + "core-graphics-types", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "copypasta" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4423d79fed83ebd9ab81ec21fa97144300a961782158287dc9bf7eddac37ff0b" +dependencies = [ + "clipboard-win", + "objc", + "objc-foundation", + "objc_id", + "smithay-clipboard", + "x11-clipboard", +] + +[[package]] +name = "core-foundation" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" +dependencies = [ + "core-foundation-sys 0.7.0", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3" +dependencies = [ + "core-foundation-sys 0.8.3", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "core-graphics" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3889374e6ea6ab25dba90bb5d96202f61108058361f6dc72e8b03e6f8bbe923" +dependencies = [ + "bitflags", + "core-foundation 0.7.0", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +dependencies = [ + "bitflags", + "core-foundation 0.9.2", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" +dependencies = [ + "bitflags", + "core-foundation 0.9.2", + "foreign-types", + "libc", +] + +[[package]] +name = "core-video-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ecad23610ad9757664d644e369246edde1803fcb43ed72876565098a5d3828" +dependencies = [ + "cfg-if 0.1.10", + "core-foundation-sys 0.7.0", + "core-graphics 0.19.2", + "libc", + "objc", +] + +[[package]] +name = "crc32fast" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "738c290dfaea84fc1ca15ad9c168d083b05a714e1efddd8edaab678dc28d2836" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "crossbeam" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae5588f6b3c3cb05239e90bd110f257254aecd01e4635400391aeae07497845" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b10ddc024425c88c2ad148c1b0fd53f4c6d38db9697c9f1588381212fa657c9" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" +dependencies = [ + "cfg-if 1.0.0", + "lazy_static", +] + +[[package]] +name = "cty" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" + +[[package]] +name = "darling" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "deflate" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" +dependencies = [ + "adler32", + "byteorder", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dirs" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if 1.0.0", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "dlib" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b11f15d1e3268f140f68d390637d5e76d849782d971ae7063e0da69fe9709a76" +dependencies = [ + "libloading 0.6.7", +] + +[[package]] +name = "dlib" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794" +dependencies = [ + "libloading 0.7.2", +] + +[[package]] +name = "downcast-rs" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + +[[package]] +name = "eframe" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a084390b90aa223d5fb6ee3d2ac3a2ded0df212f684f91fbb5f0f45ab9e0724" +dependencies = [ + "egui", + "egui-winit", + "egui_glium", + "egui_web", + "epi", +] + +[[package]] +name = "egui" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c8d416a3343cbfc6f4d17bb1cba46b4d7efecb9ee541967763e0b5e04e5fae7" +dependencies = [ + "ahash", + "epaint", + "nohash-hasher", +] + +[[package]] +name = "egui-winit" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc403e91d1bd693239f1c734193cdf0eb38c8682bbfb9990c4b6cd2db5ee368e" +dependencies = [ + "copypasta", + "egui", + "epi", + "webbrowser", + "winit", +] + +[[package]] +name = "egui_glium" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26478ec89b8c9c41a45687a90f9c8fc18106e3ffd8a08559285d625185a2ac92" +dependencies = [ + "egui", + "egui-winit", + "epi", + "glium", +] + +[[package]] +name = "egui_web" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f2af8984a1c9ecaaaf7f11424c78185c89b5cfe8dab3bd0fac641db81c5763" +dependencies = [ + "egui", + "epi", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "email-parser" +version = "0.5.0" +source = "git+https://github.com/terhechte/email-parser#dba59d86771f7df67bb9e7f3a2c4b1e36b02d19b" +dependencies = [ + "textcode", + "timezone-abbreviations", +] + +[[package]] +name = "emath" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24a1aaa922d55da6a2bf32957c3d153e7fb9d52ed8d69777a75092240172eb6e" + +[[package]] +name = "emlx" +version = "0.1.5" +source = "git+https://github.com/terhechte/emlx#44c2f278551d9e7a9ae0c3c3207c4471da3049fe" +dependencies = [ + "email-parser", + "plist", + "thiserror", +] + +[[package]] +name = "epaint" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16bb4d3b8bbbd132c99d2a5efec8567e8b6d09b742f758ae6cf1e4b104fe0231" +dependencies = [ + "ab_glyph", + "ahash", + "atomic_refcell", + "emath", + "nohash-hasher", +] + +[[package]] +name = "epi" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f5e4e08127f9b86e2c450c96a3032764b63546eb170c2fc54684dc70ff3fc82" +dependencies = [ + "egui", +] + +[[package]] +name = "eyre" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "221239d1d5ea86bf5d6f91c9d6bc3646ffe471b08ff9b0f91c44f115ac969d2b" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "flate2" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" +dependencies = [ + "cfg-if 1.0.0", + "crc32fast", + "libc", + "miniz_oxide 0.4.4", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" + +[[package]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", +] + +[[package]] +name = "glium" +version = "0.30.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "506a2aa1564891d447ae5d1ba37519a8efd6d01ea3e7952da81aa30430c90007" +dependencies = [ + "backtrace", + "fnv", + "gl_generator", + "glutin", + "lazy_static", + "memoffset", + "smallvec", + "takeable-option", +] + +[[package]] +name = "glutin" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "762d6cd2e1b855d99668ebe591cc9058659d85ac39a9a2078000eb122ddba8f0" +dependencies = [ + "android_glue", + "cgl", + "cocoa", + "core-foundation 0.9.2", + "glutin_egl_sys", + "glutin_emscripten_sys", + "glutin_gles2_sys", + "glutin_glx_sys", + "glutin_wgl_sys", + "lazy_static", + "libloading 0.7.2", + "log", + "objc", + "osmesa-sys", + "parking_lot", + "wayland-client 0.28.6", + "wayland-egl", + "winapi", + "winit", +] + +[[package]] +name = "glutin_egl_sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2abb6aa55523480c4adc5a56bbaa249992e2dddb2fc63dc96e04a3355364c211" +dependencies = [ + "gl_generator", + "winapi", +] + +[[package]] +name = "glutin_emscripten_sys" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80de4146df76e8a6c32b03007bc764ff3249dcaeb4f675d68a06caf1bac363f1" + +[[package]] +name = "glutin_gles2_sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8094e708b730a7c8a1954f4f8a31880af00eb8a1c5b5bf85d28a0a3c6d69103" +dependencies = [ + "gl_generator", + "objc", +] + +[[package]] +name = "glutin_glx_sys" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e393c8fc02b807459410429150e9c4faffdb312d59b8c038566173c81991351" +dependencies = [ + "gl_generator", + "x11-dl", +] + +[[package]] +name = "glutin_wgl_sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3da5951a1569dbab865c6f2a863efafff193a93caf05538d193e9e3816d21696" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashlink" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "image" +version = "0.23.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "num-iter", + "num-rational", + "num-traits", + "png", +] + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "indexmap" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "js-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + +[[package]] +name = "lazy-bytes-cast" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10257499f089cd156ad82d0a9cd57d9501fa2c989068992a97eb3c27836f206b" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.112" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" + +[[package]] +name = "libloading" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "351a32417a12d5f7e82c368a66781e307834dae04c6ce0cd4456d52989229883" +dependencies = [ + "cfg-if 1.0.0", + "winapi", +] + +[[package]] +name = "libloading" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afe203d669ec979b7128619bae5a63b7b42e9203c1b29146079ee05e2f604b52" +dependencies = [ + "cfg-if 1.0.0", + "winapi", +] + +[[package]] +name = "libsqlite3-sys" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cafc7c74096c336d9d27145f7ebd4f4b6f95ba16aa5a282387267e6925cb58" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "line-wrap" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9" +dependencies = [ + "safemem", +] + +[[package]] +name = "lock_api" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "lru" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c748cfe47cb8da225c37595b3108bea1c198c84aaae8ea0ba76d01dda9fc803" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "mbox-reader" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6231e973c0a8caceed71fac7355555012ba73fe230365989b298b36022e9e2ab" +dependencies = [ + "memmap", +] + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "memmap" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "memmap2" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b70ca2a6103ac8b665dc150b142ef0e4e89df640c9e6cf295d189c3caebe5a" +dependencies = [ + "libc", +] + +[[package]] +name = "memmap2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b6c2ebff6180198788f5db08d7ce3bc1d0b617176678831a7510825973e357" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" +dependencies = [ + "adler32", +] + +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg", +] + +[[package]] +name = "mio" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "winapi", +] + +[[package]] +name = "mio-misc" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b47412f3a52115b936ff2a229b803498c7b4d332adeb87c2f1498c9da54c398c" +dependencies = [ + "crossbeam", + "crossbeam-queue", + "log", + "mio", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi", +] + +[[package]] +name = "ndk" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8794322172319b972f528bf90c6b467be0079f1fa82780ffb431088e741a73ab" +dependencies = [ + "jni-sys", + "ndk-sys", + "num_enum", + "thiserror", +] + +[[package]] +name = "ndk-glue" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5caf0c24d51ac1c905c27d4eda4fa0635bbe0de596b8f79235e0b17a4d29385" +dependencies = [ + "lazy_static", + "libc", + "log", + "ndk", + "ndk-macro", + "ndk-sys", +] + +[[package]] +name = "ndk-macro" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d1c6307dc424d0f65b9b06e94f88248e6305726b14729fd67a5e47b2dc481d" +dependencies = [ + "darling", + "proc-macro-crate 0.1.5", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ndk-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1bcdd74c20ad5d95aacd60ef9ba40fdf77f767051040541df557b7a9b2a2121" + +[[package]] +name = "nix" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83450fe6a6142ddd95fb064b746083fc4ef1705fe81f64a64e1d4b39f54a1055" +dependencies = [ + "bitflags", + "cc", + "cfg-if 0.1.10", + "libc", +] + +[[package]] +name = "nix" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa9b4819da1bc61c0ea48b63b7bc8604064dd43013e7cc325df098d49cd7c18a" +dependencies = [ + "bitflags", + "cc", + "cfg-if 1.0.0", + "libc", +] + +[[package]] +name = "nix" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1e25ee6b412c2a1e3fcb6a4499a5c1bfe7f43e014bdce9a6b6666e5aa2d187" +dependencies = [ + "bitflags", + "cc", + "cfg-if 1.0.0", + "libc", + "memoffset", +] + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "nom" +version = "7.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109" +dependencies = [ + "memchr", + "minimal-lexical", + "version_check", +] + +[[package]] +name = "ntapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +dependencies = [ + "winapi", +] + +[[package]] +name = "num-format" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bafe4179722c2894288ee77a9f044f02811c86af699344c498b0840c698a2465" +dependencies = [ + "arrayvec", + "itoa 0.4.8", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9bd055fb730c4f8f4f57d45d35cd6b3f0980535b056dc7ff119cee6a66ed6f" +dependencies = [ + "derivative", + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "486ea01961c4a818096de679a8b740b26d9033146ac5291b1c98557658f8cdd9" +dependencies = [ + "proc-macro-crate 1.1.0", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + +[[package]] +name = "object" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" + +[[package]] +name = "osmesa-sys" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88cfece6e95d2e717e0872a7f53a8684712ad13822a7979bc760b9c77ec0013b" +dependencies = [ + "shared_library", +] + +[[package]] +name = "owned_ttf_parser" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f923fb806c46266c02ab4a5b239735c144bdeda724a50ed058e5226f594cde3" +dependencies = [ + "ttf-parser 0.6.2", +] + +[[package]] +name = "owned_ttf_parser" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65ee3f72636e6f164cc41c9f9057f4e58c4e13507699ea7f5e5242b64b8198ee" +dependencies = [ + "ttf-parser 0.13.4", +] + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +dependencies = [ + "cfg-if 1.0.0", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_macros", + "phf_shared", + "proc-macro-hack", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" + +[[package]] +name = "pkg-config" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" + +[[package]] +name = "plist" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd39bc6cdc9355ad1dc5eeedefee696bb35c34caf21768741e81826c0bbd7225" +dependencies = [ + "base64", + "indexmap", + "line-wrap", + "serde", + "time 0.3.5", + "xml-rs", +] + +[[package]] +name = "png" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" +dependencies = [ + "bitflags", + "crc32fast", + "deflate", + "miniz_oxide 0.3.7", +] + +[[package]] +name = "postsack" +version = "0.2.0" +dependencies = [ + "ps-core", + "ps-gui", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" + +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", +] + +[[package]] +name = "proc-macro-crate" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebace6889caf889b4d3f76becee12e90353f2b8c7d875534a71e5742f8f6f83" +dependencies = [ + "thiserror", + "toml", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro2" +version = "1.0.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f84e92c0f7c9d58328b85a78557813e4bd845130db68d7184635344399423b1" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "ps-core" +version = "0.2.0" +dependencies = [ + "chrono", + "crossbeam-channel", + "eyre", + "flate2", + "lru", + "once_cell", + "regex", + "rsql_builder", + "serde", + "serde_json", + "shellexpand", + "strum", + "strum_macros", + "thiserror", + "tracing", + "tracing-subscriber", + "treemap", +] + +[[package]] +name = "ps-database" +version = "0.2.0" +dependencies = [ + "chrono", + "eyre", + "ps-core", + "rsql_builder", + "rusqlite", + "serde", + "serde_json", + "thiserror", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "ps-gui" +version = "0.2.0" +dependencies = [ + "chrono", + "cocoa", + "crossbeam-channel", + "eframe", + "eyre", + "image", + "num-format", + "objc", + "once_cell", + "ps-core", + "ps-database", + "ps-importer", + "rand", + "shellexpand", + "thiserror", + "tinyfiledialogs", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "ps-importer" +version = "0.2.0" +dependencies = [ + "chrono", + "email-parser", + "emlx", + "eyre", + "flate2", + "mbox-reader", + "once_cell", + "ps-core", + "rand", + "rayon", + "regex", + "serde", + "serde_json", + "shellexpand", + "thiserror", + "tracing", + "tracing-subscriber", + "walkdir", +] + +[[package]] +name = "quick-xml" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8533f14c8382aaad0d592c812ac3b826162128b65662331e1127b45c3d18536b" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" +dependencies = [ + "rand_core", +] + +[[package]] +name = "raw-window-handle" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28f55143d0548dad60bb4fbdc835a3d7ac6acc3324506450c5fdd6e42903a76" +dependencies = [ + "libc", + "raw-window-handle 0.4.2", +] + +[[package]] +name = "raw-window-handle" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba75eee94a9d5273a68c9e1e105d9cffe1ef700532325788389e5a83e2522b7" +dependencies = [ + "cty", +] + +[[package]] +name = "rayon" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "redox_syscall" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" +dependencies = [ + "getrandom", + "redox_syscall", +] + +[[package]] +name = "regex" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "rsql_builder" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dbd5712883cef396d13516bb52b300fd97a29d52ca20361f0a4905bd38a2355" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "rusqlite" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ba4d3462c8b2e4d7f4fcfcf2b296dc6b65404fbbc7b63daa37fd485c149daf7" +dependencies = [ + "bitflags", + "chrono", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "memchr", + "serde_json", + "smallvec", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + +[[package]] +name = "rusttype" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc7c727aded0be18c5b80c1640eae0ac8e396abf6fa8477d96cb37d18ee5ec59" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser 0.6.0", +] + +[[package]] +name = "rustversion" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f" + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + +[[package]] +name = "safemem" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scoped-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "serde" +version = "1.0.131" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ad69dfbd3e45369132cc64e6748c2d65cdfb001a2b1c232d128b4ad60561c1" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.131" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b710a83c4e0dff6a3d511946b95274ad9ca9e5d3ae497b63fda866ac955358d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcbd0344bc6533bc7ec56df11d42fb70f1b912351c0825ccb7211b59d8af7cf5" +dependencies = [ + "itoa 1.0.1", + "ryu", + "serde", +] + +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shared_library" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11" +dependencies = [ + "lazy_static", + "libc", +] + +[[package]] +name = "shellexpand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83bdb7831b2d85ddf4a7b148aa19d0587eddbe8671a436b7bd1182eaad0f2829" +dependencies = [ + "dirs-next", +] + +[[package]] +name = "siphasher" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "533494a8f9b724d33625ab53c6c4800f7cc445895924a8ef649222dcb76e938b" + +[[package]] +name = "smallvec" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" + +[[package]] +name = "smithay-client-toolkit" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4750c76fd5d3ac95fa3ed80fe667d6a3d8590a960e5b575b98eea93339a80b80" +dependencies = [ + "andrew", + "bitflags", + "calloop", + "dlib 0.4.2", + "lazy_static", + "log", + "memmap2 0.1.0", + "nix 0.18.0", + "wayland-client 0.28.6", + "wayland-cursor 0.28.6", + "wayland-protocols 0.28.6", +] + +[[package]] +name = "smithay-client-toolkit" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "210cf40de565aaaa085face1d860b17f6aee9f76f9d2816307ea2cc45eeb64f3" +dependencies = [ + "bitflags", + "dlib 0.5.0", + "lazy_static", + "log", + "memmap2 0.3.1", + "nix 0.22.0", + "pkg-config", + "wayland-client 0.29.1", + "wayland-cursor 0.29.1", + "wayland-protocols 0.29.1", +] + +[[package]] +name = "smithay-clipboard" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "610b551bd25378bfd2b8e7a0fcbd83d427e8f2f6a40c47ae0f70688e9949dd55" +dependencies = [ + "smithay-client-toolkit 0.15.2", + "wayland-client 0.29.1", +] + +[[package]] +name = "strsim" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" + +[[package]] +name = "strum" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cae14b91c7d11c9a851d3fbc80a963198998c2a64eec840477fa92d8ce9b70bb" + +[[package]] +name = "strum_macros" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bb0dc7ee9c15cea6199cde9a127fa16a4c5819af85395457ad72d68edc85a38" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "takeable-option" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36ae8932fcfea38b7d3883ae2ab357b0d57a02caaa18ebb4f5ece08beaec4aa0" + +[[package]] +name = "textcode" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13de2d432b3eea016f6a010139c8b5a5bf050b5a05b8993d04033ca5232e44a9" + +[[package]] +name = "thiserror" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" +dependencies = [ + "once_cell", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi", + "winapi", +] + +[[package]] +name = "time" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41effe7cfa8af36f439fac33861b66b049edc6f9a32331e2312660529c1c24ad" +dependencies = [ + "itoa 0.4.8", + "libc", +] + +[[package]] +name = "timezone-abbreviations" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ead19eae5d0834473ce509eb859282c262e5b869a0171641d1668cd54c594d8f" +dependencies = [ + "phf", +] + +[[package]] +name = "tinyfiledialogs" +version = "3.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9545b2375cbcb7a7d70cca5e92fbaa096fd89bebd2fbc54a3da7f37d15a54e6b" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] +name = "tracing" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" +dependencies = [ + "cfg-if 1.0.0", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "tracing-log" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6923477a48e41c1951f1999ef8bb5a3023eb723ceadafe78ffb65dc366761e3" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245da694cc7fc4729f3f418b304cb57789f1bed2a78c575407ab8a23f53cb4d3" +dependencies = [ + "ansi_term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "treemap" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1571f89da27a5e1aa83304ee1ab9519ea8c6432b4c8903aaaa6c9a9eecb6f36" + +[[package]] +name = "ttf-parser" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e5d7cd7ab3e47dda6e56542f4bbf3824c15234958c6e1bd6aaa347e93499fdc" + +[[package]] +name = "ttf-parser" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76dacc724328b3d5e2ed67f9e30cdb56893a34ab239032502cc8f19f8dae4bbc" + +[[package]] +name = "unicode-segmentation" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" + +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasm-bindgen" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" + +[[package]] +name = "wayland-client" +version = "0.28.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ab332350e502f159382201394a78e3cc12d0f04db863429260164ea40e0355" +dependencies = [ + "bitflags", + "downcast-rs", + "libc", + "nix 0.20.0", + "scoped-tls", + "wayland-commons 0.28.6", + "wayland-scanner 0.28.6", + "wayland-sys 0.28.6", +] + +[[package]] +name = "wayland-client" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9108ec1c37f4774d0c2937ba1a6c23d1786b2152c4a13bd9fdb20e42d16e8841" +dependencies = [ + "bitflags", + "downcast-rs", + "libc", + "nix 0.22.0", + "scoped-tls", + "wayland-commons 0.29.1", + "wayland-scanner 0.29.1", + "wayland-sys 0.29.1", +] + +[[package]] +name = "wayland-commons" +version = "0.28.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21817947c7011bbd0a27e11b17b337bfd022e8544b071a2641232047966fbda" +dependencies = [ + "nix 0.20.0", + "once_cell", + "smallvec", + "wayland-sys 0.28.6", +] + +[[package]] +name = "wayland-commons" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "265ef51b3b3e5c9ef098f10425c39624663f459c3821dcaacc4748be975f1beb" +dependencies = [ + "nix 0.22.0", + "once_cell", + "smallvec", + "wayland-sys 0.29.1", +] + +[[package]] +name = "wayland-cursor" +version = "0.28.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be610084edd1586d45e7bdd275fe345c7c1873598caa464c4fb835dee70fa65a" +dependencies = [ + "nix 0.20.0", + "wayland-client 0.28.6", + "xcursor", +] + +[[package]] +name = "wayland-cursor" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c19bb6628daf4097e58b7911481e8371e13318d5a60894779901bd3267407a7" +dependencies = [ + "nix 0.22.0", + "wayland-client 0.29.1", + "xcursor", +] + +[[package]] +name = "wayland-egl" +version = "0.28.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99ba1ab1e18756b23982d36f08856d521d7df45015f404a2d7c4f0b2d2f66956" +dependencies = [ + "wayland-client 0.28.6", + "wayland-sys 0.28.6", +] + +[[package]] +name = "wayland-protocols" +version = "0.28.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "286620ea4d803bacf61fa087a4242ee316693099ee5a140796aaba02b29f861f" +dependencies = [ + "bitflags", + "wayland-client 0.28.6", + "wayland-commons 0.28.6", + "wayland-scanner 0.28.6", +] + +[[package]] +name = "wayland-protocols" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b3b6f1dc0193072ef4eadcb144da30d58c1f2895516c063804d213310703c8e" +dependencies = [ + "bitflags", + "wayland-client 0.29.1", + "wayland-commons 0.29.1", + "wayland-scanner 0.29.1", +] + +[[package]] +name = "wayland-scanner" +version = "0.28.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce923eb2deb61de332d1f356ec7b6bf37094dc5573952e1c8936db03b54c03f1" +dependencies = [ + "proc-macro2", + "quote", + "xml-rs", +] + +[[package]] +name = "wayland-scanner" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaaf2bc85e7b9143159af96bd23d954a5abe391c4376db712320643280fdc6f4" +dependencies = [ + "proc-macro2", + "quote", + "xml-rs", +] + +[[package]] +name = "wayland-sys" +version = "0.28.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d841fca9aed7febf9bed2e9796c49bf58d4152ceda8ac949ebe00868d8f0feb8" +dependencies = [ + "dlib 0.5.0", + "lazy_static", + "pkg-config", +] + +[[package]] +name = "wayland-sys" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba9e06acb775b3007f8d3094438306979e572d1d3b844d7a71557a84b055d959" +dependencies = [ + "dlib 0.5.0", + "lazy_static", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webbrowser" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecad156490d6b620308ed411cfee90d280b3cbd13e189ea0d3fada8acc89158a" +dependencies = [ + "web-sys", + "widestring", + "winapi", +] + +[[package]] +name = "widestring" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "winit" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79610794594d5e86be473ef7763f604f2159cbac8c94debd00df8fb41e86c2f8" +dependencies = [ + "bitflags", + "cocoa", + "core-foundation 0.9.2", + "core-graphics 0.22.3", + "core-video-sys", + "dispatch", + "instant", + "lazy_static", + "libc", + "log", + "mio", + "mio-misc", + "ndk", + "ndk-glue", + "ndk-sys", + "objc", + "parking_lot", + "percent-encoding", + "raw-window-handle 0.3.4", + "scopeguard", + "smithay-client-toolkit 0.12.3", + "wayland-client 0.28.6", + "winapi", + "x11-dl", +] + +[[package]] +name = "x11-clipboard" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "473068b7b80ac86a18328824f1054e5e007898c47b5bbc281bd7abe32bc3653c" +dependencies = [ + "xcb", +] + +[[package]] +name = "x11-dl" +version = "2.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea26926b4ce81a6f5d9d0f3a0bc401e5a37c6ae14a1bfaa8ff6099ca80038c59" +dependencies = [ + "lazy_static", + "libc", + "pkg-config", +] + +[[package]] +name = "xcb" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771e2b996df720cd1c6dd9ff90f62d91698fd3610cc078388d0564bdd6622a9c" +dependencies = [ + "libc", + "log", + "quick-xml", +] + +[[package]] +name = "xcursor" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "463705a63313cd4301184381c5e8042f0a7e9b4bb63653f216311d4ae74690b7" +dependencies = [ + "nom", +] + +[[package]] +name = "xdg" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a23fe958c70412687039c86f578938b4a0bb50ec788e96bce4d6ab00ddd5803" +dependencies = [ + "dirs", +] + +[[package]] +name = "xml-rs" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" diff --git a/postsack/Cargo.toml b/postsack/Cargo.toml new file mode 100644 index 0000000..b52f906 --- /dev/null +++ b/postsack/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "postsack" +version = "0.2.0" +edition = "2021" +description = "Provides a high level visual overview of swaths of email" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[package.metadata.bundle] +name = "Postsack" +identifier = "com.stylemac.postsack" +icon = ["icons/Icon.icns", "icons/icon-win-256.png", "icons/icon-win-32.png", "icons/icon-win-16.png"] +version = "1.0.0" +copyright = "Copyright (c) Benedikt Terhechte (2021). All rights reserved." +category = "Developer Tool" +short_description = "Provides a high level visual overview of swaths of email" +osx_minimum_system_version = "10.14" + +[dependencies] +ps-gui = { path = "../ps-gui" } +ps-core = { path = "../ps-core" } + +[profile.dev] +split-debuginfo = "unpacked" + +#[profile.release] +#lto = "fat" +#codegen-units = 1 +#panic = "abort" diff --git a/postsack/src/main.rs b/postsack/src/main.rs new file mode 100644 index 0000000..3d45b9e --- /dev/null +++ b/postsack/src/main.rs @@ -0,0 +1,6 @@ +fn main() { + #[cfg(debug_assertions)] + ps_core::setup_tracing(); + + ps_gui::run_ui(); +} diff --git a/ps-core/.gitignore b/ps-core/.gitignore new file mode 100644 index 0000000..00f02bd --- /dev/null +++ b/ps-core/.gitignore @@ -0,0 +1,3 @@ +/target +target +.DS_Store diff --git a/ps-core/Cargo.lock b/ps-core/Cargo.lock index 406836d..377cfc9 100644 --- a/ps-core/Cargo.lock +++ b/ps-core/Cargo.lock @@ -37,15 +37,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "arrayvec" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" -dependencies = [ - "nodrop", -] - [[package]] name = "autocfg" version = "1.0.1" @@ -96,30 +87,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "crossbeam-deque" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" -dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" -dependencies = [ - "cfg-if", - "crossbeam-utils", - "lazy_static", - "memoffset", - "scopeguard", -] - [[package]] name = "crossbeam-utils" version = "0.8.5" @@ -151,12 +118,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "either" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" - [[package]] name = "eyre" version = "0.6.5" @@ -208,27 +169,12 @@ dependencies = [ "unicode-segmentation", ] -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - [[package]] name = "indenter" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" -[[package]] -name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - [[package]] name = "itoa" version = "1.0.1" @@ -271,15 +217,6 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" -[[package]] -name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg", -] - [[package]] name = "miniz_oxide" version = "0.4.4" @@ -290,22 +227,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "nodrop" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" - -[[package]] -name = "num-format" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bafe4179722c2894288ee77a9f044f02811c86af699344c498b0840c698a2465" -dependencies = [ - "arrayvec", - "itoa 0.4.8", -] - [[package]] name = "num-integer" version = "0.1.44" @@ -325,16 +246,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "num_cpus" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" -dependencies = [ - "hermit-abi", - "libc", -] - [[package]] name = "once_cell" version = "1.9.0" @@ -347,12 +258,6 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" -[[package]] -name = "ppv-lite86" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" - [[package]] name = "proc-macro2" version = "1.0.34" @@ -371,10 +276,7 @@ dependencies = [ "eyre", "flate2", "lru", - "num-format", "once_cell", - "rand", - "rayon", "regex", "rsql_builder", "serde", @@ -397,71 +299,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", - "rand_hc", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_hc" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" -dependencies = [ - "rand_core", -] - -[[package]] -name = "rayon" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" -dependencies = [ - "autocfg", - "crossbeam-deque", - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "lazy_static", - "num_cpus", -] - [[package]] name = "redox_syscall" version = "0.2.10" @@ -520,12 +357,6 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - [[package]] name = "serde" version = "1.0.131" @@ -552,7 +383,7 @@ version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcbd0344bc6533bc7ec56df11d42fb70f1b912351c0825ccb7211b59d8af7cf5" dependencies = [ - "itoa 1.0.1", + "itoa", "ryu", "serde", ] diff --git a/ps-core/Cargo.toml b/ps-core/Cargo.toml index 8bea116..818f9e2 100644 --- a/ps-core/Cargo.toml +++ b/ps-core/Cargo.toml @@ -12,19 +12,16 @@ tracing-subscriber = "0.3.0" regex = "1.5.3" flate2 = "1.0.22" once_cell = "1.8.0" -rayon = "1.5.1" chrono = "0.4.19" serde_json = "1.0.70" -serde = { version = "1.0.130", features = ["derive"]} +serde = { version = "1.0.131", features = ["derive"]} crossbeam-channel = "0.5.1" rsql_builder = "0.1.2" treemap = "0.3.2" -num-format = "0.4.0" strum = "0.23.0" strum_macros = "0.23.0" lru = { version = "0.7.0", optional = true } -rand = "0.8.4" shellexpand = "2.1.0" [features] -default = ["lru"] \ No newline at end of file +default = ["lru"] diff --git a/ps-core/src/database/database_like.rs b/ps-core/src/database/database_like.rs index 3baaf15..793bdab 100644 --- a/ps-core/src/database/database_like.rs +++ b/ps-core/src/database/database_like.rs @@ -8,7 +8,7 @@ use crate::Config; use super::{db_message::DBMessage, query::Query, query_result::QueryResult}; -pub trait DatabaseLike: Send + Sync { +pub trait DatabaseLike: Clone + Send { fn new(path: impl AsRef) -> Result where Self: Sized; diff --git a/ps-core/src/lib.rs b/ps-core/src/lib.rs index d2b7b12..d4cf820 100644 --- a/ps-core/src/lib.rs +++ b/ps-core/src/lib.rs @@ -1,13 +1,29 @@ mod database; mod importer; -mod model; +pub mod message_adapter; +pub mod model; mod types; pub use database::database_like::DatabaseLike; pub use database::db_message::DBMessage; -pub use database::query::{Field, OtherQuery, Query, ValueField, AMOUNT_FIELD_NAME}; -pub use database::query_result::QueryResult; +pub use database::query::{Field, Filter, OtherQuery, Query, ValueField, AMOUNT_FIELD_NAME}; +pub use database::query_result::{QueryResult, QueryRow}; pub use types::{Config, EmailEntry, EmailMeta, FormatType}; pub use crossbeam_channel; pub use importer::{Importerlike, Message, MessageReceiver, MessageSender}; + +// Tracing + +use tracing_subscriber::fmt; +use tracing_subscriber::prelude::*; + +pub fn setup_tracing() { + if std::env::var("RUST_LOG").is_err() { + std::env::set_var("RUST_LOG", "error") + } + + let collector = tracing_subscriber::registry().with(fmt::layer().with_writer(std::io::stdout)); + + tracing::subscriber::set_global_default(collector).expect("Unable to set a global collector"); +} diff --git a/ps-importer/src/message_adapter.rs b/ps-core/src/message_adapter.rs similarity index 94% rename from ps-importer/src/message_adapter.rs rename to ps-core/src/message_adapter.rs index b9bd404..edc51f8 100644 --- a/ps-importer/src/message_adapter.rs +++ b/ps-core/src/message_adapter.rs @@ -3,8 +3,7 @@ use eyre::{bail, eyre, Report, Result}; use std::sync::{Arc, RwLock}; use std::thread::JoinHandle; -use super::formats::ImporterFormat; -use ps_core::{DatabaseLike, Importerlike, Message}; +use crate::{DatabaseLike, Importerlike, Message}; #[derive(Debug, Default)] struct Data { @@ -43,7 +42,6 @@ pub struct Adapter { } impl Adapter { - #[allow(clippy::new_without_default)] pub fn new() -> Self { let rw_lock = Arc::new(RwLock::default()); // FIXME: Look up this warning. It looks like the clones are necessary? @@ -59,10 +57,10 @@ impl Adapter { /// Starts up a thread that handles the `MessageReceiver` messages /// into state that can be accessed via [`read_count`], [`write_count`] and [`finished`] - pub fn process( + pub fn process( &self, - importer: super::importer::Importer, database: Database, + importer: Importer, ) -> Result>> { let (receiver, handle) = importer.import(database)?; let lock = self.producer_lock.clone(); diff --git a/ps-core/src/model/engine.rs b/ps-core/src/model/engine.rs index 0b0900b..c1bc41f 100644 --- a/ps-core/src/model/engine.rs +++ b/ps-core/src/model/engine.rs @@ -48,11 +48,8 @@ pub struct Engine { } impl Engine { - pub fn new( - config: &Config, - database: Database, - ) -> Result { - let link = super::link::run(config, database)?; + pub fn new(config: &Config) -> Result { + let link = super::link::run::<_, Database>(config)?; let engine = Engine { link, search_stack: Vec::new(), diff --git a/ps-core/src/model/link.rs b/ps-core/src/model/link.rs index d1b1bcc..0883f61 100644 --- a/ps-core/src/model/link.rs +++ b/ps-core/src/model/link.rs @@ -83,10 +83,9 @@ impl Link { pub(super) fn run( config: &Config, - database: Database, ) -> Result> { // Create a new database connection, just for reading - //let database = Database::new(&config.database_path)?; + let database = Database::new(&config.database_path.clone())?; let (input_sender, input_receiver) = unbounded(); let (output_sender, output_receiver) = unbounded(); let _ = std::thread::spawn(move || inner_loop(database, input_receiver, output_sender)); diff --git a/ps-core/src/model/mod.rs b/ps-core/src/model/mod.rs index 7e702c3..ab50f27 100644 --- a/ps-core/src/model/mod.rs +++ b/ps-core/src/model/mod.rs @@ -5,4 +5,4 @@ pub mod segmentations; mod types; pub use engine::Engine; -pub use types::Segment; +pub use types::{Rect, Segment}; diff --git a/ps-core/src/types/config.rs b/ps-core/src/types/config.rs index 05c3649..46f59a8 100644 --- a/ps-core/src/types/config.rs +++ b/ps-core/src/types/config.rs @@ -1,5 +1,4 @@ use eyre::{eyre, Result}; -use rand::Rng; use serde_json::Value; use strum::{self, IntoEnumIterator}; use strum_macros::{EnumIter, IntoStaticStr}; @@ -155,7 +154,7 @@ impl Config { let database_path = match db { Some(n) => n.as_ref().to_path_buf(), None => { - let number: u32 = rand::thread_rng().gen(); + let number = timestamp(); let folder = "postsack"; let filename = format!("{}.sqlite", number); let mut temp_dir = std::env::temp_dir(); @@ -201,3 +200,12 @@ impl Config { Some(new) } } + +fn timestamp() -> u32 { + use std::time::{SystemTime, UNIX_EPOCH}; + let start = SystemTime::now(); + let since_the_epoch = start + .duration_since(UNIX_EPOCH) + .expect("Time went backwards"); + since_the_epoch.as_millis() as u32 +} diff --git a/ps-database/.gitignore b/ps-database/.gitignore new file mode 100644 index 0000000..00f02bd --- /dev/null +++ b/ps-database/.gitignore @@ -0,0 +1,3 @@ +/target +target +.DS_Store diff --git a/ps-database/Cargo.lock b/ps-database/Cargo.lock index 00a56a8..58cee44 100644 --- a/ps-database/Cargo.lock +++ b/ps-database/Cargo.lock @@ -37,15 +37,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "arrayvec" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" -dependencies = [ - "nodrop", -] - [[package]] name = "autocfg" version = "1.0.1" @@ -102,30 +93,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "crossbeam-deque" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" -dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" -dependencies = [ - "cfg-if", - "crossbeam-utils", - "lazy_static", - "memoffset", - "scopeguard", -] - [[package]] name = "crossbeam-utils" version = "0.8.5" @@ -157,12 +124,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "either" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" - [[package]] name = "eyre" version = "0.6.5" @@ -235,27 +196,12 @@ dependencies = [ "unicode-segmentation", ] -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - [[package]] name = "indenter" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" -[[package]] -name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - [[package]] name = "itoa" version = "1.0.1" @@ -309,15 +255,6 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" -[[package]] -name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg", -] - [[package]] name = "miniz_oxide" version = "0.4.4" @@ -328,22 +265,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "nodrop" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" - -[[package]] -name = "num-format" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bafe4179722c2894288ee77a9f044f02811c86af699344c498b0840c698a2465" -dependencies = [ - "arrayvec", - "itoa 0.4.8", -] - [[package]] name = "num-integer" version = "0.1.44" @@ -363,16 +284,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "num_cpus" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" -dependencies = [ - "hermit-abi", - "libc", -] - [[package]] name = "once_cell" version = "1.9.0" @@ -391,12 +302,6 @@ version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" -[[package]] -name = "ppv-lite86" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" - [[package]] name = "proc-macro2" version = "1.0.34" @@ -415,10 +320,7 @@ dependencies = [ "eyre", "flate2", "lru", - "num-format", "once_cell", - "rand", - "rayon", "regex", "rsql_builder", "serde", @@ -457,71 +359,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", - "rand_hc", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_hc" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" -dependencies = [ - "rand_core", -] - -[[package]] -name = "rayon" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" -dependencies = [ - "autocfg", - "crossbeam-deque", - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "lazy_static", - "num_cpus", -] - [[package]] name = "redox_syscall" version = "0.2.10" @@ -597,12 +434,6 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - [[package]] name = "serde" version = "1.0.131" @@ -629,7 +460,7 @@ version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcbd0344bc6533bc7ec56df11d42fb70f1b912351c0825ccb7211b59d8af7cf5" dependencies = [ - "itoa 1.0.1", + "itoa", "ryu", "serde", ] diff --git a/ps-database/src/db.rs b/ps-database/src/db.rs index 8272317..906a6e7 100644 --- a/ps-database/src/db.rs +++ b/ps-database/src/db.rs @@ -2,30 +2,32 @@ use chrono::Datelike; use eyre::{bail, Report, Result}; use rusqlite::{self, params, Connection, Statement}; +use std::path::PathBuf; use std::{collections::HashMap, path::Path, thread::JoinHandle}; -use super::{sql::*, DBMessage}; +use super::sql::*; use super::{value_from_field, RowConversion}; use ps_core::{ crossbeam_channel::{unbounded, Sender}, - Config, EmailEntry, OtherQuery, Query, QueryResult, + Config, DBMessage, DatabaseLike, EmailEntry, OtherQuery, Query, QueryResult, }; #[derive(Debug)] pub struct Database { connection: Option, + path: PathBuf, } -impl Database { - /// Open a database and try to retrieve a config from the information stored in there - pub fn config>(path: P) -> Result { - let database = Self::new(path.as_ref())?; - let fields = database.select_config_fields()?; - Config::from_fields(path.as_ref(), fields) +impl Clone for Database { + fn clone(&self) -> Self { + // If we could open one before, we hopefully can open one again + Database::new(&self.path).unwrap() } +} +impl DatabaseLike for Database { /// Open database at path `Path`. - pub fn new>(path: P) -> Result { + fn new(path: impl AsRef) -> Result { #[allow(unused_mut)] let mut connection = Connection::open(path.as_ref())?; @@ -42,10 +44,11 @@ impl Database { Ok(Database { connection: Some(connection), + path: path.as_ref().into(), }) } - pub fn total_mails(&self) -> Result { + fn total_mails(&self) -> Result { let connection = match &self.connection { Some(n) => n, None => bail!("No connection to database available in query"), @@ -55,14 +58,14 @@ impl Database { Ok(count) } - pub fn save_config(&self, config: Config) -> Result<()> { + fn save_config(&self, config: Config) -> Result<()> { let fields = config .into_fields() .ok_or_else(|| eyre::eyre!("Could not create fields from config"))?; self.insert_config_fields(fields) } - pub fn query(&self, query: &Query) -> Result> { + fn query(&self, query: &Query) -> Result> { use rusqlite::params_from_iter; let c = match &self.connection { Some(n) => n, @@ -113,7 +116,7 @@ impl Database { /// sender.send(DBMessage::Mail(m2)).unwrap(); /// handle.join().unwrap(); /// ``` - pub fn import(mut self) -> (Sender, JoinHandle>) { + fn import(mut self) -> (Sender, JoinHandle>) { let (sender, receiver) = unbounded(); // Import can only be called *once* on a database created with `new`. @@ -165,6 +168,15 @@ impl Database { }); (sender, handle) } +} + +impl Database { + /// Open a database and try to retrieve a config from the information stored in there + pub fn config>(path: P) -> Result { + let database = Self::new(path.as_ref())?; + let fields = database.select_config_fields()?; + Config::from_fields(path.as_ref(), fields) + } fn create_tables(connection: &Connection) -> Result<()> { connection.execute(TBL_EMAILS, params![])?; diff --git a/ps-database/src/lib.rs b/ps-database/src/lib.rs index c61fb42..e6b86d1 100644 --- a/ps-database/src/lib.rs +++ b/ps-database/src/lib.rs @@ -1,8 +1,6 @@ mod conversion; mod db; -mod db_message; mod sql; pub use conversion::{value_from_field, RowConversion}; pub use db::Database; -pub use db_message::DBMessage; diff --git a/ps-gui/.gitignore b/ps-gui/.gitignore new file mode 100644 index 0000000..00f02bd --- /dev/null +++ b/ps-gui/.gitignore @@ -0,0 +1,3 @@ +/target +target +.DS_Store diff --git a/ps-gui/Cargo.lock b/ps-gui/Cargo.lock new file mode 100644 index 0000000..12ad843 --- /dev/null +++ b/ps-gui/Cargo.lock @@ -0,0 +1,2625 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ab_glyph" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20b228f2c198f98d4337ceb560333fb12cbb2f4948a953bf8c57d09deb219603" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser 0.13.2", +] + +[[package]] +name = "ab_glyph_rasterizer" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13739d7177fbd22bb0ed28badfff9f372f8bef46c863db4e1c6248f6b223b6e" + +[[package]] +name = "addr2line" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "andrew" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c4afb09dd642feec8408e33f92f3ffc4052946f6b20f32fb99c1f58cd4fa7cf" +dependencies = [ + "bitflags", + "rusttype", + "walkdir", + "xdg", + "xml-rs", +] + +[[package]] +name = "android_glue" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "000444226fcff248f2bc4c7625be32c63caccfecc2723a2b9f78a7487a49c407" + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "arrayvec" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" +dependencies = [ + "nodrop", +] + +[[package]] +name = "atomic_refcell" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b5e5f48b927f04e952dedc932f31995a65a0bf65ec971c74436e51bf6e970d" + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "backtrace" +version = "0.3.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "321629d8ba6513061f26707241fa9bc89524ff1cd7a915a97ef0c62c666ce1b6" +dependencies = [ + "addr2line", + "cc", + "cfg-if 1.0.0", + "libc", + "miniz_oxide 0.4.4", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "bumpalo" +version = "3.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c" + +[[package]] +name = "bytemuck" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72957246c41db82b8ef88a5486143830adeb8227ef9837740bdec67724cf2c5b" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "calloop" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b036167e76041694579972c28cf4877b4f92da222560ddb49008937b6a6727c" +dependencies = [ + "log", + "nix 0.18.0", +] + +[[package]] +name = "cc" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cgl" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff" +dependencies = [ + "libc", +] + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time 0.1.44", + "winapi", +] + +[[package]] +name = "clipboard-win" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fdf5e01086b6be750428ba4a40619f847eb2e95756eee84b18e06e5f0b50342" +dependencies = [ + "lazy-bytes-cast", + "winapi", +] + +[[package]] +name = "cocoa" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63902e9223530efb4e26ccd0cf55ec30d592d3b42e21a28defc42a9586e832" +dependencies = [ + "bitflags", + "block", + "cocoa-foundation", + "core-foundation 0.9.2", + "core-graphics 0.22.3", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318" +dependencies = [ + "bitflags", + "block", + "core-foundation 0.9.2", + "core-graphics-types", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "copypasta" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4423d79fed83ebd9ab81ec21fa97144300a961782158287dc9bf7eddac37ff0b" +dependencies = [ + "clipboard-win", + "objc", + "objc-foundation", + "objc_id", + "smithay-clipboard", + "x11-clipboard", +] + +[[package]] +name = "core-foundation" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" +dependencies = [ + "core-foundation-sys 0.7.0", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3" +dependencies = [ + "core-foundation-sys 0.8.3", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "core-graphics" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3889374e6ea6ab25dba90bb5d96202f61108058361f6dc72e8b03e6f8bbe923" +dependencies = [ + "bitflags", + "core-foundation 0.7.0", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +dependencies = [ + "bitflags", + "core-foundation 0.9.2", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" +dependencies = [ + "bitflags", + "core-foundation 0.9.2", + "foreign-types", + "libc", +] + +[[package]] +name = "core-video-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ecad23610ad9757664d644e369246edde1803fcb43ed72876565098a5d3828" +dependencies = [ + "cfg-if 0.1.10", + "core-foundation-sys 0.7.0", + "core-graphics 0.19.2", + "libc", + "objc", +] + +[[package]] +name = "crc32fast" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "738c290dfaea84fc1ca15ad9c168d083b05a714e1efddd8edaab678dc28d2836" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "crossbeam" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae5588f6b3c3cb05239e90bd110f257254aecd01e4635400391aeae07497845" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b10ddc024425c88c2ad148c1b0fd53f4c6d38db9697c9f1588381212fa657c9" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" +dependencies = [ + "cfg-if 1.0.0", + "lazy_static", +] + +[[package]] +name = "cty" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" + +[[package]] +name = "darling" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "deflate" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" +dependencies = [ + "adler32", + "byteorder", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dirs" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if 1.0.0", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "dlib" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b11f15d1e3268f140f68d390637d5e76d849782d971ae7063e0da69fe9709a76" +dependencies = [ + "libloading 0.6.7", +] + +[[package]] +name = "dlib" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794" +dependencies = [ + "libloading 0.7.2", +] + +[[package]] +name = "downcast-rs" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + +[[package]] +name = "eframe" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a084390b90aa223d5fb6ee3d2ac3a2ded0df212f684f91fbb5f0f45ab9e0724" +dependencies = [ + "egui", + "egui-winit", + "egui_glium", + "egui_web", + "epi", +] + +[[package]] +name = "egui" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c8d416a3343cbfc6f4d17bb1cba46b4d7efecb9ee541967763e0b5e04e5fae7" +dependencies = [ + "ahash", + "epaint", + "nohash-hasher", +] + +[[package]] +name = "egui-winit" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc403e91d1bd693239f1c734193cdf0eb38c8682bbfb9990c4b6cd2db5ee368e" +dependencies = [ + "copypasta", + "egui", + "epi", + "webbrowser", + "winit", +] + +[[package]] +name = "egui_glium" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26478ec89b8c9c41a45687a90f9c8fc18106e3ffd8a08559285d625185a2ac92" +dependencies = [ + "egui", + "egui-winit", + "epi", + "glium", +] + +[[package]] +name = "egui_web" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f2af8984a1c9ecaaaf7f11424c78185c89b5cfe8dab3bd0fac641db81c5763" +dependencies = [ + "egui", + "epi", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "email-parser" +version = "0.5.0" +source = "git+https://github.com/terhechte/email-parser#dba59d86771f7df67bb9e7f3a2c4b1e36b02d19b" +dependencies = [ + "textcode", + "timezone-abbreviations", +] + +[[package]] +name = "emath" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24a1aaa922d55da6a2bf32957c3d153e7fb9d52ed8d69777a75092240172eb6e" + +[[package]] +name = "emlx" +version = "0.1.5" +source = "git+https://github.com/terhechte/emlx#44c2f278551d9e7a9ae0c3c3207c4471da3049fe" +dependencies = [ + "email-parser", + "plist", + "thiserror", +] + +[[package]] +name = "epaint" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16bb4d3b8bbbd132c99d2a5efec8567e8b6d09b742f758ae6cf1e4b104fe0231" +dependencies = [ + "ab_glyph", + "ahash", + "atomic_refcell", + "emath", + "nohash-hasher", +] + +[[package]] +name = "epi" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f5e4e08127f9b86e2c450c96a3032764b63546eb170c2fc54684dc70ff3fc82" +dependencies = [ + "egui", +] + +[[package]] +name = "eyre" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "221239d1d5ea86bf5d6f91c9d6bc3646ffe471b08ff9b0f91c44f115ac969d2b" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "flate2" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" +dependencies = [ + "cfg-if 1.0.0", + "crc32fast", + "libc", + "miniz_oxide 0.4.4", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" + +[[package]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", +] + +[[package]] +name = "glium" +version = "0.30.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "506a2aa1564891d447ae5d1ba37519a8efd6d01ea3e7952da81aa30430c90007" +dependencies = [ + "backtrace", + "fnv", + "gl_generator", + "glutin", + "lazy_static", + "memoffset", + "smallvec", + "takeable-option", +] + +[[package]] +name = "glutin" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "762d6cd2e1b855d99668ebe591cc9058659d85ac39a9a2078000eb122ddba8f0" +dependencies = [ + "android_glue", + "cgl", + "cocoa", + "core-foundation 0.9.2", + "glutin_egl_sys", + "glutin_emscripten_sys", + "glutin_gles2_sys", + "glutin_glx_sys", + "glutin_wgl_sys", + "lazy_static", + "libloading 0.7.2", + "log", + "objc", + "osmesa-sys", + "parking_lot", + "wayland-client 0.28.6", + "wayland-egl", + "winapi", + "winit", +] + +[[package]] +name = "glutin_egl_sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2abb6aa55523480c4adc5a56bbaa249992e2dddb2fc63dc96e04a3355364c211" +dependencies = [ + "gl_generator", + "winapi", +] + +[[package]] +name = "glutin_emscripten_sys" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80de4146df76e8a6c32b03007bc764ff3249dcaeb4f675d68a06caf1bac363f1" + +[[package]] +name = "glutin_gles2_sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8094e708b730a7c8a1954f4f8a31880af00eb8a1c5b5bf85d28a0a3c6d69103" +dependencies = [ + "gl_generator", + "objc", +] + +[[package]] +name = "glutin_glx_sys" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e393c8fc02b807459410429150e9c4faffdb312d59b8c038566173c81991351" +dependencies = [ + "gl_generator", + "x11-dl", +] + +[[package]] +name = "glutin_wgl_sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3da5951a1569dbab865c6f2a863efafff193a93caf05538d193e9e3816d21696" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashlink" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "image" +version = "0.23.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "num-iter", + "num-rational", + "num-traits", + "png", +] + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "indexmap" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "js-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + +[[package]] +name = "lazy-bytes-cast" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10257499f089cd156ad82d0a9cd57d9501fa2c989068992a97eb3c27836f206b" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.112" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" + +[[package]] +name = "libloading" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "351a32417a12d5f7e82c368a66781e307834dae04c6ce0cd4456d52989229883" +dependencies = [ + "cfg-if 1.0.0", + "winapi", +] + +[[package]] +name = "libloading" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afe203d669ec979b7128619bae5a63b7b42e9203c1b29146079ee05e2f604b52" +dependencies = [ + "cfg-if 1.0.0", + "winapi", +] + +[[package]] +name = "libsqlite3-sys" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cafc7c74096c336d9d27145f7ebd4f4b6f95ba16aa5a282387267e6925cb58" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "line-wrap" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9" +dependencies = [ + "safemem", +] + +[[package]] +name = "lock_api" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "lru" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c748cfe47cb8da225c37595b3108bea1c198c84aaae8ea0ba76d01dda9fc803" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "mbox-reader" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6231e973c0a8caceed71fac7355555012ba73fe230365989b298b36022e9e2ab" +dependencies = [ + "memmap", +] + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "memmap" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "memmap2" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b70ca2a6103ac8b665dc150b142ef0e4e89df640c9e6cf295d189c3caebe5a" +dependencies = [ + "libc", +] + +[[package]] +name = "memmap2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b6c2ebff6180198788f5db08d7ce3bc1d0b617176678831a7510825973e357" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" +dependencies = [ + "adler32", +] + +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg", +] + +[[package]] +name = "mio" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "winapi", +] + +[[package]] +name = "mio-misc" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b47412f3a52115b936ff2a229b803498c7b4d332adeb87c2f1498c9da54c398c" +dependencies = [ + "crossbeam", + "crossbeam-queue", + "log", + "mio", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi", +] + +[[package]] +name = "ndk" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8794322172319b972f528bf90c6b467be0079f1fa82780ffb431088e741a73ab" +dependencies = [ + "jni-sys", + "ndk-sys", + "num_enum", + "thiserror", +] + +[[package]] +name = "ndk-glue" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5caf0c24d51ac1c905c27d4eda4fa0635bbe0de596b8f79235e0b17a4d29385" +dependencies = [ + "lazy_static", + "libc", + "log", + "ndk", + "ndk-macro", + "ndk-sys", +] + +[[package]] +name = "ndk-macro" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d1c6307dc424d0f65b9b06e94f88248e6305726b14729fd67a5e47b2dc481d" +dependencies = [ + "darling", + "proc-macro-crate 0.1.5", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ndk-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1bcdd74c20ad5d95aacd60ef9ba40fdf77f767051040541df557b7a9b2a2121" + +[[package]] +name = "nix" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83450fe6a6142ddd95fb064b746083fc4ef1705fe81f64a64e1d4b39f54a1055" +dependencies = [ + "bitflags", + "cc", + "cfg-if 0.1.10", + "libc", +] + +[[package]] +name = "nix" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa9b4819da1bc61c0ea48b63b7bc8604064dd43013e7cc325df098d49cd7c18a" +dependencies = [ + "bitflags", + "cc", + "cfg-if 1.0.0", + "libc", +] + +[[package]] +name = "nix" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1e25ee6b412c2a1e3fcb6a4499a5c1bfe7f43e014bdce9a6b6666e5aa2d187" +dependencies = [ + "bitflags", + "cc", + "cfg-if 1.0.0", + "libc", + "memoffset", +] + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "nom" +version = "7.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109" +dependencies = [ + "memchr", + "minimal-lexical", + "version_check", +] + +[[package]] +name = "ntapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +dependencies = [ + "winapi", +] + +[[package]] +name = "num-format" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bafe4179722c2894288ee77a9f044f02811c86af699344c498b0840c698a2465" +dependencies = [ + "arrayvec", + "itoa 0.4.8", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9bd055fb730c4f8f4f57d45d35cd6b3f0980535b056dc7ff119cee6a66ed6f" +dependencies = [ + "derivative", + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "486ea01961c4a818096de679a8b740b26d9033146ac5291b1c98557658f8cdd9" +dependencies = [ + "proc-macro-crate 1.1.0", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + +[[package]] +name = "object" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" + +[[package]] +name = "osmesa-sys" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88cfece6e95d2e717e0872a7f53a8684712ad13822a7979bc760b9c77ec0013b" +dependencies = [ + "shared_library", +] + +[[package]] +name = "owned_ttf_parser" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f923fb806c46266c02ab4a5b239735c144bdeda724a50ed058e5226f594cde3" +dependencies = [ + "ttf-parser 0.6.2", +] + +[[package]] +name = "owned_ttf_parser" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65ee3f72636e6f164cc41c9f9057f4e58c4e13507699ea7f5e5242b64b8198ee" +dependencies = [ + "ttf-parser 0.13.4", +] + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +dependencies = [ + "cfg-if 1.0.0", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_macros", + "phf_shared", + "proc-macro-hack", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" + +[[package]] +name = "pkg-config" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" + +[[package]] +name = "plist" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd39bc6cdc9355ad1dc5eeedefee696bb35c34caf21768741e81826c0bbd7225" +dependencies = [ + "base64", + "indexmap", + "line-wrap", + "serde", + "time 0.3.5", + "xml-rs", +] + +[[package]] +name = "png" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" +dependencies = [ + "bitflags", + "crc32fast", + "deflate", + "miniz_oxide 0.3.7", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" + +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", +] + +[[package]] +name = "proc-macro-crate" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebace6889caf889b4d3f76becee12e90353f2b8c7d875534a71e5742f8f6f83" +dependencies = [ + "thiserror", + "toml", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro2" +version = "1.0.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f84e92c0f7c9d58328b85a78557813e4bd845130db68d7184635344399423b1" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "ps-core" +version = "0.2.0" +dependencies = [ + "chrono", + "crossbeam-channel", + "eyre", + "flate2", + "lru", + "once_cell", + "regex", + "rsql_builder", + "serde", + "serde_json", + "shellexpand", + "strum", + "strum_macros", + "thiserror", + "tracing", + "tracing-subscriber", + "treemap", +] + +[[package]] +name = "ps-database" +version = "0.2.0" +dependencies = [ + "chrono", + "eyre", + "ps-core", + "rsql_builder", + "rusqlite", + "serde", + "serde_json", + "thiserror", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "ps-gui" +version = "0.2.0" +dependencies = [ + "chrono", + "cocoa", + "crossbeam-channel", + "eframe", + "eyre", + "image", + "num-format", + "objc", + "once_cell", + "ps-core", + "ps-database", + "ps-importer", + "rand", + "shellexpand", + "thiserror", + "tinyfiledialogs", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "ps-importer" +version = "0.2.0" +dependencies = [ + "chrono", + "email-parser", + "emlx", + "eyre", + "flate2", + "mbox-reader", + "once_cell", + "ps-core", + "rand", + "rayon", + "regex", + "serde", + "serde_json", + "shellexpand", + "thiserror", + "tracing", + "tracing-subscriber", + "walkdir", +] + +[[package]] +name = "quick-xml" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8533f14c8382aaad0d592c812ac3b826162128b65662331e1127b45c3d18536b" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" +dependencies = [ + "rand_core", +] + +[[package]] +name = "raw-window-handle" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28f55143d0548dad60bb4fbdc835a3d7ac6acc3324506450c5fdd6e42903a76" +dependencies = [ + "libc", + "raw-window-handle 0.4.2", +] + +[[package]] +name = "raw-window-handle" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba75eee94a9d5273a68c9e1e105d9cffe1ef700532325788389e5a83e2522b7" +dependencies = [ + "cty", +] + +[[package]] +name = "rayon" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "redox_syscall" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" +dependencies = [ + "getrandom", + "redox_syscall", +] + +[[package]] +name = "regex" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "rsql_builder" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dbd5712883cef396d13516bb52b300fd97a29d52ca20361f0a4905bd38a2355" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "rusqlite" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ba4d3462c8b2e4d7f4fcfcf2b296dc6b65404fbbc7b63daa37fd485c149daf7" +dependencies = [ + "bitflags", + "chrono", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "memchr", + "serde_json", + "smallvec", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + +[[package]] +name = "rusttype" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc7c727aded0be18c5b80c1640eae0ac8e396abf6fa8477d96cb37d18ee5ec59" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser 0.6.0", +] + +[[package]] +name = "rustversion" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f" + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + +[[package]] +name = "safemem" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scoped-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "serde" +version = "1.0.131" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ad69dfbd3e45369132cc64e6748c2d65cdfb001a2b1c232d128b4ad60561c1" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.131" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b710a83c4e0dff6a3d511946b95274ad9ca9e5d3ae497b63fda866ac955358d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcbd0344bc6533bc7ec56df11d42fb70f1b912351c0825ccb7211b59d8af7cf5" +dependencies = [ + "itoa 1.0.1", + "ryu", + "serde", +] + +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shared_library" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11" +dependencies = [ + "lazy_static", + "libc", +] + +[[package]] +name = "shellexpand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83bdb7831b2d85ddf4a7b148aa19d0587eddbe8671a436b7bd1182eaad0f2829" +dependencies = [ + "dirs-next", +] + +[[package]] +name = "siphasher" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "533494a8f9b724d33625ab53c6c4800f7cc445895924a8ef649222dcb76e938b" + +[[package]] +name = "smallvec" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" + +[[package]] +name = "smithay-client-toolkit" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4750c76fd5d3ac95fa3ed80fe667d6a3d8590a960e5b575b98eea93339a80b80" +dependencies = [ + "andrew", + "bitflags", + "calloop", + "dlib 0.4.2", + "lazy_static", + "log", + "memmap2 0.1.0", + "nix 0.18.0", + "wayland-client 0.28.6", + "wayland-cursor 0.28.6", + "wayland-protocols 0.28.6", +] + +[[package]] +name = "smithay-client-toolkit" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "210cf40de565aaaa085face1d860b17f6aee9f76f9d2816307ea2cc45eeb64f3" +dependencies = [ + "bitflags", + "dlib 0.5.0", + "lazy_static", + "log", + "memmap2 0.3.1", + "nix 0.22.0", + "pkg-config", + "wayland-client 0.29.1", + "wayland-cursor 0.29.1", + "wayland-protocols 0.29.1", +] + +[[package]] +name = "smithay-clipboard" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "610b551bd25378bfd2b8e7a0fcbd83d427e8f2f6a40c47ae0f70688e9949dd55" +dependencies = [ + "smithay-client-toolkit 0.15.2", + "wayland-client 0.29.1", +] + +[[package]] +name = "strsim" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" + +[[package]] +name = "strum" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cae14b91c7d11c9a851d3fbc80a963198998c2a64eec840477fa92d8ce9b70bb" + +[[package]] +name = "strum_macros" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bb0dc7ee9c15cea6199cde9a127fa16a4c5819af85395457ad72d68edc85a38" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "takeable-option" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36ae8932fcfea38b7d3883ae2ab357b0d57a02caaa18ebb4f5ece08beaec4aa0" + +[[package]] +name = "textcode" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13de2d432b3eea016f6a010139c8b5a5bf050b5a05b8993d04033ca5232e44a9" + +[[package]] +name = "thiserror" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" +dependencies = [ + "once_cell", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi", + "winapi", +] + +[[package]] +name = "time" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41effe7cfa8af36f439fac33861b66b049edc6f9a32331e2312660529c1c24ad" +dependencies = [ + "itoa 0.4.8", + "libc", +] + +[[package]] +name = "timezone-abbreviations" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ead19eae5d0834473ce509eb859282c262e5b869a0171641d1668cd54c594d8f" +dependencies = [ + "phf", +] + +[[package]] +name = "tinyfiledialogs" +version = "3.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9545b2375cbcb7a7d70cca5e92fbaa096fd89bebd2fbc54a3da7f37d15a54e6b" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] +name = "tracing" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" +dependencies = [ + "cfg-if 1.0.0", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "tracing-log" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6923477a48e41c1951f1999ef8bb5a3023eb723ceadafe78ffb65dc366761e3" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245da694cc7fc4729f3f418b304cb57789f1bed2a78c575407ab8a23f53cb4d3" +dependencies = [ + "ansi_term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "treemap" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1571f89da27a5e1aa83304ee1ab9519ea8c6432b4c8903aaaa6c9a9eecb6f36" + +[[package]] +name = "ttf-parser" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e5d7cd7ab3e47dda6e56542f4bbf3824c15234958c6e1bd6aaa347e93499fdc" + +[[package]] +name = "ttf-parser" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76dacc724328b3d5e2ed67f9e30cdb56893a34ab239032502cc8f19f8dae4bbc" + +[[package]] +name = "unicode-segmentation" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" + +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasm-bindgen" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" + +[[package]] +name = "wayland-client" +version = "0.28.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ab332350e502f159382201394a78e3cc12d0f04db863429260164ea40e0355" +dependencies = [ + "bitflags", + "downcast-rs", + "libc", + "nix 0.20.0", + "scoped-tls", + "wayland-commons 0.28.6", + "wayland-scanner 0.28.6", + "wayland-sys 0.28.6", +] + +[[package]] +name = "wayland-client" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9108ec1c37f4774d0c2937ba1a6c23d1786b2152c4a13bd9fdb20e42d16e8841" +dependencies = [ + "bitflags", + "downcast-rs", + "libc", + "nix 0.22.0", + "scoped-tls", + "wayland-commons 0.29.1", + "wayland-scanner 0.29.1", + "wayland-sys 0.29.1", +] + +[[package]] +name = "wayland-commons" +version = "0.28.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21817947c7011bbd0a27e11b17b337bfd022e8544b071a2641232047966fbda" +dependencies = [ + "nix 0.20.0", + "once_cell", + "smallvec", + "wayland-sys 0.28.6", +] + +[[package]] +name = "wayland-commons" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "265ef51b3b3e5c9ef098f10425c39624663f459c3821dcaacc4748be975f1beb" +dependencies = [ + "nix 0.22.0", + "once_cell", + "smallvec", + "wayland-sys 0.29.1", +] + +[[package]] +name = "wayland-cursor" +version = "0.28.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be610084edd1586d45e7bdd275fe345c7c1873598caa464c4fb835dee70fa65a" +dependencies = [ + "nix 0.20.0", + "wayland-client 0.28.6", + "xcursor", +] + +[[package]] +name = "wayland-cursor" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c19bb6628daf4097e58b7911481e8371e13318d5a60894779901bd3267407a7" +dependencies = [ + "nix 0.22.0", + "wayland-client 0.29.1", + "xcursor", +] + +[[package]] +name = "wayland-egl" +version = "0.28.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99ba1ab1e18756b23982d36f08856d521d7df45015f404a2d7c4f0b2d2f66956" +dependencies = [ + "wayland-client 0.28.6", + "wayland-sys 0.28.6", +] + +[[package]] +name = "wayland-protocols" +version = "0.28.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "286620ea4d803bacf61fa087a4242ee316693099ee5a140796aaba02b29f861f" +dependencies = [ + "bitflags", + "wayland-client 0.28.6", + "wayland-commons 0.28.6", + "wayland-scanner 0.28.6", +] + +[[package]] +name = "wayland-protocols" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b3b6f1dc0193072ef4eadcb144da30d58c1f2895516c063804d213310703c8e" +dependencies = [ + "bitflags", + "wayland-client 0.29.1", + "wayland-commons 0.29.1", + "wayland-scanner 0.29.1", +] + +[[package]] +name = "wayland-scanner" +version = "0.28.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce923eb2deb61de332d1f356ec7b6bf37094dc5573952e1c8936db03b54c03f1" +dependencies = [ + "proc-macro2", + "quote", + "xml-rs", +] + +[[package]] +name = "wayland-scanner" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaaf2bc85e7b9143159af96bd23d954a5abe391c4376db712320643280fdc6f4" +dependencies = [ + "proc-macro2", + "quote", + "xml-rs", +] + +[[package]] +name = "wayland-sys" +version = "0.28.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d841fca9aed7febf9bed2e9796c49bf58d4152ceda8ac949ebe00868d8f0feb8" +dependencies = [ + "dlib 0.5.0", + "lazy_static", + "pkg-config", +] + +[[package]] +name = "wayland-sys" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba9e06acb775b3007f8d3094438306979e572d1d3b844d7a71557a84b055d959" +dependencies = [ + "dlib 0.5.0", + "lazy_static", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webbrowser" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecad156490d6b620308ed411cfee90d280b3cbd13e189ea0d3fada8acc89158a" +dependencies = [ + "web-sys", + "widestring", + "winapi", +] + +[[package]] +name = "widestring" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "winit" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79610794594d5e86be473ef7763f604f2159cbac8c94debd00df8fb41e86c2f8" +dependencies = [ + "bitflags", + "cocoa", + "core-foundation 0.9.2", + "core-graphics 0.22.3", + "core-video-sys", + "dispatch", + "instant", + "lazy_static", + "libc", + "log", + "mio", + "mio-misc", + "ndk", + "ndk-glue", + "ndk-sys", + "objc", + "parking_lot", + "percent-encoding", + "raw-window-handle 0.3.4", + "scopeguard", + "smithay-client-toolkit 0.12.3", + "wayland-client 0.28.6", + "winapi", + "x11-dl", +] + +[[package]] +name = "x11-clipboard" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "473068b7b80ac86a18328824f1054e5e007898c47b5bbc281bd7abe32bc3653c" +dependencies = [ + "xcb", +] + +[[package]] +name = "x11-dl" +version = "2.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea26926b4ce81a6f5d9d0f3a0bc401e5a37c6ae14a1bfaa8ff6099ca80038c59" +dependencies = [ + "lazy_static", + "libc", + "pkg-config", +] + +[[package]] +name = "xcb" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771e2b996df720cd1c6dd9ff90f62d91698fd3610cc078388d0564bdd6622a9c" +dependencies = [ + "libc", + "log", + "quick-xml", +] + +[[package]] +name = "xcursor" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "463705a63313cd4301184381c5e8042f0a7e9b4bb63653f216311d4ae74690b7" +dependencies = [ + "nom", +] + +[[package]] +name = "xdg" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a23fe958c70412687039c86f578938b4a0bb50ec788e96bce4d6ab00ddd5803" +dependencies = [ + "dirs", +] + +[[package]] +name = "xml-rs" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" diff --git a/ps-gui/Cargo.toml b/ps-gui/Cargo.toml new file mode 100644 index 0000000..90adfa2 --- /dev/null +++ b/ps-gui/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "ps-gui" +version = "0.2.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +eyre = "0.6.5" +thiserror = "1.0.29" +tracing = "0.1.29" +tracing-subscriber = "0.3.0" +once_cell = "1.8.0" +crossbeam-channel = "0.5.1" +eframe = "0.15.0" +num-format = "0.4.0" +tinyfiledialogs = "3.0" +rand = "0.8.4" +image = { version = "0.23", default-features = false, features = ["png"] } +chrono = "0.4.19" +shellexpand = "2.1.0" +ps-core = { path = "../ps-core" } + +[target."cfg(target_os = \"macos\")".dependencies.cocoa] +version = "0.24" +[target."cfg(target_os = \"macos\")".dependencies.objc] +version = "0.2.7" + +[target."cfg(not(target_arch = \"wasm32\"))".dependencies] +ps-importer = { path = "../ps-importer" } +# Do I need this? +ps-database = { path = "../ps-database" } diff --git a/build_mac.sh b/ps-gui/build_mac.sh similarity index 100% rename from build_mac.sh rename to ps-gui/build_mac.sh diff --git a/ps-gui/build_web.sh b/ps-gui/build_web.sh new file mode 100755 index 0000000..a2c3296 --- /dev/null +++ b/ps-gui/build_web.sh @@ -0,0 +1,42 @@ +#!/bin/bash +set -eu + +# ./setup_web.sh # <- call this first! + +FOLDER_NAME=${PWD##*/} +CRATE_NAME=$FOLDER_NAME # assume crate name is the same as the folder name +CRATE_NAME_SNAKE_CASE="${CRATE_NAME//-/_}" # for those who name crates with-kebab-case + +# This is required to enable the web_sys clipboard API which egui_web uses +# https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Clipboard.html +# https://rustwasm.github.io/docs/wasm-bindgen/web-sys/unstable-apis.html +export RUSTFLAGS=--cfg=web_sys_unstable_apis + +# Clear output from old stuff: +rm -f web_demo/${CRATE_NAME_SNAKE_CASE}_bg.wasm + +echo "Building rust…" +BUILD=release +cargo build --release -p ${CRATE_NAME} --lib --target wasm32-unknown-unknown + +echo "Generating JS bindings for wasm…" +TARGET_NAME="${CRATE_NAME_SNAKE_CASE}.wasm" +wasm-bindgen "target/wasm32-unknown-unknown/${BUILD}/${TARGET_NAME}" \ + --out-dir web_demo --no-modules --no-typescript + +# to get wasm-opt: apt/brew/dnf install binaryen +# echo "Optimizing wasm…" +# wasm-opt web_demo/${CRATE_NAME_SNAKE_CASE}_bg.wasm -O2 --fast-math -o web_demo/${CRATE_NAME_SNAKE_CASE}_bg.wasm # add -g to get debug symbols + +echo "Finished: web_demo/${CRATE_NAME_SNAKE_CASE}.wasm" + +if [[ "$OSTYPE" == "linux-gnu"* ]]; then + # Linux, ex: Fedora + xdg-open http://localhost:8080/index.html +elif [[ "$OSTYPE" == "msys" ]]; then + # Windows + start http://localhost:8080/index.html +else + # Darwin/MacOS, or something else + open http://localhost:8080/index.html +fi diff --git a/ps-gui/setup_web.sh b/ps-gui/setup_web.sh new file mode 100755 index 0000000..6a60478 --- /dev/null +++ b/ps-gui/setup_web.sh @@ -0,0 +1,10 @@ +#!/bin/bash +set -eu + +# Pre-requisites: +rustup target add wasm32-unknown-unknown +cargo install -f wasm-bindgen-cli +cargo update -p wasm-bindgen + +# For local tests with `./start_server`: +cargo install basic-http-server diff --git a/src/gui/app.rs b/ps-gui/src/app.rs similarity index 100% rename from src/gui/app.rs rename to ps-gui/src/app.rs diff --git a/src/gui/app_state/error.rs b/ps-gui/src/app_state/error.rs similarity index 99% rename from src/gui/app_state/error.rs rename to ps-gui/src/app_state/error.rs index 3fdef2e..d715dbd 100644 --- a/src/gui/app_state/error.rs +++ b/ps-gui/src/app_state/error.rs @@ -5,7 +5,7 @@ use eframe::{ use super::Textures; use super::{StateUIAction, StateUIVariant}; -use crate::types::Config; +use ps_core::Config; pub struct ErrorUI { /// The error to display diff --git a/src/gui/app_state/import.rs b/ps-gui/src/app_state/import.rs similarity index 94% rename from src/gui/app_state/import.rs rename to ps-gui/src/app_state/import.rs index 1f87918..e890461 100644 --- a/src/gui/app_state/import.rs +++ b/ps-gui/src/app_state/import.rs @@ -10,12 +10,14 @@ use super::super::platform::platform_colors; use super::super::widgets::background::{shadow_background, AnimatedBackground}; use super::Textures; use super::{StateUIAction, StateUIVariant}; -use crate::types::Config; -use crate::{ - importer::{self, Adapter, State}, - types::FormatType, +use ps_core::{ + message_adapter::{Adapter, State}, + Config, DatabaseLike, FormatType, }; +#[cfg(not(target_arch = "wasm32"))] +use ps_importer; + pub struct ImporterUI { /// The config for this configuration config: Config, @@ -51,7 +53,10 @@ pub struct ImporterUI { } impl ImporterUI { - pub fn new(config: Config) -> Result { + pub fn new( + config: Config, + database: Database, + ) -> Result { let cloned_config = config.clone(); // Build a random distribution of elements // to animate the import process @@ -73,16 +78,16 @@ impl ImporterUI { // Will try again when I'm online. let handle = match config.format { FormatType::AppleMail => { - let importer = importer::applemail_importer(config); - adapter.process(importer)? + let importer = ps_importer::applemail_importer(config); + adapter.process(database, importer)? } FormatType::GmailVault => { - let importer = importer::gmail_importer(config); - adapter.process(importer)? + let importer = ps_importer::gmail_importer(config); + adapter.process(database, importer)? } FormatType::Mbox => { - let importer = importer::mbox_importer(config); - adapter.process(importer)? + let importer = ps_importer::mbox_importer(config); + adapter.process(database, importer)? } }; diff --git a/src/gui/app_state/main.rs b/ps-gui/src/app_state/main.rs similarity index 96% rename from src/gui/app_state/main.rs rename to ps-gui/src/app_state/main.rs index b133ede..1608c05 100644 --- a/src/gui/app_state/main.rs +++ b/ps-gui/src/app_state/main.rs @@ -4,9 +4,8 @@ use eyre::{Report, Result}; use super::super::widgets::{FilterState, Spinner}; use super::Textures; use super::{StateUIAction, StateUIVariant}; -use crate::types::Config; - -use crate::model::Engine; +use ps_core::{model::Engine, Config}; +use ps_database::Database; #[derive(Default)] pub struct UIState { @@ -27,7 +26,7 @@ pub struct MainUI { impl MainUI { pub fn new(config: Config, total: usize) -> Result { - let mut engine = Engine::new(&config)?; + let mut engine = Engine::new::(&config)?; engine.start()?; Ok(Self { config, diff --git a/src/gui/app_state/mod.rs b/ps-gui/src/app_state/mod.rs similarity index 84% rename from src/gui/app_state/mod.rs rename to ps-gui/src/app_state/mod.rs index c66b73c..8efc84e 100644 --- a/src/gui/app_state/mod.rs +++ b/ps-gui/src/app_state/mod.rs @@ -13,7 +13,9 @@ pub use import::ImporterUI; pub use main::{MainUI, UIState}; pub use startup::StartupUI; -use crate::types::{Config, FormatType}; +use ps_core::{Config, DatabaseLike, FormatType}; +// FIXME: Abstract away with a trait? +use ps_database::Database; pub enum StateUIAction { CreateDatabase { @@ -114,20 +116,24 @@ impl StateUI { } }; - self.importer_with_config(config) + let database = match Database::new(&config.database_path) { + Ok(config) => config, + Err(report) => return StateUI::Error(error::ErrorUI::new(report, None)), + }; + + self.importer_with_config(config, database) } pub fn open_database(&mut self, database_path: PathBuf) -> StateUI { - let config = match crate::database::Database::config(&database_path) { + let config = match Database::config(&database_path) { Ok(config) => config, Err(report) => return StateUI::Error(error::ErrorUI::new(report, None)), }; - let total = - match crate::database::Database::new(&database_path).and_then(|db| db.total_mails()) { - Ok(config) => config, - Err(report) => return StateUI::Error(error::ErrorUI::new(report, None)), - }; + let total = match Database::new(&database_path).and_then(|db| db.total_mails()) { + Ok(config) => config, + Err(report) => return StateUI::Error(error::ErrorUI::new(report, None)), + }; match main::MainUI::new(config.clone(), total) { Ok(n) => StateUI::Main(n), @@ -135,8 +141,8 @@ impl StateUI { } } - fn importer_with_config(&self, config: Config) -> StateUI { - let importer = match import::ImporterUI::new(config.clone()) { + fn importer_with_config(&self, config: Config, database: Database) -> StateUI { + let importer = match import::ImporterUI::new(config.clone(), database) { Ok(n) => n, Err(e) => { return StateUI::Error(error::ErrorUI::new(e, Some(config))); diff --git a/src/gui/app_state/startup.rs b/ps-gui/src/app_state/startup.rs similarity index 99% rename from src/gui/app_state/startup.rs rename to ps-gui/src/app_state/startup.rs index 3ca751f..d1d00bf 100644 --- a/src/gui/app_state/startup.rs +++ b/ps-gui/src/app_state/startup.rs @@ -9,7 +9,7 @@ use super::super::platform::platform_colors; use super::super::widgets::background::{shadow_background, AnimatedBackground}; use super::Textures; use super::{StateUIAction, StateUIVariant}; -use crate::types::{Config, FormatType}; +use ps_core::{Config, FormatType}; #[derive(Default)] pub struct StartupUI { diff --git a/src/gui/fonts/mac_mono.ttc b/ps-gui/src/fonts/mac_mono.ttc similarity index 100% rename from src/gui/fonts/mac_mono.ttc rename to ps-gui/src/fonts/mac_mono.ttc diff --git a/src/gui/fonts/mac_regular.otf b/ps-gui/src/fonts/mac_regular.otf similarity index 100% rename from src/gui/fonts/mac_regular.otf rename to ps-gui/src/fonts/mac_regular.otf diff --git a/src/gui/mod.rs b/ps-gui/src/lib.rs similarity index 92% rename from src/gui/mod.rs rename to ps-gui/src/lib.rs index b95c43b..976bc3e 100644 --- a/src/gui/mod.rs +++ b/ps-gui/src/lib.rs @@ -7,7 +7,9 @@ mod segmentation_bar; mod textures; pub(crate) mod widgets; -pub fn run_gui() { + +pub fn run_ui() { let options = eframe::NativeOptions::default(); eframe::run_native(Box::new(app::PostsackApp::new()), options); } + diff --git a/src/gui/mail_panel.rs b/ps-gui/src/mail_panel.rs similarity index 96% rename from src/gui/mail_panel.rs rename to ps-gui/src/mail_panel.rs index bd6ba71..ebd5a29 100644 --- a/src/gui/mail_panel.rs +++ b/ps-gui/src/mail_panel.rs @@ -1,9 +1,7 @@ -use crate::database::query::Field; -use crate::database::query_result::QueryRow; -use crate::model::{items, Engine}; use chrono::prelude::*; use eframe::egui::{self, Widget}; use eyre::Report; +use ps_core::{model::items, model::Engine, Field, QueryRow}; use super::widgets::Table; diff --git a/src/gui/navigation_bar.rs b/ps-gui/src/navigation_bar.rs similarity index 99% rename from src/gui/navigation_bar.rs rename to ps-gui/src/navigation_bar.rs index e850a3b..963b591 100644 --- a/src/gui/navigation_bar.rs +++ b/ps-gui/src/navigation_bar.rs @@ -1,7 +1,7 @@ -use crate::model::Engine; use eframe::egui::{self, Color32, Label, Widget}; use eyre::Report; use num_format::{Locale, ToFormattedString}; +use ps_core::model::Engine; use super::app_state::UIState; use super::platform::navigation_button; diff --git a/src/gui/platform/linux.rs b/ps-gui/src/platform/linux.rs similarity index 100% rename from src/gui/platform/linux.rs rename to ps-gui/src/platform/linux.rs diff --git a/src/gui/platform/macos.rs b/ps-gui/src/platform/macos.rs similarity index 99% rename from src/gui/platform/macos.rs rename to ps-gui/src/platform/macos.rs index c8e1f9c..1695682 100644 --- a/src/gui/platform/macos.rs +++ b/ps-gui/src/platform/macos.rs @@ -9,7 +9,9 @@ const SYSTEM_MONO_FONT: &[u8] = include_bytes!("../fonts/mac_mono.ttc"); use cocoa; use eframe::egui::{self, Color32, FontDefinitions, FontFamily, Stroke}; use eyre::{bail, Result}; + use objc::runtime::{Object, YES}; +use objc::*; use super::{PlatformColors, Theme}; diff --git a/src/gui/platform/mod.rs b/ps-gui/src/platform/mod.rs similarity index 100% rename from src/gui/platform/mod.rs rename to ps-gui/src/platform/mod.rs diff --git a/src/gui/platform/windows.rs b/ps-gui/src/platform/windows.rs similarity index 100% rename from src/gui/platform/windows.rs rename to ps-gui/src/platform/windows.rs diff --git a/src/gui/resources/add_permissions.png b/ps-gui/src/resources/add_permissions.png similarity index 100% rename from src/gui/resources/add_permissions.png rename to ps-gui/src/resources/add_permissions.png diff --git a/src/gui/segmentation_bar.rs b/ps-gui/src/segmentation_bar.rs similarity index 97% rename from src/gui/segmentation_bar.rs rename to ps-gui/src/segmentation_bar.rs index 2e5b0dc..69e5e68 100644 --- a/src/gui/segmentation_bar.rs +++ b/ps-gui/src/segmentation_bar.rs @@ -1,6 +1,6 @@ -use crate::model::{segmentations, Engine}; use eframe::egui::{self, Widget}; use eyre::Report; +use ps_core::model::{segmentations, Engine}; pub struct SegmentationBar<'a> { engine: &'a mut Engine, diff --git a/src/gui/textures.rs b/ps-gui/src/textures.rs similarity index 100% rename from src/gui/textures.rs rename to ps-gui/src/textures.rs diff --git a/src/gui/widgets/background.rs b/ps-gui/src/widgets/background.rs similarity index 99% rename from src/gui/widgets/background.rs rename to ps-gui/src/widgets/background.rs index 270e241..4b67231 100644 --- a/src/gui/widgets/background.rs +++ b/ps-gui/src/widgets/background.rs @@ -7,7 +7,7 @@ use eframe::egui::{ use std::ops::Rem; -use crate::gui::platform::{platform_colors, PlatformColors}; +use crate::platform::{platform_colors, PlatformColors}; /// This will draw Ui with a background color and margins. /// This can be used for calls that don't provide a `Frame`, diff --git a/src/gui/widgets/color_utils.rs b/ps-gui/src/widgets/color_utils.rs similarity index 100% rename from src/gui/widgets/color_utils.rs rename to ps-gui/src/widgets/color_utils.rs diff --git a/src/gui/widgets/filter_panel.rs b/ps-gui/src/widgets/filter_panel.rs similarity index 98% rename from src/gui/widgets/filter_panel.rs rename to ps-gui/src/widgets/filter_panel.rs index e89c00c..633e211 100644 --- a/src/gui/widgets/filter_panel.rs +++ b/ps-gui/src/widgets/filter_panel.rs @@ -2,9 +2,9 @@ use eframe::egui::{self, vec2, Color32, Response, Widget}; use eyre::Report; -use crate::{ - database::query::{Field, Filter, ValueField}, +use ps_core::{ model::{segmentations, Engine}, + Field, Filter, ValueField, }; /// Filter values for the UI. @@ -56,7 +56,7 @@ impl FilterState { n.clone(), ))); } - *error = crate::model::segmentations::set_filters(engine, &filters).err(); + *error = segmentations::set_filters(engine, &filters).err(); } fn clear(&mut self) { diff --git a/src/gui/widgets/mod.rs b/ps-gui/src/widgets/mod.rs similarity index 100% rename from src/gui/widgets/mod.rs rename to ps-gui/src/widgets/mod.rs diff --git a/src/gui/widgets/popover.rs b/ps-gui/src/widgets/popover.rs similarity index 100% rename from src/gui/widgets/popover.rs rename to ps-gui/src/widgets/popover.rs diff --git a/src/gui/widgets/rectangles.rs b/ps-gui/src/widgets/rectangles.rs similarity index 87% rename from src/gui/widgets/rectangles.rs rename to ps-gui/src/widgets/rectangles.rs index ceb7e5d..66ec64e 100644 --- a/src/gui/widgets/rectangles.rs +++ b/ps-gui/src/widgets/rectangles.rs @@ -1,9 +1,9 @@ use std::collections::hash_map::DefaultHasher; -use crate::model::{segmentations, Engine, Segment}; use eframe::egui::{self, epaint::Galley, Color32, Pos2, Rect, Rgba, Stroke, TextStyle, Widget}; use eyre::Report; use num_format::{Locale, ToFormattedString}; +use ps_core::model::{self, segmentations, Engine, Segment}; use super::super::platform::platform_colors; @@ -32,12 +32,12 @@ impl<'a> Widget for Rectangles<'a> { let size = ui.available_size(); let (rect, mut response) = ui.allocate_exact_size(size, egui::Sense::hover()); - let items = match segmentations::layouted_segments(self.engine, rect) { + let items = match segmentations::layouted_segments(self.engine, convert_rect(rect)) { Some(n) => n.to_owned(), None => return response, }; - let active = crate::model::segmentations::can_aggregate_more(self.engine); + let active = segmentations::can_aggregate_more(self.engine); let colors = platform_colors(); @@ -45,7 +45,7 @@ impl<'a> Widget for Rectangles<'a> { let mut hovered: Option = None; for (index, item) in items.iter().enumerate() { let item_response = ui.put( - item.layout_rect(), + convert_rect_back(item.layout_rect()), rectangle(item, active, colors.content_background, index, total), ); if item_response.clicked() && active { @@ -174,3 +174,24 @@ fn rectangle( ) -> impl egui::Widget + '_ { move |ui: &mut egui::Ui| rectangle_ui(ui, segment, active, stroke_color, position, total) } + +// Can't implement into / from as the trait is in another +// crate. Instead of a newtype, to simple fns +fn convert_rect(rect: Rect) -> model::Rect { + model::Rect { + left: rect.left() as f64, + top: rect.top() as f64, + width: rect.width() as f64, + height: rect.height() as f64, + } +} + +fn convert_rect_back(rect: model::Rect) -> Rect { + Rect::from_min_size( + Pos2::new(rect.left as f32, rect.top as f32), + egui::Vec2 { + x: rect.width as f32, + y: rect.height as f32, + }, + ) +} diff --git a/src/gui/widgets/spinner.rs b/ps-gui/src/widgets/spinner.rs similarity index 100% rename from src/gui/widgets/spinner.rs rename to ps-gui/src/widgets/spinner.rs diff --git a/src/gui/widgets/table.rs b/ps-gui/src/widgets/table.rs similarity index 100% rename from src/gui/widgets/table.rs rename to ps-gui/src/widgets/table.rs diff --git a/ps-importer/.gitignore b/ps-importer/.gitignore new file mode 100644 index 0000000..00f02bd --- /dev/null +++ b/ps-importer/.gitignore @@ -0,0 +1,3 @@ +/target +target +.DS_Store diff --git a/ps-importer/Cargo.lock b/ps-importer/Cargo.lock index 61c52e0..9215c33 100644 --- a/ps-importer/Cargo.lock +++ b/ps-importer/Cargo.lock @@ -37,15 +37,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "arrayvec" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" -dependencies = [ - "nodrop", -] - [[package]] name = "autocfg" version = "1.0.1" @@ -353,22 +344,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "nodrop" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" - -[[package]] -name = "num-format" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bafe4179722c2894288ee77a9f044f02811c86af699344c498b0840c698a2465" -dependencies = [ - "arrayvec", - "itoa 0.4.8", -] - [[package]] name = "num-integer" version = "0.1.44" @@ -498,10 +473,7 @@ dependencies = [ "eyre", "flate2", "lru", - "num-format", "once_cell", - "rand", - "rayon", "regex", "rsql_builder", "serde", diff --git a/ps-importer/src/format_type.rs b/ps-importer/src/format_type.rs deleted file mode 100644 index e69de29..0000000 diff --git a/ps-importer/src/importer.rs b/ps-importer/src/importer.rs deleted file mode 100644 index a01a689..0000000 --- a/ps-importer/src/importer.rs +++ /dev/null @@ -1,61 +0,0 @@ -use std::thread::JoinHandle; - -use super::formats::shared; -use ps_core::{ - crossbeam_channel::{self, unbounded}, - Config, DatabaseLike, Importerlike, Message, MessageReceiver, -}; - -use super::formats::ImporterFormat; - -use eyre::Result; - -pub struct Importer { - config: Config, - format: Format, -} - -impl Importer { - pub fn new(config: Config, format: Format) -> Self { - Self { config, format } - } -} - -impl Importerlike for Importer { - fn import( - self, - database: Database, - ) -> Result<(MessageReceiver, JoinHandle>)> { - let Importer { format, .. } = self; - let (sender, receiver) = unbounded(); - - let config = self.config; - let handle: JoinHandle> = std::thread::spawn(move || { - let outer_sender = sender.clone(); - let processed = move || { - let emails = format.emails(&config, sender.clone())?; - let processed = - shared::database::into_database(&config, emails, sender.clone(), database)?; - - Ok(processed) - }; - let result = processed(); - - // Send the error away and map it to a crossbeam channel error - match result { - Ok(_) => Ok(()), - Err(e) => match outer_sender.send(Message::Error(e)) { - Ok(_) => Ok(()), - Err(e) => Err(eyre::Report::new(e)), - }, - } - }); - Ok((receiver, handle)) - } -} - -// impl Importerlike for Box { -// fn import(self) -> Result<(MessageReceiver, JoinHandle>)> { -// (*self).import() -// } -// } diff --git a/ps-importer/src/lib.rs b/ps-importer/src/lib.rs index df267ab..723af96 100644 --- a/ps-importer/src/lib.rs +++ b/ps-importer/src/lib.rs @@ -1,12 +1,67 @@ +use eyre::Result; + pub(crate) mod formats; -#[allow(clippy::module_inception)] -mod importer; -mod message_adapter; -pub use message_adapter::*; -use ps_core::{Config, EmailEntry, EmailMeta, FormatType, Importerlike}; +use formats::{shared, ImporterFormat}; + +use std::thread::JoinHandle; + +use ps_core::{ + crossbeam_channel::{self, unbounded}, + Config, DatabaseLike, EmailEntry, EmailMeta, FormatType, Importerlike, Message, + MessageReceiver, +}; + +pub struct Importer { + config: Config, + format: Format, +} + +impl Importer { + pub fn new(config: Config, format: Format) -> Self { + Self { config, format } + } +} + +impl Importerlike for Importer { + fn import( + self, + database: Database, + ) -> Result<(MessageReceiver, JoinHandle>)> { + let Importer { format, .. } = self; + let (sender, receiver) = unbounded(); + + let config = self.config; + let handle: JoinHandle> = std::thread::spawn(move || { + let outer_sender = sender.clone(); + let processed = move || { + let emails = format.emails(&config, sender.clone())?; + let processed = + shared::database::into_database(&config, emails, sender.clone(), database)?; -use formats::ImporterFormat; + Ok(processed) + }; + let result = processed(); + + // Send the error away and map it to a crossbeam channel error + match result { + Ok(_) => Ok(()), + Err(e) => match outer_sender.send(Message::Error(e)) { + Ok(_) => Ok(()), + Err(e) => Err(eyre::Report::new(e)), + }, + } + }); + Ok((receiver, handle)) + } +} + +// FIXME: +// impl Importerlike for Box { +// fn import(self) -> Result<(MessageReceiver, JoinHandle>)> { +// (*self).import() +// } +// } // pub fn importer(config: &Config) -> Box { // match config.format { @@ -16,14 +71,14 @@ use formats::ImporterFormat; // } // } -pub fn gmail_importer(config: Config) -> importer::Importer { - importer::Importer::new(config, formats::Gmail::default()) +pub fn gmail_importer(config: Config) -> Importer { + Importer::new(config, formats::Gmail::default()) } -pub fn applemail_importer(config: Config) -> importer::Importer { - importer::Importer::new(config, formats::AppleMail::default()) +pub fn applemail_importer(config: Config) -> Importer { + Importer::new(config, formats::AppleMail::default()) } -pub fn mbox_importer(config: Config) -> importer::Importer { - importer::Importer::new(config, formats::Mbox::default()) +pub fn mbox_importer(config: Config) -> Importer { + Importer::new(config, formats::Mbox::default()) } diff --git a/src/.DS_Store b/src/.DS_Store deleted file mode 100644 index d29525a..0000000 Binary files a/src/.DS_Store and /dev/null differ diff --git a/src/bin/cli.rs b/src/bin/cli.rs deleted file mode 100644 index 3814093..0000000 --- a/src/bin/cli.rs +++ /dev/null @@ -1,87 +0,0 @@ -use eyre::Result; - -use std::{ - io::{stdout, Write}, - thread::sleep, - time::Duration, -}; - -use postsack::{ - self, - importer::{Adapter, State}, - types::FormatType, -}; - -fn main() -> Result<()> { - postsack::setup_tracing(); - - let config = postsack::make_config(); - - let adapter = postsack::importer::Adapter::new(); - - // Could not figure out how to build this properly - // with dynamic dispatch. (to abstract away the match) - // Will try again when I'm online. - let handle = match config.format { - FormatType::AppleMail => { - let importer = postsack::importer::applemail_importer(config); - adapter.process(importer)? - } - FormatType::GmailVault => { - let importer = postsack::importer::gmail_importer(config); - adapter.process(importer)? - } - FormatType::Mbox => { - let importer = postsack::importer::mbox_importer(config); - adapter.process(importer)? - } - }; - - let mut stdout = stdout(); - - loop { - match handle_adapter(&adapter) { - Ok(true) => break, - Ok(false) => (), - Err(e) => { - println!("Execution Error:\n{:?}", &e); - panic!(); - } - } - stdout.flush().unwrap(); - } - - match handle.join() { - Err(e) => println!("Error: {:?}", e), - Ok(Err(e)) => println!("Error: {:?}", e), - _ => (), - } - println!("\rDone"); - - Ok(()) -} - -fn handle_adapter(adapter: &Adapter) -> Result { - let State { - done, finishing, .. - } = adapter.finished()?; - if done { - return Ok(true); - } - if finishing { - print!("\rFinishing up..."); - } else { - let write = adapter.write_count()?; - if write.count > 0 { - print!("\rWriting emails to DB {}/{}...", write.count, write.total); - } else { - let read = adapter.read_count()?; - print!( - "\rReading Emails {}%...", - (read.count as f32 / read.total as f32) * 100.0 - ); - } - } - sleep(Duration::from_millis(50)); - Ok(false) -} diff --git a/src/bin/postsack.rs b/src/bin/postsack.rs deleted file mode 100644 index f6b8b59..0000000 --- a/src/bin/postsack.rs +++ /dev/null @@ -1,12 +0,0 @@ -#[cfg(feature = "gui")] -fn main() { - #[cfg(debug_assertions)] - postsack::setup_tracing(); - - postsack::gui::run_gui(); -} - -#[cfg(not(feature = "gui"))] -fn main() { - println!("Gui not selected") -} diff --git a/src/database/conversion.rs b/src/database/conversion.rs deleted file mode 100644 index 863a4e7..0000000 --- a/src/database/conversion.rs +++ /dev/null @@ -1,145 +0,0 @@ -use std::collections::HashMap; -use std::convert::TryInto; -use std::str::FromStr; - -use chrono::prelude::*; -use eyre::{bail, eyre, Result}; -use rusqlite::{self, types, Row}; -use serde_json::Value; - -use super::query::{Field, ValueField, AMOUNT_FIELD_NAME}; -use super::query_result::QueryResult; -use crate::importer::{EmailEntry, EmailMeta}; - -/// rusqlite does offer Serde to Value conversion, but it -/// converts everything to strings! -pub fn json_to_value(input: &Value) -> Result { - let ok = match input { - Value::Number(n) if n.is_i64() => { - types::Value::Integer(n.as_i64().ok_or_else(|| eyre!("Invalid Number {:?}", n))?) - } - Value::Number(n) if n.is_u64() => { - let value = n.as_u64().ok_or_else(|| eyre!("Invalid Number {:?}", n))?; - let converted: i64 = value.try_into()?; - types::Value::Integer(converted) - } - Value::Number(n) if n.is_f64() => { - types::Value::Real(n.as_f64().ok_or_else(|| eyre!("Invalid Number {:?}", n))?) - } - Value::Bool(n) => types::Value::Integer(*n as i64), - Value::String(n) => types::Value::Text(n.clone()), - _ => bail!("Invalid type: {}", &input), - }; - Ok(ok) -} - -pub trait RowConversion<'a>: Sized { - fn grouped_from_row<'stmt>(field: &'a Field, row: &Row<'stmt>) -> Result; - fn from_row<'stmt>(fields: &'a [Field], row: &Row<'stmt>) -> Result; -} - -impl<'a> RowConversion<'a> for QueryResult { - fn grouped_from_row<'stmt>(field: &'a Field, row: &Row<'stmt>) -> Result { - let amount: usize = row.get(AMOUNT_FIELD_NAME)?; - let values = values_from_fields(&[*field], row)?; - - Ok(QueryResult::Grouped { - count: amount, - value: values[field].clone(), - }) - } - fn from_row<'stmt>(fields: &'a [Field], row: &Row<'stmt>) -> Result { - let values = values_from_fields(fields, row)?; - Ok(QueryResult::Normal(values)) - } -} - -fn values_from_fields<'stmt>( - fields: &[Field], - row: &Row<'stmt>, -) -> Result> { - let mut values: HashMap = HashMap::default(); - for field in fields { - values.insert(*field, value_from_field(field, row)?); - } - Ok(values) -} - -pub fn value_from_field<'stmt>(field: &Field, row: &Row<'stmt>) -> Result { - use Field::*; - // Use type safety when unpacking - match field { - Path | SenderDomain | SenderLocalPart | SenderName | ToGroup | ToName | ToAddress - | Subject => { - let string: String = row.get::<&str, String>(field.as_str())?; - Ok(ValueField::string(field, &string)) - } - Year | Month | Day | Timestamp => { - return Ok(ValueField::usize( - field, - row.get::<&str, usize>(field.as_str())?, - )); - } - MetaTags => { - let tag_string = row.get::<&str, String>(field.as_str())?; - let tags = - crate::importer::formats::shared::email::EmailMeta::tags_from_string(&tag_string); - Ok(ValueField::array( - field, - tags.into_iter().map(Value::String).collect(), - )) - } - IsReply | IsSend | MetaIsSeen => { - return Ok(ValueField::bool( - field, - row.get::<&str, bool>(field.as_str())?, - )); - } - } -} - -impl EmailEntry { - #[allow(unused)] - fn from_row(row: &Row<'_>) -> Result { - let path: String = row.get("path")?; - let path = std::path::PathBuf::from_str(&path)?; - let sender_domain: String = row.get("sender_domain")?; - let sender_local_part: String = row.get("sender_local_part")?; - let sender_name: String = row.get("sender_name")?; - let timestamp: i64 = row.get("timestamp")?; - let datetime = Utc.timestamp(timestamp, 0); - let subject: String = row.get("subject")?; - let to_count: usize = row.get("to_count")?; - let to_group: Option = row.get("to_group")?; - let to_name: Option = row.get("to_name")?; - let to_address: Option = row.get("to_address")?; - - let to_first = to_address.map(|a| (to_name.unwrap_or_default(), a)); - - let is_reply: bool = row.get("is_reply")?; - let is_send: bool = row.get("is_send")?; - - // Parse EmailMeta - let meta_tags: Option = row.get("meta_tags")?; - let meta_is_seen: Option = row.get("meta_is_seen")?; - let meta = match (meta_tags, meta_is_seen) { - (Some(a), Some(b)) => Some(EmailMeta::from(b, &a)), - _ => None, - }; - - Ok(EmailEntry { - path, - sender_domain, - sender_local_part, - sender_name, - datetime, - subject, - to_count, - to_group, - to_first, - is_reply, - is_send, - meta, - }) - } -} diff --git a/src/database/db.rs b/src/database/db.rs deleted file mode 100644 index 4a74920..0000000 --- a/src/database/db.rs +++ /dev/null @@ -1,255 +0,0 @@ -use chrono::Datelike; -use crossbeam_channel::{unbounded, Sender}; -use eyre::{bail, Report, Result}; -use rusqlite::{self, params, Connection, Statement}; - -use core::panic; -use std::{collections::HashMap, path::Path, thread::JoinHandle}; - -use super::{query::Query, query_result::QueryResult, sql::*, DBMessage}; -use crate::database::query::OtherQuery; -use crate::types::Config; -use crate::{ - database::{value_from_field, RowConversion}, - importer::EmailEntry, -}; - -#[derive(Debug)] -pub struct Database { - connection: Option, -} - -impl Database { - /// Open a database and try to retrieve a config from the information stored in there - pub fn config>(path: P) -> Result { - let database = Self::new(path.as_ref())?; - let fields = database.select_config_fields()?; - Config::from_fields(path.as_ref(), fields) - } - - /// Open database at path `Path`. - pub fn new>(path: P) -> Result { - #[allow(unused_mut)] - let mut connection = Connection::open(path.as_ref())?; - - // Improve the insertion performance. - connection.pragma_update(None, "journal_mode", &"memory")?; - connection.pragma_update(None, "synchronous", &"OFF")?; - - Self::create_tables(&connection)?; - - #[cfg(feature = "trace-sql")] - connection.trace(Some(|query| { - tracing::trace!("SQL: {}", &query); - })); - - Ok(Database { - connection: Some(connection), - }) - } - - pub fn total_mails(&self) -> Result { - let connection = match &self.connection { - Some(n) => n, - None => bail!("No connection to database available in query"), - }; - let mut stmt = connection.prepare(QUERY_COUNT_MAILS)?; - let count: usize = stmt.query_row([], |q| q.get(0))?; - Ok(count) - } - - pub fn save_config(&self, config: Config) -> Result<()> { - let fields = config - .into_fields() - .ok_or_else(|| eyre::eyre!("Could not create fields from config"))?; - self.insert_config_fields(fields) - } - - pub fn query(&self, query: &super::query::Query) -> Result> { - use rusqlite::params_from_iter; - let c = match &self.connection { - Some(n) => n, - None => bail!("No connection to database available in query"), - }; - let (sql, values) = query.to_sql(); - let mut stmt = c.prepare(&sql)?; - let mut query_results = Vec::new(); - let mut converted = Vec::new(); - for value in values { - converted.push(super::conversion::json_to_value(&value)?); - } - - let p = params_from_iter(converted.iter()); - - let mut rows = stmt.query(p)?; - while let Some(row) = rows.next()? { - match query { - Query::Grouped { group_by, .. } => { - let result = QueryResult::grouped_from_row(group_by, row)?; - query_results.push(result); - } - Query::Normal { fields, .. } => { - let result = QueryResult::from_row(fields, row)?; - query_results.push(result); - } - Query::Other { - query: OtherQuery::All(field), - } => query_results.push(QueryResult::Other(value_from_field(field, row)?)), - } - } - Ok(query_results) - } - - /// Begin the data import. - /// This will consume the `Database`. A new one has to be opened - /// afterwards in order to support multi-threading. - /// Returns an input `Sender` and a `JoinHandle`. - /// The `Sender` is used to submit work to the database via `DBMessage` - /// cases. The `JoinHandle` is used to wait for database completion. - /// - /// # Examples - /// - /// ``` ignore - /// let db = Database::new("db.sqlite").unwrap(); - /// let (sender, handle) = db.import(); - /// sender.send(DBMessage::Mail(m1)).unwrap(); - /// sender.send(DBMessage::Mail(m2)).unwrap(); - /// handle.join().unwrap(); - /// ``` - pub fn import(mut self) -> (Sender, JoinHandle>) { - let (sender, receiver) = unbounded(); - - // Import can only be called *once* on a database created with `new`. - // Therefore there should always be a value to unwrap; - let mut connection = self.connection.take().unwrap(); - let handle = std::thread::spawn(move || { - let mut counter = 0; - { - let transaction = connection.transaction()?; - { - let mut mail_prepared = transaction.prepare(QUERY_EMAILS)?; - let mut error_prepared = transaction.prepare(QUERY_ERRORS)?; - loop { - let next = match receiver.recv() { - Ok(n) => n, - Err(e) => { - println!("Receiver error: {:?}", &e); - panic!("should not happen"); - } - }; - match next { - DBMessage::Mail(mail) => { - counter += 1; - insert_mail(&mut mail_prepared, &mail) - } - DBMessage::Error(report) => insert_error(&mut error_prepared, &report), - DBMessage::Done => { - tracing::trace!("Received DBMessage::Done"); - break; - } - }?; - } - } - if let Err(e) = transaction.commit() { - return Err(eyre::eyre!("Transaction Error: {:?}", &e)); - } - } - // In case closing the database fails, we try again until we succeed - let mut c = connection; - loop { - tracing::trace!("Attempting close"); - match c.close() { - Ok(_n) => break, - Err((a, _b)) => c = a, - } - } - tracing::trace!("Finished SQLITE: {}", &counter); - Ok(counter) - }); - (sender, handle) - } - - fn create_tables(connection: &Connection) -> Result<()> { - connection.execute(TBL_EMAILS, params![])?; - connection.execute(TBL_ERRORS, params![])?; - connection.execute(TBL_META, params![])?; - Ok(()) - } - - fn select_config_fields(&self) -> Result> { - let connection = match &self.connection { - Some(n) => n, - None => bail!("No connection to database available in query"), - }; - let mut stmt = connection.prepare(QUERY_SELECT_META)?; - let mut query_results = HashMap::new(); - let mut rows = stmt.query([])?; - while let Some(row) = rows.next()? { - let (k, v) = match ( - row.get::<_, String>("key"), - row.get::<_, serde_json::Value>("value"), - ) { - (Ok(k), Ok(v)) => (k, v), - (a, b) => { - tracing::error!("Invalid row data. Missing fields key and or value:\nkey: {:?}\nvalue: {:?}\n", a, b); - continue; - } - }; - query_results.insert(k, v); - } - Ok(query_results) - } - - fn insert_config_fields(&self, fields: HashMap) -> Result<()> { - let connection = match &self.connection { - Some(n) => n, - None => bail!("No connection to database available in query"), - }; - let mut stmt = connection.prepare(QUERY_INSERT_META)?; - for (key, value) in fields { - stmt.execute(params![key, value])?; - } - Ok(()) - } -} - -fn insert_mail(statement: &mut Statement, entry: &EmailEntry) -> Result<()> { - let path = entry.path.display().to_string(); - let year = entry.datetime.date().year(); - let month = entry.datetime.date().month(); - let day = entry.datetime.date().day(); - let timestamp = entry.datetime.timestamp(); - let e = entry; - let to_name = e.to_first.as_ref().map(|e| &e.0); - let to_address = e.to_first.as_ref().map(|e| &e.1); - let meta_tags = e.meta.as_ref().map(|e| e.tags_string()); - let meta_is_seen = e.meta.as_ref().map(|e| e.is_seen); - let p = params![ - path, - e.sender_domain, - e.sender_local_part, - e.sender_name, - year, - month, - day, - timestamp, - e.subject, - e.to_count, - e.to_group, - to_name, - to_address, - e.is_reply, - e.is_send, - meta_tags, - meta_is_seen - ]; - statement.execute(p)?; - tracing::trace!("Insert Mail {}", &path); - Ok(()) -} - -fn insert_error(statement: &mut Statement, message: &Report) -> Result<()> { - statement.execute(params![message.to_string()])?; - tracing::trace!("Insert Error {}", message); - Ok(()) -} diff --git a/src/database/db_message.rs b/src/database/db_message.rs deleted file mode 100644 index 1baa764..0000000 --- a/src/database/db_message.rs +++ /dev/null @@ -1,14 +0,0 @@ -use eyre::Report; - -use crate::importer::EmailEntry; - -/// Parameter for sending work to the database during `import`. -pub enum DBMessage { - /// Send for a successfuly parsed mail - Mail(Box), - /// Send for any kind of error during reading / parsing - Error(Report), - /// Send once all parsing is done. - /// This is used to break out of the receiving loop - Done, -} diff --git a/src/database/mod.rs b/src/database/mod.rs deleted file mode 100644 index 1afd0b7..0000000 --- a/src/database/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -mod conversion; -mod db; -mod db_message; -pub mod query; -pub mod query_result; -mod sql; - -pub use conversion::{value_from_field, RowConversion}; -pub use db::Database; -pub use db_message::DBMessage; diff --git a/src/database/query.rs b/src/database/query.rs deleted file mode 100644 index 9cdd472..0000000 --- a/src/database/query.rs +++ /dev/null @@ -1,242 +0,0 @@ -use rsql_builder; -use serde_json; -pub use serde_json::Value; -use strum::{self, IntoEnumIterator}; -use strum_macros::{EnumIter, IntoStaticStr}; - -use std::ops::Range; - -pub const AMOUNT_FIELD_NAME: &str = "amount"; - -#[derive(Clone, Debug)] -pub enum Filter { - /// A database Like Operation - Like(ValueField), - NotLike(ValueField), - /// A extended like that implies: - /// - wildcards on both sides (like '%test%') - /// - case in-sensitive comparison - /// - Trying to handle values as strings - Contains(ValueField), - Is(ValueField), -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, IntoStaticStr, EnumIter)] -#[strum(serialize_all = "snake_case")] -pub enum Field { - Path, - SenderDomain, - SenderLocalPart, - SenderName, - Year, - Month, - Day, - Timestamp, - ToGroup, - ToName, - ToAddress, - IsReply, - IsSend, - Subject, - MetaIsSeen, - MetaTags, -} - -const INVALID_FIELDS: &[Field] = &[ - Field::Path, - Field::Subject, - Field::Timestamp, - Field::IsReply, - Field::IsSend, - Field::MetaIsSeen, - Field::MetaTags, -]; - -impl Field { - pub fn all_cases() -> impl Iterator { - Field::iter().filter(|f| !INVALID_FIELDS.contains(f)) - } - - /// Just a wrapper to offer `into` without the type ambiguity - /// that sometimes arises - pub fn as_str(&self) -> &'static str { - self.into() - } - - /// A human readable name - pub fn name(&self) -> &str { - use Field::*; - match self { - SenderDomain => "Domain", - SenderLocalPart => "Address", - SenderName => "Name", - ToGroup => "Group", - ToName => "To name", - ToAddress => "To address", - Year => "Year", - Month => "Month", - Day => "Day", - Subject => "Subject", - _ => self.as_str(), - } - } -} - -impl std::fmt::Display for Field { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{}", self.name()) - } -} - -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct ValueField { - field: Field, - value: Value, -} - -impl ValueField { - pub fn string>(field: &Field, value: S) -> ValueField { - ValueField { - field: *field, - value: Value::String(value.as_ref().to_string()), - } - } - - pub fn bool(field: &Field, value: bool) -> ValueField { - ValueField { - field: *field, - value: Value::Bool(value), - } - } - - pub fn usize(field: &Field, value: usize) -> ValueField { - ValueField { - field: *field, - value: Value::Number(value.into()), - } - } - - pub fn array(field: &Field, value: Vec) -> ValueField { - ValueField { - field: *field, - value: Value::Array(value), - } - } - - pub fn field(&self) -> &Field { - &self.field - } - - pub fn value(&self) -> &Value { - &self.value - } - - #[allow(clippy::inherent_to_string)] - pub fn to_string(&self) -> String { - match &self.value { - Value::String(s) => s.clone(), - _ => format!("{}", &self.value), - } - } -} - -#[derive(Debug, Clone)] -pub enum OtherQuery { - /// Get all contents of a specific field - All(Field), -} - -#[derive(Clone, Debug)] -pub enum Query { - Grouped { - filters: Vec, - group_by: Field, - }, - Normal { - fields: Vec, - filters: Vec, - range: Range, - }, - Other { - query: OtherQuery, - }, -} - -impl Query { - fn filters(&self) -> &[Filter] { - match self { - Query::Grouped { ref filters, .. } => filters, - Query::Normal { ref filters, .. } => filters, - Query::Other { .. } => &[], - } - } -} - -impl Query { - pub fn to_sql(&self) -> (String, Vec) { - let mut conditions = { - let mut whr = rsql_builder::B::new_where(); - for filter in self.filters() { - match filter { - Filter::Like(f) => whr.like(f.field.into(), f.value()), - Filter::NotLike(f) => whr.not_like(f.field.into(), f.value()), - Filter::Contains(f) => whr.like( - f.field.into(), - &format!("%{}%", f.to_string().to_lowercase()), - ), - Filter::Is(f) => whr.eq(f.field.into(), f.value()), - }; - } - whr - }; - - let (header, group_by) = match self { - Query::Grouped { group_by, .. } => ( - format!( - "SELECT count(path) as {}, {} FROM emails", - AMOUNT_FIELD_NAME, - group_by.as_str() - ), - format!("GROUP BY {}", group_by.as_str()), - ), - Query::Normal { fields, range, .. } => { - let fields: Vec<&str> = fields.iter().map(|e| e.into()).collect(); - ( - format!("SELECT {} FROM emails", fields.join(", ")), - format!("LIMIT {}, {}", range.start, range.end - range.start), - ) - } - Query::Other { - query: OtherQuery::All(field), - } => ( - format!("SELECT {} FROM emails", field.as_str()), - format!(""), - ), - }; - - let (sql, values) = rsql_builder::B::prepare( - rsql_builder::B::new_sql(&header) - .push_build(&mut conditions) - .push_sql(&group_by), - ); - - (sql, values) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_test() { - let query = Query::Grouped { - filters: vec![ - Filter::Like(ValueField::string(&Field::SenderDomain, "gmail.com")), - Filter::Is(ValueField::usize(&Field::Year, 2021)), - ], - group_by: Field::Month, - }; - dbg!(&query.to_sql()); - } -} diff --git a/src/database/query_result.rs b/src/database/query_result.rs deleted file mode 100644 index b18b7f8..0000000 --- a/src/database/query_result.rs +++ /dev/null @@ -1,17 +0,0 @@ -use super::query::{Field, ValueField}; -use std::collections::HashMap; - -pub type QueryRow = HashMap; - -#[derive(Debug)] -pub enum QueryResult { - Grouped { - /// How many items did we find? - count: usize, - /// All the itmes that we grouped by including their values. - /// So that we can use each of them to limit the next query. - value: ValueField, - }, - Normal(QueryRow), - Other(ValueField), -} diff --git a/src/database/sql.rs b/src/database/sql.rs deleted file mode 100644 index 9d4875e..0000000 --- a/src/database/sql.rs +++ /dev/null @@ -1,71 +0,0 @@ -pub const TBL_EMAILS: &str = r#" -CREATE TABLE IF NOT EXISTS emails ( - path TEXT NOT NULL, - sender_domain TEXT NOT NULL, - sender_local_part TEXT NOT NULL, - sender_name TEXT NOT NULL, - year INTEGER NOT NULL, - month INTEGER NOT NULL, - day INTEGER NOT NULL, - timestamp INTEGER NOT NULL, - subject TEXT NOT NULL, - to_count INTEGER NOT NULL, - to_group TEXT NULL, - to_name TEXT NULL, - to_address TEXT NULL, - is_reply BOOL, - is_send BOOL, - meta_tags TEXT NULL, - meta_is_seen BOOL NULL -);"#; - -pub const QUERY_EMAILS: &str = r#" -INSERT INTO emails - ( - path, sender_domain, sender_local_part, sender_name, - year, month, day, timestamp, subject, - to_count, to_group, to_name, to_address, - is_reply, is_send, - meta_tags, meta_is_seen - ) -VALUES - ( - ?, ?, ?, ?, - ?, ?, ?, ?, ?, - ?, ?, ?, ?, - ?, ?, - ?, ? - ) -"#; - -pub const TBL_ERRORS: &str = r#" -CREATE TABLE IF NOT EXISTS errors ( - message TEXT NOT NULL -);"#; - -pub const QUERY_ERRORS: &str = r#" -INSERT INTO errors - (message) -VALUES - (?) -"#; - -pub const TBL_META: &str = r#" -CREATE TABLE IF NOT EXISTS meta ( - key TEXT NOT NULL, - value TEXT NOT NULL -);"#; - -pub const QUERY_INSERT_META: &str = r#" -INSERT INTO meta - (key, value) -VALUES - (?, ?) -"#; - -pub const QUERY_SELECT_META: &str = r#" -SELECT key, value FROM meta"#; - -pub const QUERY_COUNT_MAILS: &str = r#" -SELECT count(path) FROM emails -"#; diff --git a/src/gui/.DS_Store b/src/gui/.DS_Store deleted file mode 100644 index 12eba66..0000000 Binary files a/src/gui/.DS_Store and /dev/null differ diff --git a/src/importer/formats/apple_mail/filesystem.rs b/src/importer/formats/apple_mail/filesystem.rs deleted file mode 100644 index c1a44e0..0000000 --- a/src/importer/formats/apple_mail/filesystem.rs +++ /dev/null @@ -1,88 +0,0 @@ -//! We use a stubbornly stupid algorithm where we just -//! recursively drill down into the appropriate folder -//! until we find `emlx` files and return those. - -use eyre::{eyre, Result}; -use rayon::prelude::*; -use walkdir::WalkDir; - -use super::super::shared::filesystem::emails_in; -use super::super::{Message, MessageSender}; -use crate::types::Config; - -use super::mail::Mail; -use std::path::PathBuf; - -pub fn read_emails(config: &Config, sender: MessageSender) -> Result> { - // on macOS, we might need permission for the `Library` folder... - match std::fs::read_dir(&config.emails_folder_path) { - Ok(_) => (), - Err(e) => match e.kind() { - #[cfg(target_os = "macos")] - std::io::ErrorKind::PermissionDenied => { - tracing::info!("Could not read folder: {}", e); - if let Err(e) = sender.send(Message::MissingPermissions) { - tracing::error!("Error sending: {}", e); - } - // We should return early now, otherwise the code below will send a different - // error - return Ok(Vec::new()); - } - _ => { - if let Err(e) = sender.send(Message::Error(eyre!("Error: {:?}", &e))) { - tracing::error!("Error sending: {}", e); - } - } - }, - } - - // As `walkdir` does not support `par_iter` (see https://www.reddit.com/r/rust/comments/6eif7r/walkdir_users_we_need_you/) - // - -we first collect all folders, - // then all sub-folders in those ending in mboxending in .mbox and then iterate over them in paralell - let folders: Vec = WalkDir::new(&config.emails_folder_path) - .into_iter() - .filter_map(|e| match e { - Ok(n) - if n.path().is_dir() - && n.path() - .to_str() - .map(|e| e.contains(".mbox")) - .unwrap_or(false) => - { - tracing::trace!("Found folder {}", n.path().display()); - Some(n.path().to_path_buf()) - } - Err(e) => { - tracing::info!("Could not read folder: {}", e); - if let Err(e) = sender.send(Message::Error(eyre!("Could not read folder: {:?}", e))) - { - tracing::error!("Error sending error {}", e); - } - None - } - _ => None, - }) - .collect(); - sender.send(Message::ReadTotal(folders.len()))?; - let mails: Vec = folders - .into_par_iter() - .filter_map( - |path| match emails_in(path.clone(), sender.clone(), Mail::new) { - Ok(n) => Some(n), - Err(e) => { - tracing::error!("{} {:?}", path.display(), &e); - if let Err(e) = sender.send(Message::Error(eyre!( - "Could read mails in {}: {:?}", - path.display(), - e - ))) { - tracing::error!("Error sending error {}", e); - } - None - } - }, - ) - .flatten() - .collect(); - Ok(mails) -} diff --git a/src/importer/formats/apple_mail/mail.rs b/src/importer/formats/apple_mail/mail.rs deleted file mode 100644 index b1159bf..0000000 --- a/src/importer/formats/apple_mail/mail.rs +++ /dev/null @@ -1,69 +0,0 @@ -use emlx::parse_emlx; -use eyre::Result; - -use std::borrow::Cow; -use std::path::{Path, PathBuf}; - -use super::super::shared::email::EmailMeta; -use super::super::shared::parse::ParseableEmail; - -pub struct Mail { - path: PathBuf, - // This is parsed out of the `emlx` as it is parsed - is_seen: bool, - // This is parsed out of the `path` - label: Option, - // Maildata - data: Vec, -} - -impl Mail { - pub fn new>(path: P) -> Option { - let path = path.as_ref(); - let name = path.file_name()?.to_str()?; - if !name.ends_with(".emlx") { - return None; - } - // find the folder ending with `.mbox` in the path - let ext = ".mbox"; - let label = path - .iter() - .map(|e| e.to_str()) - .flatten() - .find(|s| s.ends_with(ext)) - .map(|s| s.replace(ext, "")); - Some(Self { - path: path.to_path_buf(), - is_seen: false, - label, - data: Vec::new(), - }) - } -} - -impl ParseableEmail for Mail { - fn prepare(&mut self) -> Result<()> { - let data = std::fs::read(self.path.as_path())?; - let parsed = parse_emlx(&data)?; - self.is_seen = !parsed.flags.is_read; - self.data = parsed.message.to_vec(); - Ok(()) - } - fn message(&self) -> Result> { - Ok(Cow::Borrowed(self.data.as_slice())) - } - fn path(&self) -> &Path { - self.path.as_path() - } - fn meta(&self) -> Result> { - let tags = match self.label { - Some(ref n) => vec![n.clone()], - None => vec![], - }; - let meta = EmailMeta { - tags, - is_seen: self.is_seen, - }; - Ok(Some(meta)) - } -} diff --git a/src/importer/formats/apple_mail/mod.rs b/src/importer/formats/apple_mail/mod.rs deleted file mode 100644 index de76310..0000000 --- a/src/importer/formats/apple_mail/mod.rs +++ /dev/null @@ -1,23 +0,0 @@ -mod filesystem; -mod mail; - -use shellexpand; -use std::{path::PathBuf, str::FromStr}; - -use super::{Config, ImporterFormat, MessageSender, Result}; - -#[derive(Default)] -pub struct AppleMail {} - -impl ImporterFormat for AppleMail { - type Item = mail::Mail; - - fn default_path() -> Option { - let path = shellexpand::tilde("~/Library/Mail"); - Some(PathBuf::from_str(&path.to_string()).unwrap()) - } - - fn emails(&self, config: &Config, sender: MessageSender) -> Result> { - filesystem::read_emails(config, sender) - } -} diff --git a/src/importer/formats/gmailbackup/meta.rs b/src/importer/formats/gmailbackup/meta.rs deleted file mode 100644 index a2a6129..0000000 --- a/src/importer/formats/gmailbackup/meta.rs +++ /dev/null @@ -1,46 +0,0 @@ -use chrono::prelude::*; - -use eyre::{bail, Result}; -use serde::Deserialize; -use serde_json; - -use super::super::shared::email::EmailMeta; -use super::raw_email::RawEmailEntry; - -#[derive(Deserialize, Debug, Clone)] -pub struct Meta { - pub msg_id: String, - pub subject: String, - pub labels: Vec, - pub flags: Vec, - internal_date: i64, - - #[serde(skip, default = "Utc::now")] - pub created: DateTime, -} - -impl Meta { - pub fn is_seen(&self) -> bool { - self.labels.contains(&"\\seen".to_owned()) - } -} - -impl From for EmailMeta { - fn from(meta: Meta) -> Self { - let is_seen = meta.is_seen(); - EmailMeta { - tags: meta.labels, - is_seen, - } - } -} - -pub fn parse_meta(raw_entry: &RawEmailEntry) -> Result { - let content = match raw_entry.read_gmail_meta() { - None => bail!("No Gmail Meta Information Available"), - Some(content) => content?, - }; - let mut meta: Meta = serde_json::from_slice(&content)?; - meta.created = Utc.timestamp(meta.internal_date, 0); - Ok(meta) -} diff --git a/src/importer/formats/gmailbackup/mod.rs b/src/importer/formats/gmailbackup/mod.rs deleted file mode 100644 index 7aceffc..0000000 --- a/src/importer/formats/gmailbackup/mod.rs +++ /dev/null @@ -1,23 +0,0 @@ -mod meta; -mod raw_email; - -use super::shared::filesystem::{emails_in, folders_in}; -use super::{Config, ImporterFormat, MessageSender, Result}; -use raw_email::RawEmailEntry; - -#[derive(Default)] -pub struct Gmail {} - -impl ImporterFormat for Gmail { - type Item = raw_email::RawEmailEntry; - - fn default_path() -> Option { - None - } - - fn emails(&self, config: &Config, sender: MessageSender) -> Result> { - folders_in(&config.emails_folder_path, sender, |path, sender| { - emails_in(path, sender, RawEmailEntry::new) - }) - } -} diff --git a/src/importer/formats/gmailbackup/raw_email.rs b/src/importer/formats/gmailbackup/raw_email.rs deleted file mode 100644 index 9bd66d1..0000000 --- a/src/importer/formats/gmailbackup/raw_email.rs +++ /dev/null @@ -1,118 +0,0 @@ -use eyre::{eyre, Result}; -use flate2::read::GzDecoder; - -use std::borrow::Cow; -use std::io::Read; -use std::path::{Path, PathBuf}; - -use super::super::shared::email::EmailMeta; -use super::super::shared::parse::ParseableEmail; - -/// Raw representation of an email. -/// Contains the paths to the relevant files as well -/// as the name of the folder the email was in. -#[derive(Debug)] -pub struct RawEmailEntry { - #[allow(unused)] - folder_name: String, - eml_path: PathBuf, - gmail_meta_path: Option, - is_compressed: bool, - #[allow(unused)] - size: u64, -} - -impl RawEmailEntry { - pub fn path(&self) -> &Path { - self.eml_path.as_path() - } - - pub fn read(&self) -> Result> { - if self.is_compressed { - let reader = std::fs::File::open(&self.eml_path)?; - let mut decoder = GzDecoder::new(reader); - let mut buffer = Vec::new(); - decoder.read_to_end(&mut buffer)?; - Ok(buffer) - } else { - std::fs::read(&self.eml_path).map_err(|e| eyre!("IO Error: {}", &e)) - } - } - - pub fn has_gmail_meta(&self) -> bool { - self.gmail_meta_path.is_some() - } - - pub fn read_gmail_meta(&self) -> Option>> { - // Just using map here returns a `&Option` whereas we want `Option` - #[allow(clippy::manual_map)] - match &self.gmail_meta_path { - Some(p) => Some(std::fs::read(p).map_err(|e| eyre!("IO Error: {}", &e))), - None => None, - } - } -} - -impl RawEmailEntry { - pub(super) fn new>(path: P) -> Option { - let path = path.as_ref(); - let stem = path.file_stem()?.to_str()?; - let name = path.file_name()?.to_str()?; - let is_eml_gz = name.ends_with(".eml.gz"); - let is_eml = name.ends_with(".eml"); - if !is_eml_gz && !is_eml { - return None; - } - let is_compressed = is_eml_gz; - let folder_name = path.parent()?.file_name()?.to_str()?.to_owned(); - let eml_path = path.to_path_buf(); - - let file_metadata = path.metadata().ok()?; - - // Build a meta path - let meta_path = path - .parent()? - .join(format!("{}.meta", stem.replace(".eml", ""))); - - // Only embed it, if it exists - let gmail_meta_path = if meta_path.exists() { - Some(meta_path) - } else { - None - }; - tracing::trace!( - "Email [c?: {}] {} {:?}", - is_compressed, - eml_path.display(), - gmail_meta_path - ); - Some(RawEmailEntry { - folder_name, - eml_path, - gmail_meta_path, - is_compressed, - size: file_metadata.len(), - }) - } -} - -impl ParseableEmail for RawEmailEntry { - fn prepare(&mut self) -> Result<()> { - Ok(()) - } - fn message(&self) -> Result> { - Ok(Cow::Owned(self.read()?)) - } - - fn path(&self) -> &Path { - self.eml_path.as_path() - } - - fn meta(&self) -> Result> { - if self.has_gmail_meta() { - Ok(Some(super::meta::parse_meta(self)?.into())) - } else { - Ok(None) - } - } -} diff --git a/src/importer/formats/mbox/mod.rs b/src/importer/formats/mbox/mod.rs deleted file mode 100644 index b83e0ae..0000000 --- a/src/importer/formats/mbox/mod.rs +++ /dev/null @@ -1,133 +0,0 @@ -//! FIXME: Implement our own Mailbox reader that better implements the spec. -//! use jetsci for efficient searching: -//! https://github.com/shepmaster/jetscii -//! (or aho corasick) -//! MBox parsing is also not particularly fast as it currently doesn't use parallelism - -use eyre::eyre; -use mbox_reader; -use rayon::prelude::*; -use tracing; -use walkdir::WalkDir; - -use super::{Config, ImporterFormat, Message, MessageSender, Result}; - -use super::shared::email::EmailMeta; -use super::shared::parse::ParseableEmail; - -use std::borrow::Cow; -use std::path::{Path, PathBuf}; - -pub struct Mail { - path: PathBuf, - /// For now, we go with a very simple implementation: - /// Each mal will have a heap-allocated vec of the corresponding - /// bytes in the mbox. - /// This wastes a lot of allocations and shows the limits of our current abstraction. - /// It would be better to just save the headers and ignore the rest. - content: Vec, -} - -#[derive(Default)] -pub struct Mbox; - -/// The inner parsing code -fn inner_emails(config: &Config, sender: MessageSender) -> Result> { - // find all files ending in .mbox - let mboxes: Vec = WalkDir::new(&config.emails_folder_path) - .into_iter() - .filter_map(|e| match e { - Ok(n) - if n.path().is_file() - && n.path() - .to_str() - .map(|e| e.contains(".mbox")) - .unwrap_or(false) => - { - tracing::trace!("Found mbox file {}", n.path().display()); - Some(n.path().to_path_buf()) - } - Err(e) => { - tracing::info!("Could not read folder: {}", e); - if let Err(e) = sender.send(Message::Error(eyre!("Could not read folder: {:?}", e))) - { - tracing::error!("Error sending error {}", e); - } - None - } - _ => None, - }) - .collect(); - - let mails: Vec = mboxes - .into_par_iter() - .filter_map(|mbox_file| { - let mbox = match mbox_reader::MboxFile::from_file(&mbox_file) { - Ok(n) => n, - Err(e) => { - tracing::error!( - "Could not open mbox file at {}: {}", - &mbox_file.display(), - e - ); - return None; - } - }; - let inner_mails: Vec = mbox - .iter() - .filter_map(|e| { - let content = match e.message() { - Some(n) => n, - None => { - tracing::error!("Could not parse mail at offset {}", e.offset()); - return None; - } - }; - Some(Mail { - path: mbox_file.clone(), - content: content.to_owned(), - }) - }) - .collect(); - Some(inner_mails) - }) - .flatten() - .collect(); - Ok(mails) -} - -impl ImporterFormat for Mbox { - type Item = Mail; - - fn default_path() -> Option { - None - } - - fn emails(&self, config: &Config, sender: MessageSender) -> Result> { - inner_emails(config, sender) - } -} - -impl ParseableEmail for Mail { - fn prepare(&mut self) -> Result<()> { - Ok(()) - } - fn message(&self) -> Result> { - Ok(self.content.as_slice().into()) - } - fn path(&self) -> &Path { - self.path.as_path() - } - fn meta(&self) -> Result> { - // The filename is a tag, e.g. `INBOX.mbox`, `WORK.mbox` - if let Some(prefix) = self.path.file_stem() { - if let Some(s) = prefix.to_str().map(|s| s.to_owned()) { - return Ok(Some(EmailMeta { - tags: vec![s], - is_seen: false, - })); - } - } - Ok(None) - } -} diff --git a/src/importer/formats/mod.rs b/src/importer/formats/mod.rs deleted file mode 100644 index 87e5430..0000000 --- a/src/importer/formats/mod.rs +++ /dev/null @@ -1,31 +0,0 @@ -use std::path::PathBuf; - -pub use eyre::Result; - -mod apple_mail; -mod gmailbackup; -mod mbox; -pub mod shared; - -pub use apple_mail::AppleMail; -pub use gmailbackup::Gmail; -pub use mbox::Mbox; - -pub use crate::types::Config; -use shared::parse::ParseableEmail; - -pub use super::{Message, MessageReceiver, MessageSender}; - -/// This is implemented by the various formats -/// to define how they return email data. -pub trait ImporterFormat: Send + Sync { - type Item: ParseableEmail; - - /// The default location path where the data for this format resides - /// on system. If there is none (such as for mbox) return `None` - fn default_path() -> Option; - - /// Return all the emails in this format. - /// Use the sneder to give progress updates via the `ReadProgress` case. - fn emails(&self, config: &Config, sender: MessageSender) -> Result>; -} diff --git a/src/importer/formats/shared/database.rs b/src/importer/formats/shared/database.rs deleted file mode 100644 index e510fe3..0000000 --- a/src/importer/formats/shared/database.rs +++ /dev/null @@ -1,83 +0,0 @@ -use super::parse::{parse_email, ParseableEmail}; -use crate::database::{DBMessage, Database}; -use crate::types::Config; - -use super::super::{Message, MessageSender}; - -use eyre::{bail, Result}; -use rayon::prelude::*; - -pub fn into_database( - config: &Config, - mut emails: Vec, - tx: MessageSender, -) -> Result { - let total = emails.len(); - tracing::info!("Loaded {} emails", &total); - - // First, communicate the total amount of mails received - if let Err(e) = tx.send(Message::WriteTotal(total)) { - bail!("Channel Failure {:?}", &e); - } - - // Create a new database connection, just for writing - let database = Database::new(config.database_path.clone()).unwrap(); - - // Save the config into the database - if let Err(e) = database.save_config(config.clone()) { - bail!("Could not save config to database {:?}", &e); - } - - // Consume the connection to begin the import. It will return the `handle` to use for - // waiting for the database to finish importing, and the `sender` to submit work. - let (sender, handle) = database.import(); - - // Iterate over the mails.. - emails - // in paralell.. - .par_iter_mut() - // parsing them - .map(|raw_mail| parse_email(raw_mail, &config.sender_emails)) - // and inserting them into SQLite - .for_each(|entry| { - // Try to write the message into the database - if let Err(e) = match entry { - Ok(mail) => sender.send(DBMessage::Mail(Box::new(mail))), - Err(e) => sender.send(DBMessage::Error(e)), - } { - tracing::error!("Error Inserting into Database: {:?}", &e); - } - // Signal the write - if let Err(e) = tx.send(Message::WriteOne) { - tracing::error!("Channel Failure: {:?}", &e); - } - }); - - // Tell SQLite there's no more work coming. This will exit the listening loop - if let Err(e) = sender.send(DBMessage::Done) { - bail!("Channel Failure {:?}", &e); - } - - // Wait for SQLite to finish parsing - tracing::info!("Waiting for SQLite to finish"); - - if let Err(e) = tx.send(Message::FinishingUp) { - bail!("Channel Failure {:?}", &e); - } - - tracing::trace!("Waiting for database handle..."); - let output = match handle.join() { - Ok(Ok(count)) => Ok(count), - Ok(Err(e)) => Err(e), - Err(e) => Err(eyre::eyre!("Join Error: {:?}", &e)), - }; - - // Tell the caller that we're done processing. This will allow leaving the - // display loop - tracing::trace!("Messaging Done"); - if let Err(e) = tx.send(Message::Done) { - bail!("Channel Failure {:?}", &e); - } - - output -} diff --git a/src/importer/formats/shared/email.rs b/src/importer/formats/shared/email.rs deleted file mode 100644 index 17f96f8..0000000 --- a/src/importer/formats/shared/email.rs +++ /dev/null @@ -1,50 +0,0 @@ -use chrono::prelude::*; -use std::path::PathBuf; - -pub type Tag = String; - -/// This is based on additional information in some systems such as -/// Gmail labels or Apple Mail tags or Apple XML -#[derive(Debug, Default)] -pub struct EmailMeta { - pub tags: Vec, - pub is_seen: bool, -} - -const TAG_SEP: &str = ":|:"; - -impl EmailMeta { - pub fn tags_from_string(tag_string: &str) -> Vec { - tag_string.split(TAG_SEP).map(|e| e.to_string()).collect() - } - - pub fn from(is_seen: bool, tag_string: &str) -> Self { - let tags = EmailMeta::tags_from_string(tag_string); - EmailMeta { tags, is_seen } - } - - pub fn tags_string(&self) -> String { - self.tags.join(TAG_SEP) - } -} - -/// Representation of an email -#[derive(Debug)] -pub struct EmailEntry { - pub path: PathBuf, - pub sender_domain: String, - pub sender_local_part: String, - pub sender_name: String, - pub datetime: chrono::DateTime, - pub subject: String, - /// The amount of `to:` adresses - pub to_count: usize, - /// When this email was send to a group, the group name - pub to_group: Option, - /// The first address and name in `To`, if any - pub to_first: Option<(String, String)>, - pub is_reply: bool, - /// Was this email send from the account we're importing? - pub is_send: bool, - pub meta: Option, -} diff --git a/src/importer/formats/shared/filesystem.rs b/src/importer/formats/shared/filesystem.rs deleted file mode 100644 index f37af28..0000000 --- a/src/importer/formats/shared/filesystem.rs +++ /dev/null @@ -1,76 +0,0 @@ -use eyre::{bail, Result}; -use rayon::prelude::*; -use tracing::trace; - -use std::path::{Path, PathBuf}; - -use super::super::{Message, MessageSender}; - -/// Call `FolderAction` on all files in all sub folders in -/// folder `folder`. -pub fn folders_in( - folder: P, - sender: MessageSender, - action: FolderAction, -) -> Result> -where - P: AsRef, - FolderAction: Fn(PathBuf, MessageSender) -> Result> + Send + Sync, - ActionResult: Send, -{ - let folder = folder.as_ref(); - if !folder.exists() { - bail!("Folder {} does not exist", &folder.display()); - } - // For progress reporting, we collect the iterator in order to - // know how many items there are. - let items: Vec<_> = std::fs::read_dir(&folder)?.collect(); - let total = items.len(); - sender.send(Message::ReadTotal(total))?; - Ok(items - .into_iter() - .par_bridge() - .filter_map(|entry| { - let path = entry - .map_err(|e| tracing::error!("{} {:?}", &folder.display(), &e)) - .ok()? - .path(); - if !path.is_dir() { - return None; - } - let sender = sender.clone(); - trace!("Reading folder {}", path.display()); - action(path.clone(), sender) - .map_err(|e| tracing::error!("{} {:?}", path.display(), &e)) - .ok() - }) - .flatten() - .collect()) -} - -pub fn emails_in>(path: P, sender: MessageSender, make: F) -> Result> -where - F: Fn(PathBuf) -> Option, - F: Send + Sync + 'static, - O: Send + Sync, -{ - let path = path.as_ref(); - let result = Ok(std::fs::read_dir(path)? - .into_iter() - .par_bridge() - .filter_map(|entry| { - let path = entry - .map_err(|e| tracing::error!("{} {:?}", &path.display(), &e)) - .ok()? - .path(); - if path.is_dir() { - return None; - } - trace!("Reading {}", &path.display()); - make(path) - }) - .collect()); - // We're done reading the folder - sender.send(Message::ReadOne).unwrap(); - result -} diff --git a/src/importer/formats/shared/mod.rs b/src/importer/formats/shared/mod.rs deleted file mode 100644 index 96a3e3c..0000000 --- a/src/importer/formats/shared/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod database; -pub mod email; -pub mod filesystem; -pub mod parse; diff --git a/src/importer/formats/shared/parse.rs b/src/importer/formats/shared/parse.rs deleted file mode 100644 index 730fc75..0000000 --- a/src/importer/formats/shared/parse.rs +++ /dev/null @@ -1,155 +0,0 @@ -use chrono::prelude::*; -use email_parser::address::{Address, EmailAddress, Mailbox}; -use eyre::{eyre, Result}; - -use std::borrow::Cow; -use std::collections::HashSet; -use std::path::Path; - -use super::email::{EmailEntry, EmailMeta}; - -/// Different `importer`s can implement this trait to provide the necessary -/// data to parse their data into a `EmailEntry`. -pub trait ParseableEmail: Send + Sized + Sync { - /// This will be called once before `message`, `path` and `meta` - /// are called. It can be used to perform parsing operations - fn prepare(&mut self) -> Result<()>; - /// The message content as bytes - fn message(&self) -> Result>; - /// The original path of the email in the filesystem - fn path(&self) -> &Path; - /// Optional meta information if they're available. - /// (Depending on the `importer` capabilities and system) - fn meta(&self) -> Result>; -} - -pub fn parse_email( - entry: &mut Entry, - sender_emails: &HashSet, -) -> Result { - if let Err(e) = entry.prepare() { - tracing::error!("Prepare Error: {:?}", e); - return Err(e); - } - let content = entry.message()?; - match email_parser::email::Email::parse(&content) { - Ok(email) => { - let path = entry.path(); - tracing::trace!("Parsing {}", path.display()); - let (sender_name, _, sender_local_part, sender_domain) = - mailbox_to_string(&email.sender); - - let datetime = emaildatetime_to_chrono(&email.date); - let subject = email.subject.map(|e| e.to_string()).unwrap_or_default(); - - let to_count = match email.to.as_ref() { - Some(n) => n.len(), - None => 0, - }; - let to = match email.to.as_ref().map(|v| v.first()).flatten() { - Some(n) => address_to_name_string(n), - None => None, - }; - let to_group = to.as_ref().map(|e| e.0.clone()).flatten(); - let to_first = to.as_ref().map(|e| (e.1.clone(), e.2.clone())); - - let is_reply = email.in_reply_to.map(|v| !v.is_empty()).unwrap_or(false); - - let meta = entry.meta()?; - - // In order to determine the sender, we have to - // build up the address again :-( - let is_send = { - let email = format!("{}@{}", sender_local_part, sender_domain); - sender_emails.contains(&email) - }; - - Ok(EmailEntry { - path: path.to_path_buf(), - sender_domain, - sender_local_part, - sender_name, - datetime, - subject, - meta, - is_reply, - to_count, - to_group, - to_first, - is_send, - }) - } - Err(error) => { - let error = eyre!( - "Could not parse email (trace to see contents): {:?} [{}]", - &error, - entry.path().display() - ); - tracing::error!("{:?}", &error); - if let Ok(content_string) = String::from_utf8(content.into_owned()) { - tracing::trace!("Contents:\n{}\n---\n", content_string); - } else { - tracing::trace!("Contents:\nInvalid UTF8\n---\n"); - } - Err(error) - } - } -} - -/// Returns a conversion from address to the fields we care about: -/// ([group name], display name, email address) -fn address_to_name_string(address: &Address) -> Option<(Option, String, String)> { - match address { - Address::Group((names, boxes)) => match (names.first(), boxes.first()) { - (group_name, Some(mailbox)) => { - let group = group_name.map(|e| e.to_string()); - let (display_name, address, _, _) = mailbox_to_string(mailbox); - Some((group, display_name, address)) - } - _ => None, - }, - Address::Mailbox(mailbox) => { - let (display_name, address, _, _) = mailbox_to_string(mailbox); - Some((None, display_name, address)) - } - } -} - -/// Returns (display name, email address, local part, domain) -fn mailbox_to_string(mailbox: &Mailbox) -> (String, String, String, String) { - let names = match mailbox.name.as_ref() { - Some(n) => n - .iter() - .map(|e| e.as_ref()) - .collect::>() - .join(" "), - None => "".to_owned(), - }; - ( - names, - emailaddress_to_string(&mailbox.address), - mailbox.address.local_part.to_string(), - mailbox.address.domain.to_string(), - ) -} - -fn emailaddress_to_string(address: &EmailAddress) -> String { - format!( - "{}@{}", - address.local_part.to_string(), - address.domain.to_string() - ) -} - -fn emaildatetime_to_chrono(dt: &email_parser::time::DateTime) -> chrono::DateTime { - Utc.ymd( - dt.date.year as i32, - dt.date.month_number() as u32, - dt.date.day as u32, - ) - .and_hms( - dt.time.time.hour as u32, - dt.time.time.minute as u32, - dt.time.time.second as u32, - ) -} diff --git a/src/importer/importer.rs b/src/importer/importer.rs deleted file mode 100644 index bcb754a..0000000 --- a/src/importer/importer.rs +++ /dev/null @@ -1,58 +0,0 @@ -use super::formats::shared; -use super::{Config, ImporterFormat}; - -use super::{Message, MessageReceiver}; - -use crossbeam_channel::{self, unbounded}; -use eyre::Result; -use std::thread::JoinHandle; - -pub trait Importerlike { - fn import(self) -> Result<(MessageReceiver, JoinHandle>)>; -} - -pub struct Importer { - config: Config, - format: Format, -} - -impl Importer { - pub fn new(config: Config, format: Format) -> Self { - Self { config, format } - } -} - -impl Importerlike for Importer { - fn import(self) -> Result<(MessageReceiver, JoinHandle>)> { - let Importer { format, .. } = self; - let (sender, receiver) = unbounded(); - - let config = self.config; - let handle: JoinHandle> = std::thread::spawn(move || { - let outer_sender = sender.clone(); - let processed = move || { - let emails = format.emails(&config, sender.clone())?; - let processed = shared::database::into_database(&config, emails, sender.clone())?; - - Ok(processed) - }; - let result = processed(); - - // Send the error away and map it to a crossbeam channel error - match result { - Ok(_) => Ok(()), - Err(e) => match outer_sender.send(Message::Error(e)) { - Ok(_) => Ok(()), - Err(e) => Err(eyre::Report::new(e)), - }, - } - }); - Ok((receiver, handle)) - } -} - -impl Importerlike for Box { - fn import(self) -> Result<(MessageReceiver, JoinHandle>)> { - (*self).import() - } -} diff --git a/src/importer/message_adapter.rs b/src/importer/message_adapter.rs deleted file mode 100644 index 7dbe2b4..0000000 --- a/src/importer/message_adapter.rs +++ /dev/null @@ -1,151 +0,0 @@ -use eyre::{bail, eyre, Report, Result}; - -use std::sync::{Arc, RwLock}; -use std::thread::JoinHandle; - -use super::formats::ImporterFormat; -use super::importer::Importerlike; -use super::Message; - -#[derive(Debug, Default)] -struct Data { - total_read: usize, - read: usize, - total_write: usize, - write: usize, - finishing: bool, - done: bool, - error: Option, - #[cfg(target_os = "macos")] - missing_permissions: bool, -} - -#[derive(Clone, Debug, Copy)] -pub struct Progress { - pub total: usize, - pub count: usize, -} - -#[derive(Clone, Debug, Copy)] -pub struct State { - pub finishing: bool, - pub done: bool, - pub written: usize, - #[cfg(target_os = "macos")] - pub missing_permissions: bool, -} - -/// This can be initialized with a [`MessageSender`] and it will -/// automatically tally up the information into a thread-safe -/// datastructure -pub struct Adapter { - producer_lock: Arc>, - consumer_lock: Arc>, -} - -impl Adapter { - #[allow(clippy::new_without_default)] - pub fn new() -> Self { - let rw_lock = Arc::new(RwLock::default()); - // FIXME: Look up this warning. It looks like the clones are necessary? - #[allow(clippy::redundant_clone)] - let producer_lock = rw_lock.clone(); - #[allow(clippy::redundant_clone)] - let consumer_lock = rw_lock.clone(); - Self { - producer_lock, - consumer_lock, - } - } - - /// Starts up a thread that handles the `MessageReceiver` messages - /// into state that can be accessed via [`read_count`], [`write_count`] and [`finished`] - pub fn process( - &self, - importer: super::importer::Importer, - ) -> Result>> { - let (receiver, handle) = importer.import()?; - let lock = self.producer_lock.clone(); - let handle = std::thread::spawn(move || { - 'outer: loop { - let mut write_guard = match lock.write() { - Ok(n) => n, - Err(e) => bail!("RwLock Error: {:?}", e), - }; - for entry in receiver.try_iter() { - match entry { - Message::ReadTotal(n) => write_guard.total_read = n, - Message::ReadOne => { - write_guard.read += 1; - // Depending on the implementation, we may receive read calls before - // the total size is known. We prevent division by zero by - // always setting the total to read + 1 in these cases - if write_guard.total_read <= write_guard.read { - write_guard.total_read = write_guard.read + 1; - } - } - Message::WriteTotal(n) => write_guard.total_write = n, - Message::WriteOne => write_guard.write += 1, - Message::FinishingUp => write_guard.finishing = true, - Message::Done => { - write_guard.done = true; - break 'outer; - } - Message::Error(e) => { - write_guard.error = Some(e); - } - #[cfg(target_os = "macos")] - Message::MissingPermissions => { - write_guard.missing_permissions = true; - } - }; - } - } - - let _ = handle.join().map_err(|op| eyre::eyre!("{:?}", &op))??; - - Ok(()) - }); - Ok(handle) - } - - pub fn read_count(&self) -> Result { - let item = self.consumer_lock.read().map_err(|e| eyre!("{:?}", &e))?; - Ok(Progress { - total: item.total_read, - count: item.read, - }) - } - - pub fn write_count(&self) -> Result { - let item = self.consumer_lock.read().map_err(|e| eyre!("{:?}", &e))?; - Ok(Progress { - total: item.total_write, - count: item.write, - }) - } - - pub fn finished(&self) -> Result { - let item = self.consumer_lock.read().map_err(|e| eyre!("{:?}", &e))?; - Ok(State { - finishing: item.finishing, - done: item.done, - written: item.write, - #[cfg(target_os = "macos")] - missing_permissions: item.missing_permissions, - }) - } - - pub fn error(&self) -> Result> { - // We take the error of out of the write lock only if there is an error. - let item = self.consumer_lock.read().map_err(|e| eyre!("{:?}", &e))?; - let is_error = item.error.is_some(); - drop(item); - if is_error { - let mut item = self.producer_lock.write().map_err(|e| eyre!("{:?}", &e))?; - Ok(item.error.take()) - } else { - Ok(None) - } - } -} diff --git a/src/importer/mod.rs b/src/importer/mod.rs deleted file mode 100644 index dcafbab..0000000 --- a/src/importer/mod.rs +++ /dev/null @@ -1,67 +0,0 @@ -use crossbeam_channel; - -pub(crate) mod formats; -#[allow(clippy::module_inception)] -mod importer; -mod message_adapter; - -use crate::types::Config; -pub use formats::shared::email::{EmailEntry, EmailMeta}; -pub use importer::Importerlike; -pub use message_adapter::*; - -use formats::ImporterFormat; - -/// The message that informs of the importers progress -#[derive(Debug)] -pub enum Message { - /// How much progress are we making on reading the contents - /// of the emails. - /// The `usize` parameter marks the total amount of items to read - if it is known. - /// The values here can vary wildly based on the type of Importer `Format` in use. - /// A Gmail backup will list the folders and how many of them - /// are already read. A mbox format will list other things as there - /// no folders. - ReadTotal(usize), - /// Whenever an item out of the total is read, this message will be emitted - ReadOne, - /// Similar to [`ReadTotal`] - WriteTotal(usize), - /// Similar to `ReadOne` - WriteOne, - /// Once everything has been written, we need to wait for the database - /// to sync - FinishingUp, - /// Finally, this indicates that we're done. - Done, - /// An error happened during processing - Error(eyre::Report), - /// A special case for macOS, where a permission error means we have to grant this app - /// the right to see the mail folder - #[cfg(target_os = "macos")] - MissingPermissions, -} - -pub type MessageSender = crossbeam_channel::Sender; -pub type MessageReceiver = crossbeam_channel::Receiver; - -pub fn importer(config: &Config) -> Box { - use crate::types::FormatType::*; - match config.format { - AppleMail => Box::new(applemail_importer(config.clone())), - GmailVault => Box::new(gmail_importer(config.clone())), - Mbox => Box::new(gmail_importer(config.clone())), - } -} - -pub fn gmail_importer(config: Config) -> importer::Importer { - importer::Importer::new(config, formats::Gmail::default()) -} - -pub fn applemail_importer(config: Config) -> importer::Importer { - importer::Importer::new(config, formats::AppleMail::default()) -} - -pub fn mbox_importer(config: Config) -> importer::Importer { - importer::Importer::new(config, formats::Mbox::default()) -} diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 0cf954c..0000000 --- a/src/lib.rs +++ /dev/null @@ -1,70 +0,0 @@ -#[cfg(target_os = "macos")] -#[macro_use] -extern crate objc; - -use tracing_subscriber::fmt; -use tracing_subscriber::prelude::*; - -pub mod database; -#[cfg(feature = "gui")] -pub mod gui; -pub mod importer; -pub mod model; -pub mod types; - -pub fn setup_tracing() { - if std::env::var("RUST_LOG").is_err() { - std::env::set_var("RUST_LOG", "error") - } - - let collector = tracing_subscriber::registry().with(fmt::layer().with_writer(std::io::stdout)); - - tracing::subscriber::set_global_default(collector).expect("Unable to set a global collector"); -} - -/// Create a config for the `cli` and validate the input -pub fn make_config() -> types::Config { - use std::path::Path; - use types::FormatType; - let arguments: Vec = std::env::args().collect(); - let folder = arguments - .get(1) - .unwrap_or_else(|| usage("Missing email folder argument")); - let database = arguments - .get(2) - .unwrap_or_else(|| usage("Missing database path argument")); - let sender = arguments - .get(3) - .unwrap_or_else(|| usage("Missing sender email address argument")); - let format: FormatType = arguments - .get(4) - .unwrap_or_else(|| usage("Missing sender email address argument")) - .into(); - - let database_path = Path::new(database); - if database_path.is_dir() { - panic!( - "Database Path can't be a directory: {}", - &database_path.display() - ); - } - let emails_folder_path = Path::new(folder); - // For non-mbox files, we make sure we have a directory - if !emails_folder_path.is_dir() { - panic!( - "Emails Folder Path is not a directory: {}", - &emails_folder_path.display() - ); - } - - match crate::types::Config::new(Some(database), folder, vec![sender.to_string()], format) { - Ok(n) => n, - Err(r) => panic!("Error: {:?}", &r), - } -} - -fn usage(error: &'static str) -> ! { - println!("Usage: cli [email-folder] [database-path] [sender-email-address] [format]"); - println!("\tExample: cli ~/Library/Mails/V9/ ./db.sqlite my-address@gmail.com apple"); - panic!("{}", error); -} diff --git a/src/model/engine.rs b/src/model/engine.rs deleted file mode 100644 index aee171d..0000000 --- a/src/model/engine.rs +++ /dev/null @@ -1,243 +0,0 @@ -//! The `Engine` is the entry point to the data that should be -//! displayed in Segmentations. -//! See [`Engine`] for more information. -//! See also: -//! - [`segmentations::`] -//! - [`items::`] -use eyre::{bail, Result}; -use lru::LruCache; - -use crate::database::query::{Field, Filter, OtherQuery, Query, ValueField}; -use crate::model::link::Response; -use crate::types::Config; - -use super::link::Link; -use super::segmentations; -use super::types::{LoadingState, Segment, Segmentation}; - -/// This signifies the action we're currently evaluating -/// It is used for sending requests and receiving responses -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub(super) enum Action { - /// Recalculate the current `Segmentation` based on a changed aggregation - RecalculateSegmentation, - /// Push a new `Segmentation` - PushSegmentation, - /// Load the mails for the current `Segmentation` - LoadItems, - /// Load all tags - AllTags, -} - -/// Interact with the `Database`, operate on `Segmentations`, `Segments`, and `Items`. -/// `Engine` is used as the input for almost all operations in the -/// `items::` and `segmentation::` modules. -pub struct Engine { - pub(super) search_stack: Vec, - pub(super) group_by_stack: Vec, - pub(super) link: Link, - pub(super) segmentations: Vec, - /// Additional filters. See [`segmentations::set_filters`] - pub(super) filters: Vec, - /// This is a very simple cache from ranges to rows. - /// It doesn't account for overlapping ranges. - /// There's a lot of room for improvement here. - pub(super) item_cache: LruCache, - pub(super) known_tags: Vec, -} - -impl Engine { - pub fn new(config: &Config) -> Result { - let link = super::link::run(config)?; - let engine = Engine { - link, - search_stack: Vec::new(), - group_by_stack: vec![default_group_by_stack(0).unwrap()], - segmentations: Vec::new(), - filters: Vec::new(), - item_cache: LruCache::new(10000), - known_tags: Vec::new(), - }; - Ok(engine) - } - - /// Start the `Engine`. This will create a thread to - /// asynchronously communicate with the underlying backend - /// in a non-blocking manner. - pub fn start(&mut self) -> Result<()> { - // The initial segmentation - self.link - .request(&segmentations::make_query(self)?, Action::PushSegmentation)?; - // Get all tags - self.link.request( - &Query::Other { - query: OtherQuery::All(Field::MetaTags), - }, - Action::AllTags, - ) - } - - /// Information on the underlying `Format`. Does it have tags - pub fn format_has_tags(&self) -> bool { - !self.known_tags.is_empty() - } - - /// Information on the underlying `Format`. Does it have `seen` information - pub fn format_has_seen(&self) -> bool { - // FIXME: The current implementation just assumes that the existance of meta tags also implies is_seen - !self.known_tags.is_empty() - } - - /// All the known tags in the current emails - pub fn known_tags(&self) -> &[String] { - &self.known_tags - } - - /// Return the current stack of `Segmentations` - pub fn segmentations(&self) -> &[Segmentation] { - &self.segmentations - } - - /// Push a new `Segment` to select a more specific `Segmentation`. - /// - /// Pushing will create an additional `Aggregation` based on the selected - /// `Segment`, retrieve the data from the backend, and add it to the - /// current stack of `Segmentations`. - /// It allows to **drill down** into the data. - pub fn push(&mut self, segment: Segment) -> Result<()> { - // Assign the segmentation - let current = match self.segmentations.last_mut() { - Some(n) => n, - None => return Ok(()), - }; - current.selected = Some(segment); - - // Create the new search stack - self.search_stack = self - .segmentations - .iter() - .filter_map(|e| e.selected.as_ref()) - .map(|p| p.field.clone()) - .collect(); - - // Add the next group by - let index = self.group_by_stack.len(); - let next = default_group_by_stack(index) - .ok_or_else(|| eyre::eyre!("default group by stack out of bounds"))?; - self.group_by_stack.push(next); - - // Block UI & Wait for updates - self.link - .request(&segmentations::make_query(self)?, Action::PushSegmentation) - } - - /// Pop the current `Segmentation` from the stack. - /// The opposite of [`engine::push`] - pub fn pop(&mut self) { - if self.group_by_stack.is_empty() - || self.segmentations.is_empty() - || self.search_stack.is_empty() - { - tracing::error!( - "Invalid state. Not everything has the same length: {:?}, {:?}, {:?}", - &self.group_by_stack, - self.segmentations, - self.search_stack - ); - return; - } - - // Remove the last entry of everything - self.group_by_stack.remove(self.group_by_stack.len() - 1); - self.segmentations.remove(self.segmentations.len() - 1); - self.search_stack.remove(self.search_stack.len() - 1); - - // Remove the selection in the last segmentation - if let Some(e) = self.segmentations.last_mut() { - e.selected = None - } - - // Remove any rows that were cached for this segmentation - self.item_cache.clear(); - } - - /// Call this continously to retrieve calculation results and apply them. - /// Any mutating function on [`Engine`], such as [`Engine::push`] or [`items::items`] - /// require calling this method to apply there results once they're - /// available from the asynchronous backend. - /// This method is specifically non-blocking for usage in - /// `Eventloop` based UI frameworks such as `egui`. - pub fn process(&mut self) -> Result<()> { - let response = match self.link.receive()? { - Some(n) => n, - None => return Ok(()), - }; - - match response { - Response::Grouped(_, Action::PushSegmentation, p) => { - self.segmentations.push(p); - // Remove any rows that were cached for this segmentation - self.item_cache.clear(); - } - Response::Grouped(_, Action::RecalculateSegmentation, p) => { - let len = self.segmentations.len(); - self.segmentations[len - 1] = p; - // Remove any rows that were cached for this segmentation - self.item_cache.clear(); - } - Response::Normal(Query::Normal { range, .. }, Action::LoadItems, r) => { - for (index, row) in range.zip(r) { - let entry = LoadingState::Loaded(row.clone()); - self.item_cache.put(index, entry); - } - } - Response::Other(Query::Other { .. }, Action::AllTags, r) => { - self.known_tags = r; - } - _ => bail!("Invalid Query / Response combination"), - } - - Ok(()) - } - - /// Returns true if there're currently calculations open and `process` - /// needs to be called. This can be used in `Eventloop` based frameworks - /// such as `egui` to know when to continue calling `process` in the `loop` - /// ```ignore - /// loop { - /// self.engine.process().unwrap(); - /// if self.engine.is_busy() { - /// // Call the library function to run the event-loop again. - /// ctx.request_repaint(); - /// } - /// } - /// ``` - pub fn is_busy(&self) -> bool { - self.link.is_processing() || self.segmentations.is_empty() - } - - /// Blocking waiting until the current operation is done - /// This is useful for usage on a commandline or in unit tests - #[allow(unused)] - pub fn wait(&mut self) -> Result<()> { - loop { - self.process()?; - if !self.link.is_processing() { - break; - } - } - Ok(()) - } -} - -/// Return the default aggregation fields for each segmentation stack level -pub fn default_group_by_stack(index: usize) -> Option { - match index { - 0 => Some(Field::Year), - 1 => Some(Field::SenderDomain), - 2 => Some(Field::SenderLocalPart), - 3 => Some(Field::Month), - 4 => Some(Field::Day), - _ => None, - } -} diff --git a/src/model/items.rs b/src/model/items.rs deleted file mode 100644 index e2362f1..0000000 --- a/src/model/items.rs +++ /dev/null @@ -1,93 +0,0 @@ -//! Operations related to retrieving `items` from the current `Segmentation` -//! -//! A `Segmentation` is a aggregation of items into many `Segments`. -//! These operations allow retreiving the individual items for all -//! segments in the `Segmentation. - -use eyre::Result; - -use super::types::LoadingState; -use super::{engine::Action, Engine}; -use crate::database::{ - query::{Field, Filter, Query}, - query_result::QueryRow, -}; - -use std::ops::Range; - -/// Return the `items` in the current `Segmentation` -/// -/// If the items don't exist in the cache, they will be queried -/// asynchronously from the database. The return value distinguishes -/// between `Loaded` and `Loading` items. -/// -/// # Arguments -/// -/// * `engine` - The engine to use for retrieving data -/// * `range` - The range of items to retrieve. If `None` then all items will be retrieved -pub fn items(engine: &mut Engine, range: Option>) -> Result>> { - // build an array with either empty values or values from our cache. - let mut rows = Vec::new(); - - // The given range or all items - let range = range.unwrap_or_else(|| Range { - start: 0, - end: count(engine), - }); - - let mut missing_data = false; - for index in range.clone() { - let entry = engine.item_cache.get(&index); - let entry = match entry { - Some(LoadingState::Loaded(n)) => Some((*n).clone()), - Some(LoadingState::Loading) => None, - None => { - // for simplicity, we keep the "something is missing" state separate - missing_data = true; - - // Mark the row as being loaded - engine.item_cache.put(index, LoadingState::Loading); - None - } - }; - rows.push(entry); - } - // Only if at least some data is missing do we perform the request - if missing_data && !range.is_empty() { - let request = make_query(engine, range); - engine.link.request(&request, Action::LoadItems)?; - } - Ok(rows) -} - -/// The total amount of elements in the current `Segmentation` -/// -/// # Arguments -/// -/// * `engine` - The engine to use for retrieving data -pub fn count(engine: &Engine) -> usize { - let segmentation = match engine.segmentations.last() { - Some(n) => n, - None => return 0, - }; - segmentation.element_count() -} - -/// Make the query for retrieving items -fn make_query(engine: &Engine, range: Range) -> Query { - let mut filters = Vec::new(); - for entry in &engine.search_stack { - filters.push(Filter::Like(entry.clone())); - } - Query::Normal { - filters, - fields: vec![ - Field::SenderDomain, - Field::SenderLocalPart, - Field::Subject, - Field::Path, - Field::Timestamp, - ], - range, - } -} diff --git a/src/model/link.rs b/src/model/link.rs deleted file mode 100644 index 7ec56ad..0000000 --- a/src/model/link.rs +++ /dev/null @@ -1,163 +0,0 @@ -//! Abstraction to perform asynchronous calculations & queries without blocking UI -//! -//! This opens a `crossbeam` `channel` to communicate with a backend. -//! Each backend operation is send and retrieved in a loop on a thread. -//! This allows sending operations into `Link` and retrieving the contents -//! asynchronously without blocking the UI. - -use std::sync::{ - atomic::{AtomicUsize, Ordering}, - Arc, -}; -use std::{collections::HashSet, convert::TryInto}; - -use crossbeam_channel::{unbounded, Receiver, Sender}; -use eyre::Result; -use serde_json::Value; - -use crate::database::{ - query::Query, - query_result::{QueryResult, QueryRow}, - Database, -}; -use crate::types::Config; - -use super::types::Segmentation; - -#[derive(Debug)] -pub enum Response { - Grouped(Query, Context, Segmentation), - Normal(Query, Context, Vec), - /// FIXME: OtherQuery results are currently limited to strings as that's enough right now. - Other(Query, Context, Vec), -} - -pub(super) type InputSender = Sender<(Query, Context)>; -pub(super) type OutputReciever = Receiver>>; - -pub(super) struct Link { - pub input_sender: InputSender, - pub output_receiver: OutputReciever, - // We need to account for the brief moment where the processing channel is empty - // but we're applying the results. If there is a UI update in this window, - // the UI will not update again after the changes were applied because an empty - // channel indicates completed processing. - // There's also a delay between a request taken out of the input channel and being - // put into the output channel. In order to account for all of this, we employ a - // request counter to know how many requests are currently in the pipeline - request_counter: Arc, -} - -impl Link { - pub fn request(&mut self, query: &Query, context: Context) -> Result<()> { - self.request_counter.fetch_add(1, Ordering::Relaxed); - self.input_sender.send((query.clone(), context))?; - Ok(()) - } - - pub fn receive(&mut self) -> Result>> { - match self.output_receiver.try_recv() { - // We received something - Ok(Ok(response)) => { - // Only subtract if we successfuly received a value - self.request_counter.fetch_sub(1, Ordering::Relaxed); - Ok(Some(response)) - } - // We received nothing - Err(_) => Ok(None), - // There was an error, we forward it - Ok(Err(e)) => Err(e), - } - } - - pub fn is_processing(&self) -> bool { - self.request_counter.load(Ordering::Relaxed) > 0 - } - - /// This can be used to track the `link` from a different thread. - #[allow(unused)] - pub fn request_counter(&self) -> Arc { - self.request_counter.clone() - } -} - -pub(super) fn run(config: &Config) -> Result> { - // Create a new database connection, just for reading - let database = Database::new(&config.database_path)?; - let (input_sender, input_receiver) = unbounded(); - let (output_sender, output_receiver) = unbounded(); - let _ = std::thread::spawn(move || inner_loop(database, input_receiver, output_sender)); - Ok(Link { - input_sender, - output_receiver, - request_counter: Arc::new(AtomicUsize::new(0)), - }) -} - -fn inner_loop( - database: Database, - input_receiver: Receiver<(Query, Context)>, - output_sender: Sender>>, -) -> Result<()> { - loop { - let (query, context) = input_receiver.recv()?; - let result = database.query(&query)?; - let response = match query { - Query::Grouped { .. } => { - let segmentations = calculate_segmentations(&result)?; - Response::Grouped(query, context, segmentations) - } - Query::Normal { .. } => { - let converted = calculate_rows(&result)?; - Response::Normal(query, context, converted) - } - Query::Other { .. } => { - let mut results = HashSet::new(); - for entry in result { - match entry { - QueryResult::Other(field) => match field.value() { - Value::Array(s) => { - for n in s { - if let Value::String(s) = n { - if !results.contains(s) { - results.insert(s.to_owned()); - } - } - } - } - _ => panic!("Should not end up here"), - }, - _ => panic!("Should not end up here"), - } - } - Response::Other(query, context, results.into_iter().collect()) - } - }; - output_sender.send(Ok(response))?; - } -} - -fn calculate_segmentations(result: &[QueryResult]) -> Result { - let mut segmentations = Vec::new(); - for r in result.iter() { - let segmentation = r.try_into()?; - segmentations.push(segmentation); - } - - Ok(Segmentation::new(segmentations)) -} - -fn calculate_rows(result: &[QueryResult]) -> Result> { - Ok(result - .iter() - .map(|r| { - let values = match r { - QueryResult::Normal(values) => values, - _ => { - panic!("Invalid result type, expected `Normal`") - } - }; - values.clone() - }) - .collect()) -} diff --git a/src/model/mod.rs b/src/model/mod.rs deleted file mode 100644 index 7e702c3..0000000 --- a/src/model/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -mod engine; -pub mod items; -mod link; -pub mod segmentations; -mod types; - -pub use engine::Engine; -pub use types::Segment; diff --git a/src/model/segmentations.rs b/src/model/segmentations.rs deleted file mode 100644 index 31e30f0..0000000 --- a/src/model/segmentations.rs +++ /dev/null @@ -1,206 +0,0 @@ -//! Operations on `Segmentations` -//! -//! `Segmentations` are collections of `Segments` based on an aggregation of `Items`. -//! -//! A `Segmentation` can be changed to be aggregated on a different `Field. -//! - [`aggregations`] -//! - [`aggregated_by`] -//! - [`set_aggregation`] -//! A `Segmentation` can be changed to only return a `Range` of segments. -//! - [`current_range`] -//! - [`set_current_range`] -//! A `Segmentation` has multiple `Segments` which each can be layouted -//! to fit into a rectangle. -//! - [`layouted_segments] - -use eyre::{eyre, Result}; - -use super::engine::Action; -use super::{ - types::{Aggregation, Segment}, - Engine, -}; -use crate::database::query::{Field, Filter, Query}; -use std::ops::RangeInclusive; - -/// Filter the `Range` of segments of the current `Segmentation` -/// -/// Returns the `Range` and the total number of segments. -/// If no custom range has been set with [`set_segments_range`], returns -/// the full range of items, otherwise the custom range. -/// -/// Returns `None` if no current `Segmentation` exists. -/// -/// # Arguments -/// -/// * `engine` - The engine to use for retrieving data -/// * `aggregation` - The aggregation to return the fields for. Required to also return the current aggregation field. -pub fn segments_range(engine: &Engine) -> Option<(RangeInclusive, usize)> { - let segmentation = engine.segmentations.last()?; - let len = segmentation.len(); - Some(match &segmentation.range { - Some(n) => (0..=len, *n.end()), - None => (0..=len, len), - }) -} - -/// Set the `Range` of segments of the current `Segmentation` -/// -/// # Arguments -/// -/// * `engine` - The engine to use for setting data -/// * `range` - The range to apply. `None` to reset it to all `Segments` -pub fn set_segments_range(engine: &mut Engine, range: Option>) { - if let Some(n) = engine.segmentations.last_mut() { - // Make sure the range does not go beyond the current semgents count - if let Some(r) = range { - let len = n.len(); - if len > *r.start() && *r.end() < len { - n.range = Some(r); - } - } else { - n.range = None; - } - } -} - -/// Additional filters to use in the query -/// -/// These filters will be evaluated in addition to the `segmentation` conditions -/// in the query. -/// Setting this value will recalculate the current segmentations. -pub fn set_filters(engine: &mut Engine, filters: &[Filter]) -> Result<()> { - engine.filters = filters.to_vec(); - - // Remove any rows that were cached for this Segmentation - engine.item_cache.clear(); - engine - .link - .request(&make_query(engine)?, Action::RecalculateSegmentation) -} - -/// The fields available for the given aggregation -/// -/// As the user `pushes` Segmentations and dives into the data, -/// less fields become available to aggregate by. It is inconsequential -/// to aggregate, say, by year, then by month, and then again by year. -/// This method returns the possible fields still available for aggregation. -/// -/// # Arguments -/// -/// * `engine` - The engine to use for retrieving data -/// * `aggregation` - The aggregation to return the fields for. Required to also return the current aggregation field. -pub fn aggregation_fields(engine: &Engine, aggregation: &Aggregation) -> Vec { - #[allow(clippy::unnecessary_filter_map)] - Field::all_cases() - .filter_map(|f| { - if f == aggregation.field { - return Some(f); - } - if engine.group_by_stack.contains(&f) { - None - } else { - Some(f) - } - }) - .collect() -} - -/// Return all `Aggregation`s applied for the current `Segmentation` -/// -/// E.g. if we're first aggregating by Year, and then by Month, this -/// will return a `Vec` of `[Year, Month]`. -/// -/// # Arguments -/// -/// * `engine` - The engine to use for retrieving data -pub fn aggregated_by(engine: &Engine) -> Vec { - let mut result = Vec::new(); - // for everything in the current stack - let len = engine.group_by_stack.len(); - for (index, field) in engine.group_by_stack.iter().enumerate() { - let value = match ( - len, - engine.segmentations.get(index).map(|e| e.selected.as_ref()), - ) { - (n, Some(Some(segment))) if len == n => Some(segment.field.clone()), - _ => None, - }; - result.push(Aggregation { - value, - field: *field, - index, - }); - } - result -} - -/// Change the `Field` in the given `Aggregation` to the new one. -/// -/// The `Aggregation` will identify the `Segmentation` to use. So this function -/// can be used to change the way a `Segmentation` is the aggregated. -/// -/// Retrieve the available aggregations with [`segmentation::aggregated_by`]. -/// -/// # Arguments -/// -/// * `engine` - The engine to use for retrieving data -/// * `aggregation` - The aggregation to change -/// * `field` - The field to aggregate the `aggregation` by. -pub fn set_aggregation( - engine: &mut Engine, - aggregation: &Aggregation, - field: &Field, -) -> Result<()> { - if let Some(e) = engine.group_by_stack.get_mut(aggregation.index) { - *e = *field; - } - // Remove any rows that were cached for this Segmentation - engine.item_cache.clear(); - engine - .link - .request(&make_query(engine)?, Action::RecalculateSegmentation) -} - -/// Return the `Segment`s in the current `Segmentation`. Apply layout based on `Rect`. -/// -/// It will perform the calculations so that all segments fit into bounds. -/// The results will be applied to each `Segment`. -/// -/// Returns the layouted segments. -/// -/// # Arguments -/// -/// * `engine` - The engine to use for retrieving data -/// * `Rect` - The bounds into which the segments have to fit. -pub fn layouted_segments(engine: &mut Engine, bounds: eframe::egui::Rect) -> Option<&[Segment]> { - let segmentation = engine.segmentations.last_mut()?; - segmentation.update_layout(bounds); - Some(segmentation.items()) -} - -/// Can another level of aggregation be performed? Based on -/// [`Engine::default_group_by_stack`] -pub fn can_aggregate_more(engine: &Engine) -> bool { - let index = engine.group_by_stack.len(); - super::engine::default_group_by_stack(index).is_some() -} - -/// Perform the query that returns an aggregated `Segmentation` -pub(super) fn make_query(engine: &Engine) -> Result { - let mut filters = Vec::new(); - for entry in &engine.search_stack { - filters.push(Filter::Like(entry.clone())); - } - for entry in &engine.filters { - filters.push(entry.clone()); - } - let last = engine - .group_by_stack - .last() - .ok_or_else(|| eyre!("Invalid Segmentation state"))?; - Ok(Query::Grouped { - filters, - group_by: *last, - }) -} diff --git a/src/model/types/aggregation.rs b/src/model/types/aggregation.rs deleted file mode 100644 index b77d919..0000000 --- a/src/model/types/aggregation.rs +++ /dev/null @@ -1,27 +0,0 @@ -use crate::database::query::{Field, ValueField}; - -/// A aggregation field. -/// Contains the `Field` to aggregate by, the `Value` used for aggregation -/// As well as the index in the stack of Segmentations that this relates to. -pub struct Aggregation { - pub(in super::super) value: Option, - pub(in super::super) field: Field, - pub(in super::super) index: usize, -} - -impl Aggregation { - /// Return the value in this aggregation as a string - pub fn value(&self) -> Option { - self.value.as_ref().map(|e| e.value().to_string()) - } - - /// The name of the field as a `String` - pub fn name(&self) -> &str { - self.field.name() - } - - /// The indes of the field within the given fields - pub fn index(&self, in_fields: &[Field]) -> Option { - in_fields.iter().position(|p| p == &self.field) - } -} diff --git a/src/model/types/loading_state.rs b/src/model/types/loading_state.rs deleted file mode 100644 index 4bfbb83..0000000 --- a/src/model/types/loading_state.rs +++ /dev/null @@ -1,8 +0,0 @@ -use crate::database::query_result::QueryRow; - -/// Is a individual row/item being loaded or already loaded. -/// Used in a cache to improve the loading of data for the UI. -pub enum LoadingState { - Loaded(QueryRow), - Loading, -} diff --git a/src/model/types/mod.rs b/src/model/types/mod.rs deleted file mode 100644 index 31359ff..0000000 --- a/src/model/types/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -mod aggregation; -mod loading_state; -mod segment; -mod segmentation; - -pub use aggregation::Aggregation; -pub use loading_state::LoadingState; -pub use segment::*; -pub use segmentation::*; diff --git a/src/model/types/segment.rs b/src/model/types/segment.rs deleted file mode 100644 index 1ba583c..0000000 --- a/src/model/types/segment.rs +++ /dev/null @@ -1,61 +0,0 @@ -use std::convert::TryFrom; - -use eframe::egui::Rect as EguiRect; -use eyre::{Report, Result}; -use treemap::{Mappable, Rect}; - -use crate::database::{query::ValueField, query_result::QueryResult}; - -#[derive(Debug, Clone)] -pub struct Segment { - pub field: ValueField, - pub count: usize, - /// A TreeMap Rect - pub rect: Rect, -} - -impl Segment { - /// Perform rect conversion from TreeMap to Egui - pub fn layout_rect(&self) -> EguiRect { - use eframe::egui::pos2; - EguiRect { - min: pos2(self.rect.x as f32, self.rect.y as f32), - max: pos2( - self.rect.x as f32 + self.rect.w as f32, - self.rect.y as f32 + self.rect.h as f32, - ), - } - } -} - -impl Mappable for Segment { - fn size(&self) -> f64 { - self.count as f64 - } - - fn bounds(&self) -> &Rect { - &self.rect - } - - fn set_bounds(&mut self, bounds: Rect) { - self.rect = bounds; - } -} - -impl<'a> TryFrom<&'a QueryResult> for Segment { - type Error = Report; - fn try_from(result: &'a QueryResult) -> Result { - let (count, field) = match result { - QueryResult::Grouped { count, value } => (count, value), - _ => return Err(eyre::eyre!("Invalid result type, expected `Grouped`")), - }; - // so far we can only support one group by at a time. - // at least in here. The queries support it - - Ok(Segment { - field: field.clone(), - count: *count, - rect: Rect::new(), - }) - } -} diff --git a/src/model/types/segmentation.rs b/src/model/types/segmentation.rs deleted file mode 100644 index 09bac08..0000000 --- a/src/model/types/segmentation.rs +++ /dev/null @@ -1,58 +0,0 @@ -use eframe::egui::Rect as EguiRect; -use treemap::{Rect, TreemapLayout}; - -use super::segment::Segment; - -/// A small NewType so that we can keep all the `TreeMap` code in here and don't -/// have to do the layout calculation in a widget. -#[derive(Debug)] -pub struct Segmentation { - items: Vec, - pub selected: Option, - pub range: Option>, -} - -impl Segmentation { - pub fn new(items: Vec) -> Self { - Self { - items, - selected: None, - range: None, - } - } - - pub fn len(&self) -> usize { - self.items.len() - } - - /// Update the layout information in the Segments - /// based on the current size - pub fn update_layout(&mut self, rect: EguiRect) { - let layout = TreemapLayout::new(); - let bounds = Rect::from_points( - rect.left() as f64, - rect.top() as f64, - rect.width() as f64, - rect.height() as f64, - ); - layout.layout_items(self.items(), bounds); - } - - /// The total amount of items in all the `Segments`. - /// E.g. the sum of the count of the `Segments` - pub fn element_count(&self) -> usize { - self.items.iter().map(|e| e.count).sum::() - } - - /// The items in this `Segmentation`, with range applied - pub fn items(&mut self) -> &mut [Segment] { - match &self.range { - Some(n) => { - // we reverse the range - let reversed_range = (self.len() - n.end())..=(self.len() - 1); - &mut self.items[reversed_range] - } - None => self.items.as_mut_slice(), - } - } -} diff --git a/src/types/config.rs b/src/types/config.rs deleted file mode 100644 index 7800c24..0000000 --- a/src/types/config.rs +++ /dev/null @@ -1,200 +0,0 @@ -use eyre::{eyre, Result}; -use rand::Rng; -use serde_json::Value; -use strum::{self, IntoEnumIterator}; -use strum_macros::{EnumIter, IntoStaticStr}; - -use std::collections::{HashMap, HashSet}; -use std::iter::FromIterator; -use std::path::{Path, PathBuf}; -use std::str::FromStr; - -#[derive(Debug, Clone, Copy, PartialEq, Eq, IntoStaticStr, EnumIter)] -pub enum FormatType { - AppleMail, - GmailVault, - Mbox, -} - -impl FormatType { - pub fn all_cases() -> impl Iterator { - FormatType::iter() - } - - pub fn name(&self) -> &'static str { - match self { - FormatType::AppleMail => "Apple Mail", - FormatType::GmailVault => "Gmail Vault Download", - FormatType::Mbox => "Mbox", - } - } - - /// Forward the importer format location - pub fn default_path(&self) -> Option { - use crate::importer::formats::{self, ImporterFormat}; - match self { - FormatType::AppleMail => formats::AppleMail::default_path(), - FormatType::GmailVault => formats::Gmail::default_path(), - FormatType::Mbox => formats::Mbox::default_path(), - } - } -} - -impl Default for FormatType { - /// We return a different default, based on the platform we're on - /// FIXME: We don't have support for Outlook yet, so on windows we go with Mbox as well - fn default() -> Self { - #[cfg(target_os = "macos")] - return FormatType::AppleMail; - - #[cfg(not(target_os = "macos"))] - return FormatType::Mbox; - } -} - -impl From<&String> for FormatType { - fn from(format: &String) -> Self { - FormatType::from(format.as_str()) - } -} - -impl From<&str> for FormatType { - fn from(format: &str) -> Self { - match format { - "apple" => FormatType::AppleMail, - "gmailvault" => FormatType::GmailVault, - "mbox" => FormatType::Mbox, - _ => panic!("Unknown format: {}", &format), - } - } -} - -impl From for String { - fn from(format: FormatType) -> Self { - match format { - FormatType::AppleMail => "apple".to_owned(), - FormatType::GmailVault => "gmailvault".to_owned(), - FormatType::Mbox => "mbox".to_owned(), - } - } -} - -#[derive(Debug, Clone)] -pub struct Config { - /// The path to where the database should be stored - pub database_path: PathBuf, - /// The path where the emails are - pub emails_folder_path: PathBuf, - /// The addresses used to send emails - pub sender_emails: HashSet, - /// The importer format we're using - pub format: FormatType, - /// Did the user intend to keep the database - /// (e.g. is the database path temporary?) - pub persistent: bool, -} - -impl Config { - /// Construct a config from a hashmap of field values. - /// For missing fields, take a reasonable default value, - /// in order to be somewhat backwards compatible. - pub fn from_fields>(path: P, fields: HashMap) -> Result { - // The following fields are of version 1.0, so they should aways exist - let emails_folder_path_str = fields - .get("emails_folder_path") - .ok_or_else(|| eyre!("Missing config field emails_folder_path"))? - .as_str() - .ok_or_else(|| eyre!("Invalid field type for emails_folder_path"))?; - let emails_folder_path = PathBuf::from_str(emails_folder_path_str).map_err(|e| { - eyre!( - "Invalid emails_folder_path: {}: {}", - &emails_folder_path_str, - e - ) - })?; - #[allow(clippy::needless_collect)] - let sender_emails: Vec = fields - .get("sender_emails") - .map(|v| v.as_str().map(|e| e.to_string())) - .flatten() - .ok_or_else(|| eyre!("Missing config field sender_emails"))? - .split(',') - .map(|e| e.trim().to_owned()) - .collect(); - let format = fields - .get("format") - .map(|e| e.as_str()) - .flatten() - .map(FormatType::from) - .ok_or_else(|| eyre!("Missing config field format_type"))?; - let persistent = fields - .get("persistent") - .map(|e| e.as_bool()) - .flatten() - .ok_or_else(|| eyre!("Missing config field persistent"))?; - Ok(Config { - database_path: path.as_ref().to_path_buf(), - emails_folder_path, - sender_emails: HashSet::from_iter(sender_emails.into_iter()), - format, - persistent, - }) - } - - pub fn new>( - db: Option, - mails: A, - sender_emails: Vec, - format: FormatType, - ) -> eyre::Result { - // If we don't have a database path, we use a temporary folder. - let persistent = db.is_some(); - let database_path = match db { - Some(n) => n.as_ref().to_path_buf(), - None => { - let number: u32 = rand::thread_rng().gen(); - let folder = "postsack"; - let filename = format!("{}.sqlite", number); - let mut temp_dir = std::env::temp_dir(); - temp_dir.push(folder); - // the folder has to be created - std::fs::create_dir_all(&temp_dir)?; - temp_dir.push(filename); - temp_dir - } - }; - Ok(Config { - database_path, - emails_folder_path: mails.as_ref().to_path_buf(), - sender_emails: HashSet::from_iter(sender_emails.into_iter()), - format, - persistent, - }) - } - - pub fn into_fields(&self) -> Option> { - let mut new = HashMap::new(); - new.insert( - "database_path".to_owned(), - self.database_path.to_str()?.into(), - ); - new.insert( - "emails_folder_path".to_owned(), - self.emails_folder_path.to_str()?.into(), - ); - new.insert("persistent".to_owned(), self.persistent.into()); - new.insert( - "sender_emails".to_owned(), - self.sender_emails - .iter() - .cloned() - .collect::>() - .join(",") - .into(), - ); - let format: String = self.format.into(); - new.insert("format".to_owned(), format.into()); - - Some(new) - } -} diff --git a/src/types/mod.rs b/src/types/mod.rs deleted file mode 100644 index 3c149b7..0000000 --- a/src/types/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod config; - -pub use config::{Config, FormatType};