Bugfixes, show time ago, perf improvements
Also allow unique listing and more ergonomic cwd usage
This commit is contained in:
parent
a9b117aad7
commit
4a50ce3666
20 changed files with 586 additions and 347 deletions
382
Cargo.lock
generated
382
Cargo.lock
generated
|
@ -51,9 +51,9 @@ checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
|||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.49"
|
||||
version = "0.1.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589652ce7ccb335d1e7ecb3be145425702b290dbcb7029bbeaae263fc1d87b48"
|
||||
checksum = "0b98e84bbb4cbcdd97da190ba0c58a1bb0de2c1fdf67d159e192ed766aeca722"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -94,13 +94,12 @@ dependencies = [
|
|||
"cli-table",
|
||||
"directories",
|
||||
"eyre",
|
||||
"fern",
|
||||
"fork",
|
||||
"humantime",
|
||||
"humantime 2.1.0",
|
||||
"indicatif",
|
||||
"itertools",
|
||||
"log",
|
||||
"reqwest",
|
||||
"pretty_env_logger",
|
||||
"rusqlite",
|
||||
"serde 1.0.125",
|
||||
"serde_derive",
|
||||
|
@ -125,7 +124,7 @@ dependencies = [
|
|||
"directories",
|
||||
"eyre",
|
||||
"fern",
|
||||
"humantime",
|
||||
"humantime 2.1.0",
|
||||
"indicatif",
|
||||
"itertools",
|
||||
"log",
|
||||
|
@ -180,7 +179,6 @@ dependencies = [
|
|||
"log",
|
||||
"parse_duration",
|
||||
"rand 0.8.3",
|
||||
"reqwest",
|
||||
"rmp-serde",
|
||||
"rust-crypto",
|
||||
"serde 1.0.125",
|
||||
|
@ -270,9 +268,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "build_const"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39"
|
||||
checksum = "b4ae4235e6dac0694637c763029ecea1a2ec9e4e06ec2729bd21ba4d9c863eb7"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
|
@ -282,9 +280,9 @@ checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe"
|
|||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.2"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b"
|
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
|
@ -300,9 +298,9 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.66"
|
||||
version = "1.0.67"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48"
|
||||
checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
|
@ -326,9 +324,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "chrono-english"
|
||||
version = "0.1.4"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4233ee19352739cfdcb5d7c2085005b166f6170ef2845ed9eef27a8fa5f95206"
|
||||
checksum = "0be5180df5f7c41fc2416bc038bc8d78d44db8136c415b94ccbc95f523dc38e9"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"scanlex",
|
||||
|
@ -402,9 +400,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.14.0"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7cc80946b3480f421c2f17ed1cb841753a371c7c5104f51d507e13f532c856aa"
|
||||
checksum = "3993e6445baa160675931ec041a5e03ca84b9c6e32a056150d3aa2bdda0a1f45"
|
||||
dependencies = [
|
||||
"encode_unicode",
|
||||
"lazy_static",
|
||||
|
@ -421,22 +419,6 @@ version = "0.1.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b"
|
||||
|
||||
[[package]]
|
||||
name = "cpuid-bool"
|
||||
version = "0.1.2"
|
||||
|
@ -474,9 +456,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.1"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d"
|
||||
checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
|
@ -601,6 +583,19 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"humantime 1.3.0",
|
||||
"log",
|
||||
"regex",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "eyre"
|
||||
version = "0.6.5"
|
||||
|
@ -639,21 +634,6 @@ version = "1.0.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
||||
dependencies = [
|
||||
"foreign-types-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types-shared"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||
|
||||
[[package]]
|
||||
name = "fork"
|
||||
version = "0.1.18"
|
||||
|
@ -935,9 +915,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "httparse"
|
||||
version = "1.3.5"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "615caabe2c3160b313d52ccc905335f4ed5f10881dd63dc5699d47e90be85691"
|
||||
checksum = "4a1ce40d6fc9764887c2fdc7305c3dcc429ba11ff981c1509416afd5697e4437"
|
||||
|
||||
[[package]]
|
||||
name = "httpdate"
|
||||
|
@ -945,6 +925,15 @@ version = "0.3.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47"
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
|
||||
dependencies = [
|
||||
"quick-error",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.1.0"
|
||||
|
@ -976,23 +965,25 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-tls"
|
||||
version = "0.5.0"
|
||||
name = "hyper-rustls"
|
||||
version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
|
||||
checksum = "5f9f7a97316d44c0af9b0301e65010573a853a9fc97046d7331d7f6bc0fd5a64"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"hyper",
|
||||
"native-tls",
|
||||
"log",
|
||||
"rustls",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tokio-rustls",
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.2.2"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89829a5d69c23d348314a7ac337fe39173b61149a9864deabd260983aed48c21"
|
||||
checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
|
||||
dependencies = [
|
||||
"matches",
|
||||
"unicode-bidi",
|
||||
|
@ -1001,15 +992,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "indenter"
|
||||
version = "0.3.2"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4d5eb2e114fec2b7fe0fadc22888ad2658789bb7acac4dbee9cf8389f971ec8"
|
||||
checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.6.1"
|
||||
version = "1.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b"
|
||||
checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
|
@ -1096,9 +1087,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.86"
|
||||
version = "0.2.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c"
|
||||
checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
|
||||
|
||||
[[package]]
|
||||
name = "libsodium-sys"
|
||||
|
@ -1130,9 +1121,9 @@ checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
|
|||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.2"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312"
|
||||
checksum = "5a3c91c24eae6777794bb1997ad98bbb87daf92890acab859f7eaa4320333176"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
@ -1231,24 +1222,6 @@ dependencies = [
|
|||
"twoway",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8d96b2e1c8da3957d58100b09f102c6d9cfdfced01b7ec5a8974044bb09dbd4"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"log",
|
||||
"openssl",
|
||||
"openssl-probe",
|
||||
"openssl-sys",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
"security-framework-sys",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "5.1.2"
|
||||
|
@ -1392,9 +1365,9 @@ checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
|
|||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.5.2"
|
||||
version = "1.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0"
|
||||
checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
|
@ -1402,39 +1375,6 @@ version = "0.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a61075b62a23fef5a29815de7536d940aa35ce96d18ce0cc5076272db678a577"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"openssl-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.61"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "313752393519e876837e09e1fa183ddef0be7735868dced3196f4472d536277f"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.11.1"
|
||||
|
@ -1455,7 +1395,7 @@ dependencies = [
|
|||
"cfg-if",
|
||||
"instant",
|
||||
"libc",
|
||||
"redox_syscall 0.2.4",
|
||||
"redox_syscall 0.2.6",
|
||||
"smallvec",
|
||||
"winapi",
|
||||
]
|
||||
|
@ -1479,18 +1419,18 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
|||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.0.6"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc174859768806e91ae575187ada95c91a29e96a98dc5d2cd9a1fed039501ba6"
|
||||
checksum = "c7509cc106041c40a4518d2af7a61530e1eed0e6285296a3d8c5472806ccc4a4"
|
||||
dependencies = [
|
||||
"pin-project-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-internal"
|
||||
version = "1.0.6"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a490329918e856ed1b083f244e3bfe2d8c4f336407e4ea9e1a9f479ff09049e5"
|
||||
checksum = "48c950132583b500556b1efd71d45b319029f2b71518d979fcc208e16b42426f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -1521,6 +1461,16 @@ version = "0.2.10"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
|
||||
|
||||
[[package]]
|
||||
name = "pretty_env_logger"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d"
|
||||
dependencies = [
|
||||
"env_logger",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
|
@ -1723,9 +1673,9 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
|
|||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.4"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05ec8ca9416c5ea37062b502703cd7fcb207736bc294f6e0cf367ac6fc234570"
|
||||
checksum = "8270314b5ccceb518e7e578952f0b72b88222d02e8f77f5ecf7abbb673539041"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
@ -1736,7 +1686,7 @@ version = "0.1.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f"
|
||||
dependencies = [
|
||||
"redox_syscall 0.2.4",
|
||||
"redox_syscall 0.2.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1757,19 +1707,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
|
||||
dependencies = [
|
||||
"getrandom 0.2.2",
|
||||
"redox_syscall 0.2.4",
|
||||
"redox_syscall 0.2.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.4.3"
|
||||
version = "1.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a"
|
||||
checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
"thread_local",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1783,9 +1732,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.22"
|
||||
version = "0.6.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581"
|
||||
checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548"
|
||||
|
||||
[[package]]
|
||||
name = "remove_dir_all"
|
||||
|
@ -1810,27 +1759,43 @@ dependencies = [
|
|||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"hyper-tls",
|
||||
"hyper-rustls",
|
||||
"ipnet",
|
||||
"js-sys",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"mime",
|
||||
"native-tls",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"rustls",
|
||||
"serde 1.0.125",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tokio-rustls",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"webpki-roots",
|
||||
"winreg",
|
||||
]
|
||||
|
||||
[[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 = "rmp"
|
||||
version = "0.8.10"
|
||||
|
@ -1904,6 +1869,19 @@ version = "0.3.24"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
|
||||
|
||||
[[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"
|
||||
|
@ -1922,16 +1900,6 @@ version = "0.1.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "088c5d71572124929ea7549a8ce98e1a6fd33d0a38367b09027b382e67c033db"
|
||||
|
||||
[[package]]
|
||||
name = "schannel"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scoped-tls"
|
||||
version = "1.0.0"
|
||||
|
@ -1945,26 +1913,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.2.0"
|
||||
name = "sct"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3670b1d2fdf6084d192bc71ead7aabe6c06aa2ea3fbd9cc3ac111fa5c2b1bd84"
|
||||
checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"core-foundation",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"security-framework-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3676258fd3cfe2c9a0ec99ce3038798d847ce3e4bb17746373eb9f0f1ac16339"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2074,9 +2029,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.2"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
|
||||
checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
|
@ -2105,6 +2060,12 @@ dependencies = [
|
|||
"serde 1.0.125",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
@ -2162,6 +2123,7 @@ dependencies = [
|
|||
"parking_lot",
|
||||
"percent-encoding",
|
||||
"rand 0.8.3",
|
||||
"rustls",
|
||||
"serde 1.0.125",
|
||||
"serde_json",
|
||||
"sha-1",
|
||||
|
@ -2174,6 +2136,8 @@ dependencies = [
|
|||
"tokio-stream",
|
||||
"url",
|
||||
"uuid",
|
||||
"webpki",
|
||||
"webpki-roots",
|
||||
"whoami",
|
||||
]
|
||||
|
||||
|
@ -2202,10 +2166,9 @@ version = "0.5.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6ae97ab05063ed515cdc23d90253213aa24dda0a288c5ec079af3d10f9771bc"
|
||||
dependencies = [
|
||||
"native-tls",
|
||||
"once_cell",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tokio-rustls",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2286,7 +2249,7 @@ dependencies = [
|
|||
"cfg-if",
|
||||
"libc",
|
||||
"rand 0.8.3",
|
||||
"redox_syscall 0.2.4",
|
||||
"redox_syscall 0.2.6",
|
||||
"remove_dir_all",
|
||||
"winapi",
|
||||
]
|
||||
|
@ -2318,7 +2281,7 @@ checksum = "077185e2eac69c3f8379a4298e1e07cd36beb962290d4a51199acf0fdc10607e"
|
|||
dependencies = [
|
||||
"libc",
|
||||
"numtoa",
|
||||
"redox_syscall 0.2.4",
|
||||
"redox_syscall 0.2.6",
|
||||
"redox_termios",
|
||||
]
|
||||
|
||||
|
@ -2351,15 +2314,6 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.43"
|
||||
|
@ -2372,9 +2326,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.1.1"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "317cca572a0e89c3ce0ca1f1bdc9369547fe318a683418e42ac8f59d14701023"
|
||||
checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
|
@ -2387,9 +2341,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
|||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.4.0"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "134af885d758d645f0f0505c9a8b3f9bf8a348fd822e112ab5248138348f1722"
|
||||
checksum = "83f0c8e7c0addab50b663055baf787d0af7f413a46e6e7fb9559a4e4db7137a5"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bytes",
|
||||
|
@ -2417,13 +2371,14 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-native-tls"
|
||||
version = "0.3.0"
|
||||
name = "tokio-rustls"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b"
|
||||
checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6"
|
||||
dependencies = [
|
||||
"native-tls",
|
||||
"rustls",
|
||||
"tokio",
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2452,9 +2407,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.6.5"
|
||||
version = "0.6.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5143d049e85af7fbc36f5454d990e62c2df705b3589f123b71f441b6b59f443f"
|
||||
checksum = "940a12c99365c31ea8dd9ba04ec1be183ffe4920102bb7122c2f515437601e8e"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
|
@ -2549,9 +2504,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.12.0"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
|
||||
checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
|
@ -2564,9 +2519,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.4"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
|
||||
checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0"
|
||||
dependencies = [
|
||||
"matches",
|
||||
]
|
||||
|
@ -2604,6 +2559,12 @@ 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.1"
|
||||
|
@ -2624,9 +2585,9 @@ checksum = "c9232eb53352b4442e40d7900465dfc534e8cb2dc8f18656fcb2ac16112b5593"
|
|||
|
||||
[[package]]
|
||||
name = "utf-8"
|
||||
version = "0.7.5"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7"
|
||||
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
|
@ -2639,9 +2600,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.11"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb"
|
||||
checksum = "cbdbff6266a24120518560b5dc983096efb98462e51d0d68169895b237be3e5d"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
|
@ -2651,9 +2612,9 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
|||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.2"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
|
||||
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
|
||||
|
||||
[[package]]
|
||||
name = "want"
|
||||
|
@ -2784,6 +2745,25 @@ dependencies = [
|
|||
"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 = "whoami"
|
||||
version = "1.1.2"
|
||||
|
|
|
@ -15,7 +15,7 @@ atuin-client = { path = "atuin-client", version = "0.6.0" }
|
|||
atuin-common = { path = "atuin-common", version = "0.6.0" }
|
||||
|
||||
log = "0.4"
|
||||
fern = {version = "0.6.0", features = ["colored"] }
|
||||
pretty_env_logger = "0.4"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
eyre = "0.6"
|
||||
structopt = "0.3"
|
||||
|
@ -33,7 +33,6 @@ tokio = { version = "1", features = ["full"] }
|
|||
async-trait = "0.1.49"
|
||||
chrono-english = "0.1.4"
|
||||
cli-table = "0.4"
|
||||
reqwest = { version = "0.11", features = ["blocking", "json"] }
|
||||
base64 = "0.13.0"
|
||||
humantime = "2.1.0"
|
||||
|
||||
|
|
|
@ -17,6 +17,12 @@ COPY --from=cacher $CARGO_HOME $CARGO_HOME
|
|||
RUN cargo build --release --bin atuin
|
||||
|
||||
FROM debian:buster-slim as runtime
|
||||
|
||||
WORKDIR app
|
||||
|
||||
ENV TZ=Etc/UTC
|
||||
ENV RUST_LOG=info
|
||||
ENV ATUIN_CONFIG_DIR=/config
|
||||
|
||||
COPY --from=builder /app/target/release/atuin /usr/local/bin
|
||||
ENTRYPOINT ["/usr/local/bin/atuin"]
|
||||
|
|
|
@ -26,7 +26,7 @@ serde = "1.0.125"
|
|||
serde_json = "1.0.64"
|
||||
rmp-serde = "0.15.4"
|
||||
sodiumoxide = "0.2.6"
|
||||
reqwest = { version = "0.11", features = ["blocking", "json"] }
|
||||
reqwest = { version = "0.11", features = ["blocking", "json", "rustls-tls"], default-features = false }
|
||||
base64 = "0.13.0"
|
||||
parse_duration = "2.1.1"
|
||||
rand = "0.8.3"
|
||||
|
|
|
@ -1,15 +1,24 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use chrono::Utc;
|
||||
use eyre::Result;
|
||||
use reqwest::header::{HeaderMap, AUTHORIZATION};
|
||||
use reqwest::Url;
|
||||
use eyre::{eyre, Result};
|
||||
use reqwest::header::{HeaderMap, AUTHORIZATION, USER_AGENT};
|
||||
use reqwest::{StatusCode, Url};
|
||||
use sodiumoxide::crypto::secretbox;
|
||||
|
||||
use atuin_common::api::{AddHistoryRequest, CountResponse, SyncHistoryResponse};
|
||||
use atuin_common::api::{
|
||||
AddHistoryRequest, CountResponse, LoginResponse, RegisterResponse, SyncHistoryResponse,
|
||||
};
|
||||
use atuin_common::utils::hash_str;
|
||||
|
||||
use crate::encryption::decrypt;
|
||||
use crate::encryption::{decode_key, decrypt};
|
||||
use crate::history::History;
|
||||
|
||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
// TODO: remove all references to the encryption key from this
|
||||
// It should be handled *elsewhere*
|
||||
|
||||
pub struct Client<'a> {
|
||||
sync_addr: &'a str,
|
||||
token: &'a str,
|
||||
|
@ -17,14 +26,70 @@ pub struct Client<'a> {
|
|||
client: reqwest::Client,
|
||||
}
|
||||
|
||||
pub fn register(
|
||||
address: &str,
|
||||
username: &str,
|
||||
email: &str,
|
||||
password: &str,
|
||||
) -> Result<RegisterResponse> {
|
||||
let mut map = HashMap::new();
|
||||
map.insert("username", username);
|
||||
map.insert("email", email);
|
||||
map.insert("password", password);
|
||||
|
||||
let url = format!("{}/user/{}", address, username);
|
||||
let resp = reqwest::blocking::get(url)?;
|
||||
|
||||
if resp.status().is_success() {
|
||||
return Err(eyre!("username already in use"));
|
||||
}
|
||||
|
||||
let url = format!("{}/register", address);
|
||||
let client = reqwest::blocking::Client::new();
|
||||
let resp = client
|
||||
.post(url)
|
||||
.header(USER_AGENT, format!("atuin/{}", VERSION))
|
||||
.json(&map)
|
||||
.send()?;
|
||||
|
||||
if !resp.status().is_success() {
|
||||
return Err(eyre!("failed to register user"));
|
||||
}
|
||||
|
||||
let session = resp.json::<RegisterResponse>()?;
|
||||
Ok(session)
|
||||
}
|
||||
|
||||
pub fn login(address: &str, username: &str, password: &str) -> Result<LoginResponse> {
|
||||
let mut map = HashMap::new();
|
||||
map.insert("username", username);
|
||||
map.insert("password", password);
|
||||
|
||||
let url = format!("{}/login", address);
|
||||
let client = reqwest::blocking::Client::new();
|
||||
|
||||
let resp = client
|
||||
.post(url)
|
||||
.header(USER_AGENT, format!("atuin/{}", VERSION))
|
||||
.json(&map)
|
||||
.send()?;
|
||||
|
||||
if resp.status() != reqwest::StatusCode::OK {
|
||||
return Err(eyre!("invalid login details"));
|
||||
}
|
||||
|
||||
let session = resp.json::<LoginResponse>()?;
|
||||
Ok(session)
|
||||
}
|
||||
|
||||
impl<'a> Client<'a> {
|
||||
pub fn new(sync_addr: &'a str, token: &'a str, key: secretbox::Key) -> Self {
|
||||
Client {
|
||||
pub fn new(sync_addr: &'a str, token: &'a str, key: String) -> Result<Self> {
|
||||
Ok(Client {
|
||||
sync_addr,
|
||||
token,
|
||||
key,
|
||||
key: decode_key(key)?,
|
||||
client: reqwest::Client::new(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn count(&self) -> Result<i64> {
|
||||
|
@ -36,7 +101,17 @@ impl<'a> Client<'a> {
|
|||
let mut headers = HeaderMap::new();
|
||||
headers.insert(AUTHORIZATION, token);
|
||||
|
||||
let resp = self.client.get(url).headers(headers).send().await?;
|
||||
let resp = self
|
||||
.client
|
||||
.get(url)
|
||||
.header(USER_AGENT, format!("atuin/{}", VERSION))
|
||||
.headers(headers)
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
if resp.status() != StatusCode::OK {
|
||||
return Err(eyre!("failed to get count (are you logged in?)"));
|
||||
}
|
||||
|
||||
let count = resp.json::<CountResponse>().await?;
|
||||
|
||||
|
@ -66,6 +141,7 @@ impl<'a> Client<'a> {
|
|||
.client
|
||||
.get(url)
|
||||
.header(AUTHORIZATION, format!("Token {}", self.token))
|
||||
.header(USER_AGENT, format!("atuin/{}", VERSION))
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
|
@ -88,9 +164,33 @@ impl<'a> Client<'a> {
|
|||
.post(url)
|
||||
.json(history)
|
||||
.header(AUTHORIZATION, format!("Token {}", self.token))
|
||||
.header(USER_AGENT, format!("atuin/{}", VERSION))
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn login(&self, username: &str, password: &str) -> Result<LoginResponse> {
|
||||
let mut map = HashMap::new();
|
||||
map.insert("username", username);
|
||||
map.insert("password", password);
|
||||
|
||||
let url = format!("{}/login", self.sync_addr);
|
||||
let resp = self
|
||||
.client
|
||||
.post(url)
|
||||
.json(&map)
|
||||
.header(USER_AGENT, format!("atuin/{}", VERSION))
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
if resp.status() != reqwest::StatusCode::OK {
|
||||
return Err(eyre!("invalid login details"));
|
||||
}
|
||||
|
||||
let session = resp.json::<LoginResponse>().await?;
|
||||
|
||||
Ok(session)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use chrono::prelude::*;
|
|||
use chrono::Utc;
|
||||
use std::path::Path;
|
||||
|
||||
use eyre::Result;
|
||||
use eyre::{eyre, Result};
|
||||
|
||||
use rusqlite::{params, Connection};
|
||||
use rusqlite::{Params, Transaction};
|
||||
|
@ -14,7 +14,7 @@ pub trait Database {
|
|||
fn save_bulk(&mut self, h: &[History]) -> Result<()>;
|
||||
|
||||
fn load(&self, id: &str) -> Result<History>;
|
||||
fn list(&self) -> Result<Vec<History>>;
|
||||
fn list(&self, max: Option<usize>, unique: bool) -> Result<Vec<History>>;
|
||||
fn range(&self, from: chrono::DateTime<Utc>, to: chrono::DateTime<Utc>)
|
||||
-> Result<Vec<History>>;
|
||||
|
||||
|
@ -27,6 +27,8 @@ pub trait Database {
|
|||
fn before(&self, timestamp: chrono::DateTime<Utc>, count: i64) -> Result<Vec<History>>;
|
||||
|
||||
fn prefix_search(&self, query: &str) -> Result<Vec<History>>;
|
||||
|
||||
fn search(&self, cwd: Option<String>, exit: Option<i64>, query: &str) -> Result<Vec<History>>;
|
||||
}
|
||||
|
||||
// Intended for use on a developer machine and not a sync server.
|
||||
|
@ -81,6 +83,16 @@ impl Sqlite {
|
|||
[],
|
||||
)?;
|
||||
|
||||
conn.execute(
|
||||
"create index if not exists idx_history_timestamp on history(timestamp)",
|
||||
[],
|
||||
)?;
|
||||
|
||||
conn.execute(
|
||||
"create index if not exists idx_history_command on history(command)",
|
||||
[],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -136,16 +148,19 @@ impl Database for Sqlite {
|
|||
}
|
||||
|
||||
fn load(&self, id: &str) -> Result<History> {
|
||||
debug!("loading history item");
|
||||
debug!("loading history item {}", id);
|
||||
|
||||
let mut stmt = self.conn.prepare(
|
||||
let history = self.query(
|
||||
"select id, timestamp, duration, exit, command, cwd, session, hostname from history
|
||||
where id = ?1",
|
||||
where id = ?1 limit 1",
|
||||
&[id],
|
||||
)?;
|
||||
|
||||
let history = stmt.query_row(params![id], |row| {
|
||||
history_from_sqlite_row(Some(id.to_string()), row)
|
||||
})?;
|
||||
if history.is_empty() {
|
||||
return Err(eyre!("could not find history with id {}", id));
|
||||
}
|
||||
|
||||
let history = history[0].clone();
|
||||
|
||||
Ok(history)
|
||||
}
|
||||
|
@ -163,16 +178,39 @@ impl Database for Sqlite {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn list(&self) -> Result<Vec<History>> {
|
||||
// make a unique list, that only shows the *newest* version of things
|
||||
fn list(&self, max: Option<usize>, unique: bool) -> Result<Vec<History>> {
|
||||
debug!("listing history");
|
||||
|
||||
let mut stmt = self
|
||||
.conn
|
||||
.prepare("SELECT * FROM history order by timestamp asc")?;
|
||||
// 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 h
|
||||
{}
|
||||
order by timestamp desc
|
||||
{}",
|
||||
// inject the unique check
|
||||
if unique {
|
||||
"where timestamp = (
|
||||
select max(timestamp) from history
|
||||
where h.command = history.command
|
||||
)"
|
||||
} else {
|
||||
""
|
||||
},
|
||||
// inject the limit
|
||||
if let Some(max) = max {
|
||||
format!("limit {}", max)
|
||||
} else {
|
||||
"".to_string()
|
||||
}
|
||||
);
|
||||
|
||||
let history_iter = stmt.query_map(params![], |row| history_from_sqlite_row(None, row))?;
|
||||
let history = self.query(query.as_str(), params![])?;
|
||||
|
||||
Ok(history_iter.filter_map(Result::ok).collect())
|
||||
Ok(history)
|
||||
}
|
||||
|
||||
fn range(
|
||||
|
@ -207,7 +245,7 @@ impl Database for Sqlite {
|
|||
fn last(&self) -> Result<History> {
|
||||
let mut stmt = self
|
||||
.conn
|
||||
.prepare("SELECT * FROM history order by timestamp desc limit 1")?;
|
||||
.prepare("SELECT * FROM history where duration >= 0 order by timestamp desc limit 1")?;
|
||||
|
||||
let history = stmt.query_row(params![], |row| history_from_sqlite_row(None, row))?;
|
||||
|
||||
|
@ -235,9 +273,17 @@ impl Database for Sqlite {
|
|||
}
|
||||
|
||||
fn prefix_search(&self, query: &str) -> Result<Vec<History>> {
|
||||
let query = query.to_string().replace("*", "%"); // allow wildcard char
|
||||
|
||||
self.query(
|
||||
"select * from history where command like ?1 || '%' order by timestamp asc limit 1000",
|
||||
&[query],
|
||||
"select * from history h
|
||||
where command like ?1 || '%'
|
||||
and timestamp = (
|
||||
select max(timestamp) from history
|
||||
where h.command = history.command
|
||||
)
|
||||
order by timestamp desc limit 200",
|
||||
&[query.as_str()],
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -248,6 +294,39 @@ impl Database for Sqlite {
|
|||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn search(&self, cwd: Option<String>, exit: Option<i64>, query: &str) -> Result<Vec<History>> {
|
||||
match (cwd, exit) {
|
||||
(Some(cwd), Some(exit)) => self.query(
|
||||
"select * from history
|
||||
where command like ?1 || '%'
|
||||
and cwd = ?2
|
||||
and exit = ?3
|
||||
order by timestamp asc limit 1000",
|
||||
&[query, cwd.as_str(), exit.to_string().as_str()],
|
||||
),
|
||||
(Some(cwd), None) => self.query(
|
||||
"select * from history
|
||||
where command like ?1 || '%'
|
||||
and cwd = ?2
|
||||
order by timestamp asc limit 1000",
|
||||
&[query, cwd.as_str()],
|
||||
),
|
||||
(None, Some(exit)) => self.query(
|
||||
"select * from history
|
||||
where command like ?1 || '%'
|
||||
and exit = ?2
|
||||
order by timestamp asc limit 1000",
|
||||
&[query, exit.to_string().as_str()],
|
||||
),
|
||||
(None, None) => self.query(
|
||||
"select * from history
|
||||
where command like ?1 || '%'
|
||||
order by timestamp asc limit 1000",
|
||||
&[query],
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn history_from_sqlite_row(
|
||||
|
|
|
@ -29,20 +29,51 @@ pub fn load_key(settings: &Settings) -> Result<secretbox::Key> {
|
|||
let path = settings.key_path.as_str();
|
||||
|
||||
if PathBuf::from(path).exists() {
|
||||
let bytes = std::fs::read(path)?;
|
||||
let key: secretbox::Key = rmp_serde::from_read_ref(&bytes)?;
|
||||
let key = std::fs::read_to_string(path)?;
|
||||
let key = decode_key(key)?;
|
||||
Ok(key)
|
||||
} else {
|
||||
let key = secretbox::gen_key();
|
||||
let buf = rmp_serde::to_vec(&key)?;
|
||||
let encoded = encode_key(key.clone())?;
|
||||
|
||||
let mut file = File::create(path)?;
|
||||
file.write_all(&buf)?;
|
||||
file.write_all(encoded.as_bytes())?;
|
||||
|
||||
Ok(key)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_encoded_key(settings: &Settings) -> Result<String> {
|
||||
let path = settings.key_path.as_str();
|
||||
|
||||
if PathBuf::from(path).exists() {
|
||||
let key = std::fs::read_to_string(path)?;
|
||||
Ok(key)
|
||||
} else {
|
||||
let key = secretbox::gen_key();
|
||||
let encoded = encode_key(key)?;
|
||||
|
||||
let mut file = File::create(path)?;
|
||||
file.write_all(encoded.as_bytes())?;
|
||||
|
||||
Ok(encoded)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encode_key(key: secretbox::Key) -> Result<String> {
|
||||
let buf = rmp_serde::to_vec(&key)?;
|
||||
let buf = base64::encode(buf);
|
||||
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
pub fn decode_key(key: String) -> Result<secretbox::Key> {
|
||||
let buf = base64::decode(key)?;
|
||||
let buf: secretbox::Key = rmp_serde::from_read_ref(&buf)?;
|
||||
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
pub fn encrypt(history: &History, key: &secretbox::Key) -> Result<EncryptedHistory> {
|
||||
// serialize with msgpack
|
||||
let buf = rmp_serde::to_vec(history)?;
|
||||
|
|
|
@ -6,7 +6,7 @@ use chrono::Utc;
|
|||
use atuin_common::utils::uuid_v4;
|
||||
|
||||
// Any new fields MUST be Optional<>!
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Ord, PartialOrd)]
|
||||
pub struct History {
|
||||
pub id: String,
|
||||
pub timestamp: chrono::DateTime<Utc>,
|
||||
|
|
|
@ -78,15 +78,16 @@ impl Settings {
|
|||
|
||||
create_dir_all(config_dir)?;
|
||||
|
||||
let config_file = if let Ok(p) = std::env::var("ATUIN_CONFIG") {
|
||||
let mut config_file = if let Ok(p) = std::env::var("ATUIN_CONFIG_DIR") {
|
||||
PathBuf::from(p)
|
||||
} else {
|
||||
let mut config_file = PathBuf::new();
|
||||
config_file.push(config_dir);
|
||||
config_file.push("config.toml");
|
||||
config_file
|
||||
};
|
||||
|
||||
config_file.push("config.toml");
|
||||
|
||||
let mut s = Config::new();
|
||||
|
||||
let db_path = ProjectDirs::from("com", "elliehuxtable", "atuin")
|
||||
|
|
|
@ -7,7 +7,7 @@ use atuin_common::{api::AddHistoryRequest, utils::hash_str};
|
|||
|
||||
use crate::api_client;
|
||||
use crate::database::Database;
|
||||
use crate::encryption::{encrypt, load_key};
|
||||
use crate::encryption::{encrypt, load_encoded_key, load_key};
|
||||
use crate::settings::{Settings, HISTORY_PAGE_SIZE};
|
||||
|
||||
// Currently sync is kinda naive, and basically just pages backwards through
|
||||
|
@ -26,6 +26,8 @@ async fn sync_download(
|
|||
client: &api_client::Client<'_>,
|
||||
db: &mut (impl Database + Send),
|
||||
) -> Result<(i64, i64)> {
|
||||
debug!("starting sync download");
|
||||
|
||||
let remote_count = client.count().await?;
|
||||
|
||||
let initial_local = db.history_count()?;
|
||||
|
@ -46,14 +48,14 @@ async fn sync_download(
|
|||
.get_history(last_sync, last_timestamp, host.clone())
|
||||
.await?;
|
||||
|
||||
if page.len() < HISTORY_PAGE_SIZE.try_into().unwrap() {
|
||||
break;
|
||||
}
|
||||
|
||||
db.save_bulk(&page)?;
|
||||
|
||||
local_count = db.history_count()?;
|
||||
|
||||
if page.len() < HISTORY_PAGE_SIZE.try_into().unwrap() {
|
||||
break;
|
||||
}
|
||||
|
||||
let page_last = page
|
||||
.last()
|
||||
.expect("could not get last element of page")
|
||||
|
@ -80,11 +82,15 @@ async fn sync_upload(
|
|||
client: &api_client::Client<'_>,
|
||||
db: &mut (impl Database + Send),
|
||||
) -> Result<()> {
|
||||
debug!("starting sync upload");
|
||||
|
||||
let initial_remote_count = client.count().await?;
|
||||
let mut remote_count = initial_remote_count;
|
||||
|
||||
let local_count = db.history_count()?;
|
||||
|
||||
debug!("remote has {}, we have {}", remote_count, local_count);
|
||||
|
||||
let key = load_key(settings)?; // encryption key
|
||||
|
||||
// first just try the most recent set
|
||||
|
@ -127,8 +133,8 @@ pub async fn sync(settings: &Settings, force: bool, db: &mut (impl Database + Se
|
|||
let client = api_client::Client::new(
|
||||
settings.sync_address.as_str(),
|
||||
settings.session_token.as_str(),
|
||||
load_key(settings)?,
|
||||
);
|
||||
load_encoded_key(settings)?,
|
||||
)?;
|
||||
|
||||
sync_upload(settings, force, &client, db).await?;
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@ serde_json = "1.0.64"
|
|||
rmp-serde = "0.15.4"
|
||||
unicode-width = "0.1"
|
||||
sodiumoxide = "0.2.6"
|
||||
reqwest = { version = "0.11", features = ["blocking", "json"] }
|
||||
base64 = "0.13.0"
|
||||
fork = "0.1.18"
|
||||
parse_duration = "2.1.1"
|
||||
|
@ -33,6 +32,6 @@ rand = "0.8.3"
|
|||
rust-crypto = "^0.2"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
warp = "0.3"
|
||||
sqlx = { version = "0.5", features = [ "runtime-tokio-native-tls", "uuid", "chrono", "postgres" ] }
|
||||
sqlx = { version = "0.5", features = [ "runtime-tokio-rustls", "uuid", "chrono", "postgres" ] }
|
||||
async-trait = "0.1.49"
|
||||
urlencoding = "1.1.1"
|
||||
|
|
|
@ -23,15 +23,16 @@ impl Settings {
|
|||
|
||||
create_dir_all(config_dir)?;
|
||||
|
||||
let config_file = if let Ok(p) = std::env::var("ATUIN_CONFIG") {
|
||||
let mut config_file = if let Ok(p) = std::env::var("ATUIN_CONFIG_DIR") {
|
||||
PathBuf::from(p)
|
||||
} else {
|
||||
let mut config_file = PathBuf::new();
|
||||
config_file.push(config_dir);
|
||||
config_file.push("server.toml");
|
||||
config_file
|
||||
};
|
||||
|
||||
config_file.push("server.toml");
|
||||
|
||||
// create the config file if it does not exist
|
||||
|
||||
let mut s = Config::new();
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use std::env;
|
||||
|
||||
use eyre::Result;
|
||||
use fork::{fork, Fork};
|
||||
use structopt::StructOpt;
|
||||
|
||||
use atuin_client::database::Database;
|
||||
|
@ -44,6 +43,12 @@ pub enum Cmd {
|
|||
aliases=&["se", "sea", "sear", "searc"],
|
||||
)]
|
||||
Search { query: Vec<String> },
|
||||
|
||||
#[structopt(
|
||||
about="get the last command ran",
|
||||
aliases=&["la", "las"],
|
||||
)]
|
||||
Last {},
|
||||
}
|
||||
|
||||
fn print_list(h: &[History]) {
|
||||
|
@ -74,22 +79,24 @@ impl Cmd {
|
|||
}
|
||||
|
||||
let mut h = db.load(id)?;
|
||||
|
||||
if h.duration > 0 {
|
||||
debug!("cannot end history - already has duration");
|
||||
|
||||
// returning OK as this can occur if someone Ctrl-c a prompt
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
h.exit = *exit;
|
||||
h.duration = chrono::Utc::now().timestamp_nanos() - h.timestamp.timestamp_nanos();
|
||||
|
||||
db.update(&h)?;
|
||||
|
||||
if settings.should_sync()? {
|
||||
match fork() {
|
||||
Ok(Fork::Parent(child)) => {
|
||||
debug!("launched sync background process with PID {}", child);
|
||||
}
|
||||
Ok(Fork::Child) => {
|
||||
debug!("running periodic background sync");
|
||||
sync::sync(settings, false, db).await?;
|
||||
}
|
||||
Err(_) => println!("Fork failed"),
|
||||
}
|
||||
debug!("running periodic background sync");
|
||||
sync::sync(settings, false, db).await?;
|
||||
} else {
|
||||
debug!("sync disabled! not syncing");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -107,7 +114,7 @@ impl Cmd {
|
|||
let session = env::var("ATUIN_SESSION")?;
|
||||
|
||||
let history = match params {
|
||||
(false, false) => db.list()?,
|
||||
(false, false) => db.list(None, false)?,
|
||||
(true, false) => db.query(QUERY_SESSION, &[session.as_str()])?,
|
||||
(false, true) => db.query(QUERY_DIR, &[cwd.as_str()])?,
|
||||
(true, true) => {
|
||||
|
@ -126,6 +133,13 @@ impl Cmd {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Self::Last {} => {
|
||||
let last = db.last()?;
|
||||
print_list(&[last]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
|
||||
use eyre::{eyre, Result};
|
||||
use eyre::Result;
|
||||
use structopt::StructOpt;
|
||||
|
||||
use atuin_client::api_client;
|
||||
use atuin_client::settings::Settings;
|
||||
|
||||
#[derive(StructOpt)]
|
||||
|
@ -22,25 +22,15 @@ pub struct Cmd {
|
|||
|
||||
impl Cmd {
|
||||
pub fn run(&self, settings: &Settings) -> Result<()> {
|
||||
let mut map = HashMap::new();
|
||||
map.insert("username", self.username.clone());
|
||||
map.insert("password", self.password.clone());
|
||||
|
||||
let url = format!("{}/login", settings.sync_address);
|
||||
let client = reqwest::blocking::Client::new();
|
||||
|
||||
let resp = client.post(url).json(&map).send()?;
|
||||
|
||||
if resp.status() != reqwest::StatusCode::OK {
|
||||
return Err(eyre!("invalid login details"));
|
||||
}
|
||||
|
||||
let session = resp.json::<HashMap<String, String>>()?;
|
||||
let session = session["session"].clone();
|
||||
let session = api_client::login(
|
||||
settings.sync_address.as_str(),
|
||||
self.username.as_str(),
|
||||
self.password.as_str(),
|
||||
)?;
|
||||
|
||||
let session_path = settings.session_path.as_str();
|
||||
let mut file = File::create(session_path)?;
|
||||
file.write_all(session.as_bytes())?;
|
||||
file.write_all(session.session.as_bytes())?;
|
||||
|
||||
let key_path = settings.key_path.as_str();
|
||||
let mut file = File::create(key_path)?;
|
||||
|
|
|
@ -43,7 +43,18 @@ pub enum AtuinCmd {
|
|||
Uuid,
|
||||
|
||||
#[structopt(about = "interactive history search")]
|
||||
Search { query: Vec<String> },
|
||||
Search {
|
||||
#[structopt(long, short, about = "filter search result by directory")]
|
||||
cwd: Option<String>,
|
||||
|
||||
#[structopt(long, short, about = "filter search result by exit code")]
|
||||
exit: Option<i64>,
|
||||
|
||||
#[structopt(long, short, about = "open interactive search UI")]
|
||||
interactive: bool,
|
||||
|
||||
query: Vec<String>,
|
||||
},
|
||||
|
||||
#[structopt(about = "sync with the configured server")]
|
||||
Sync {
|
||||
|
@ -76,7 +87,12 @@ impl AtuinCmd {
|
|||
Self::Server(server) => server.run(&server_settings).await,
|
||||
Self::Stats(stats) => stats.run(&mut db, &client_settings),
|
||||
Self::Init => init::init(),
|
||||
Self::Search { query } => search::run(&query, &mut db),
|
||||
Self::Search {
|
||||
cwd,
|
||||
exit,
|
||||
interactive,
|
||||
query,
|
||||
} => search::run(cwd, exit, interactive, &query, &mut db),
|
||||
|
||||
Self::Sync { force } => sync::run(&client_settings, force, &mut db).await,
|
||||
Self::Login(l) => l.run(&client_settings),
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
|
||||
use eyre::{eyre, Result};
|
||||
use eyre::Result;
|
||||
use structopt::StructOpt;
|
||||
|
||||
use atuin_client::api_client;
|
||||
use atuin_client::settings::Settings;
|
||||
|
||||
#[derive(StructOpt)]
|
||||
|
@ -21,34 +21,11 @@ pub struct Cmd {
|
|||
}
|
||||
|
||||
pub fn run(settings: &Settings, username: &str, email: &str, password: &str) -> Result<()> {
|
||||
let mut map = HashMap::new();
|
||||
map.insert("username", username);
|
||||
map.insert("email", email);
|
||||
map.insert("password", password);
|
||||
|
||||
let url = format!("{}/user/{}", settings.sync_address, username);
|
||||
let resp = reqwest::blocking::get(url)?;
|
||||
|
||||
if resp.status().is_success() {
|
||||
println!("Username is already in use! Please try another.");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let url = format!("{}/register", settings.sync_address);
|
||||
let client = reqwest::blocking::Client::new();
|
||||
let resp = client.post(url).json(&map).send()?;
|
||||
|
||||
if !resp.status().is_success() {
|
||||
println!("Failed to register user - please check your details and try again");
|
||||
return Err(eyre!("failed to register user"));
|
||||
}
|
||||
|
||||
let session = resp.json::<HashMap<String, String>>()?;
|
||||
let session = session["session"].clone();
|
||||
let session = api_client::register(settings.sync_address.as_str(), username, email, password)?;
|
||||
|
||||
let path = settings.session_path.as_str();
|
||||
let mut file = File::create(path)?;
|
||||
file.write_all(session.as_bytes())?;
|
||||
file.write_all(session.session.as_bytes())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use eyre::Result;
|
||||
use itertools::Itertools;
|
||||
use std::io::stdout;
|
||||
use std::time::Duration;
|
||||
use std::{io::stdout, ops::Sub};
|
||||
|
||||
use termion::{event::Key, input::MouseTerminal, raw::IntoRawMode, screen::AlternateScreen};
|
||||
use tui::{
|
||||
|
@ -31,7 +30,7 @@ struct State {
|
|||
|
||||
#[allow(clippy::clippy::cast_sign_loss)]
|
||||
impl State {
|
||||
fn durations(&self) -> Vec<String> {
|
||||
fn durations(&self) -> Vec<(String, String)> {
|
||||
self.results
|
||||
.iter()
|
||||
.map(|h| {
|
||||
|
@ -40,7 +39,33 @@ impl State {
|
|||
let duration = humantime::format_duration(duration).to_string();
|
||||
let duration: Vec<&str> = duration.split(' ').collect();
|
||||
|
||||
duration[0].to_string()
|
||||
let ago = chrono::Utc::now().sub(h.timestamp);
|
||||
let ago = humantime::format_duration(ago.to_std().unwrap()).to_string();
|
||||
let ago: Vec<&str> = ago.split(' ').collect();
|
||||
|
||||
(
|
||||
duration[0]
|
||||
.to_string()
|
||||
.replace("days", "d")
|
||||
.replace("day", "d")
|
||||
.replace("weeks", "w")
|
||||
.replace("week", "w")
|
||||
.replace("months", "mo")
|
||||
.replace("month", "mo")
|
||||
.replace("years", "y")
|
||||
.replace("year", "y"),
|
||||
ago[0]
|
||||
.to_string()
|
||||
.replace("days", "d")
|
||||
.replace("day", "d")
|
||||
.replace("weeks", "w")
|
||||
.replace("week", "w")
|
||||
.replace("months", "mo")
|
||||
.replace("month", "mo")
|
||||
.replace("years", "y")
|
||||
.replace("year", "y")
|
||||
+ " ago",
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
@ -51,9 +76,9 @@ impl State {
|
|||
r: tui::layout::Rect,
|
||||
) {
|
||||
let durations = self.durations();
|
||||
let max_length = durations
|
||||
.iter()
|
||||
.fold(0, |largest, i| std::cmp::max(largest, i.len()));
|
||||
let max_length = durations.iter().fold(0, |largest, i| {
|
||||
std::cmp::max(largest, i.0.len() + i.1.len())
|
||||
});
|
||||
|
||||
let results: Vec<ListItem> = self
|
||||
.results
|
||||
|
@ -64,10 +89,10 @@ impl State {
|
|||
|
||||
let mut command = Span::raw(command);
|
||||
|
||||
let mut duration = durations[i].clone();
|
||||
let (duration, mut ago) = durations[i].clone();
|
||||
|
||||
while duration.len() < max_length {
|
||||
duration.push(' ');
|
||||
while (duration.len() + ago.len()) < max_length {
|
||||
ago = " ".to_owned() + ago.as_str();
|
||||
}
|
||||
|
||||
let duration = Span::styled(
|
||||
|
@ -79,6 +104,8 @@ impl State {
|
|||
}),
|
||||
);
|
||||
|
||||
let ago = Span::styled(ago, Style::default().fg(Color::Blue));
|
||||
|
||||
if let Some(selected) = self.results_state.selected() {
|
||||
if selected == i {
|
||||
command.style =
|
||||
|
@ -86,7 +113,8 @@ impl State {
|
|||
}
|
||||
}
|
||||
|
||||
let spans = Spans::from(vec![duration, Span::raw(" "), command]);
|
||||
let spans =
|
||||
Spans::from(vec![duration, Span::raw(" "), ago, Span::raw(" "), command]);
|
||||
|
||||
ListItem::new(spans)
|
||||
})
|
||||
|
@ -103,12 +131,12 @@ impl State {
|
|||
|
||||
fn query_results(app: &mut State, db: &mut impl Database) {
|
||||
let results = match app.input.as_str() {
|
||||
"" => db.list(),
|
||||
"" => db.list(Some(200), true),
|
||||
i => db.prefix_search(i),
|
||||
};
|
||||
|
||||
if let Ok(results) = results {
|
||||
app.results = results.into_iter().rev().unique().collect();
|
||||
app.results = results;
|
||||
}
|
||||
|
||||
if app.results.is_empty() {
|
||||
|
@ -120,7 +148,8 @@ fn query_results(app: &mut State, db: &mut impl Database) {
|
|||
|
||||
fn key_handler(input: Key, db: &mut impl Database, app: &mut State) -> Option<String> {
|
||||
match input {
|
||||
Key::Esc | Key::Char('\n') => {
|
||||
Key::Esc => return Some(String::from("")),
|
||||
Key::Char('\n') => {
|
||||
let i = app.results_state.selected().unwrap_or(0);
|
||||
|
||||
return Some(
|
||||
|
@ -268,9 +297,37 @@ fn select_history(query: &[String], db: &mut impl Database) -> Result<String> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn run(query: &[String], db: &mut impl Database) -> Result<()> {
|
||||
let item = select_history(query, db)?;
|
||||
eprintln!("{}", item);
|
||||
pub fn run(
|
||||
cwd: Option<String>,
|
||||
exit: Option<i64>,
|
||||
interactive: bool,
|
||||
query: &[String],
|
||||
db: &mut impl Database,
|
||||
) -> Result<()> {
|
||||
let dir = if let Some(cwd) = cwd {
|
||||
if cwd == "." {
|
||||
let current = std::env::current_dir()?;
|
||||
let current = current.as_os_str();
|
||||
let current = current.to_str().unwrap();
|
||||
|
||||
Some(current.to_owned())
|
||||
} else {
|
||||
Some(cwd)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if interactive {
|
||||
let item = select_history(query, db)?;
|
||||
eprintln!("{}", item);
|
||||
} else {
|
||||
let results = db.search(dir, exit, query.join(" ").as_str())?;
|
||||
|
||||
for i in &results {
|
||||
println!("{}", i.command);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -94,7 +94,7 @@ impl Cmd {
|
|||
}
|
||||
|
||||
Self::All => {
|
||||
let history = db.list()?;
|
||||
let history = db.list(None, false)?;
|
||||
|
||||
compute_stats(&history)?;
|
||||
|
||||
|
|
19
src/main.rs
19
src/main.rs
|
@ -2,7 +2,6 @@
|
|||
#![allow(clippy::use_self)] // not 100% reliable
|
||||
|
||||
use eyre::Result;
|
||||
use fern::colors::{Color, ColoredLevelConfig};
|
||||
use structopt::{clap::AppSettings, StructOpt};
|
||||
|
||||
#[macro_use]
|
||||
|
@ -32,23 +31,7 @@ impl Atuin {
|
|||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let colors = ColoredLevelConfig::new()
|
||||
.warn(Color::Yellow)
|
||||
.error(Color::Red);
|
||||
|
||||
fern::Dispatch::new()
|
||||
.format(move |out, message, record| {
|
||||
out.finish(format_args!(
|
||||
"{} [{}] {}",
|
||||
chrono::Local::now().to_rfc3339(),
|
||||
colors.color(record.level()),
|
||||
message
|
||||
))
|
||||
})
|
||||
.level(log::LevelFilter::Info)
|
||||
.level_for("sqlx", log::LevelFilter::Warn)
|
||||
.chain(std::io::stdout())
|
||||
.apply()?;
|
||||
pretty_env_logger::init();
|
||||
|
||||
Atuin::from_args().run().await
|
||||
}
|
||||
|
|
|
@ -15,8 +15,8 @@ _atuin_precmd(){
|
|||
|
||||
[[ -z "${ATUIN_HISTORY_ID}" ]] && return
|
||||
|
||||
atuin history end $ATUIN_HISTORY_ID --exit $EXIT
|
||||
export ATUIN_HISTORY_ID=""
|
||||
|
||||
(RUST_LOG=error atuin history end $ATUIN_HISTORY_ID --exit $EXIT &) > /dev/null 2>&1
|
||||
}
|
||||
|
||||
_atuin_search(){
|
||||
|
@ -27,7 +27,7 @@ _atuin_search(){
|
|||
echoti rmkx
|
||||
# swap stderr and stdout, so that the tui stuff works
|
||||
# TODO: not this
|
||||
output=$(atuin search $BUFFER 3>&1 1>&2 2>&3)
|
||||
output=$(RUST_LOG=error atuin search -i $BUFFER 3>&1 1>&2 2>&3)
|
||||
echoti smkx
|
||||
|
||||
if [[ -n $output ]] ; then
|
||||
|
|
Loading…
Reference in a new issue