Merge pull request #7 from terhechte/feature/workspace

Split up into Cargo Workspaces
pull/12/head
Benedikt Terhechte 2 years ago committed by GitHub
commit 93cbcea02a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

1
.gitignore vendored

@ -1,2 +1,3 @@
/target
target
.DS_Store

558
Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -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"

@ -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,9 +14,14 @@ 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?
- [ ] Demo Video
- [ ] Documentation
- [ ] wasm build?
## Windows Issues

@ -0,0 +1,3 @@
/target
target
.DS_Store

2633
postsack/Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -0,0 +1,28 @@
[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" }
ps-importer = { path = "../ps-importer" }
ps-database = { path = "../ps-database" }
#[profile.release]
#lto = "fat"
#codegen-units = 1
#panic = "abort"

@ -0,0 +1,6 @@
fn main() {
#[cfg(debug_assertions)]
ps_core::setup_tracing();
ps_gui::run_ui();
}

@ -1,12 +1,10 @@
use eframe::egui;
use postsack::{
use ps_core::{
self,
database::query::{Field, Filter, ValueField},
importer::Importerlike,
model::{self, Engine},
types::Config,
types::FormatType,
model::{self, Engine, Rect},
Config, DatabaseLike, Field, Filter, FormatType, Importerlike, ValueField,
};
use ps_database::Database;
use ps_importer::mbox_importer;
#[cfg(test)]
mod tests {
@ -20,7 +18,7 @@ mod tests {
if std::env::var("RUST_LOG").is_err() {
std::env::set_var("RUST_LOG", "trace");
}
postsack::setup_tracing();
ps_core::setup_tracing();
});
}
@ -28,7 +26,7 @@ mod tests {
fn test_engine_all() {
initialize();
let config = create_database();
let mut engine = Engine::new(&config).expect("Expected Engine");
let mut engine = Engine::new::<Database>(&config).expect("Expected Engine");
engine.start().expect("Expect to start engine");
engine.wait().expect("Expected working wait");
let segment = {
@ -85,19 +83,21 @@ mod tests {
}
}
fn default_rect() -> egui::Rect {
egui::Rect::from_min_size(
egui::Pos2 { x: 50.0, y: 50.0 },
egui::Vec2 { x: 500.0, y: 500.0 },
)
fn default_rect() -> Rect {
Rect {
left: 50.0,
top: 50.0,
width: 500.0,
height: 500.0,
}
}
fn create_database() -> Config {
let path = "tests/resources/mbox";
let config = postsack::types::Config::new(None, path, vec!["".to_string()], FormatType::Mbox)
.expect("Config");
let importer = postsack::importer::mbox_importer(config.clone());
let (_receiver, handle) = importer.import().unwrap();
let config = Config::new(None, path, vec!["".to_string()], FormatType::Mbox).expect("Config");
let importer = mbox_importer(config.clone());
let database = Database::new(&config.database_path).unwrap();
let (_receiver, handle) = importer.import(database).unwrap();
handle.join().expect("").expect("");
config
}

@ -1,14 +1,10 @@
use postsack::{
self,
database::{query, Database},
importer::Importerlike,
types::FormatType,
};
use ps_core::{self, DatabaseLike, FormatType, Importerlike};
use ps_database::Database;
use ps_importer;
#[cfg(test)]
mod tests {
use postsack::database::{query::Field, query_result::QueryResult};
use ps_core::{Config, Field, Query, QueryResult};
use std::sync::Once;
use super::*;
@ -20,7 +16,7 @@ mod tests {
if std::env::var("RUST_LOG").is_err() {
std::env::set_var("RUST_LOG", "trace");
}
postsack::setup_tracing();
ps_core::setup_tracing();
});
}
@ -30,20 +26,19 @@ mod tests {
initialize();
let path = "tests/resources/mbox";
let config =
postsack::types::Config::new(None, path, vec!["".to_string()], FormatType::Mbox)
.expect("Config");
let importer = postsack::importer::mbox_importer(config.clone());
let (_receiver, handle) = importer.import().unwrap();
Config::new(None, path, vec!["".to_string()], FormatType::Mbox).expect("Config");
let importer = ps_importer::mbox_importer(config.clone());
let database = Database::new(&config.database_path).unwrap();
let (_receiver, handle) = importer.import(database).unwrap();
handle.join().expect("").expect("");
// The temporary database path
let db = Database::new(&config.database_path).unwrap();
let total_mails = db.total_mails().expect("Expected total mails");
assert_eq!(total_mails, 141);
let mails = db.query(&query::Query::Normal {
fields: vec![query::Field::Subject],
let mails = db.query(&Query::Normal {
fields: vec![Field::Subject],
filters: Vec::new(),
range: 0..141,
});
@ -69,15 +64,17 @@ mod tests {
initialize();
let path = "tests/resources/applemail";
let config =
postsack::types::Config::new(None, path, vec!["".to_string()], FormatType::AppleMail)
.expect("Config");
let importer = postsack::importer::applemail_importer(config.clone());
let (_receiver, handle) = importer.import().unwrap();
Config::new(None, path, vec!["".to_string()], FormatType::AppleMail).expect("Config");
let importer = ps_importer::applemail_importer(config.clone());
let (_receiver, handle) = importer
.import(Database::new(&config.database_path).unwrap())
.unwrap();
handle.join().expect("").expect("");
// The temporary database path
let db = Database::new(&config.database_path).unwrap();
let mails = db.query(&query::Query::Normal {
fields: vec![query::Field::Subject],
let mails = db.query(&Query::Normal {
fields: vec![Field::Subject],
filters: Vec::new(),
range: 0..10,
});

@ -0,0 +1,3 @@
/target
target
.DS_Store

592
ps-core/Cargo.lock generated

@ -0,0 +1,592 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[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 = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
]
[[package]]
name = "autocfg"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[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",
"winapi",
]
[[package]]
name = "crc32fast"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "738c290dfaea84fc1ca15ad9c168d083b05a714e1efddd8edaab678dc28d2836"
dependencies = [
"cfg-if",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4"
dependencies = [
"cfg-if",
"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",
"lazy_static",
]
[[package]]
name = "dirs-next"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
dependencies = [
"cfg-if",
"dirs-sys-next",
]
[[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 = "eyre"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "221239d1d5ea86bf5d6f91c9d6bc3646ffe471b08ff9b0f91c44f115ac969d2b"
dependencies = [
"indenter",
"once_cell",
]
[[package]]
name = "flate2"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
dependencies = [
"cfg-if",
"crc32fast",
"libc",
"miniz_oxide",
]
[[package]]
name = "getrandom"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "hashbrown"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
dependencies = [
"ahash",
]
[[package]]
name = "heck"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "indenter"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
[[package]]
name = "itoa"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
[[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 = "log"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if",
]
[[package]]
name = "lru"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c748cfe47cb8da225c37595b3108bea1c198c84aaae8ea0ba76d01dda9fc803"
dependencies = [
"hashbrown",
]
[[package]]
name = "memchr"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[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 = "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-traits"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
[[package]]
name = "pin-project-lite"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443"
[[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 = "quote"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
dependencies = [
"proc-macro2",
]
[[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 = "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 = "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",
"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 = "shellexpand"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83bdb7831b2d85ddf4a7b148aa19d0587eddbe8671a436b7bd1182eaad0f2829"
dependencies = [
"dirs-next",
]
[[package]]
name = "smallvec"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309"
[[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 = "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 = "tracing"
version = "0.1.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105"
dependencies = [
"cfg-if",
"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 = "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 = "version_check"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[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-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

@ -0,0 +1,33 @@
[package]
name = "ps-core"
version = "0.2.0"
edition = "2021"
description = "Provides a high level visual overview of swaths of email"
[dependencies]
eyre = "0.6.5"
thiserror = "1.0.29"
tracing = "0.1.29"
tracing-subscriber = "0.3.0"
regex = "1.5.3"
flate2 = "1.0.22"
once_cell = "1.8.0"
chrono = "0.4.19"
serde_json = "1.0.70"
serde = { version = "1.0.131", features = ["derive"]}
crossbeam-channel = "0.5.1"
rsql_builder = "0.1.2"
treemap = "0.3.2"
strum = "0.23.0"
strum_macros = "0.23.0"
lru = { version = "0.7.0", optional = true }
shellexpand = "2.1.0"
rand = "0.8.4"
[target."cfg(target_arch = \"wasm32\")".dependencies]
# https://docs.rs/getrandom/latest/getrandom/#webassembly-support
getrandom = { version = "0.2", features = ["js"] }
[features]
default = ["lru"]

@ -0,0 +1,19 @@
use std::path::Path;
use std::thread::JoinHandle;
use crossbeam_channel::Sender;
use eyre::Result;
use crate::Config;
use super::{db_message::DBMessage, query::Query, query_result::QueryResult};
pub trait DatabaseLike: Clone + Send {
fn new(path: impl AsRef<Path>) -> Result<Self>
where
Self: Sized;
fn total_mails(&self) -> Result<usize>;
fn query(&self, query: &Query) -> Result<Vec<QueryResult>>;
fn import(self) -> (Sender<DBMessage>, JoinHandle<Result<usize>>);
fn save_config(&self, config: Config) -> Result<()>;
}

@ -1,6 +1,6 @@
use eyre::Report;
use crate::importer::EmailEntry;
use crate::EmailEntry;
/// Parameter for sending work to the database during `import`.
pub enum DBMessage {

@ -0,0 +1,4 @@
pub mod database_like;
pub mod db_message;
pub mod query;
pub mod query_result;

@ -1,16 +1,15 @@
use crossbeam_channel;
use eyre::Result;
use std::thread::JoinHandle;
pub(crate) mod formats;
#[allow(clippy::module_inception)]
mod importer;
mod message_adapter;
use crate::DatabaseLike;
use crate::types::Config;
pub use formats::shared::email::{EmailEntry, EmailMeta};
pub use importer::Importerlike;
pub use message_adapter::*;
use formats::ImporterFormat;
pub trait Importerlike {
fn import<Database: DatabaseLike + 'static>(
self,
database: Database,
) -> Result<(MessageReceiver, JoinHandle<Result<()>>)>;
}
/// The message that informs of the importers progress
#[derive(Debug)]
@ -44,24 +43,3 @@ pub enum Message {
pub type MessageSender = crossbeam_channel::Sender<Message>;
pub type MessageReceiver = crossbeam_channel::Receiver<Message>;
pub fn importer(config: &Config) -> Box<dyn importer::Importerlike> {
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<formats::Gmail> {
importer::Importer::new(config, formats::Gmail::default())
}
pub fn applemail_importer(config: Config) -> importer::Importer<formats::AppleMail> {
importer::Importer::new(config, formats::AppleMail::default())
}
pub fn mbox_importer(config: Config) -> importer::Importer<formats::Mbox> {
importer::Importer::new(config, formats::Mbox::default())
}

@ -0,0 +1,29 @@
mod database;
mod importer;
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, 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");
}

@ -3,9 +3,7 @@ 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;
use crate::{DatabaseLike, Importerlike, Message};
#[derive(Debug, Default)]
struct Data {
@ -44,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?
@ -60,11 +57,12 @@ 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<Format: ImporterFormat + 'static>(
pub fn process<Database: DatabaseLike + 'static, Importer: Importerlike + 'static>(
&self,
importer: super::importer::Importer<Format>,
database: Database,
importer: Importer,
) -> Result<JoinHandle<Result<()>>> {
let (receiver, handle) = importer.import()?;
let (receiver, handle) = importer.import(database)?;
let lock = self.producer_lock.clone();
let handle = std::thread::spawn(move || {
'outer: loop {

@ -14,6 +14,7 @@ use crate::types::Config;
use super::link::Link;
use super::segmentations;
use super::types::{LoadingState, Segment, Segmentation};
use crate::database::database_like::DatabaseLike;
/// This signifies the action we're currently evaluating
/// It is used for sending requests and receiving responses
@ -47,8 +48,8 @@ pub struct Engine {
}
impl Engine {
pub fn new(config: &Config) -> Result<Self> {
let link = super::link::run(config)?;
pub fn new<Database: DatabaseLike + 'static>(config: &Config) -> Result<Self> {
let link = super::link::run::<_, Database>(config)?;
let engine = Engine {
link,
search_stack: Vec::new(),

@ -16,9 +16,9 @@ use eyre::Result;
use serde_json::Value;
use crate::database::{
database_like::DatabaseLike,
query::Query,
query_result::{QueryResult, QueryRow},
Database,
};
use crate::types::Config;
@ -81,9 +81,11 @@ impl<Context: Send + Sync + 'static> Link<Context> {
}
}
pub(super) fn run<Context: Send + Sync + 'static>(config: &Config) -> Result<Link<Context>> {
pub(super) fn run<Context: Send + Sync + 'static, Database: DatabaseLike + 'static>(
config: &Config,
) -> Result<Link<Context>> {
// 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));
@ -94,7 +96,7 @@ pub(super) fn run<Context: Send + Sync + 'static>(config: &Config) -> Result<Lin
})
}
fn inner_loop<Context: Send + Sync + 'static>(
fn inner_loop<Context: Send + Sync + 'static, Database: DatabaseLike>(
database: Database,
input_receiver: Receiver<(Query, Context)>,
output_sender: Sender<Result<Response<Context>>>,

@ -5,4 +5,4 @@ pub mod segmentations;
mod types;
pub use engine::Engine;
pub use types::Segment;
pub use types::{Rect, Segment};

@ -17,7 +17,7 @@ use eyre::{eyre, Result};
use super::engine::Action;
use super::{
types::{Aggregation, Segment},
types::{self, Aggregation, Segment},
Engine,
};
use crate::database::query::{Field, Filter, Query};
@ -173,7 +173,7 @@ pub fn set_aggregation(
///
/// * `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]> {
pub fn layouted_segments(engine: &mut Engine, bounds: types::Rect) -> Option<&[Segment]> {
let segmentation = engine.segmentations.last_mut()?;
segmentation.update_layout(bounds);
Some(segmentation.items())

@ -1,9 +1,11 @@
mod aggregation;
mod loading_state;
mod rect;
mod segment;
mod segmentation;
pub use aggregation::Aggregation;
pub use loading_state::LoadingState;
pub use rect::Rect;
pub use segment::*;
pub use segmentation::*;

@ -0,0 +1,18 @@
/// Sort of mirror `egui::rect` for simplicity
pub struct Rect {
pub left: f64,
pub top: f64,
pub width: f64,
pub height: f64,
}
impl Rect {
pub fn new(min: (f64, f64), max: (f64, f64)) -> Rect {
Rect {
left: min.0,
top: min.1,
width: max.0 - min.0,
height: max.1 - min.1,
}
}
}

@ -1,8 +1,8 @@
use super::Rect;
use std::convert::TryFrom;
use eframe::egui::Rect as EguiRect;
use eyre::{Report, Result};
use treemap::{Mappable, Rect};
use treemap::{self, Mappable};
use crate::database::{query::ValueField, query_result::QueryResult};
@ -11,20 +11,16 @@ pub struct Segment {
pub field: ValueField,
pub count: usize,
/// A TreeMap Rect
pub rect: Rect,
pub rect: treemap::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,
),
}
/// Perform rect conversion from TreeMap to the public type
pub fn layout_rect(&self) -> Rect {
Rect::new(
(self.rect.x, self.rect.y),
(self.rect.x + self.rect.w, self.rect.y + self.rect.h),
)
}
}
@ -33,11 +29,11 @@ impl Mappable for Segment {
self.count as f64
}
fn bounds(&self) -> &Rect {
fn bounds(&self) -> &treemap::Rect {
&self.rect
}
fn set_bounds(&mut self, bounds: Rect) {
fn set_bounds(&mut self, bounds: treemap::Rect) {
self.rect = bounds;
}
}
@ -55,7 +51,7 @@ impl<'a> TryFrom<&'a QueryResult> for Segment {
Ok(Segment {
field: field.clone(),
count: *count,
rect: Rect::new(),
rect: treemap::Rect::new(),
})
}
}

@ -1,7 +1,7 @@
use eframe::egui::Rect as EguiRect;
use treemap::{Rect, TreemapLayout};
use treemap::{self, TreemapLayout};
use super::segment::Segment;
use super::Rect;
/// 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.
@ -27,14 +27,9 @@ impl Segmentation {
/// Update the layout information in the Segments
/// based on the current size
pub fn update_layout(&mut self, rect: EguiRect) {
pub fn update_layout(&mut self, rect: Rect) {
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,
);
let bounds = treemap::Rect::from_points(rect.left, rect.top, rect.width, rect.height);
layout.layout_items(self.items(), bounds);
}

@ -1,5 +1,4 @@
use eyre::{eyre, Result};
use rand::Rng;
use serde_json::Value;
use strum::{self, IntoEnumIterator};
use strum_macros::{EnumIter, IntoStaticStr};
@ -9,6 +8,9 @@ use std::iter::FromIterator;
use std::path::{Path, PathBuf};
use std::str::FromStr;
// FIXME: This abstraction should be in the `ps-importer` crate with only
// a protocol here.
#[derive(Debug, Clone, Copy, PartialEq, Eq, IntoStaticStr, EnumIter)]
pub enum FormatType {
AppleMail,
@ -28,16 +30,6 @@ impl FormatType {
FormatType::Mbox => "Mbox",
}
}
/// Forward the importer format location
pub fn default_path(&self) -> Option<PathBuf> {
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 {
@ -152,7 +144,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 = random_filename();
let folder = "postsack";
let filename = format!("{}.sqlite", number);
let mut temp_dir = std::env::temp_dir();
@ -198,3 +190,11 @@ impl Config {
Some(new)
}
}
fn random_filename() -> String {
use rand::Rng;
let number: u32 = rand::thread_rng().gen();
let folder = "postsack";
let filename = format!("{}.sqlite", number);
return filename;
}

@ -0,0 +1,10 @@
// use std::default::Default;
// use std::path::PathBuf;
// pub trait ImporterFormatType: Default + Clone + Copy + Eq {
// // fn all_formats() -> Vec<Self>
// // where
// // Self: Sized;
// fn name(&self) -> &'static str;
// fn default_path(&self) -> Option<PathBuf>;
// }

@ -0,0 +1,5 @@
mod config;
mod email;
mod format_type;
pub use config::{Config, FormatType};
pub use email::{EmailEntry, EmailMeta};

@ -0,0 +1,3 @@
/target
target
.DS_Store

@ -0,0 +1,675 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[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 = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
]
[[package]]
name = "autocfg"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "cc"
version = "1.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[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",
"winapi",
]
[[package]]
name = "crc32fast"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "738c290dfaea84fc1ca15ad9c168d083b05a714e1efddd8edaab678dc28d2836"
dependencies = [
"cfg-if",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4"
dependencies = [
"cfg-if",
"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",
"lazy_static",
]
[[package]]
name = "dirs-next"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
dependencies = [
"cfg-if",
"dirs-sys-next",
]
[[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 = "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",
"crc32fast",
"libc",
"miniz_oxide",
]
[[package]]
name = "getrandom"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[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 = "indenter"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
[[package]]
name = "itoa"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
[[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 = "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 = "log"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if",
]
[[package]]
name = "lru"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c748cfe47cb8da225c37595b3108bea1c198c84aaae8ea0ba76d01dda9fc803"
dependencies = [
"hashbrown",
]
[[package]]
name = "memchr"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[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 = "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-traits"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
[[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 = "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 = "quote"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
dependencies = [
"proc-macro2",
]
[[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 = "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 = "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",
"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 = "shellexpand"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83bdb7831b2d85ddf4a7b148aa19d0587eddbe8671a436b7bd1182eaad0f2829"
dependencies = [
"dirs-next",
]
[[package]]
name = "smallvec"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309"
[[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 = "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 = "tracing"
version = "0.1.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105"
dependencies = [
"cfg-if",
"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 = "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 = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[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-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

@ -0,0 +1,16 @@
[package]
name = "ps-database"
version = "0.2.0"
edition = "2021"
[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"]}
chrono = "0.4.19"
serde_json = "1.0.70"
serde = { version = "1.0.130", features = ["derive"]}
rsql_builder = "0.1.2"
ps-core = { path = "../ps-core" }

@ -1,15 +1,11 @@
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};
use ps_core::{EmailMeta, Field, QueryResult, ValueField, AMOUNT_FIELD_NAME};
/// rusqlite does offer Serde to Value conversion, but it
/// converts everything to strings!
@ -82,8 +78,7 @@ pub fn value_from_field<'stmt>(field: &Field, row: &Row<'stmt>) -> Result<ValueF
}
MetaTags => {
let tag_string = row.get::<&str, String>(field.as_str())?;
let tags =
crate::importer::formats::shared::email::EmailMeta::tags_from_string(&tag_string);
let tags = EmailMeta::tags_from_string(&tag_string);
Ok(ValueField::array(
field,
tags.into_iter().map(Value::String).collect(),
@ -97,49 +92,3 @@ pub fn value_from_field<'stmt>(field: &Field, row: &Row<'stmt>) -> Result<ValueF
}
}
}
impl EmailEntry {
#[allow(unused)]
fn from_row(row: &Row<'_>) -> Result<Self> {
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<String> = row.get("to_group")?;
let to_name: Option<String> = row.get("to_name")?;
let to_address: Option<String> = 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<String> = row.get("meta_tags")?;
let meta_is_seen: Option<bool> = 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,
})
}
}

@ -1,34 +1,33 @@
use chrono::Datelike;
use crossbeam_channel::{unbounded, Sender};
use eyre::{bail, Report, Result};
use rusqlite::{self, params, Connection, Statement};
use core::panic;
use std::path::PathBuf;
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,
use super::sql::*;
use super::{value_from_field, RowConversion};
use ps_core::{
crossbeam_channel::{unbounded, Sender},
Config, DBMessage, DatabaseLike, EmailEntry, OtherQuery, Query, QueryResult,
};
#[derive(Debug)]
pub struct Database {
connection: Option<Connection>,
path: PathBuf,
}
impl Database {
/// Open a database and try to retrieve a config from the information stored in there
pub fn config<P: AsRef<Path>>(path: P) -> Result<Config> {
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<P: AsRef<Path>>(path: P) -> Result<Self> {
fn new(path: impl AsRef<Path>) -> Result<Self> {
#[allow(unused_mut)]
let mut connection = Connection::open(path.as_ref())?;
@ -45,10 +44,11 @@ impl Database {
Ok(Database {
connection: Some(connection),
path: path.as_ref().into(),
})
}
pub fn total_mails(&self) -> Result<usize> {
fn total_mails(&self) -> Result<usize> {
let connection = match &self.connection {
Some(n) => n,
None => bail!("No connection to database available in query"),
@ -58,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: &super::query::Query) -> Result<Vec<QueryResult>> {
fn query(&self, query: &Query) -> Result<Vec<QueryResult>> {
use rusqlite::params_from_iter;
let c = match &self.connection {
Some(n) => n,
@ -116,7 +116,7 @@ impl Database {
/// sender.send(DBMessage::Mail(m2)).unwrap();
/// handle.join().unwrap();
/// ```
pub fn import(mut self) -> (Sender<DBMessage>, JoinHandle<Result<usize>>) {
fn import(mut self) -> (Sender<DBMessage>, JoinHandle<Result<usize>>) {
let (sender, receiver) = unbounded();
// Import can only be called *once* on a database created with `new`.
@ -168,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<P: AsRef<Path>>(path: P) -> Result<Config> {
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![])?;

@ -1,10 +1,6 @@
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;

3
ps-gui/.gitignore vendored

@ -0,0 +1,3 @@
/target
target
.DS_Store

2625
ps-gui/Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -0,0 +1,35 @@
[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"
rand = "0.8.4"
image = { version = "0.23", default-features = false, features = ["png"] }
chrono = "0.4.19"
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" }
ps-database = { path = "../ps-database" }
shellexpand = "2.1.0"
tinyfiledialogs = "3.0"
[target."cfg(target_arch = \"wasm32\")".dependencies]
# https://docs.rs/getrandom/latest/getrandom/#webassembly-support
getrandom = { version = "0.2", features = ["js"] }

@ -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

@ -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

@ -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

@ -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<Self> {
pub fn new<Database: DatabaseLike + 'static>(
config: Config,
database: Database,
) -> Result<Self> {
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)?
}
};

@ -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<Self> {
let mut engine = Engine::new(&config)?;
let mut engine = Engine::new::<Database>(&config)?;
engine.start()?;
Ok(Self {
config,

@ -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)));

@ -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 {
@ -155,7 +155,7 @@ impl StartupUI {
self.open_email_folder_dialog()
}
if self.format == FormatType::AppleMail && ui.button("or Mail.app default folder").clicked(){
self.email_folder = self.format.default_path();
self.email_folder = ps_importer::default_path(&self.format);
}
});
ui.end_row();
@ -302,9 +302,7 @@ impl StartupUI {
fn open_email_folder_dialog(&mut self) {
let fallback = shellexpand::tilde("~/");
let default_path = self
.format
.default_path()
let default_path = ps_importer::default_path(&self.format)
.unwrap_or_else(|| std::path::Path::new(&fallback.to_string()).to_path_buf());
let folder = match tinyfiledialogs::select_folder_dialog(

@ -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);
}

@ -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;

@ -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;

@ -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};

Before

Width:  |  Height:  |  Size: 515 KiB

After

Width:  |  Height:  |  Size: 515 KiB

@ -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,

@ -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`,

@ -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) {

@ -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<String> = 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,
},
)
}

@ -0,0 +1,3 @@
/target
target
.DS_Store

@ -0,0 +1,956 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[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 = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
]
[[package]]
name = "autocfg"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[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 = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[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 = "crc32fast"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "738c290dfaea84fc1ca15ad9c168d083b05a714e1efddd8edaab678dc28d2836"
dependencies = [
"cfg-if",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4"
dependencies = [
"cfg-if",
"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"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db"
dependencies = [
"cfg-if",
"lazy_static",
]
[[package]]
name = "dirs-next"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
dependencies = [
"cfg-if",
"dirs-sys-next",
]
[[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 = "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 = "emlx"
version = "0.1.5"
source = "git+https://github.com/terhechte/emlx#44c2f278551d9e7a9ae0c3c3207c4471da3049fe"
dependencies = [
"email-parser",
"plist",
"thiserror",
]
[[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 = "flate2"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
dependencies = [
"cfg-if",
"crc32fast",
"libc",
"miniz_oxide",
]
[[package]]
name = "getrandom"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "hashbrown"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
dependencies = [
"ahash",
]
[[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 = "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 = "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 = "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 = "line-wrap"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9"
dependencies = [
"safemem",
]
[[package]]
name = "log"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if",
]
[[package]]
name = "lru"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c748cfe47cb8da225c37595b3108bea1c198c84aaae8ea0ba76d01dda9fc803"
dependencies = [
"hashbrown",
]
[[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 = "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"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
dependencies = [
"adler",
"autocfg",
]
[[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-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 = "once_cell"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
[[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 = "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 = "ppv-lite86"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba"
[[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-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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "tracing"
version = "0.1.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105"
dependencies = [
"cfg-if",
"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 = "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 = "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 = "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 = "xml-rs"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3"

@ -0,0 +1,26 @@
[package]
name = "ps-importer"
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"
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"
emlx = { git = "https://github.com/terhechte/emlx", features = []}
walkdir = "2.3.2"
mbox-reader = "0.2.0"
rand = "0.8.4"
shellexpand = "2.1.0"
serde_json = "1.0.70"
serde = { version = "1.0.130", features = ["derive"]}
ps-core = { path = "../ps-core" }

@ -7,8 +7,7 @@ use rayon::prelude::*;
use walkdir::WalkDir;
use super::super::shared::filesystem::emails_in;
use super::super::{Message, MessageSender};
use crate::types::Config;
use ps_core::{Config, Message, MessageSender};
use super::mail::Mail;
use std::path::PathBuf;

@ -4,7 +4,8 @@ use eyre::Result;
use std::borrow::Cow;
use std::path::{Path, PathBuf};
use super::super::shared::email::EmailMeta;
use ps_core::EmailMeta;
use super::super::shared::parse::ParseableEmail;
pub struct Mail {

@ -4,7 +4,8 @@ mod mail;
use shellexpand;
use std::{path::PathBuf, str::FromStr};
use super::{Config, ImporterFormat, MessageSender, Result};
use super::{ImporterFormat, Result};
use ps_core::{Config, MessageSender};
#[derive(Default)]
pub struct AppleMail {}

@ -4,8 +4,8 @@ use eyre::{bail, Result};
use serde::Deserialize;
use serde_json;
use super::super::shared::email::EmailMeta;
use super::raw_email::RawEmailEntry;
use ps_core::EmailMeta;
#[derive(Deserialize, Debug, Clone)]
pub struct Meta {

@ -5,8 +5,8 @@ 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;
use ps_core::EmailMeta;
/// Raw representation of an email.
/// Contains the paths to the relevant files as well

@ -12,8 +12,8 @@ use walkdir::WalkDir;
use super::{Config, ImporterFormat, Message, MessageSender, Result};
use super::shared::email::EmailMeta;
use super::shared::parse::ParseableEmail;
use ps_core::EmailMeta;
use std::borrow::Cow;
use std::path::{Path, PathBuf};

@ -11,10 +11,9 @@ 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};
pub use ps_core::{Config, Message, MessageReceiver, MessageSender};
/// This is implemented by the various formats
/// to define how they return email data.

@ -1,16 +1,14 @@
use super::parse::{parse_email, ParseableEmail};
use crate::database::{DBMessage, Database};
use crate::types::Config;
use super::super::{Message, MessageSender};
use ps_core::{Config, DBMessage, DatabaseLike, Message, MessageSender};
use eyre::{bail, Result};
use rayon::prelude::*;
pub fn into_database<Mail: ParseableEmail + 'static>(
pub fn into_database<Mail: ParseableEmail + 'static, Database: DatabaseLike + 'static>(
config: &Config,
mut emails: Vec<Mail>,
tx: MessageSender,
database: Database,
) -> Result<usize> {
let total = emails.len();
tracing::info!("Loaded {} emails", &total);
@ -20,9 +18,6 @@ pub fn into_database<Mail: ParseableEmail + 'static>(
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);

@ -4,7 +4,7 @@ use tracing::trace;
use std::path::{Path, PathBuf};
use super::super::{Message, MessageSender};
use ps_core::{Message, MessageSender};
/// Call `FolderAction` on all files in all sub folders in
/// folder `folder`.

@ -1,4 +1,3 @@
pub mod database;
pub mod email;
pub mod filesystem;
pub mod parse;

@ -6,7 +6,7 @@ use std::borrow::Cow;
use std::collections::HashSet;
use std::path::Path;
use super::email::{EmailEntry, EmailMeta};
use ps_core::{EmailEntry, EmailMeta};
/// Different `importer`s can implement this trait to provide the necessary
/// data to parse their data into a `EmailEntry`.

@ -1,15 +1,15 @@
use super::formats::shared;
use super::{Config, ImporterFormat};
use eyre::Result;
use super::{Message, MessageReceiver};
pub(crate) mod formats;
use crossbeam_channel::{self, unbounded};
use eyre::Result;
use std::thread::JoinHandle;
use formats::{shared, ImporterFormat};
pub trait Importerlike {
fn import(self) -> Result<(MessageReceiver, JoinHandle<Result<()>>)>;
}
use std::{path::PathBuf, thread::JoinHandle};
use ps_core::{
crossbeam_channel::unbounded, Config, DatabaseLike, FormatType, Importerlike, Message,
MessageReceiver,
};
pub struct Importer<Format: ImporterFormat> {
config: Config,
@ -23,7 +23,10 @@ impl<Format: ImporterFormat + 'static> Importer<Format> {
}
impl<Format: ImporterFormat + 'static> Importerlike for Importer<Format> {
fn import(self) -> Result<(MessageReceiver, JoinHandle<Result<()>>)> {
fn import<Database: DatabaseLike + 'static>(
self,
database: Database,
) -> Result<(MessageReceiver, JoinHandle<Result<()>>)> {
let Importer { format, .. } = self;
let (sender, receiver) = unbounded();
@ -32,7 +35,8 @@ impl<Format: ImporterFormat + 'static> Importerlike for Importer<Format> {
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())?;
let processed =
shared::database::into_database(&config, emails, sender.clone(), database)?;
Ok(processed)
};
@ -51,8 +55,22 @@ impl<Format: ImporterFormat + 'static> Importerlike for Importer<Format> {
}
}
impl<T: Importerlike + Sized> Importerlike for Box<T> {
fn import(self) -> Result<(MessageReceiver, JoinHandle<Result<()>>)> {
(*self).import()
pub fn gmail_importer(config: Config) -> Importer<formats::Gmail> {
Importer::new(config, formats::Gmail::default())
}
pub fn applemail_importer(config: Config) -> Importer<formats::AppleMail> {
Importer::new(config, formats::AppleMail::default())
}
pub fn mbox_importer(config: Config) -> Importer<formats::Mbox> {
Importer::new(config, formats::Mbox::default())
}
pub fn default_path(format: &FormatType) -> Option<PathBuf> {
match format {
FormatType::AppleMail => formats::AppleMail::default_path(),
FormatType::GmailVault => formats::Gmail::default_path(),
FormatType::Mbox => formats::Mbox::default_path(),
}
}

BIN
src/.DS_Store vendored

Binary file not shown.

@ -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<bool> {
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)
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save