Compare commits

...

12 Commits

12 changed files with 650 additions and 57 deletions

413
Cargo.lock generated
View File

@@ -2,6 +2,16 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "Inflector"
version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3"
dependencies = [
"lazy_static",
"regex",
]
[[package]] [[package]]
name = "addr2line" name = "addr2line"
version = "0.21.0" version = "0.21.0"
@@ -56,6 +66,99 @@ version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
[[package]]
name = "ascii_utils"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71938f30533e4d95a6d17aa530939da3842c2ab6f4f84b9dae68447e4129f74a"
[[package]]
name = "async-graphql"
version = "6.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "298a5d587d6e6fdb271bf56af2dc325a80eb291fd0fc979146584b9a05494a8c"
dependencies = [
"async-graphql-derive",
"async-graphql-parser",
"async-graphql-value",
"async-stream",
"async-trait",
"base64 0.13.1",
"bytes",
"fast_chemail",
"fnv",
"futures-util",
"handlebars",
"http",
"indexmap 2.0.0",
"mime 0.3.17",
"multer",
"num-traits",
"once_cell",
"pin-project-lite",
"regex",
"serde",
"serde_json",
"serde_urlencoded",
"static_assertions",
"tempfile",
"thiserror",
]
[[package]]
name = "async-graphql-derive"
version = "6.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7f329c7eb9b646a72f70c9c4b516c70867d356ec46cb00dcac8ad343fd006b0"
dependencies = [
"Inflector",
"async-graphql-parser",
"darling",
"proc-macro-crate",
"proc-macro2 1.0.66",
"quote 1.0.33",
"strum",
"syn 2.0.29",
"thiserror",
]
[[package]]
name = "async-graphql-parser"
version = "6.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6139181845757fd6a73fbb8839f3d036d7150b798db0e9bb3c6e83cdd65bd53b"
dependencies = [
"async-graphql-value",
"pest",
"serde",
"serde_json",
]
[[package]]
name = "async-graphql-rocket"
version = "6.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10c5004043928e9ea8ca2faecc473e3c4fe4f5be259f63c9d735c9a0e4760c2b"
dependencies = [
"async-graphql",
"rocket 0.5.0",
"serde",
"serde_json",
"tokio-util",
]
[[package]]
name = "async-graphql-value"
version = "6.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "323a5143f5bdd2030f45e3f2e0c821c9b1d36e79cf382129c64299c50a7f3750"
dependencies = [
"bytes",
"indexmap 2.0.0",
"serde",
"serde_json",
]
[[package]] [[package]]
name = "async-stream" name = "async-stream"
version = "0.3.5" version = "0.3.5"
@@ -181,6 +284,15 @@ version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]] [[package]]
name = "bumpalo" name = "bumpalo"
version = "3.13.0" version = "3.13.0"
@@ -198,6 +310,9 @@ name = "bytes"
version = "1.4.0" version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "cc" name = "cc"
@@ -222,15 +337,14 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "chrono" name = "chrono"
version = "0.4.28" version = "0.4.31"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95ed24df0632f708f5f6d8082675bef2596f7084dee3dd55f632290bf35bfe0f" checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
dependencies = [ dependencies = [
"android-tzdata", "android-tzdata",
"iana-time-zone", "iana-time-zone",
"js-sys", "js-sys",
"num-traits", "num-traits",
"time 0.1.45",
"wasm-bindgen", "wasm-bindgen",
"windows-targets", "windows-targets",
] ]
@@ -290,9 +404,9 @@ dependencies = [
[[package]] [[package]]
name = "cookie" name = "cookie"
version = "0.17.0" version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7efb37c3e1ccb1ff97164ad95ac1606e8ccd35b3fa0a7d99a304c7f4a428cc24" checksum = "3cd91cf61412820176e137621345ee43b3f4423e589e7ae4e50d601d93e35ef8"
dependencies = [ dependencies = [
"percent-encoding 2.3.0", "percent-encoding 2.3.0",
"time 0.3.28", "time 0.3.28",
@@ -305,6 +419,15 @@ version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
[[package]]
name = "cpufeatures"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "crc32fast" name = "crc32fast"
version = "1.3.2" version = "1.3.2"
@@ -357,6 +480,16 @@ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
] ]
[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
]
[[package]] [[package]]
name = "css-inline" name = "css-inline"
version = "0.8.5" version = "0.8.5"
@@ -418,6 +551,41 @@ dependencies = [
"syn 2.0.29", "syn 2.0.29",
] ]
[[package]]
name = "darling"
version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621"
dependencies = [
"fnv",
"ident_case",
"proc-macro2 1.0.66",
"quote 1.0.33",
"strsim",
"syn 2.0.29",
]
[[package]]
name = "darling_macro"
version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5"
dependencies = [
"darling_core",
"quote 1.0.33",
"syn 2.0.29",
]
[[package]] [[package]]
name = "dbg" name = "dbg"
version = "1.0.4" version = "1.0.4"
@@ -517,6 +685,16 @@ version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]] [[package]]
name = "dtoa" name = "dtoa"
version = "1.0.9" version = "1.0.9"
@@ -580,6 +758,15 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "fast_chemail"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "495a39d30d624c2caabe6312bfead73e7717692b44e0b32df168c275a2e8e9e4"
dependencies = [
"ascii_utils",
]
[[package]] [[package]]
name = "fastrand" name = "fastrand"
version = "2.0.0" version = "2.0.0"
@@ -793,6 +980,16 @@ dependencies = [
"windows 0.48.0", "windows 0.48.0",
] ]
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check 0.9.4",
]
[[package]] [[package]]
name = "gethostname" name = "gethostname"
version = "0.2.3" version = "0.2.3"
@@ -925,6 +1122,20 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "handlebars"
version = "4.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "faa67bab9ff362228eb3d00bd024a4965d8231bbb7921167f0cfa66c6626b225"
dependencies = [
"log 0.4.20",
"pest",
"pest_derive",
"serde",
"serde_json",
"thiserror",
]
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.12.3" version = "0.12.3"
@@ -937,6 +1148,12 @@ version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.1.19" version = "0.1.19"
@@ -1066,6 +1283,12 @@ dependencies = [
"cc", "cc",
] ]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]] [[package]]
name = "idna" name = "idna"
version = "0.1.5" version = "0.1.5"
@@ -1101,7 +1324,6 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"hashbrown 0.12.3", "hashbrown 0.12.3",
"serde",
] ]
[[package]] [[package]]
@@ -1112,6 +1334,7 @@ checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown 0.14.0", "hashbrown 0.14.0",
"serde",
] ]
[[package]] [[package]]
@@ -1243,6 +1466,7 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
name = "letterbox" name = "letterbox"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"chrono",
"console_error_panic_hook", "console_error_panic_hook",
"console_log", "console_log",
"css-inline", "css-inline",
@@ -1673,6 +1897,51 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
[[package]]
name = "pest"
version = "2.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae9cee2a55a544be8b89dc6848072af97a20f2422603c10865be2a42b580fff5"
dependencies = [
"memchr",
"thiserror",
"ucd-trie",
]
[[package]]
name = "pest_derive"
version = "2.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81d78524685f5ef2a3b3bd1cafbc9fcabb036253d9b1463e726a91cd16e2dfc2"
dependencies = [
"pest",
"pest_generator",
]
[[package]]
name = "pest_generator"
version = "2.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68bd1206e71118b5356dae5ddc61c8b11e28b09ef6a31acbd15ea48a28e0c227"
dependencies = [
"pest",
"pest_meta",
"proc-macro2 1.0.66",
"quote 1.0.33",
"syn 2.0.29",
]
[[package]]
name = "pest_meta"
version = "2.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c747191d4ad9e4a4ab9c8798f1e82a39affe7ef9648390b7e5548d18e099de6"
dependencies = [
"once_cell",
"pest",
"sha2",
]
[[package]] [[package]]
name = "phf" name = "phf"
version = "0.8.0" version = "0.8.0"
@@ -1811,6 +2080,16 @@ dependencies = [
"yansi 0.5.1", "yansi 0.5.1",
] ]
[[package]]
name = "proc-macro-crate"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
dependencies = [
"once_cell",
"toml_edit",
]
[[package]] [[package]]
name = "proc-macro-hack" name = "proc-macro-hack"
version = "0.5.20+deprecated" version = "0.5.20+deprecated"
@@ -2096,9 +2375,9 @@ dependencies = [
[[package]] [[package]]
name = "rocket" name = "rocket"
version = "0.5.0-rc.3" version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58734f7401ae5cfd129685b48f61182331745b357b96f2367f01aebaf1cc9cc9" checksum = "9e7bb57ccb26670d73b6a47396c83139447b9e7878cab627fdfe9ea8da489150"
dependencies = [ dependencies = [
"async-stream", "async-stream",
"async-trait", "async-trait",
@@ -2108,8 +2387,7 @@ dependencies = [
"either", "either",
"figment", "figment",
"futures", "futures",
"indexmap 1.9.3", "indexmap 2.0.0",
"is-terminal",
"log 0.4.20", "log 0.4.20",
"memchr", "memchr",
"multer", "multer",
@@ -2118,11 +2396,11 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
"rand 0.8.5", "rand 0.8.5",
"ref-cast", "ref-cast",
"rocket_codegen 0.5.0-rc.3", "rocket_codegen 0.5.0",
"rocket_http 0.5.0-rc.3", "rocket_http 0.5.0",
"serde", "serde",
"serde_json", "serde_json",
"state 0.5.3", "state 0.6.0",
"tempfile", "tempfile",
"time 0.3.28", "time 0.3.28",
"tokio", "tokio",
@@ -2130,7 +2408,7 @@ dependencies = [
"tokio-util", "tokio-util",
"ubyte", "ubyte",
"version_check 0.9.4", "version_check 0.9.4",
"yansi 0.5.1", "yansi 1.0.0-rc.1",
] ]
[[package]] [[package]]
@@ -2150,18 +2428,19 @@ dependencies = [
[[package]] [[package]]
name = "rocket_codegen" name = "rocket_codegen"
version = "0.5.0-rc.3" version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7093353f14228c744982e409259fb54878ba9563d08214f2d880d59ff2fc508b" checksum = "a2238066abf75f21be6cd7dc1a09d5414a671f4246e384e49fe3f8a4936bd04c"
dependencies = [ dependencies = [
"devise 0.4.1", "devise 0.4.1",
"glob", "glob",
"indexmap 1.9.3", "indexmap 2.0.0",
"proc-macro2 1.0.66", "proc-macro2 1.0.66",
"quote 1.0.33", "quote 1.0.33",
"rocket_http 0.5.0-rc.3", "rocket_http 0.5.0",
"syn 2.0.29", "syn 2.0.29",
"unicode-xid 0.2.4", "unicode-xid 0.2.4",
"version_check 0.9.4",
] ]
[[package]] [[package]]
@@ -2179,13 +2458,14 @@ dependencies = [
[[package]] [[package]]
name = "rocket_cors" name = "rocket_cors"
version = "0.6.0-alpha2" version = "0.6.0"
source = "git+https://github.com/lawliet89/rocket_cors?branch=master#985098dd8f3b052716111eaa872d184cc21a1a68" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfac3a1df83f8d4fc96aa41dba3b86c786417b7fc0f52ec76295df2ba781aa69"
dependencies = [ dependencies = [
"http", "http",
"log 0.4.20", "log 0.4.20",
"regex", "regex",
"rocket 0.5.0-rc.3", "rocket 0.5.0",
"serde", "serde",
"serde_derive", "serde_derive",
"unicase 2.7.0", "unicase 2.7.0",
@@ -2212,16 +2492,16 @@ dependencies = [
[[package]] [[package]]
name = "rocket_http" name = "rocket_http"
version = "0.5.0-rc.3" version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "936012c99162a03a67f37f9836d5f938f662e26f2717809761a9ac46432090f4" checksum = "37a1663694d059fe5f943ea5481363e48050acedd241d46deb2e27f71110389e"
dependencies = [ dependencies = [
"cookie 0.17.0", "cookie 0.18.0",
"either", "either",
"futures", "futures",
"http", "http",
"hyper 0.14.27", "hyper 0.14.27",
"indexmap 1.9.3", "indexmap 2.0.0",
"log 0.4.20", "log 0.4.20",
"memchr", "memchr",
"pear 0.2.7", "pear 0.2.7",
@@ -2231,7 +2511,7 @@ dependencies = [
"serde", "serde",
"smallvec", "smallvec",
"stable-pattern", "stable-pattern",
"state 0.5.3", "state 0.6.0",
"time 0.3.28", "time 0.3.28",
"tokio", "tokio",
"uncased", "uncased",
@@ -2429,14 +2709,28 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
dependencies = [
"form_urlencoded",
"itoa 1.0.9",
"ryu",
"serde",
]
[[package]] [[package]]
name = "server" name = "server"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"async-graphql",
"async-graphql-rocket",
"glog", "glog",
"log 0.4.20", "log 0.4.20",
"notmuch", "notmuch",
"rocket 0.5.0-rc.3", "rocket 0.5.0",
"rocket_contrib", "rocket_contrib",
"rocket_cors", "rocket_cors",
"serde", "serde",
@@ -2457,6 +2751,17 @@ dependencies = [
"stable_deref_trait", "stable_deref_trait",
] ]
[[package]]
name = "sha2"
version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if 1.0.0",
"cpufeatures",
"digest",
]
[[package]] [[package]]
name = "sharded-slab" name = "sharded-slab"
version = "0.1.4" version = "0.1.4"
@@ -2559,13 +2864,19 @@ checksum = "3015a7d0a5fd5105c91c3710d42f9ccf0abfb287d62206484dcc67f9569a6483"
[[package]] [[package]]
name = "state" name = "state"
version = "0.5.3" version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbe866e1e51e8260c9eed836a042a5e7f6726bb2b411dffeaa712e19c388f23b" checksum = "2b8c4a4445d81357df8b1a650d0d0d6fbbbfe99d064aa5e02f3e4022061476d8"
dependencies = [ dependencies = [
"loom", "loom",
] ]
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]] [[package]]
name = "string_cache" name = "string_cache"
version = "0.8.7" version = "0.8.7"
@@ -2592,6 +2903,34 @@ dependencies = [
"quote 1.0.33", "quote 1.0.33",
] ]
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "strum"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125"
dependencies = [
"strum_macros",
]
[[package]]
name = "strum_macros"
version = "0.25.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0"
dependencies = [
"heck",
"proc-macro2 1.0.66",
"quote 1.0.33",
"rustversion",
"syn 2.0.29",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "0.15.44" version = "0.15.44"
@@ -2795,6 +3134,7 @@ checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures-core", "futures-core",
"futures-io",
"futures-sink", "futures-sink",
"pin-project-lite", "pin-project-lite",
"tokio", "tokio",
@@ -2930,6 +3270,12 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" checksum = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887"
[[package]]
name = "typenum"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]] [[package]]
name = "ubyte" name = "ubyte"
version = "0.10.3" version = "0.10.3"
@@ -2939,6 +3285,12 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "ucd-trie"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
[[package]] [[package]]
name = "uncased" name = "uncased"
version = "0.9.9" version = "0.9.9"
@@ -3422,3 +3774,6 @@ name = "yansi"
version = "1.0.0-rc.1" version = "1.0.0-rc.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1367295b8f788d371ce2dbc842c7b709c73ee1364d30351dd300ec2203b12377" checksum = "1367295b8f788d371ce2dbc842c7b709c73ee1364d30351dd300ec2203b12377"
dependencies = [
"is-terminal",
]

2
dev.sh
View File

@@ -3,5 +3,5 @@ tmux new-session -d -s letterbox-dev
tmux rename-window web tmux rename-window web
tmux send-keys "cd web; trunk serve -w ../shared -w ../notmuch -w ./" C-m tmux send-keys "cd web; trunk serve -w ../shared -w ../notmuch -w ./" C-m
tmux new-window -n server tmux new-window -n server
tmux send-keys "cd server; cargo watch -x run -w ../shared -w ../notmuch -w ./" C-m tmux send-keys "cd server; cargo watch -c -x run -w ../shared -w ../notmuch -w ./" C-m
tmux attach -d -t letterbox-dev tmux attach -d -t letterbox-dev

View File

@@ -480,6 +480,11 @@ impl Notmuch {
self.run_notmuch(std::iter::empty::<&str>()) self.run_notmuch(std::iter::empty::<&str>())
} }
pub fn tags(&self) -> Result<Vec<String>, NotmuchError> {
let res = self.run_notmuch(["search", "--format=json", "--output=tags", "*"])?;
Ok(serde_json::from_slice(&res)?)
}
pub fn search( pub fn search(
&self, &self,
query: &str, query: &str,

View File

@@ -8,7 +8,6 @@ default-bin = "server"
[dependencies] [dependencies]
rocket = { version = "0.5.0-rc.2", features = [ "json" ] } rocket = { version = "0.5.0-rc.2", features = [ "json" ] }
rocket_cors = { git = "https://github.com/lawliet89/rocket_cors", branch = "master" }
notmuch = { path = "../notmuch" } notmuch = { path = "../notmuch" }
shared = { path = "../shared" } shared = { path = "../shared" }
serde_json = "1.0.87" serde_json = "1.0.87"
@@ -18,6 +17,9 @@ log = "0.4.17"
tokio = "1.26.0" tokio = "1.26.0"
glog = "0.1.0" glog = "0.1.0"
urlencoding = "2.1.3" urlencoding = "2.1.3"
async-graphql = "6.0.11"
async-graphql-rocket = "6.0.11"
rocket_cors = "0.6.0"
[dependencies.rocket_contrib] [dependencies.rocket_contrib]
version = "0.4.11" version = "0.4.11"

View File

@@ -1,26 +1,25 @@
#[macro_use] #[macro_use]
extern crate rocket; extern crate rocket;
use std::{error::Error, io::Cursor, str::FromStr}; use std::{error::Error, io::Cursor, str::FromStr};
use async_graphql::{http::GraphiQLSource, EmptyMutation, EmptySubscription, Schema};
use async_graphql_rocket::{GraphQLQuery, GraphQLRequest, GraphQLResponse};
use glog::Flags; use glog::Flags;
use notmuch::{Notmuch, NotmuchError, ThreadSet}; use notmuch::{Notmuch, NotmuchError, ThreadSet};
use rocket::{ use rocket::{
http::{ContentType, Header}, http::{ContentType, Header},
request::Request, request::Request,
response::{Debug, Responder}, response::{content, Debug, Responder},
serde::json::Json, serde::json::Json,
Response, State, Response, State,
}; };
use rocket_cors::{AllowedHeaders, AllowedOrigins}; use rocket_cors::{AllowedHeaders, AllowedOrigins};
use server::{error::ServerError, nm::threadset_to_messages}; use server::{
error::ServerError,
graphql::{GraphqlSchema, QueryRoot},
};
use shared::Message; use shared::Message;
#[get("/")]
fn hello() -> &'static str {
"Hello, world!"
}
#[get("/refresh")] #[get("/refresh")]
async fn refresh(nm: &State<Notmuch>) -> Result<Json<String>, Debug<NotmuchError>> { async fn refresh(nm: &State<Notmuch>) -> Result<Json<String>, Debug<NotmuchError>> {
Ok(Json(String::from_utf8_lossy(&nm.new()?).to_string())) Ok(Json(String::from_utf8_lossy(&nm.new()?).to_string()))
@@ -41,7 +40,7 @@ async fn search(
results_per_page: Option<usize>, results_per_page: Option<usize>,
) -> Result<Json<shared::SearchResult>, Debug<NotmuchError>> { ) -> Result<Json<shared::SearchResult>, Debug<NotmuchError>> {
let page = page.unwrap_or(0); let page = page.unwrap_or(0);
let results_per_page = results_per_page.unwrap_or(10); let results_per_page = results_per_page.unwrap_or(20);
let query = urlencoding::decode(query).map_err(NotmuchError::from)?; let query = urlencoding::decode(query).map_err(NotmuchError::from)?;
info!(" search '{query}'"); info!(" search '{query}'");
let res = shared::SearchResult { let res = shared::SearchResult {
@@ -58,9 +57,9 @@ async fn search(
async fn show_pretty( async fn show_pretty(
nm: &State<Notmuch>, nm: &State<Notmuch>,
query: &str, query: &str,
) -> Result<Json<Vec<Message>>, Debug<ServerError>> { ) -> Result<Json<ThreadSet>, Debug<ServerError>> {
let query = urlencoding::decode(query).map_err(|e| ServerError::from(NotmuchError::from(e)))?; let query = urlencoding::decode(query).map_err(|e| ServerError::from(NotmuchError::from(e)))?;
let res = threadset_to_messages(nm.show(&query).map_err(ServerError::from)?)?; let res = nm.show(&query).map_err(ServerError::from)?;
Ok(Json(res)) Ok(Json(res))
} }
@@ -125,6 +124,24 @@ async fn original(
Ok((ContentType::Plain, res)) Ok((ContentType::Plain, res))
} }
#[rocket::get("/")]
fn graphiql() -> content::RawHtml<String> {
content::RawHtml(GraphiQLSource::build().endpoint("/graphql").finish())
}
#[rocket::get("/graphql?<query..>")]
async fn graphql_query(schema: &State<GraphqlSchema>, query: GraphQLQuery) -> GraphQLResponse {
query.execute(schema.inner()).await
}
#[rocket::post("/graphql", data = "<request>", format = "application/json")]
async fn graphql_request(
schema: &State<GraphqlSchema>,
request: GraphQLRequest,
) -> GraphQLResponse {
request.execute(schema.inner()).await
}
#[rocket::main] #[rocket::main]
async fn main() -> Result<(), Box<dyn Error>> { async fn main() -> Result<(), Box<dyn Error>> {
glog::new() glog::new()
@@ -148,21 +165,28 @@ async fn main() -> Result<(), Box<dyn Error>> {
} }
.to_cors()?; .to_cors()?;
let schema = Schema::build(QueryRoot, EmptyMutation, EmptySubscription)
.data(Notmuch::default())
.finish();
let _ = rocket::build() let _ = rocket::build()
.mount( .mount(
"/", "/",
routes![ routes![
original_part, original_part,
original, original,
hello,
refresh, refresh,
search_all, search_all,
search, search,
show_pretty, show_pretty,
show show,
graphql_query,
graphql_request,
graphiql
], ],
) )
.attach(cors) .attach(cors)
.manage(schema)
.manage(Notmuch::default()) .manage(Notmuch::default())
//.manage(Notmuch::with_config("../notmuch/testdata/notmuch.config")) //.manage(Notmuch::with_config("../notmuch/testdata/notmuch.config"))
.launch() .launch()

147
server/src/graphql.rs Normal file
View File

@@ -0,0 +1,147 @@
use std::hash::{DefaultHasher, Hash, Hasher};
use async_graphql::{
connection::{self, Connection, Edge},
Context, EmptyMutation, EmptySubscription, Error, FieldResult, Object, Schema, SimpleObject,
};
use log::info;
use notmuch::Notmuch;
pub struct QueryRoot;
/// # Number of seconds since the Epoch
pub type UnixTime = isize;
/// # Thread ID, sans "thread:"
pub type ThreadId = String;
#[derive(Debug, SimpleObject)]
pub struct ThreadSummary {
pub thread: ThreadId,
pub timestamp: UnixTime,
/// user-friendly timestamp
pub date_relative: String,
/// number of matched messages
pub matched: isize,
/// total messages in thread
pub total: isize,
/// comma-separated names with | between matched and unmatched
pub authors: String,
pub subject: String,
pub tags: Vec<String>,
}
#[derive(SimpleObject)]
struct Tag {
name: String,
fg_color: String,
bg_color: String,
}
#[Object]
impl QueryRoot {
async fn count<'ctx>(&self, ctx: &Context<'ctx>, query: String) -> Result<usize, Error> {
let nm = ctx.data_unchecked::<Notmuch>();
Ok(nm.count(&query)?)
}
async fn search<'ctx>(
&self,
ctx: &Context<'ctx>,
after: Option<String>,
before: Option<String>,
first: Option<i32>,
last: Option<i32>,
query: String,
) -> Result<Connection<usize, ThreadSummary>, Error> {
let nm = ctx.data_unchecked::<Notmuch>();
connection::query(
after,
before,
first,
last,
|after, before, first, last| async move {
info!("{after:?} {before:?} {first:?} {last:?} {query}");
let mut start = 0usize;
let total = nm.count(&query)?;
let page_size = first.unwrap_or(20);
if let Some(after) = after {
if after >= total {
return Ok(Connection::new(false, false));
}
start = after + 1;
}
let mut end = start + page_size;
if end > total {
end = total;
}
// TODO(wathiede): handle last/end.
if let Some(before) = before {
if before == 0 {
return Ok(Connection::new(false, false));
}
end = before;
}
let slice: Vec<ThreadSummary> = nm
.search(&query, start, first.unwrap_or(20))?
.0
.into_iter()
.map(|ts| ThreadSummary {
thread: ts.thread,
timestamp: ts.timestamp,
date_relative: ts.date_relative,
matched: ts.matched,
total: ts.total,
authors: ts.authors,
subject: ts.subject,
tags: ts.tags,
})
.collect();
//let mut slice = &res[..];
/*
if let Some(first) = first {
slice = &slice[..first.min(slice.len())];
end -= first.min(slice.len());
} else if let Some(last) = last {
slice = &slice[slice.len() - last.min(slice.len())..];
start = end - last.min(slice.len());
}
*/
let mut connection = Connection::new(start > 0, end < total);
connection.edges.extend(
slice
.into_iter()
.enumerate()
.map(|(idx, item)| Edge::new(start + idx, item)),
);
Ok::<_, Error>(connection)
},
)
.await
}
async fn tags<'ctx>(&self, ctx: &Context<'ctx>) -> FieldResult<Vec<Tag>> {
let nm = ctx.data_unchecked::<Notmuch>();
Ok(nm
.tags()?
.into_iter()
.map(|tag| {
let mut hasher = DefaultHasher::new();
tag.hash(&mut hasher);
let hex = format!("#{:06x}", hasher.finish() % (1 << 24));
Tag {
name: tag,
fg_color: "white".to_string(),
bg_color: hex,
}
})
.collect())
}
}
pub type GraphqlSchema = Schema<QueryRoot, EmptyMutation, EmptySubscription>;

View File

@@ -1,2 +1,3 @@
pub mod error; pub mod error;
pub mod graphql;
pub mod nm; pub mod nm;

View File

@@ -7,7 +7,7 @@ pub fn threadset_to_messages(
thread_set: notmuch::ThreadSet, thread_set: notmuch::ThreadSet,
) -> Result<Vec<Message>, error::ServerError> { ) -> Result<Vec<Message>, error::ServerError> {
for t in thread_set.0 { for t in thread_set.0 {
for tn in t.0 {} for _tn in t.0 {}
} }
Ok(Vec::new()) Ok(Vec::new())
} }

View File

@@ -9,3 +9,6 @@ pub struct SearchResult {
pub results_per_page: usize, pub results_per_page: usize,
pub total: usize, pub total: usize,
} }
#[derive(Serialize, Deserialize, Debug)]
pub struct Message {}

View File

@@ -27,6 +27,7 @@ itertools = "0.10.5"
serde_json = { version = "1.0.93", features = ["unbounded_depth"] } serde_json = { version = "1.0.93", features = ["unbounded_depth"] }
wasm-timer = "0.2.5" wasm-timer = "0.2.5"
css-inline = "0.8.5" css-inline = "0.8.5"
chrono = "0.4.31"
[package.metadata.wasm-pack.profile.release] [package.metadata.wasm-pack.profile.release]
wasm-opt = ['-Os'] wasm-opt = ['-Os']

View File

@@ -6,7 +6,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet", href="https://jenil.github.io/bulmaswatch/cyborg/bulmaswatch.min.css"> <link rel="stylesheet", href="https://jenil.github.io/bulmaswatch/cyborg/bulmaswatch.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.3.0/css/all.min.css" integrity="sha512-SzlrxWUlpfuzQ+pcUCosxcglQRNAq/DZjVsC0lE40xsADsfeQoEypE+enwcOiGjk/bSuGGKHEyjSoQ1zVisanQ==" crossorigin="anonymous" referrerpolicy="no-referrer" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.3.0/css/all.min.css" integrity="sha512-SzlrxWUlpfuzQ+pcUCosxcglQRNAq/DZjVsC0lE40xsADsfeQoEypE+enwcOiGjk/bSuGGKHEyjSoQ1zVisanQ==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<link rel="icon" href="https://static.xinu.tv/favicon/letterbox.svg" />
<style> <style>
.message { .message {
padding: 0.5em;*/ padding: 0.5em;*/
} }
@@ -37,7 +39,7 @@ iframe {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
width: 15em; width: 10em;
} }
.index .subject { .index .subject {
overflow: hidden; overflow: hidden;
@@ -45,8 +47,9 @@ iframe {
white-space: nowrap; white-space: nowrap;
} }
.index .date { .index .date {
width: 8em; width: 7em;
white-space: nowrap; white-space: nowrap;
text-align: right;
} }
.footer { .footer {
background-color: #eee; background-color: #eee;
@@ -91,6 +94,30 @@ input, .input {
input::placeholder, .input::placeholder{ input::placeholder, .input::placeholder{
color: #555; color: #555;
} }
.search-results .row {
border-bottom: 1px #444 solid;
padding-bottom: .5em;
padding-top: .5em;
}
.search-results .row .subject {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.search-results .row .from {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.search-results .row .tag {
height: 1.5em;
padding-left: .5em;
padding-right: .5em;
}
.float-right {
float: right;
}
</style> </style>
</head> </head>

View File

@@ -7,6 +7,7 @@ use std::{
hash::{Hash, Hasher}, hash::{Hash, Hasher},
}; };
use chrono::{DateTime, Datelike, Duration, Local, Utc};
use itertools::Itertools; use itertools::Itertools;
use log::{debug, error, info, Level}; use log::{debug, error, info, Level};
use notmuch::{Content, Part, Thread, ThreadNode, ThreadSet}; use notmuch::{Content, Part, Thread, ThreadNode, ThreadSet};
@@ -481,6 +482,29 @@ fn pretty_authors(authors: &str) -> impl Iterator<Item = Node<Msg>> + '_ {
) )
} }
fn human_age(timestamp: i64) -> String {
let now = Local::now();
let yesterday = now - Duration::days(1);
let ts = DateTime::<Utc>::from_timestamp(timestamp, 0)
.unwrap()
.with_timezone(&Local);
let age = now - ts;
let datetime = if age < Duration::minutes(1) {
format!("{} min. ago", age.num_seconds())
} else if age < Duration::hours(1) {
format!("{} min. ago", age.num_minutes())
} else if ts.date_naive() == now.date_naive() {
ts.format("Today %H:%M").to_string()
} else if ts.date_naive() == yesterday.date_naive() {
ts.format("Yest. %H:%M").to_string()
} else if age < Duration::weeks(1) {
ts.format("%a %H:%M").to_string()
} else {
ts.format("%b %e %H:%M").to_string()
};
datetime
}
fn view_mobile_search_results(query: &str, search_results: &shared::SearchResult) -> Node<Msg> { fn view_mobile_search_results(query: &str, search_results: &shared::SearchResult) -> Node<Msg> {
if query.is_empty() { if query.is_empty() {
set_title("all mail"); set_title("all mail");
@@ -503,21 +527,24 @@ fn view_mobile_search_results(query: &str, search_results: &shared::SearchResult
] ]
*/ */
let tid = r.thread.clone(); let tid = r.thread.clone();
let datetime = human_age(r.timestamp as i64);
div![ div![
C!["row"],
div![ div![
C!["subject"], C!["subject"],
&r.subject, &r.subject,
ev(Ev::Click, move |_| Msg::ShowPrettyRequest(tid)), ev(Ev::Click, move |_| Msg::ShowPrettyRequest(tid)),
], ],
span![C!["from", "is-size-7"], pretty_authors(&r.authors)],
div![ div![
span![C!["from"], pretty_authors(&r.authors)], span![C!["is-size-7"], tags_chiclet(&r.tags, true)],
span![C!["tags"], tags_chiclet(&r.tags, true)], span![C!["is-size-7", "float-right", "date"], datetime]
], ]
span![C!["date"], &r.date_relative],
] ]
}); });
let first = search_results.page * search_results.results_per_page; let first = search_results.page * search_results.results_per_page;
div![ div![
C!["search-results"],
h1!["Search results"], h1!["Search results"],
view_search_pager(first, summaries.len(), search_results.total), view_search_pager(first, summaries.len(), search_results.total),
rows, rows,
@@ -534,6 +561,7 @@ fn view_search_results(query: &str, search_results: &shared::SearchResult) -> No
let summaries = &search_results.summary.0; let summaries = &search_results.summary.0;
let rows = summaries.iter().map(|r| { let rows = summaries.iter().map(|r| {
let tid = r.thread.clone(); let tid = r.thread.clone();
let datetime = human_age(r.timestamp as i64);
tr![ tr![
td![ td![
C!["from"], C!["from"],
@@ -552,7 +580,7 @@ fn view_search_results(query: &str, search_results: &shared::SearchResult) -> No
&r.subject, &r.subject,
] ]
], ],
td![C!["date"], &r.date_relative] td![C!["date"], datetime]
] ]
}); });
let first = search_results.page * search_results.results_per_page; let first = search_results.page * search_results.results_per_page;
@@ -618,11 +646,11 @@ fn view_thread(thread_set: &ThreadSet) -> Node<Msg> {
"Original" "Original"
], ],
/* /*
div![ div![
C!["debug"], C!["debug"],
"Add zippy for debug dump", "Add zippy for debug dump",
view_debug_thread_set(thread_set) view_debug_thread_set(thread_set)
] /* pre![format!("Thread: {:#?}", thread_set).replace(" ", " ")] */ ] /* pre![format!("Thread: {:#?}", thread_set).replace(" ", " ")] */
*/ */
] ]
} }