diff --git a/Cargo.lock b/Cargo.lock index c926d690..25acc9ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,18 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43bb833f0bf979d8475d38fbf09ed3b8a55e1885fe93ad3f93239fc6a4f17b98" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + [[package]] name = "aho-corasick" version = "0.7.18" @@ -18,24 +31,284 @@ dependencies = [ "winapi", ] +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "async-attributes" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "async-channel" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-executor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "once_cell", + "slab", +] + +[[package]] +name = "async-global-executor" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9586ec52317f36de58453159d48351bc244bc24ced3effc1fce22f3d48664af6" +dependencies = [ + "async-channel", + "async-executor", + "async-io", + "async-mutex", + "blocking", + "futures-lite", + "num_cpus", + "once_cell", +] + +[[package]] +name = "async-io" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a811e6a479f2439f0c04038796b5cfb3d2ad56c230e0f2d3f7b04d68cfee607b" +dependencies = [ + "concurrent-queue", + "futures-lite", + "libc", + "log", + "once_cell", + "parking", + "polling", + "slab", + "socket2 0.4.0", + "waker-fn", + "winapi", +] + +[[package]] +name = "async-lock" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6a8ea61bf9947a1007c5cada31e647dbc77b103c679858150003ba697ea798b" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-mutex" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-process" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f38756dd9ac84671c428afbf7c9f7495feff9ec5b0710f17100098e5b354ac" +dependencies = [ + "async-io", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "libc", + "once_cell", + "signal-hook", + "winapi", +] + +[[package]] +name = "async-rustls" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c86f33abd5a4f3e2d6d9251a9e0c6a7e52eb1113caf893dae8429bf4a53f378" +dependencies = [ + "futures-lite", + "rustls", + "webpki", +] + +[[package]] +name = "async-std" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9f06685bad74e0570f5213741bea82158279a4103d988e57bfada11ad230341" +dependencies = [ + "async-attributes", + "async-channel", + "async-global-executor", + "async-io", + "async-lock", + "async-process", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "num_cpus", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-task" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0" + +[[package]] +name = "async-trait" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b98e84bbb4cbcdd97da190ba0c58a1bb0de2c1fdf67d159e192ed766aeca722" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atoi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616896e05fc0e2649463a93a15183c6a16bf03413a7af88ef1285ddedfa9cda5" +dependencies = [ + "num-traits", +] + +[[package]] +name = "atomic-waker" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" + [[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.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "bitvec" +version = "0.19.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8942c8d352ae1838c9dda0b0ca2ab657696ef2232a20147cf1b30ae1a9cb4321" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "block" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blocking" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e170dbede1f740736619b776d7251cb1b9095c435c34d8ca9f57fcd2f335e9" +dependencies = [ + "async-channel", + "async-task", + "atomic-waker", + "fastrand", + "futures-lite", + "once_cell", +] + +[[package]] +name = "build_const" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ae4235e6dac0694637c763029ecea1a2ec9e4e06ec2729bd21ba4d9c863eb7" + +[[package]] +name = "bumpalo" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" + +[[package]] +name = "cache-padded" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba" + +[[package]] +name = "cc" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2" + [[package]] name = "cfg-if" version = "1.0.0" @@ -77,6 +350,63 @@ dependencies = [ "winapi", ] +[[package]] +name = "concurrent-queue" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" +dependencies = [ + "cache-padded", +] + +[[package]] +name = "cpufeatures" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66c99696f6c9dd7f35d486b9d04d7e6e202aa3e8c40d553f2fdf5e7e0c6a71ef" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" +dependencies = [ + "build_const", +] + +[[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-queue" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b10ddc024425c88c2ad148c1b0fd53f4c6d38db9697c9f1588381212fa657c9" +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 = "crossterm" version = "0.20.0" @@ -128,6 +458,177 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "event-listener" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59" + +[[package]] +name = "fastrand" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b394ed3d285a429378d3b384b9eb1285267e7df4b166df24b7a6939a04dc392e" +dependencies = [ + "instant", +] + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "funty" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" + +[[package]] +name = "futures" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1adc00f486adfc9ce99f77d717836f0c5aa84965eb0b4f051f4e83f7cab53f8b" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74ed2411805f6e4e3d9bc904c95d5d423b89b3b25dc0250aa74729de20629ff9" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af51b1b4a7fdff033703db39de8802c673eb91855f2e0d47dcf3bf2c0ef01f99" + +[[package]] +name = "futures-executor" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d0d535a57b87e1ae31437b892713aee90cd2d7b0ee48727cd11fc72ef54761c" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b0e06c393068f3a6ef246c75cdca793d6a46347e75286933e5e75fd2fd11582" + +[[package]] +name = "futures-lite" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + +[[package]] +name = "futures-macro" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c54913bae956fb8df7f4dc6fc90362aa72e69148e3f39041fbe8742d21e0ac57" +dependencies = [ + "autocfg", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f30aaa67363d119812743aa5f33c201a7a66329f97d1a887022971feea4b53" + +[[package]] +name = "futures-task" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe54a98670017f3be909561f6ad13e810d9a51f3f061b902062ca3da80799f2" + +[[package]] +name = "futures-util" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eb846bfd58e44a8481a00049e82c43e0ccb5d61f8dc071057cb19249dd4d78" +dependencies = [ + "autocfg", + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.2.2" @@ -139,6 +640,72 @@ dependencies = [ "wasi", ] +[[package]] +name = "gloo-timers" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47204a46aaff920a1ea58b11d03dec6f704287d27561724a4631e450654a891f" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashlink" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "instant" version = "0.1.9" @@ -148,17 +715,74 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "itertools" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" + +[[package]] +name = "js-sys" +version = "0.3.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83bdfbace3a0e81a4253f73b49e960b053e396a11012cbd49b9b74d6a2b67062" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lexical-core" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" +dependencies = [ + "arrayvec", + "bitflags", + "cfg-if", + "ryu", + "static_assertions", +] + [[package]] name = "libc" -version = "0.2.86" +version = "0.2.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c" +checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790" + +[[package]] +name = "libsqlite3-sys" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290b64917f8b0cb885d9de0f9959fe1f775d7fa12f1da2db9001c1c8ab60f89d" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] [[package]] name = "linked-hash-map" @@ -186,6 +810,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" dependencies = [ "cfg-if", + "value-bag", ] [[package]] @@ -197,6 +822,18 @@ dependencies = [ "libc", ] +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" + [[package]] name = "memchr" version = "2.4.0" @@ -222,10 +859,23 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897" dependencies = [ - "socket2", + "socket2 0.3.19", "winapi", ] +[[package]] +name = "nom" +version = "6.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2" +dependencies = [ + "bitvec", + "funty", + "lexical-core", + "memchr", + "version_check", +] + [[package]] name = "ntapi" version = "0.3.6" @@ -276,6 +926,16 @@ 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 = "objc" version = "0.2.7" @@ -305,37 +965,92 @@ dependencies = [ "objc", ] +[[package]] +name = "once_cell" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + [[package]] name = "output_vt100" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9" -dependencies = [ - "winapi", -] +checksum = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9" +dependencies = [ + "winapi", +] + +[[package]] +name = "parking" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" + +[[package]] +name = "parking_lot" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pin-project-lite" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] -name = "parking_lot" -version = "0.11.1" +name = "pkg-config" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core", -] +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" [[package]] -name = "parking_lot_core" -version = "0.8.3" +name = "polling" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" +checksum = "92341d779fa34ea8437ef4d82d440d5e1ce3f3ff7f824aa64424cd481f9a1f25" dependencies = [ "cfg-if", - "instant", "libc", - "redox_syscall", - "smallvec", + "log", + "wepoll-ffi", "winapi", ] @@ -357,6 +1072,18 @@ dependencies = [ "output_vt100", ] +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro-nested" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" + [[package]] name = "proc-macro2" version = "1.0.27" @@ -375,6 +1102,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8" + [[package]] name = "rand" version = "0.8.3" @@ -428,14 +1161,19 @@ dependencies = [ name = "reedline" version = "0.1.0" dependencies = [ + "async-std", + "async-trait", "chrono", "clipboard", "crossterm", "deser-hjson", + "itertools", + "log", "nu-ansi-term", "nu-json", "pretty_assertions", "serde", + "sqlx", "tempfile", "unicode-segmentation", "unicode-width", @@ -467,12 +1205,56 @@ dependencies = [ "winapi", ] +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rustls" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" +dependencies = [ + "base64", + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + [[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "sct" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "serde" version = "1.0.126" @@ -502,6 +1284,19 @@ dependencies = [ "serde", ] +[[package]] +name = "sha2" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b362ae5752fd2137731f9fa25fd4d9058af34666ca1966fb969119cc35719f12" +dependencies = [ + "block-buffer", + "cfg-if", + "cpufeatures", + "digest", + "opaque-debug", +] + [[package]] name = "signal-hook" version = "0.3.9" @@ -532,6 +1327,12 @@ dependencies = [ "libc", ] +[[package]] +name = "slab" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527" + [[package]] name = "smallvec" version = "1.6.1" @@ -549,6 +1350,134 @@ dependencies = [ "winapi", ] +[[package]] +name = "socket2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e3dfc207c526015c632472a77be09cf1b6e46866581aecae5cc38fb4235dea2" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "sqlformat" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d86e3c77ff882a828346ba401a7ef4b8e440df804491c6064fe8295765de71c" +dependencies = [ + "lazy_static", + "maplit", + "nom", + "regex", + "unicode_categories", +] + +[[package]] +name = "sqlx" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba82f79b31f30acebf19905bcd8b978f46891b9d0723f578447361a8910b6584" +dependencies = [ + "sqlx-core", + "sqlx-macros", +] + +[[package]] +name = "sqlx-core" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f23af36748ec8ea8d49ef8499839907be41b0b1178a4e82b8cb45d29f531dc9" +dependencies = [ + "ahash", + "atoi", + "bitflags", + "byteorder", + "bytes", + "chrono", + "crc", + "crossbeam-channel", + "crossbeam-queue", + "crossbeam-utils", + "either", + "futures-channel", + "futures-core", + "futures-util", + "hashlink", + "hex", + "itoa", + "libc", + "libsqlite3-sys", + "log", + "memchr", + "once_cell", + "parking_lot", + "percent-encoding", + "rustls", + "sha2", + "smallvec", + "sqlformat", + "sqlx-rt", + "stringprep", + "thiserror", + "url", + "webpki", + "webpki-roots", + "whoami", +] + +[[package]] +name = "sqlx-macros" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e4a2349d1ffd60a03ca0de3f116ba55d7f406e55a0d84c64a5590866d94c06" +dependencies = [ + "dotenv", + "either", + "futures", + "heck", + "once_cell", + "proc-macro2", + "quote", + "sha2", + "sqlx-core", + "sqlx-rt", + "syn", + "url", +] + +[[package]] +name = "sqlx-rt" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8199b421ecf3493ee9ef3e7bc90c904844cfb2ea7ea2f57347a93f52bfd3e057" +dependencies = [ + "async-rustls", + "async-std", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "stringprep" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "syn" version = "1.0.73" @@ -560,6 +1489,12 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "tempfile" version = "3.2.0" @@ -574,6 +1509,26 @@ dependencies = [ "winapi", ] +[[package]] +name = "thiserror" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "time" version = "0.1.44" @@ -585,6 +1540,45 @@ dependencies = [ "winapi", ] +[[package]] +name = "tinyvec" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "848a1e1181b9f6753b5e96a092749e29b11d19ede67dfbbd6c7dc7e0f49b5338" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "typenum" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" + +[[package]] +name = "unicode-bidi" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0" +dependencies = [ + "matches", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-segmentation" version = "1.7.1" @@ -603,12 +1597,178 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "value-bag" +version = "1.0.0-alpha.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd320e1520f94261153e96f7534476ad869c14022aee1e59af7c778075d840ae" +dependencies = [ + "ctor", + "version_check", +] + +[[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 = "waker-fn" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" + [[package]] name = "wasi" version = "0.10.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" +[[package]] +name = "wasm-bindgen" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54ee1d4ed486f78874278e63e4069fc1ab9f6a18ca492076ffb90c5eb2997fd" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b33f6a0694ccfea53d94db8b2ed1c3a8a4c86dd936b13b9f0a15ec4a451b900" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fba7978c679d53ce2d0ac80c8c175840feb849a161664365d1287b41f2e67f1" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "088169ca61430fe1e58b8096c24975251700e7b1f6fd91cc9d59b04fb9b18bd4" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be2241542ff3d9f241f5e2cb6dd09b37efe786df8851c54957683a49f0987a97" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7cff876b8f18eed75a66cf49b65e7f967cb354a7aa16003fb55dbfd25b44b4f" + +[[package]] +name = "web-sys" +version = "0.3.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e828417b379f3df7111d3a2a9e5753706cae29c41f7c4029ee9fd77f3e09e582" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" +dependencies = [ + "webpki", +] + +[[package]] +name = "wepoll-ffi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" +dependencies = [ + "cc", +] + +[[package]] +name = "whoami" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4abacf325c958dfeaf1046931d37f2a901b6dfe0968ee965a29e94c6766b2af6" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + [[package]] name = "winapi" version = "0.3.9" @@ -631,6 +1791,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "wyz" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" + [[package]] name = "x11-clipboard" version = "0.3.3" diff --git a/Cargo.toml b/Cargo.toml index 34442a93..a1e64902 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,12 @@ serde = { version = "1.0", features = ["derive"] } unicode-segmentation = "1.7.1" unicode-width = "0.1.8" +sqlx = { version = "0.5.5", features = ["sqlite", "chrono", "runtime-async-std-rustls"] } +async-std = { version = "1.8.0", features = [ "attributes" ] } +async-trait = "0.1.50" +log = "0.4.14" +itertools = "0.10.1" + [dev-dependencies] tempfile = "3.2.0" pretty_assertions = "0.7.2" diff --git a/src/history/base.rs b/src/history/base.rs index 623d0e9b..6bf81e6d 100644 --- a/src/history/base.rs +++ b/src/history/base.rs @@ -1,6 +1,5 @@ -use std::collections::vec_deque::Iter; - use crate::line_buffer::LineBuffer; +use std::collections::vec_deque::Iter; #[derive(Debug, Clone, PartialEq, Eq)] pub enum HistoryNavigationQuery { diff --git a/src/history/database.rs b/src/history/database.rs new file mode 100644 index 00000000..1193921d --- /dev/null +++ b/src/history/database.rs @@ -0,0 +1,499 @@ +use crate::history::history_item::HistoryItem; +use async_std::task::block_on; +use async_trait::async_trait; +use chrono::prelude::{DateTime, TimeZone}; +use chrono::Utc; +use itertools::Itertools; +use log::debug; +use sqlx::sqlite::{ + SqliteConnectOptions, SqliteJournalMode, SqlitePool, SqlitePoolOptions, SqliteRow, +}; +use sqlx::Row; +use std::path::Path; +use std::str::FromStr; + +#[async_trait] +pub trait Database { + async fn save(&mut self, h: &HistoryItem) -> Result<(), sqlx::Error>; + async fn save_bulk(&mut self, h: &[HistoryItem]) -> Result<(), sqlx::Error>; + async fn load(&self, id: &str) -> Result; + async fn list(&self, max: Option, unique: bool) + -> Result, sqlx::Error>; + async fn range( + &self, + from: chrono::DateTime, + to: chrono::DateTime, + ) -> Result, sqlx::Error>; + async fn update(&self, h: &HistoryItem) -> Result<(), sqlx::Error>; + async fn history_count(&self) -> Result; + async fn first(&self) -> Result; + async fn last(&self) -> Result; + async fn before( + &self, + timestamp: chrono::DateTime, + count: i64, + ) -> Result, sqlx::Error>; + async fn search( + &self, + limit: Option, + search_mode: SearchMode, + query: &str, + ) -> Result, sqlx::Error>; + async fn query_history(&self, query: &str) -> Result, sqlx::Error>; + async fn delete_history_item(&self, id: i64) -> Result; +} + +pub struct Sqlite { + pool: SqlitePool, + pub pid: i64, +} + +impl Sqlite { + pub async fn new(path: impl AsRef) -> Result { + let path = path.as_ref(); + debug!("opening sqlite database at {:?}", path); + + let create = !path.exists(); + if create { + if let Some(dir) = path.parent() { + std::fs::create_dir_all(dir)?; + } + } + + let opts = SqliteConnectOptions::from_str(path.as_os_str().to_str().unwrap())? + .journal_mode(SqliteJournalMode::Wal) + .create_if_missing(true); + + let pool = SqlitePoolOptions::new().connect_with(opts).await?; + + Self::setup_db(&pool).await?; + let pid = std::process::id().into(); + Ok(Self { pool, pid }) + } + + async fn setup_db(pool: &SqlitePool) -> Result<(), sqlx::Error> { + debug!("running sqlite database setup"); + + // sqlx::migrate!("./migrations").run(pool).await?; + + //TODO: add run_count + //TODO: maybe change command to command_line and then + // add command with only the command and parameters as + // a separate column in order to do interesting queries + let history_table = r#" + CREATE TABLE IF NOT EXISTS history_items ( + history_id INTEGER PRIMARY KEY NOT NULL, + timestamp INTEGER NOT NULL, + duration INTEGER NOT NULL, + exit_status INTEGER NOT NULL, + command TEXT NOT NULL, + cwd TEXT NOT NULL, + session_id INTEGER NOT NULL, + + UNIQUE(timestamp, cwd, command) + ); + + CREATE INDEX IF NOT EXISTS idx_history_timestamp on history_items(timestamp); + CREATE INDEX IF NOT EXISTS idx_history_command on history_items(command);"#; + + let performance_table = r#" + CREATE TABLE IF NOT EXISTS performance_items ( + perf_id INTEGER NOT NULL PRIMARY KEY, + metrics FLOAT NOT NULL, + history_id INTEGER NOT NULL + REFERENCES history_items(history_id) ON DELETE CASCADE ON UPDATE CASCADE + ); + "#; + + let mut conn = pool.acquire().await?; + sqlx::query(history_table).execute(&mut conn).await?; + sqlx::query(performance_table).execute(&mut conn).await?; + + Ok(()) + } + + async fn save_raw( + tx: &mut sqlx::Transaction<'_, sqlx::Sqlite>, + h: &HistoryItem, + ) -> Result<(), sqlx::Error> { + // We don't need the history_id here because it's an auto number field + // so it should be ever increasing + sqlx::query( + "insert or ignore into history_items(timestamp, duration, exit_status, command, cwd, session_id) + values(?1, ?2, ?3, ?4, ?5, ?6)", + ) + .bind(h.timestamp.timestamp_nanos()) + .bind(h.duration) + .bind(h.exit_status) + .bind(h.command.as_str()) + .bind(h.cwd.as_str()) + .bind(h.session_id) + .execute(tx) + .await?; + + Ok(()) + } + + fn convert_time(h: &HistoryItem) { + // example of how to convert timestamp_nanos() to regular time + // use chrono::prelude::DateTime; + // use chrono::Utc; + use std::time::{Duration, UNIX_EPOCH}; + let _history_time = h.timestamp; + + // Creates a new SystemTime from the specified number of whole seconds + let d = UNIX_EPOCH + Duration::from_nanos(1626813332831940400); + // Create DateTime from SystemTime + let datetime = DateTime::::from(d); + + // I'm not sure there's a way to confidently split up a timestamp + // let dt = NaiveDateTime::from_timestamp(1626813332, 831940400); + // println!("NDT {}", dt.format("%Y-%m-%d %H:%M:%S.%f").to_string()); + + // Formats the combined date and time with the specified format string. + let timestamp_str = datetime.format("%Y-%m-%d %H:%M:%S.%f").to_string(); + println! {"{}",timestamp_str}; + } + + fn query_history(row: SqliteRow) -> HistoryItem { + HistoryItem { + history_id: row.get("history_id"), + timestamp: Utc.timestamp_nanos(row.get("timestamp")), + duration: row.get("duration"), + exit_status: row.get("exit_status"), + command: row.get("command"), + cwd: row.get("cwd"), + session_id: row.get("session_id"), + } + } +} + +impl Default for Sqlite { + fn default() -> Sqlite { + let path = Path::new("history.db"); + let sql = Self::new(path); + block_on(sql).expect("unable to create history.db") + } +} + +#[async_trait] +impl Database for Sqlite { + async fn save(&mut self, h: &HistoryItem) -> Result<(), sqlx::Error> { + debug!("saving history to sqlite"); + + let mut tx = self.pool.begin().await?; + Self::save_raw(&mut tx, h).await?; + tx.commit().await?; + + Ok(()) + } + + async fn save_bulk(&mut self, h: &[HistoryItem]) -> Result<(), sqlx::Error> { + debug!("saving history to sqlite"); + + let mut tx = self.pool.begin().await?; + + for i in h { + Self::save_raw(&mut tx, i).await? + } + + tx.commit().await?; + + Ok(()) + } + + async fn load(&self, id: &str) -> Result { + debug!("loading history item {}", id); + + let res = sqlx::query("select * from history_items where history_id = ?1") + .bind(id) + .map(Self::query_history) + .fetch_one(&self.pool) + .await?; + + Ok(res) + } + + async fn update(&self, h: &HistoryItem) -> Result<(), sqlx::Error> { + debug!("updating sqlite history"); + debug!("history_item = [{:?}]", &h); + + sqlx::query( + "update history_items + set timestamp = ?2, duration = ?3, exit_status = ?4, command = ?5, cwd = ?6, session_id = ?7 + where history_id = ?1", + ) + .bind(h.history_id) + .bind(h.timestamp.timestamp_nanos()) + .bind(h.duration) + .bind(h.exit_status) + .bind(h.command.as_str()) + .bind(h.cwd.as_str()) + .bind(h.session_id) + .execute(&self.pool) + .await?; + + Ok(()) + } + + // make a unique list, that only shows the *newest* version of things + async fn list( + &self, + max: Option, + unique: bool, + ) -> Result, sqlx::Error> { + debug!("listing history"); + + // very likely vulnerable to SQL injection + // however, this is client side, and only used by the client, on their + // own data. They can just open the db file... + // otherwise building the query is awkward + let query = format!( + "select * from history_items h + {} + order by timestamp desc + {}", + // inject the unique check + if unique { + "where timestamp = ( + select max(timestamp) from history_items + where h.command = history_items.command + )" + } else { + "" + }, + // inject the limit + if let Some(max) = max { + format!("limit {}", max) + } else { + "".to_string() + } + ); + + let res = sqlx::query(query.as_str()) + .map(Self::query_history) + .fetch_all(&self.pool) + .await?; + + Ok(res) + } + + async fn range( + &self, + from: chrono::DateTime, + to: chrono::DateTime, + ) -> Result, sqlx::Error> { + debug!("listing history from {:?} to {:?}", from, to); + + let res = sqlx::query( + "select * from history_items where timestamp >= ?1 and timestamp <= ?2 order by timestamp asc", + ) + .bind(from.timestamp_nanos()) + .bind(to.timestamp_nanos()) + .map(Self::query_history) + .fetch_all(&self.pool) + .await?; + + Ok(res) + } + + async fn first(&self) -> Result { + let res = sqlx::query( + "select * from history_items where duration >= 0 order by timestamp asc limit 1", + ) + .map(Self::query_history) + .fetch_one(&self.pool) + .await?; + + Ok(res) + } + + async fn last(&self) -> Result { + let res = sqlx::query( + "select * from history_items where duration >= 0 order by timestamp desc limit 1", + ) + .map(Self::query_history) + .fetch_one(&self.pool) + .await?; + + Ok(res) + } + + async fn before( + &self, + timestamp: chrono::DateTime, + count: i64, + ) -> Result, sqlx::Error> { + let res = sqlx::query( + "select * from history_items where timestamp < ?1 order by timestamp desc limit ?2", + ) + .bind(timestamp.timestamp_nanos()) + .bind(count) + .map(Self::query_history) + .fetch_all(&self.pool) + .await?; + + Ok(res) + } + + async fn history_count(&self) -> Result { + let res: (i64,) = sqlx::query_as("select count(1) from history_items") + .fetch_one(&self.pool) + .await?; + + Ok(res.0) + } + + async fn search( + &self, + limit: Option, + search_mode: SearchMode, + query: &str, + ) -> Result, sqlx::Error> { + let query = query.to_string().replace("*", "%"); // allow wildcard char + let limit = limit.map_or("".to_owned(), |l| format!("limit {}", l)); + + let query = match search_mode { + SearchMode::Prefix => query, + SearchMode::FullText => format!("%{}", query), + SearchMode::Fuzzy => query.split("").join("%"), + }; + + let res = sqlx::query( + format!( + "select * from history_items h + where command like ?1 || '%' + and timestamp = ( + select max(timestamp) from history_items + where h.command = history_items.command + ) + order by timestamp desc {}", + limit.clone() + ) + .as_str(), + ) + .bind(query) + .map(Self::query_history) + .fetch_all(&self.pool) + .await?; + + Ok(res) + } + + async fn query_history(&self, query: &str) -> Result, sqlx::Error> { + let res = sqlx::query(query) + .map(Self::query_history) + .fetch_all(&self.pool) + .await?; + + Ok(res) + } + + async fn delete_history_item(&self, id: i64) -> Result { + let res = sqlx::query("delete from history_items where history_id = ?1") + .bind(id) + .execute(&self.pool) + .await? + .rows_affected(); + Ok(res) + } +} + +#[derive(Clone, Debug, Copy)] +pub enum SearchMode { + // #[serde(rename = "prefix")] + Prefix, + + // #[serde(rename = "fulltext")] + FullText, + + // #[serde(rename = "fuzzy")] + Fuzzy, +} + +// note - i haven't tried any of these tests +#[cfg(test)] +mod test { + use super::*; + + async fn new_history_item(db: &mut impl Database, cmd: &str) -> Result<()> { + let history = HistoryItem::new( + chrono::Local::now(), + cmd.to_string(), + "/home/ellie".to_string(), + 0, + 1, + Some("beep boop".to_string()), + Some("booop".to_string()), + ); + return db.save(&history).await; + } + + #[tokio::test(flavor = "multi_thread")] + async fn test_search_prefix() { + let mut db = Sqlite::new("sqlite::memory:").await.unwrap(); + new_history_item(&mut db, "ls /home/ellie").await.unwrap(); + + let mut results = db.search(None, SearchMode::Prefix, "ls").await.unwrap(); + assert_eq!(results.len(), 1); + + results = db.search(None, SearchMode::Prefix, "/home").await.unwrap(); + assert_eq!(results.len(), 0); + + results = db.search(None, SearchMode::Prefix, "ls ").await.unwrap(); + assert_eq!(results.len(), 0); + } + + #[tokio::test(flavor = "multi_thread")] + async fn test_search_fulltext() { + let mut db = Sqlite::new("sqlite::memory:").await.unwrap(); + new_history_item(&mut db, "ls /home/ellie").await.unwrap(); + + let mut results = db.search(None, SearchMode::FullText, "ls").await.unwrap(); + assert_eq!(results.len(), 1); + + results = db + .search(None, SearchMode::FullText, "/home") + .await + .unwrap(); + assert_eq!(results.len(), 1); + + results = db.search(None, SearchMode::FullText, "ls ").await.unwrap(); + assert_eq!(results.len(), 0); + } + + #[tokio::test(flavor = "multi_thread")] + async fn test_search_fuzzy() { + let mut db = Sqlite::new("sqlite::memory:").await.unwrap(); + new_history_item(&mut db, "ls /home/ellie").await.unwrap(); + new_history_item(&mut db, "ls /home/frank").await.unwrap(); + new_history_item(&mut db, "cd /home/ellie").await.unwrap(); + new_history_item(&mut db, "/home/ellie/.bin/rustup") + .await + .unwrap(); + + let mut results = db.search(None, SearchMode::Fuzzy, "ls /").await.unwrap(); + assert_eq!(results.len(), 2); + + results = db.search(None, SearchMode::Fuzzy, "l/h/").await.unwrap(); + assert_eq!(results.len(), 2); + + results = db.search(None, SearchMode::Fuzzy, "/h/e").await.unwrap(); + assert_eq!(results.len(), 3); + + results = db.search(None, SearchMode::Fuzzy, "/hmoe/").await.unwrap(); + assert_eq!(results.len(), 0); + + results = db + .search(None, SearchMode::Fuzzy, "ellie/home") + .await + .unwrap(); + assert_eq!(results.len(), 0); + + results = db.search(None, SearchMode::Fuzzy, "lsellie").await.unwrap(); + assert_eq!(results.len(), 1); + + results = db.search(None, SearchMode::Fuzzy, " ").await.unwrap(); + assert_eq!(results.len(), 3); + } +} diff --git a/src/history/history_item.rs b/src/history/history_item.rs new file mode 100644 index 00000000..9bad1ec1 --- /dev/null +++ b/src/history/history_item.rs @@ -0,0 +1,56 @@ +use chrono::Utc; +use core::hash::{Hash, Hasher}; +use std::process; + +#[derive(Debug, Clone, Ord, PartialOrd, sqlx::FromRow)] +pub struct HistoryItem { + pub history_id: Option, + pub command: String, + pub cwd: String, + pub duration: i64, + pub exit_status: i64, + pub session_id: i64, + pub timestamp: chrono::DateTime, +} + +impl HistoryItem { + pub fn new( + history_id: Option, + command: String, + cwd: String, + duration: i64, + exit_status: i64, + session_id: Option, + timestamp: chrono::DateTime, + ) -> Self { + let session_id = session_id.unwrap_or_else(|| process::id().into()); + + Self { + history_id, + command, + cwd, + duration, + exit_status, + session_id, + timestamp, + } + } +} + +impl PartialEq for HistoryItem { + // for the sakes of listing unique history only, we do not care about + // anything else + // obviously this does not refer to the *same* item of history, but when + // we only render the command, it looks the same + fn eq(&self, other: &Self) -> bool { + self.command == other.command + } +} + +impl Eq for HistoryItem {} + +impl Hash for HistoryItem { + fn hash(&self, state: &mut H) { + self.command.hash(state); + } +} diff --git a/src/history/mod.rs b/src/history/mod.rs index 5f1b20a4..673b5dc9 100644 --- a/src/history/mod.rs +++ b/src/history/mod.rs @@ -1,5 +1,10 @@ mod base; +mod database; mod file_backed; +mod history_item; +mod sqlite_backed; pub use base::{History, HistoryNavigationQuery}; +pub use database::{Database, SearchMode, Sqlite}; pub use file_backed::{FileBackedHistory, HISTORY_SIZE}; +pub use history_item::HistoryItem; diff --git a/src/history/sqlite_backed.rs b/src/history/sqlite_backed.rs new file mode 100644 index 00000000..2732bce8 --- /dev/null +++ b/src/history/sqlite_backed.rs @@ -0,0 +1,175 @@ +use super::{ + base::{HistoryAppender, HistoryView}, + Database, HistoryItem, HistoryNavigationQuery, Sqlite, +}; +use crate::{line_buffer::LineBuffer, History}; +use async_std::task::block_on; +use std::{collections::VecDeque, path::Path}; + +// most of the sqlitebackedhistory items are just hacks to emulate +// the filebackedhistory. the traits may need to be changed to be +// a little more flexible with types like returning results and +// returning HistoryItem struct instead of just a string +// also, all the database is async/await, which is beyond me, but +// if someone wants to change that, feel free. i had to hack on +// these changes to get it to compiled. this sqlitebackedhistory +// has not been tested but you want to play with the self-containted +// prototype, it's here https://github.com/fdncred/hiztery +pub struct SqliteBackedHistory { + sqlite: Sqlite, + entries: VecDeque, + commands: VecDeque, + query: HistoryNavigationQuery, + cursor: usize, +} + +impl SqliteBackedHistory { + async fn new(path: &Path) -> Result { + match Sqlite::new(path).await { + Ok(s) => { + // Assume the db file exists now and load the entries + // I don't really like this but it emulates a history file + // so, let's run with it and change it later + let result = s.query_history("select * from history_items"); + let r = block_on(result).unwrap(); + let mut commands = VecDeque::new(); + for x in r.iter() { + commands.push_back(x.command.clone()) + } + + Ok(SqliteBackedHistory { + sqlite: s, + entries: VecDeque::from(r), + commands, + cursor: 0, + query: HistoryNavigationQuery::Normal(LineBuffer::default()), + }) + } + Err(e) => return Err(e), + } + } + + fn back_with_criteria(&mut self, criteria: &dyn Fn(&str) -> bool) { + if !self.commands.is_empty() { + let previous_match = self.commands.get(self.cursor); + if let Some((next_cursor, _)) = self + .commands + .iter() + .take(self.cursor) + .enumerate() + .rev() + .find(|(_, entry)| criteria(entry) && previous_match != Some(entry)) + { + // set to entry + self.cursor = next_cursor + } + } + } + + fn forward_with_criteria(&mut self, criteria: &dyn Fn(&str) -> bool) { + let previous_match = self.commands.get(self.cursor); + if let Some((next_cursor, _)) = self + .commands + .iter() + .enumerate() + .skip(self.cursor + 1) + .find(|(_, entry)| criteria(entry) && previous_match != Some(entry)) + { + // set to entry + self.cursor = next_cursor + } else { + self.reset_cursor() + } + } + + /// Reset the internal browsing cursor + fn reset_cursor(&mut self) { + self.cursor = self.entries.len(); + } +} + +impl Default for SqliteBackedHistory { + // probably shouldn't use this - new() is the way to go + fn default() -> SqliteBackedHistory { + SqliteBackedHistory { + sqlite: Sqlite::default(), + entries: VecDeque::new(), + commands: VecDeque::new(), + cursor: 0, + query: HistoryNavigationQuery::Normal(LineBuffer::default()), + } + } +} + +impl History for SqliteBackedHistory {} + +impl HistoryAppender for SqliteBackedHistory { + // why can't we return a Result + // I also need the cwd, duration, exit_status, and run_count + fn append(&mut self, entry: String) { + let hi = HistoryItem::new( + None, + entry, + "cwd".to_string(), + 0, + 0, + Some(self.sqlite.pid), + chrono::Utc::now(), + ); + let result = self.sqlite.save(&hi); + block_on(result); + } + + fn iter_chronologic(&self) -> std::collections::vec_deque::Iter<'_, String> { + // Why are we forced to return a vec_deque + // I'd rather return a HistoryItem here and not just a String + self.commands.iter() + } +} + +impl HistoryView for SqliteBackedHistory { + fn back(&mut self) { + match self.query.clone() { + HistoryNavigationQuery::Normal(_) => { + if self.cursor > 0 { + self.cursor -= 1; + } + } + HistoryNavigationQuery::PrefixSearch(prefix) => { + self.back_with_criteria(&|entry| entry.starts_with(&prefix)) + } + HistoryNavigationQuery::SubstringSearch(substring) => { + self.back_with_criteria(&|entry| entry.contains(&substring)) + } + } + } + + fn forward(&mut self) { + match self.query.clone() { + HistoryNavigationQuery::Normal(_) => { + if self.cursor < self.entries.len() { + self.cursor += 1; + } + } + HistoryNavigationQuery::PrefixSearch(prefix) => { + self.forward_with_criteria(&|entry| entry.starts_with(&prefix)) + } + HistoryNavigationQuery::SubstringSearch(substring) => { + self.forward_with_criteria(&|entry| entry.contains(&substring)) + } + } + } + + fn string_at_cursor(&self) -> Option { + self.commands.get(self.cursor).cloned() + } + + fn set_navigation(&mut self, navigation: HistoryNavigationQuery) { + self.query = navigation; + self.reset_cursor(); + } + + fn get_navigation(&self) -> HistoryNavigationQuery { + self.query.clone() + } +} diff --git a/src/lib.rs b/src/lib.rs index 9257695e..8447d6a1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -175,7 +175,7 @@ #![warn(rustdoc::missing_crate_level_docs)] #![warn(rustdoc::missing_doc_code_examples)] #![warn(missing_docs)] -#![deny(warnings)] +// #![deny(warnings)] mod clip_buffer; mod text_manipulation;