aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1386
-rw-r--r--Cargo.toml56
-rw-r--r--crates/client/Cargo.toml48
-rw-r--r--crates/client/src/atuin_client/mod.rs28
-rw-r--r--crates/common/Cargo.toml18
-rw-r--r--crates/common/src/utils.rs18
-rw-r--r--crates/daemon/Cargo.toml113
-rw-r--r--crates/daemon/src/aclient/api_client.rs11
-rw-r--r--crates/daemon/src/aclient/database/mod.rs1100
-rw-r--r--crates/daemon/src/aclient/history/mod.rs36
-rw-r--r--crates/daemon/src/aclient/history/store.rs207
-rw-r--r--crates/daemon/src/aclient/mod.rs1
-rw-r--r--crates/daemon/src/aclient/ordering.rs32
-rw-r--r--crates/daemon/src/aclient/record/sqlite_store.rs147
-rw-r--r--crates/daemon/src/aclient/settings/mod.rs980
-rw-r--r--crates/daemon/src/api/control.rs8
-rw-r--r--crates/daemon/src/api/history.rs5
-rw-r--r--crates/daemon/src/main.rs10
-rw-r--r--crates/server/Cargo.toml34
-rw-r--r--crates/turtle/Cargo.toml24
20 files changed, 287 insertions, 3975 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 78a79b59..0649d85e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -105,24 +105,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
[[package]]
-name = "arboard"
-version = "3.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf"
-dependencies = [
- "clipboard-win",
- "log",
- "objc2",
- "objc2-app-kit",
- "objc2-foundation",
- "parking_lot",
- "percent-encoding",
- "windows-sys 0.60.2",
- "wl-clipboard-rs",
- "x11rb",
-]
-
-[[package]]
name = "argon2"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -135,12 +117,6 @@ dependencies = [
]
[[package]]
-name = "arrayvec"
-version = "0.7.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
-
-[[package]]
name = "async-trait"
version = "0.1.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -148,7 +124,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -161,15 +137,6 @@ dependencies = [
]
[[package]]
-name = "atomic"
-version = "0.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a89cbf775b137e9b968e67227ef7f775587cde3fd31b0d8599dbd0f598a48340"
-dependencies = [
- "bytemuck",
-]
-
-[[package]]
name = "atomic-waker"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -283,27 +250,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1"
[[package]]
-name = "bit-set"
-version = "0.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
-dependencies = [
- "bit-vec",
-]
-
-[[package]]
-name = "bit-vec"
-version = "0.6.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
-
-[[package]]
-name = "bitflags"
-version = "1.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
-
-[[package]]
name = "bitflags"
version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -337,12 +283,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
[[package]]
-name = "bytemuck"
-version = "1.25.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec"
-
-[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -355,15 +295,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
[[package]]
-name = "castaway"
-version = "0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a"
-dependencies = [
- "rustversion",
-]
-
-[[package]]
name = "cc"
version = "1.2.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -386,18 +317,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
-name = "cfg_aliases"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
-
-[[package]]
-name = "cfg_aliases"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
-
-[[package]]
name = "chacha20"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -481,7 +400,7 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -491,15 +410,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
[[package]]
-name = "clipboard-win"
-version = "5.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4"
-dependencies = [
- "error-code",
-]
-
-[[package]]
name = "colorchoice"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -526,20 +436,6 @@ dependencies = [
]
[[package]]
-name = "compact_str"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fdb1325a1cece981e8a296ab8f0f9b63ae357bd0784a9faaf548cc7b480707a"
-dependencies = [
- "castaway",
- "cfg-if",
- "itoa",
- "rustversion",
- "ryu",
- "static_assertions",
-]
-
-[[package]]
name = "concurrent-queue"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -628,15 +524,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
[[package]]
-name = "crossbeam-channel"
-version = "0.5.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
-dependencies = [
- "crossbeam-utils",
-]
-
-[[package]]
name = "crossbeam-epoch"
version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -666,7 +553,7 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b"
dependencies = [
- "bitflags 2.11.0",
+ "bitflags",
"crossterm_winapi",
"derive_more",
"document-features",
@@ -716,16 +603,6 @@ dependencies = [
]
[[package]]
-name = "csscolorparser"
-version = "0.6.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb2a7d3066da2de787b7f032c736763eb7ae5d355f81a68bab2675a96008b0bf"
-dependencies = [
- "lab",
- "phf",
-]
-
-[[package]]
name = "curve25519-dalek"
version = "4.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -749,16 +626,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
-]
-
-[[package]]
-name = "daemonize"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab8bfdaacb3c887a54d41bdf48d3af8873b3f5566469f8ba21b92057509f116e"
-dependencies = [
- "libc",
+ "syn",
]
[[package]]
@@ -781,7 +649,7 @@ dependencies = [
"proc-macro2",
"quote",
"strsim",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -792,7 +660,7 @@ checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d"
dependencies = [
"darling_core",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -810,12 +678,6 @@ dependencies = [
]
[[package]]
-name = "deltae"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5729f5117e208430e437df2f4843f5e5952997175992d1414f94c57d61e270b4"
-
-[[package]]
name = "der"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -855,7 +717,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustc_version",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -901,16 +763,6 @@ dependencies = [
]
[[package]]
-name = "dispatch2"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38"
-dependencies = [
- "bitflags 2.11.0",
- "objc2",
-]
-
-[[package]]
name = "displaydoc"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -918,7 +770,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -937,12 +789,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
[[package]]
-name = "downcast-rs"
-version = "1.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
-
-[[package]]
name = "dyn-clone"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1004,12 +850,6 @@ dependencies = [
]
[[package]]
-name = "error-code"
-version = "3.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59"
-
-[[package]]
name = "etcetera"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1021,15 +861,6 @@ dependencies = [
]
[[package]]
-name = "euclid"
-version = "0.22.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df61bf483e837f88d5c2291dcf55c67be7e676b3a51acc48db3a7b163b91ed63"
-dependencies = [
- "num-traits",
-]
-
-[[package]]
name = "event-listener"
version = "5.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1051,16 +882,6 @@ dependencies = [
]
[[package]]
-name = "fancy-regex"
-version = "0.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2"
-dependencies = [
- "bit-set",
- "regex",
-]
-
-[[package]]
name = "fastrand"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1084,35 +905,12 @@ dependencies = [
]
[[package]]
-name = "filetime"
-version = "0.2.27"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db"
-dependencies = [
- "cfg-if",
- "libc",
- "libredox",
-]
-
-[[package]]
name = "find-msvc-tools"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
[[package]]
-name = "finl_unicode"
-version = "1.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9844ddc3a6e533d62bba727eb6c28b5d360921d5175e9ff0f1e621a5c590a4d5"
-
-[[package]]
-name = "fixedbitset"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
-
-[[package]]
name = "fixedbitset"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1176,15 +974,6 @@ dependencies = [
]
[[package]]
-name = "fsevent-sys"
-version = "4.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2"
-dependencies = [
- "libc",
-]
-
-[[package]]
name = "futures-channel"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1236,7 +1025,7 @@ checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -1268,15 +1057,6 @@ dependencies = [
]
[[package]]
-name = "fuzzy-matcher"
-version = "0.3.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "54614a3312934d066701a80f20f15fa3b56d67ac7722b39eea5b4c9dd1d66c94"
-dependencies = [
- "thread_local",
-]
-
-[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1288,16 +1068,6 @@ dependencies = [
]
[[package]]
-name = "gethostname"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8"
-dependencies = [
- "rustix",
- "windows-link",
-]
-
-[[package]]
name = "getrandom"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1363,10 +1133,6 @@ name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
-dependencies = [
- "ahash",
- "allocator-api2",
-]
[[package]]
name = "hashbrown"
@@ -1385,8 +1151,6 @@ version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
dependencies = [
- "allocator-api2",
- "equivalent",
"foldhash 0.2.0",
]
@@ -1739,35 +1503,6 @@ dependencies = [
]
[[package]]
-name = "indoc"
-version = "2.0.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
-dependencies = [
- "rustversion",
-]
-
-[[package]]
-name = "inotify"
-version = "0.10.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fdd168d97690d0b8c412d6b6c10360277f4d7ee495c5d0d5d5fe0854923255cc"
-dependencies = [
- "bitflags 1.3.2",
- "inotify-sys",
- "libc",
-]
-
-[[package]]
-name = "inotify-sys"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb"
-dependencies = [
- "libc",
-]
-
-[[package]]
name = "inout"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1777,28 +1512,6 @@ dependencies = [
]
[[package]]
-name = "instability"
-version = "0.3.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5eb2d60ef19920a3a9193c3e371f726ec1dafc045dac788d0fb3704272458971"
-dependencies = [
- "darling",
- "indoc",
- "proc-macro2",
- "quote",
- "syn 2.0.117",
-]
-
-[[package]]
-name = "instant"
-version = "0.1.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222"
-dependencies = [
- "cfg-if",
-]
-
-[[package]]
name = "interim"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1836,7 +1549,7 @@ version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1082f0c48f143442a1ac6122f67e360ceee130b967af4d50996e5154a45df46"
dependencies = [
- "nom 8.0.0",
+ "nom",
]
[[package]]
@@ -1887,53 +1600,6 @@ dependencies = [
]
[[package]]
-name = "kasuari"
-version = "0.4.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bde5057d6143cc94e861d90f591b9303d6716c6b9602309150bd068853c10899"
-dependencies = [
- "hashbrown 0.16.1",
- "portable-atomic",
- "thiserror 2.0.18",
-]
-
-[[package]]
-name = "kqueue"
-version = "1.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a"
-dependencies = [
- "kqueue-sys",
- "libc",
-]
-
-[[package]]
-name = "kqueue-sys"
-version = "1.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b"
-dependencies = [
- "bitflags 1.3.2",
- "libc",
-]
-
-[[package]]
-name = "lab"
-version = "0.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf36173d4167ed999940f804952e6b08197cae5ad5d572eb4db150ce8ad5d58f"
-
-[[package]]
-name = "lasso"
-version = "0.7.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e14eda50a3494b3bf7b9ce51c52434a761e383d7238ce1dd5dcec2fbc13e9fb"
-dependencies = [
- "dashmap",
- "hashbrown 0.14.5",
-]
-
-[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1966,7 +1632,7 @@ version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a"
dependencies = [
- "bitflags 2.11.0",
+ "bitflags",
"libc",
"plain",
"redox_syscall 0.7.3",
@@ -1984,15 +1650,6 @@ dependencies = [
]
[[package]]
-name = "line-clipping"
-version = "0.3.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f4de44e98ddbf09375cbf4d17714d18f39195f4f4894e8524501726fd9a8a4a"
-dependencies = [
- "bitflags 2.11.0",
-]
-
-[[package]]
name = "linux-raw-sys"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2058,7 +1715,7 @@ dependencies = [
"quote",
"regex-syntax",
"rustc_version",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -2071,25 +1728,6 @@ dependencies = [
]
[[package]]
-name = "lru"
-version = "0.16.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593"
-dependencies = [
- "hashbrown 0.16.1",
-]
-
-[[package]]
-name = "mac_address"
-version = "1.1.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0aeb26bf5e836cc1c341c8106051b573f1766dfa05aa87f0b98be5e51b02303"
-dependencies = [
- "nix 0.29.0",
- "winapi",
-]
-
-[[package]]
name = "matchers"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2121,21 +1759,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
[[package]]
-name = "memmem"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a64a92489e2744ce060c349162be1c5f33c6969234104dbd99ddb5feb08b8c15"
-
-[[package]]
-name = "memoffset"
-version = "0.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
-dependencies = [
- "autocfg",
-]
-
-[[package]]
name = "metrics"
version = "0.24.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2194,7 +1817,7 @@ checksum = "db5b29714e950dbb20d5e6f74f9dcec4edbcc1067bb7f8ed198c097b8c1a818b"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -2204,18 +1827,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
-name = "minimal-lexical"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
-
-[[package]]
-name = "minspan"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "20219d20b3b77f743358539a182e339a6f682e09bb5675f870f320c4a1058c44"
-
-[[package]]
name = "mio"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2234,41 +1845,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084"
[[package]]
-name = "nix"
-version = "0.28.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
-dependencies = [
- "bitflags 2.11.0",
- "cfg-if",
- "cfg_aliases 0.1.1",
- "libc",
-]
-
-[[package]]
-name = "nix"
-version = "0.29.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
-dependencies = [
- "bitflags 2.11.0",
- "cfg-if",
- "cfg_aliases 0.2.1",
- "libc",
- "memoffset",
-]
-
-[[package]]
-name = "nom"
-version = "7.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
-dependencies = [
- "memchr",
- "minimal-lexical",
-]
-
-[[package]]
name = "nom"
version = "8.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2278,43 +1854,6 @@ dependencies = [
]
[[package]]
-name = "norm"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed5725a3379c44dc0adf3437af87cf21c10df473ed858d654b12603dea102508"
-dependencies = [
- "memchr",
-]
-
-[[package]]
-name = "notify"
-version = "7.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c533b4c39709f9ba5005d8002048266593c1cfaf3c5f0739d5b8ab0c6c504009"
-dependencies = [
- "bitflags 2.11.0",
- "filetime",
- "fsevent-sys",
- "inotify",
- "kqueue",
- "libc",
- "log",
- "mio",
- "notify-types",
- "walkdir",
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "notify-types"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "585d3cb5e12e01aed9e8a1f70d5c6b5e86fe2a6e48fc8cd0b3e0b8df6f6eb174"
-dependencies = [
- "instant",
-]
-
-[[package]]
name = "nu-ansi-term"
version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2346,17 +1885,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050"
[[package]]
-name = "num-derive"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.117",
-]
-
-[[package]]
name = "num-integer"
version = "0.1.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2396,76 +1924,12 @@ dependencies = [
]
[[package]]
-name = "objc2"
-version = "0.6.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f"
-dependencies = [
- "objc2-encode",
-]
-
-[[package]]
-name = "objc2-app-kit"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c"
-dependencies = [
- "bitflags 2.11.0",
- "objc2",
- "objc2-core-graphics",
- "objc2-foundation",
-]
-
-[[package]]
name = "objc2-core-foundation"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
dependencies = [
- "bitflags 2.11.0",
- "dispatch2",
- "objc2",
-]
-
-[[package]]
-name = "objc2-core-graphics"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807"
-dependencies = [
- "bitflags 2.11.0",
- "dispatch2",
- "objc2",
- "objc2-core-foundation",
- "objc2-io-surface",
-]
-
-[[package]]
-name = "objc2-encode"
-version = "4.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
-
-[[package]]
-name = "objc2-foundation"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
-dependencies = [
- "bitflags 2.11.0",
- "objc2",
- "objc2-core-foundation",
-]
-
-[[package]]
-name = "objc2-io-surface"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d"
-dependencies = [
- "bitflags 2.11.0",
- "objc2",
- "objc2-core-foundation",
+ "bitflags",
]
[[package]]
@@ -2508,25 +1972,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
-name = "ordered-float"
-version = "4.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951"
-dependencies = [
- "num-traits",
-]
-
-[[package]]
-name = "os_pipe"
-version = "1.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967"
-dependencies = [
- "libc",
- "windows-sys 0.61.2",
-]
-
-[[package]]
name = "parking"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2588,112 +2033,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
-name = "pest"
-version = "2.8.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662"
-dependencies = [
- "memchr",
- "ucd-trie",
-]
-
-[[package]]
-name = "pest_derive"
-version = "2.8.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "11f486f1ea21e6c10ed15d5a7c77165d0ee443402f0780849d1768e7d9d6fe77"
-dependencies = [
- "pest",
- "pest_generator",
-]
-
-[[package]]
-name = "pest_generator"
-version = "2.8.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8040c4647b13b210a963c1ed407c1ff4fdfa01c31d6d2a098218702e6664f94f"
-dependencies = [
- "pest",
- "pest_meta",
- "proc-macro2",
- "quote",
- "syn 2.0.117",
-]
-
-[[package]]
-name = "pest_meta"
-version = "2.8.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220"
-dependencies = [
- "pest",
- "sha2",
-]
-
-[[package]]
name = "petgraph"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455"
dependencies = [
- "fixedbitset 0.5.7",
+ "fixedbitset",
"hashbrown 0.15.5",
"indexmap 2.13.0",
]
[[package]]
-name = "phf"
-version = "0.11.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
-dependencies = [
- "phf_macros",
- "phf_shared",
-]
-
-[[package]]
-name = "phf_codegen"
-version = "0.11.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a"
-dependencies = [
- "phf_generator",
- "phf_shared",
-]
-
-[[package]]
-name = "phf_generator"
-version = "0.11.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
-dependencies = [
- "phf_shared",
- "rand 0.8.5",
-]
-
-[[package]]
-name = "phf_macros"
-version = "0.11.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216"
-dependencies = [
- "phf_generator",
- "phf_shared",
- "proc-macro2",
- "quote",
- "syn 2.0.117",
-]
-
-[[package]]
-name = "phf_shared"
-version = "0.11.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
-dependencies = [
- "siphasher",
-]
-
-[[package]]
name = "pin-project"
version = "1.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2710,7 +2060,7 @@ checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -2776,27 +2126,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
[[package]]
-name = "portable-pty"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4a596a2b3d2752d94f51fac2d4a96737b8705dddd311a32b9af47211f08671e"
-dependencies = [
- "anyhow",
- "bitflags 1.3.2",
- "downcast-rs",
- "filedescriptor",
- "lazy_static",
- "libc",
- "log",
- "nix 0.28.0",
- "serial2",
- "shared_library",
- "shell-words",
- "winapi",
- "winreg",
-]
-
-[[package]]
name = "potential_utf"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2827,7 +2156,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
dependencies = [
"proc-macro2",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -2866,7 +2195,7 @@ dependencies = [
"pulldown-cmark",
"pulldown-cmark-to-cmark",
"regex",
- "syn 2.0.117",
+ "syn",
"tempfile",
]
@@ -2880,7 +2209,7 @@ dependencies = [
"itertools",
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -2937,7 +2266,7 @@ version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83c41efbf8f90ac44de7f3a868f0867851d261b56291732d0cbf7cceaaeb55a6"
dependencies = [
- "bitflags 2.11.0",
+ "bitflags",
"memchr",
"unicase",
]
@@ -2967,15 +2296,6 @@ dependencies = [
]
[[package]]
-name = "quick-xml"
-version = "0.39.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "958f21e8e7ceb5a1aa7fa87fab28e7c75976e0bfe7e23ff069e0a260f894067d"
-dependencies = [
- "memchr",
-]
-
-[[package]]
name = "quote"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3065,97 +2385,12 @@ dependencies = [
]
[[package]]
-name = "ratatui"
-version = "0.30.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d1ce67fb8ba4446454d1c8dbaeda0557ff5e94d39d5e5ed7f10a65eb4c8266bc"
-dependencies = [
- "instability",
- "ratatui-core",
- "ratatui-crossterm",
- "ratatui-macros",
- "ratatui-termwiz",
- "ratatui-widgets",
-]
-
-[[package]]
-name = "ratatui-core"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ef8dea09a92caaf73bff7adb70b76162e5937524058a7e5bff37869cbbec293"
-dependencies = [
- "bitflags 2.11.0",
- "compact_str",
- "hashbrown 0.16.1",
- "indoc",
- "itertools",
- "kasuari",
- "lru",
- "strum",
- "thiserror 2.0.18",
- "unicode-segmentation",
- "unicode-truncate",
- "unicode-width 0.2.2",
-]
-
-[[package]]
-name = "ratatui-crossterm"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "577c9b9f652b4c121fb25c6a391dd06406d3b092ba68827e6d2f09550edc54b3"
-dependencies = [
- "cfg-if",
- "crossterm",
- "instability",
- "ratatui-core",
-]
-
-[[package]]
-name = "ratatui-macros"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7f1342a13e83e4bb9d0b793d0ea762be633f9582048c892ae9041ef39c936f4"
-dependencies = [
- "ratatui-core",
- "ratatui-widgets",
-]
-
-[[package]]
-name = "ratatui-termwiz"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f76fe0bd0ed4295f0321b1676732e2454024c15a35d01904ddb315afd3d545c"
-dependencies = [
- "ratatui-core",
- "termwiz",
-]
-
-[[package]]
-name = "ratatui-widgets"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7dbfa023cd4e604c2553483820c5fe8aa9d71a42eea5aa77c6e7f35756612db"
-dependencies = [
- "bitflags 2.11.0",
- "hashbrown 0.16.1",
- "indoc",
- "instability",
- "itertools",
- "line-clipping",
- "ratatui-core",
- "strum",
- "time",
- "unicode-segmentation",
- "unicode-width 0.2.2",
-]
-
-[[package]]
name = "raw-cpuid"
version = "11.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186"
dependencies = [
- "bitflags 2.11.0",
+ "bitflags",
]
[[package]]
@@ -3164,7 +2399,7 @@ version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
dependencies = [
- "bitflags 2.11.0",
+ "bitflags",
]
[[package]]
@@ -3173,7 +2408,7 @@ version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16"
dependencies = [
- "bitflags 2.11.0",
+ "bitflags",
]
[[package]]
@@ -3204,7 +2439,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -3319,15 +2554,6 @@ dependencies = [
]
[[package]]
-name = "runtime-format"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09958d5b38bca768ede7928c767c89a08ba568144a7b61992aecae79b03c8c94"
-dependencies = [
- "tinyvec",
-]
-
-[[package]]
name = "rustc_version"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3342,7 +2568,7 @@ version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
dependencies = [
- "bitflags 2.11.0",
+ "bitflags",
"errno",
"libc",
"linux-raw-sys",
@@ -3541,7 +2767,7 @@ version = "3.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d"
dependencies = [
- "bitflags 2.11.0",
+ "bitflags",
"core-foundation",
"core-foundation-sys",
"libc",
@@ -3591,7 +2817,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -3677,18 +2903,7 @@ dependencies = [
"darling",
"proc-macro2",
"quote",
- "syn 2.0.117",
-]
-
-[[package]]
-name = "serial2"
-version = "0.2.36"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fcdbc46aa3882ec3d48ec2b5abcb4f0d863a13d7599265f3faa6d851f23c12f3"
-dependencies = [
- "cfg-if",
- "libc",
- "winapi",
+ "syn",
]
[[package]]
@@ -3723,22 +2938,6 @@ dependencies = [
]
[[package]]
-name = "shared_library"
-version = "0.1.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11"
-dependencies = [
- "lazy_static",
- "libc",
-]
-
-[[package]]
-name = "shell-words"
-version = "1.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77"
-
-[[package]]
name = "shellexpand"
version = "3.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3795,12 +2994,6 @@ dependencies = [
]
[[package]]
-name = "siphasher"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e"
-
-[[package]]
name = "sketches-ddsketch"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3921,7 +3114,7 @@ dependencies = [
"quote",
"sqlx-core",
"sqlx-macros-core",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -3944,7 +3137,7 @@ dependencies = [
"sqlx-mysql",
"sqlx-postgres",
"sqlx-sqlite",
- "syn 2.0.117",
+ "syn",
"tokio",
"url",
]
@@ -3957,7 +3150,7 @@ checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526"
dependencies = [
"atoi",
"base64",
- "bitflags 2.11.0",
+ "bitflags",
"byteorder",
"bytes",
"crc",
@@ -4001,7 +3194,7 @@ checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46"
dependencies = [
"atoi",
"base64",
- "bitflags 2.11.0",
+ "bitflags",
"byteorder",
"crc",
"dotenvy",
@@ -4066,12 +3259,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[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.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4089,27 +3276,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
-name = "strum"
-version = "0.27.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf"
-dependencies = [
- "strum_macros",
-]
-
-[[package]]
-name = "strum_macros"
-version = "0.27.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7"
-dependencies = [
- "heck",
- "proc-macro2",
- "quote",
- "syn 2.0.117",
-]
-
-[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4117,17 +3283,6 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
-version = "1.0.109"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
-
-[[package]]
-name = "syn"
version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
@@ -4154,7 +3309,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -4171,69 +3326,6 @@ dependencies = [
]
[[package]]
-name = "terminfo"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4ea810f0692f9f51b382fff5893887bb4580f5fa246fde546e0b13e7fcee662"
-dependencies = [
- "fnv",
- "nom 7.1.3",
- "phf",
- "phf_codegen",
-]
-
-[[package]]
-name = "termios"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "411c5bf740737c7918b8b1fe232dca4dc9f8e754b8ad5e20966814001ed0ac6b"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "termwiz"
-version = "0.23.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4676b37242ccbd1aabf56edb093a4827dc49086c0ffd764a5705899e0f35f8f7"
-dependencies = [
- "anyhow",
- "base64",
- "bitflags 2.11.0",
- "fancy-regex",
- "filedescriptor",
- "finl_unicode",
- "fixedbitset 0.4.2",
- "hex",
- "lazy_static",
- "libc",
- "log",
- "memmem",
- "nix 0.29.0",
- "num-derive",
- "num-traits",
- "ordered-float",
- "pest",
- "pest_derive",
- "phf",
- "sha2",
- "signal-hook",
- "siphasher",
- "terminfo",
- "termios",
- "thiserror 1.0.69",
- "ucd-trie",
- "unicode-segmentation",
- "vtparse",
- "wezterm-bidi",
- "wezterm-blob-leases",
- "wezterm-color-types",
- "wezterm-dynamic",
- "wezterm-input-types",
- "winapi",
-]
-
-[[package]]
name = "thiserror"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4259,7 +3351,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -4270,7 +3362,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -4365,7 +3457,7 @@ checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -4490,7 +3582,7 @@ dependencies = [
"prettyplease",
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -4515,7 +3607,7 @@ dependencies = [
"prost-build",
"prost-types",
"quote",
- "syn 2.0.117",
+ "syn",
"tempfile",
"tonic-build",
]
@@ -4545,7 +3637,7 @@ version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8"
dependencies = [
- "bitflags 2.11.0",
+ "bitflags",
"bytes",
"futures-util",
"http",
@@ -4583,18 +3675,6 @@ dependencies = [
]
[[package]]
-name = "tracing-appender"
-version = "0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "786d480bce6247ab75f005b14ae1624ad978d3029d9113f0a22fa1ac773faeaf"
-dependencies = [
- "crossbeam-channel",
- "thiserror 2.0.18",
- "time",
- "tracing-subscriber",
-]
-
-[[package]]
name = "tracing-attributes"
version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4602,7 +3682,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -4658,17 +3738,6 @@ dependencies = [
]
[[package]]
-name = "tree_magic_mini"
-version = "3.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8765b90061cba6c22b5831f675da109ae5561588290f9fa2317adab2714d5a6"
-dependencies = [
- "memchr",
- "nom 8.0.0",
- "petgraph",
-]
-
-[[package]]
name = "try-lock"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4713,79 +3782,40 @@ dependencies = [
name = "turtle-daemon"
version = "18.16.1"
dependencies = [
- "arboard",
- "async-trait",
- "axum",
"base64",
"clap",
- "clap_complete",
- "clap_complete_nushell",
- "colored",
"config",
- "crossterm",
"crypto_secretbox",
- "daemonize",
"dashmap",
- "directories",
"eyre",
"fs-err",
"fs4",
- "fuzzy-matcher",
- "hyper-util",
"indicatif",
"interim",
- "itertools",
- "lasso",
"listenfd",
"log",
- "metrics",
- "metrics-exporter-prometheus",
- "minspan",
- "norm",
- "notify",
- "portable-pty",
- "prost",
- "protox",
"rand 0.8.5",
- "ratatui",
- "regex",
"reqwest",
"rmp",
- "runtime-format",
- "rustix",
"rustls",
"rusty_paserk",
"rusty_paseto",
"semver",
"serde",
"serde_json",
- "serde_regex",
- "serde_with",
"shellexpand",
- "signal-hook",
"sql-builder",
"sqlx",
"thiserror 2.0.18",
"time",
"tokio",
"tokio-stream",
- "toml_edit",
"tonic",
- "tonic-prost",
- "tonic-prost-build",
- "tower",
- "tower-http",
"tracing",
- "tracing-appender",
"tracing-subscriber",
"turtle",
"turtle-common",
- "typed-builder",
- "unicode-segmentation",
- "unicode-width 0.2.2",
- "url",
"uuid",
- "vt100",
]
[[package]]
@@ -4830,7 +3860,7 @@ checksum = "1f718dfaf347dcb5b983bfc87608144b0bad87970aebcbea5ce44d2a30c08e63"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -4840,12 +3870,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
[[package]]
-name = "ucd-trie"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971"
-
-[[package]]
name = "unicase"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4885,17 +3909,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
[[package]]
-name = "unicode-truncate"
-version = "2.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "16b380a1238663e5f8a691f9039c73e1cdae598a30e9855f541d29b08b53e9a5"
-dependencies = [
- "itertools",
- "unicode-segmentation",
- "unicode-width 0.2.2",
-]
-
-[[package]]
name = "unicode-width"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4965,7 +3978,6 @@ version = "1.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a68d3c8f01c0cfa54a75291d83601161799e4a89a39e0929f4b0354d88757a37"
dependencies = [
- "atomic",
"getrandom 0.4.2",
"js-sys",
"serde_core",
@@ -4991,36 +4003,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
-name = "vt100"
-version = "0.16.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "054ff75fb8fa83e609e685106df4faeffdf3a735d3c74ebce97ec557d5d36fd9"
-dependencies = [
- "itoa",
- "unicode-width 0.2.2",
- "vte",
-]
-
-[[package]]
-name = "vte"
-version = "0.15.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a5924018406ce0063cd67f8e008104968b74b563ee1b85dde3ed1f7cb87d3dbd"
-dependencies = [
- "arrayvec",
- "memchr",
-]
-
-[[package]]
-name = "vtparse"
-version = "0.6.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d9b2acfb050df409c972a37d3b8e08cdea3bddb0c09db9d53137e504cfabed0"
-dependencies = [
- "utf8parse",
-]
-
-[[package]]
name = "walkdir"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5133,7 +4115,7 @@ dependencies = [
"bumpalo",
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
"wasm-bindgen-shared",
]
@@ -5187,83 +4169,13 @@ version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
dependencies = [
- "bitflags 2.11.0",
+ "bitflags",
"hashbrown 0.15.5",
"indexmap 2.13.0",
"semver",
]
[[package]]
-name = "wayland-backend"
-version = "0.3.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa75f400b7f719bcd68b3f47cd939ba654cedeef690f486db71331eec4c6a406"
-dependencies = [
- "cc",
- "downcast-rs",
- "rustix",
- "smallvec",
- "wayland-sys",
-]
-
-[[package]]
-name = "wayland-client"
-version = "0.31.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab51d9f7c071abeee76007e2b742499e535148035bb835f97aaed1338cf516c3"
-dependencies = [
- "bitflags 2.11.0",
- "rustix",
- "wayland-backend",
- "wayland-scanner",
-]
-
-[[package]]
-name = "wayland-protocols"
-version = "0.32.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b23b5df31ceff1328f06ac607591d5ba360cf58f90c8fad4ac8d3a55a3c4aec7"
-dependencies = [
- "bitflags 2.11.0",
- "wayland-backend",
- "wayland-client",
- "wayland-scanner",
-]
-
-[[package]]
-name = "wayland-protocols-wlr"
-version = "0.3.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78248e4cc0eff8163370ba5c158630dcae1f3497a586b826eca2ef5f348d6235"
-dependencies = [
- "bitflags 2.11.0",
- "wayland-backend",
- "wayland-client",
- "wayland-protocols",
- "wayland-scanner",
-]
-
-[[package]]
-name = "wayland-scanner"
-version = "0.31.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c86287151a309799b821ca709b7345a048a2956af05957c89cb824ab919fa4e3"
-dependencies = [
- "proc-macro2",
- "quick-xml",
- "quote",
-]
-
-[[package]]
-name = "wayland-sys"
-version = "0.31.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "374f6b70e8e0d6bf9461a32988fd553b59ff630964924dad6e4a4eb6bd538d17"
-dependencies = [
- "pkg-config",
-]
-
-[[package]]
name = "web-sys"
version = "0.3.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5311,78 +4223,6 @@ dependencies = [
]
[[package]]
-name = "wezterm-bidi"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c0a6e355560527dd2d1cf7890652f4f09bb3433b6aadade4c9b5ed76de5f3ec"
-dependencies = [
- "log",
- "wezterm-dynamic",
-]
-
-[[package]]
-name = "wezterm-blob-leases"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "692daff6d93d94e29e4114544ef6d5c942a7ed998b37abdc19b17136ea428eb7"
-dependencies = [
- "getrandom 0.3.4",
- "mac_address",
- "sha2",
- "thiserror 1.0.69",
- "uuid",
-]
-
-[[package]]
-name = "wezterm-color-types"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7de81ef35c9010270d63772bebef2f2d6d1f2d20a983d27505ac850b8c4b4296"
-dependencies = [
- "csscolorparser",
- "deltae",
- "lazy_static",
- "wezterm-dynamic",
-]
-
-[[package]]
-name = "wezterm-dynamic"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f2ab60e120fd6eaa68d9567f3226e876684639d22a4219b313ff69ec0ccd5ac"
-dependencies = [
- "log",
- "ordered-float",
- "strsim",
- "thiserror 1.0.69",
- "wezterm-dynamic-derive",
-]
-
-[[package]]
-name = "wezterm-dynamic-derive"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46c0cf2d539c645b448eaffec9ec494b8b19bd5077d9e58cb1ae7efece8d575b"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 1.0.109",
-]
-
-[[package]]
-name = "wezterm-input-types"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7012add459f951456ec9d6c7e6fc340b1ce15d6fc9629f8c42853412c029e57e"
-dependencies = [
- "bitflags 1.3.2",
- "euclid",
- "lazy_static",
- "serde",
- "wezterm-dynamic",
-]
-
-[[package]]
name = "whoami"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5457,7 +4297,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -5468,7 +4308,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -5533,15 +4373,6 @@ dependencies = [
[[package]]
name = "windows-sys"
-version = "0.60.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
-dependencies = [
- "windows-targets 0.53.5",
-]
-
-[[package]]
-name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
@@ -5588,7 +4419,7 @@ dependencies = [
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
- "windows_i686_gnullvm 0.52.6",
+ "windows_i686_gnullvm",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
@@ -5596,23 +4427,6 @@ dependencies = [
]
[[package]]
-name = "windows-targets"
-version = "0.53.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
-dependencies = [
- "windows-link",
- "windows_aarch64_gnullvm 0.53.1",
- "windows_aarch64_msvc 0.53.1",
- "windows_i686_gnu 0.53.1",
- "windows_i686_gnullvm 0.53.1",
- "windows_i686_msvc 0.53.1",
- "windows_x86_64_gnu 0.53.1",
- "windows_x86_64_gnullvm 0.53.1",
- "windows_x86_64_msvc 0.53.1",
-]
-
-[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5631,12 +4445,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
-name = "windows_aarch64_gnullvm"
-version = "0.53.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
-
-[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5655,12 +4463,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
-name = "windows_aarch64_msvc"
-version = "0.53.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
-
-[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5679,24 +4481,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
-name = "windows_i686_gnu"
-version = "0.53.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
-
-[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
-name = "windows_i686_gnullvm"
-version = "0.53.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
-
-[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5715,12 +4505,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
-name = "windows_i686_msvc"
-version = "0.53.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
-
-[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5739,12 +4523,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
-name = "windows_x86_64_gnu"
-version = "0.53.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
-
-[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5763,12 +4541,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
-name = "windows_x86_64_gnullvm"
-version = "0.53.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
-
-[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5787,12 +4559,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
-name = "windows_x86_64_msvc"
-version = "0.53.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
-
-[[package]]
name = "winnow"
version = "0.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5808,15 +4574,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5"
[[package]]
-name = "winreg"
-version = "0.10.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
-dependencies = [
- "winapi",
-]
-
-[[package]]
name = "wit-bindgen"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5846,7 +4603,7 @@ dependencies = [
"heck",
"indexmap 2.13.0",
"prettyplease",
- "syn 2.0.117",
+ "syn",
"wasm-metadata",
"wit-bindgen-core",
"wit-component",
@@ -5862,7 +4619,7 @@ dependencies = [
"prettyplease",
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
"wit-bindgen-core",
"wit-bindgen-rust",
]
@@ -5874,7 +4631,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
dependencies = [
"anyhow",
- "bitflags 2.11.0",
+ "bitflags",
"indexmap 2.13.0",
"log",
"serde",
@@ -5905,47 +4662,12 @@ dependencies = [
]
[[package]]
-name = "wl-clipboard-rs"
-version = "0.9.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e9651471a32e87d96ef3a127715382b2d11cc7c8bb9822ded8a7cc94072eb0a3"
-dependencies = [
- "libc",
- "log",
- "os_pipe",
- "rustix",
- "thiserror 2.0.18",
- "tree_magic_mini",
- "wayland-backend",
- "wayland-client",
- "wayland-protocols",
- "wayland-protocols-wlr",
-]
-
-[[package]]
name = "writeable"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9"
[[package]]
-name = "x11rb"
-version = "0.13.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414"
-dependencies = [
- "gethostname",
- "rustix",
- "x11rb-protocol",
-]
-
-[[package]]
-name = "x11rb-protocol"
-version = "0.13.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd"
-
-[[package]]
name = "yoke"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5964,7 +4686,7 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
"synstructure",
]
@@ -5985,7 +4707,7 @@ checksum = "7e6cc098ea4d3bd6246687de65af3f920c430e236bee1e3bf2e441463f08a02f"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -6005,7 +4727,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
"synstructure",
]
@@ -6026,7 +4748,7 @@ checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
@@ -6059,7 +4781,7 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.117",
+ "syn",
]
[[package]]
diff --git a/Cargo.toml b/Cargo.toml
index bd955897..465d1403 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -18,10 +18,64 @@ repository = "https://github.com/atuinsh/atuin"
readme = "README.md"
[workspace.dependencies]
+turtle = { path = "crates/turtle" }
turtle-common = { path = "crates/common" }
turtle-daemon = { path = "crates/daemon" }
-turtle = { path = "crates/turtle" }
+
+axum = "0.8"
+base64 = "0.22"
clap = { version = "4.5.7", features = ["derive"] }
+clap_complete = "4.5.8"
+clap_complete_nushell = "4.5.4"
+colored = "2.0.4"
+config = { version = "0.15.8", default-features = false, features = ["toml"] }
+crossterm = {version = "0.29.0", features = ["use-dev-tty", "serde"] }
+crypto_secretbox = "0.1.1"
+dashmap = "6.1.0"
+directories = "6.0.0"
+eyre = "0.6"
+fs-err = "3.1"
+fs4 = "0.13.1"
+hyper-util = "0.1"
+indicatif = "0.18.0"
+interim = { version = "0.2.0", features = ["time_0_3"] }
+listenfd = "1.0.1"
+log = "0.4"
+metrics = "0.24"
+metrics-exporter-prometheus = { version = "0.18", default-features = false }
+prost = "0.14"
+rand = { version = "0.8.5", features = ["std"] }
+regex = "1.10.5"
+reqwest = { version = "0.13", features = ["json", "rustls-no-provider", "stream"], default-features = false }
+rmp = { version = "0.8.14" }
+rustix = { version = "1.1.4", features = ["process", "fs"] }
+rustls = { version = "0.23", default-features = false, features = [ "ring", "std", "tls12", ] }
+rusty_paserk = { version = "0.5.0", default-features = false, features = [ "v4", "serde", ] }
+rusty_paseto = { version = "0.8.0", default-features = false }
+semver = "1.0.20"
+serde = { version = "1.0.202", features = ["derive"] }
+serde_json = "1.0.119"
+serde_regex = "1.1.0"
+serde_with = "3.8.1"
+shellexpand = "3"
+sql-builder = "3"
+sqlx = { version = "0.8", features = ["runtime-tokio-rustls", "time", "postgres", "uuid", "sqlite", "regexp"] }
+thiserror = "2"
+time = { version = "0.3.47", features = [ "serde-human-readable", "macros", "local-offset", "macros", "formatting", "parsing"] }
+tokio = { version = "1", features = ["full"] }
+tokio-stream = { version = "0.1.14", features = ["net"] }
+toml_edit = "0.25.4"
+tonic = "0.14"
+tonic-prost = "0.14"
+tower = "0.5"
+tower-http = { version = "0.6", features = ["trace"] }
+tracing = "0.1"
+tracing-subscriber = { version = "0.3", features = ["ansi", "fmt", "registry", "env-filter", "json"] }
+typed-builder = "0.18.2"
+unicode-segmentation = "1.11.0"
+url = "2.5.2"
+uuid = { version = "1.9", features = ["v4", "v7", "serde"] }
+whoami = "2.1.0"
[profile.profiling]
inherits = "release"
diff --git a/crates/client/Cargo.toml b/crates/client/Cargo.toml
index 4387a1c1..93599cd6 100644
--- a/crates/client/Cargo.toml
+++ b/crates/client/Cargo.toml
@@ -12,31 +12,31 @@ homepage = { workspace = true }
repository = { workspace = true }
[dependencies]
-clap = { version = "4.5.7", features = ["derive"] }
-clap_complete = "4.5.8"
-clap_complete_nushell = "4.5.4"
-colored = "2.0.4"
-config = { version = "0.15.8", default-features = false, features = ["toml"] }
-crossterm = {version = "0.29.0", features = ["use-dev-tty", "serde"] }
-eyre = "0.6"
-fs-err = "3.1"
-interim = { version = "0.2.0", features = ["time_0_3"] }
-log = "0.4"
-regex = "1.10.5"
-rustix = { version = "1.1.4", features = ["process", "fs"] }
-serde = { version = "1.0.202", features = ["derive"] }
-serde_json = "1.0.119"
-serde_regex = "1.1.0"
-serde_with = "3.8.1"
-shellexpand = "3"
-time = { version = "0.3.47", features = [ "serde-human-readable", "macros", "local-offset", "macros", "formatting", "parsing"] }
-tokio = { version = "1", features = ["full"] }
-toml_edit = "0.25.4"
-tracing = "0.1"
-tracing-subscriber = { version = "0.3", features = ["ansi", "fmt", "registry", "env-filter", "json"] }
+clap = { workspace = true }
+clap_complete = { workspace = true }
+clap_complete_nushell = { workspace = true }
+colored = { workspace = true }
+config = { workspace = true }
+crossterm = { workspace = true }
+eyre = { workspace = true }
+fs-err = { workspace = true }
+interim = { workspace = true }
+log = { workspace = true }
+regex = { workspace = true }
+rustix = { workspace = true }
+serde = { workspace = true }
+serde_json = { workspace = true }
+serde_regex = { workspace = true }
+serde_with = { workspace = true }
+shellexpand = { workspace = true }
+time = { workspace = true }
+tokio = { workspace = true }
+toml_edit = { workspace = true }
+tracing = { workspace = true }
+tracing-subscriber = { workspace = true }
turtle = { workspace = true }
-turtle-common = {workspace = true}
-unicode-segmentation = "1.11.0"
+turtle-common = { workspace = true }
+unicode-segmentation = { workspace = true }
[dev-dependencies]
diff --git a/crates/client/src/atuin_client/mod.rs b/crates/client/src/atuin_client/mod.rs
index 3f36722f..ed9f57b6 100644
--- a/crates/client/src/atuin_client/mod.rs
+++ b/crates/client/src/atuin_client/mod.rs
@@ -1,16 +1,16 @@
pub(crate) mod settings;
-pub async fn current_context(session: String) -> eyre::Result<Context> {
- let hostname = get_host_user();
- let cwd = utils::get_current_dir();
- let host_id = Settings::host_id().await?;
- let git_root = utils::in_git_repo(cwd.as_str());
-
- Ok(Context {
- session,
- hostname,
- cwd,
- git_root,
- host_id: host_id.0.as_simple().to_string(),
- })
-}
+// pub async fn current_context(session: String) -> eyre::Result<Context> {
+// let hostname = get_host_user();
+// let cwd = utils::get_current_dir();
+// let host_id = Settings::host_id().await?;
+// let git_root = utils::in_git_repo(cwd.as_str());
+//
+// Ok(Context {
+// session,
+// hostname,
+// cwd,
+// git_root,
+// host_id: host_id.0.as_simple().to_string(),
+// })
+// }
diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml
index b79932ff..e25dd3d3 100644
--- a/crates/common/Cargo.toml
+++ b/crates/common/Cargo.toml
@@ -12,15 +12,15 @@ homepage = { workspace = true }
repository = { workspace = true }
[dependencies]
-directories = "6.0.0"
-eyre = "0.6"
-semver = "1.0.20"
-serde = { version = "1.0.202", features = ["derive"] }
-sqlx = { version = "0.8", features = ["runtime-tokio-rustls", "time", "postgres", "uuid", "sqlite", "regexp"] }
-time = { version = "0.3.47", features = [ "serde-human-readable", "macros", "local-offset", "macros", "formatting", "parsing"] }
-typed-builder = "0.18.2"
-uuid = { version = "1.9", features = ["v4", "v7", "serde"] }
-whoami = "2.1.0"
+directories = { workspace = true }
+eyre = { workspace = true }
+semver = { workspace = true }
+serde = { workspace = true }
+sqlx = { workspace = true }
+time = { workspace = true }
+typed-builder = { workspace = true }
+uuid = { workspace = true }
+whoami = { workspace = true }
[dev-dependencies]
diff --git a/crates/common/src/utils.rs b/crates/common/src/utils.rs
index 4451140b..7f46f223 100644
--- a/crates/common/src/utils.rs
+++ b/crates/common/src/utils.rs
@@ -110,14 +110,17 @@ pub fn data_dir() -> PathBuf {
data_dir.join("atuin")
}
+#[must_use]
pub fn runtime_dir() -> PathBuf {
env::var("XDG_RUNTIME_DIR").map_or_else(|_| data_dir(), PathBuf::from)
}
+#[must_use]
pub fn logs_dir() -> PathBuf {
home_dir().join(".atuin").join("logs")
}
+#[must_use]
pub fn get_current_dir() -> String {
// Prefer PWD environment variable over cwd if available to better support symbolic links
env::var("PWD").unwrap_or_else(|_| {
@@ -164,7 +167,7 @@ impl<T: AsRef<str>> Escapable for T {}
#[cfg(test)]
mod tests {
- use super::{Cow, Escapable, Uuid, env, in_git_repo, uuid_v7};
+ use super::{Cow, Uuid, env, in_git_repo, uuid_v7};
use std::collections::HashSet;
@@ -259,17 +262,4 @@ mod tests {
std::fs::remove_dir_all(&tmp).unwrap();
}
-
- #[test]
- fn dumb_random_test() {
- // Obviously not a test of randomness, but make sure we haven't made some
- // catastrophic error
-
- assert_ne!(crypto_random_string::<1>(), crypto_random_string::<1>());
- assert_ne!(crypto_random_string::<2>(), crypto_random_string::<2>());
- assert_ne!(crypto_random_string::<4>(), crypto_random_string::<4>());
- assert_ne!(crypto_random_string::<8>(), crypto_random_string::<8>());
- assert_ne!(crypto_random_string::<16>(), crypto_random_string::<16>());
- assert_ne!(crypto_random_string::<32>(), crypto_random_string::<32>());
- }
}
diff --git a/crates/daemon/Cargo.toml b/crates/daemon/Cargo.toml
index 52a501fc..cbc4c223 100644
--- a/crates/daemon/Cargo.toml
+++ b/crates/daemon/Cargo.toml
@@ -12,88 +12,41 @@ homepage = { workspace = true }
repository = { workspace = true }
[dependencies]
-turtle-common = { workspace = true }
+base64 = { workspace = true }
+clap = { workspace = true }
+config = { workspace = true }
+crypto_secretbox = { workspace = true }
+dashmap = { workspace = true }
+eyre = { workspace = true }
+fs-err = { workspace = true }
+fs4 = { workspace = true }
+indicatif = { workspace = true }
+interim = { workspace = true }
+listenfd = { workspace = true }
+log = { workspace = true }
+rand = { workspace = true }
+reqwest = { workspace = true }
+rmp = { workspace = true }
+rustls = { workspace = true }
+rusty_paserk = { workspace = true }
+rusty_paseto = { workspace = true }
+semver = { workspace = true }
+serde = { workspace = true }
+serde_json = { workspace = true }
+shellexpand = { workspace = true }
+sql-builder = { workspace = true }
+sqlx = { workspace = true }
+thiserror = { workspace = true }
+time = { workspace = true }
+tokio = { workspace = true }
+tokio-stream = { workspace = true }
+tonic = { workspace = true }
+tracing = { workspace = true }
+tracing-subscriber = { workspace = true }
turtle = { workspace = true }
-async-trait = "0.1.58"
-axum = "0.8"
-base64 = "0.22"
-clap = { version = "4.5.7", features = ["derive"] }
-clap_complete = "4.5.8"
-clap_complete_nushell = "4.5.4"
-colored = "2.0.4"
-config = { version = "0.15.8", default-features = false, features = ["toml"] }
-crossterm = {version = "0.29.0", features = ["use-dev-tty", "serde"] }
-crypto_secretbox = "0.1.1"
-dashmap = "6.1.0"
-directories = "6.0.0"
-eyre = "0.6"
-fs-err = "3.1"
-fs4 = "0.13.1"
-fuzzy-matcher = "0.3.7"
-hyper-util = "0.1"
-indicatif = "0.18.0"
-interim = { version = "0.2.0", features = ["time_0_3"] }
-itertools = "0.14.0"
-lasso = { version = "0.7", features = ["multi-threaded"] }
-log = "0.4"
-metrics = "0.24"
-metrics-exporter-prometheus = { version = "0.18", default-features = false }
-minspan = "0.1.5"
-norm = { version = "0.1.1", features = ["fzf-v2"] }
-notify = "7"
-prost = "0.14"
-rand = { version = "0.8.5", features = ["std"] }
-ratatui = "0.30.0"
-regex = "1.10.5"
-reqwest = { version = "0.13", features = ["json", "rustls-no-provider", "stream"], default-features = false }
-rmp = { version = "0.8.14" }
-runtime-format = "0.1.3"
-rustix = { version = "1.1.4", features = ["process", "fs"] }
-rustls = { version = "0.23", default-features = false, features = [ "ring", "std", "tls12", ] }
-rusty_paserk = { version = "0.5.0", default-features = false, features = [ "v4", "serde", ] }
-rusty_paseto = { version = "0.8.0", default-features = false }
-semver = "1.0.20"
-serde = { version = "1.0.202", features = ["derive"] }
-serde_json = "1.0.119"
-serde_regex = "1.1.0"
-serde_with = "3.8.1"
-shellexpand = "3"
-sql-builder = "3"
-sqlx = { version = "0.8", features = ["runtime-tokio-rustls", "time", "postgres", "uuid", "sqlite", "regexp"] }
-thiserror = "2"
-time = { version = "0.3.47", features = [ "serde-human-readable", "macros", "local-offset", "macros", "formatting", "parsing"] }
-tokio = { version = "1", features = ["full"] }
-tokio-stream = { version = "0.1.14", features = ["net"] }
-toml_edit = "0.25.4"
-tonic = "0.14"
-tonic-prost = "0.14"
-tower = "0.5"
-tower-http = { version = "0.6", features = ["trace"] }
-tracing = "0.1"
-tracing-appender = "0.2"
-tracing-subscriber = { version = "0.3", features = ["ansi", "fmt", "registry", "env-filter", "json"] }
-typed-builder = "0.18.2"
-unicode-segmentation = "1.11.0"
-unicode-width = "0.2"
-url = "2.5.2"
-uuid = { version = "1.9", features = ["v4", "v7", "serde"] }
-vt100 = "0.16"
-
-[target.'cfg(target_os = "linux")'.dependencies]
-arboard = { version = "3.4", default-features = false, features = [ "wayland-data-control", ] }
-listenfd = "1.0.1"
-
-[target.'cfg(unix)'.dependencies]
-daemonize = "0.5.0"
-portable-pty = "0.9"
-signal-hook = "0.3"
-
-[dev-dependencies]
-tokio = { version = "1", features = ["full"] }
+turtle-common = { workspace = true }
+uuid = { workspace = true }
-[build-dependencies]
-protox = "0.9"
-tonic-prost-build = "0.14"
[package.metadata.docs.rs]
all-features = true
diff --git a/crates/daemon/src/aclient/api_client.rs b/crates/daemon/src/aclient/api_client.rs
index 954426b4..1eba51bd 100644
--- a/crates/daemon/src/aclient/api_client.rs
+++ b/crates/daemon/src/aclient/api_client.rs
@@ -141,17 +141,6 @@ impl<'a> Client<'a> {
})
}
- async fn delete_store(&self) -> Result<()> {
- let url = make_url(self.sync_addr, "/store", self.user_id)?;
- let url = Url::parse(url.as_str())?;
-
- let resp = self.inner.delete(url).send().await?;
-
- handle_resp_error(resp).await?;
-
- Ok(())
- }
-
pub(crate) async fn post_records(&self, records: &[Record<EncryptedData>]) -> Result<()> {
let url = make_url(self.sync_addr, "/record", self.user_id)?;
let url = Url::parse(url.as_str())?;
diff --git a/crates/daemon/src/aclient/database/mod.rs b/crates/daemon/src/aclient/database/mod.rs
index 9da943ca..b112b076 100644
--- a/crates/daemon/src/aclient/database/mod.rs
+++ b/crates/daemon/src/aclient/database/mod.rs
@@ -1,11 +1,7 @@
-use std::{
- path::{Path, PathBuf},
- str::FromStr,
-};
+use std::{path::Path, str::FromStr};
use fs_err::{self as fs};
-use itertools::Itertools;
-use sql_builder::{SqlBuilder, SqlName, bind::Bind, esc, quote};
+use sql_builder::{SqlBuilder, SqlName};
use sqlx::{
Result, Row,
sqlite::{SqliteConnectOptions, SqliteJournalMode, SqlitePool, SqliteRow, SqliteSynchronous},
@@ -13,60 +9,9 @@ use sqlx::{
use time::OffsetDateTime;
use tracing::debug;
use turtle::history::{History, HistoryId};
-use turtle_common::utils::{self, get_host_user};
-use uuid::Uuid;
+use turtle_common::utils;
-use crate::aclient::{
- history::HistoryStats,
- ordering,
- settings::{FilterMode, SearchMode},
-};
-use crate::aclient::{settings::Settings, utils::setup_db};
-
-#[derive(Clone)]
-pub(crate) struct Context {
- session: String,
- cwd: String,
- hostname: String,
- host_id: String,
- git_root: Option<PathBuf>,
-}
-
-#[derive(Default, Clone)]
-struct OptFilters {
- exit: Option<i64>,
- exclude_exit: Option<i64>,
- cwd: Option<String>,
- exclude_cwd: Option<String>,
- before: Option<String>,
- after: Option<String>,
- limit: Option<i64>,
- offset: Option<i64>,
- reverse: bool,
- include_duplicates: bool,
-}
-
-impl Context {
- fn from_history(entry: &History) -> Self {
- Self {
- session: entry.session.clone(),
- cwd: entry.cwd.clone(),
- hostname: entry.hostname.clone(),
- host_id: String::new(),
- git_root: utils::in_git_repo(entry.cwd.as_str()),
- }
- }
-}
-
-fn get_session_start_time(session_id: &str) -> Option<i64> {
- if let Ok(uuid) = Uuid::parse_str(session_id)
- && let Some(timestamp) = uuid.get_timestamp()
- {
- let (seconds, nanos) = timestamp.to_unix();
- return Some(seconds as i64 * 1_000_000_000 + i64::from(nanos));
- }
- None
-}
+use crate::aclient::utils::setup_db;
// Intended for use on a developer machine and not a sync server.
// TODO: implement IntoIterator
@@ -189,36 +134,9 @@ impl ClientSqlite {
Ok(())
}
- pub(crate) async fn save_bulk(&self, h: &[History]) -> Result<()> {
- 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<Option<History>> {
- debug!("loading history item {}", id);
-
- let res = sqlx::query("select * from history where id = ?1")
- .bind(id)
- .map(Self::query_history_inner)
- .fetch_optional(&self.pool)
- .await?;
-
- Ok(res)
- }
-
/// make a unique list, that only shows the *newest* version of things
pub(crate) async fn list(
&self,
- filters: Option<(&Context, &[FilterMode])>,
max: Option<usize>,
unique: bool,
include_deleted: bool,
@@ -231,32 +149,6 @@ impl ClientSqlite {
query.and_where_is_null("deleted_at");
}
- if let Some((context, filters)) = filters {
- let git_root = context.git_root.clone().map_or_else(
- || context.cwd.clone(),
- |git_root| git_root.to_str().unwrap_or("/").to_string(),
- );
-
- let session_start = get_session_start_time(&context.session);
-
- for filter in filters {
- match filter {
- FilterMode::Global => &mut query,
- FilterMode::Host => query.and_where_eq("hostname", quote(&context.hostname)),
- FilterMode::Session => query.and_where_eq("session", quote(&context.session)),
- FilterMode::SessionPreload => {
- query.and_where_eq("session", quote(&context.session));
- if let Some(session_start) = session_start {
- query.or_where_lt("timestamp", session_start);
- }
- &mut query
- }
- FilterMode::Directory => query.and_where_eq("cwd", quote(&context.cwd)),
- FilterMode::Workspace => query.and_where_like_left("cwd", &git_root),
- };
- }
- }
-
if unique {
query.group_by("command").having("max(timestamp)");
}
@@ -294,239 +186,6 @@ impl ClientSqlite {
Ok(res)
}
- async fn last(&self) -> Result<Option<History>> {
- let res = sqlx::query(
- "select * from history where duration >= 0 order by timestamp desc limit 1",
- )
- .map(Self::query_history_inner)
- .fetch_optional(&self.pool)
- .await?;
-
- Ok(res)
- }
-
- async fn history_count(&self, include_deleted: bool) -> Result<i64> {
- let query = if include_deleted {
- "select count(1) from history"
- } else {
- "select count(1) from history where deleted_at is null"
- };
-
- let res: (i64,) = sqlx::query_as(query).fetch_one(&self.pool).await?;
- Ok(res.0)
- }
-
- // Yes I know, it's a lot.
- // Could maybe break it down to a searchparams struct or smth but that feels a little... pointless.
- // Been debating maybe a DSL for search? eg "before:time limit:1 the query"
- #[expect(clippy::too_many_lines)]
- async fn search(
- &self,
- search_mode: SearchMode,
- filter: FilterMode,
- context: &Context,
- query: &str,
- filter_options: OptFilters,
- ) -> Result<Vec<History>> {
- let mut sql = SqlBuilder::select_from("history");
-
- if !filter_options.include_duplicates {
- sql.group_by("command").having("max(timestamp)");
- }
-
- if let Some(limit) = filter_options.limit {
- sql.limit(limit);
- }
-
- if let Some(offset) = filter_options.offset {
- sql.offset(offset);
- }
-
- if filter_options.reverse {
- sql.order_asc("timestamp");
- } else {
- sql.order_desc("timestamp");
- }
-
- let git_root = context.git_root.clone().map_or_else(
- || context.cwd.clone(),
- |git_root| git_root.to_str().unwrap_or("/").to_string(),
- );
-
- let session_start = get_session_start_time(&context.session);
-
- match filter {
- FilterMode::Global => &mut sql,
- FilterMode::Host => {
- sql.and_where_eq("lower(hostname)", quote(context.hostname.to_lowercase()))
- }
- FilterMode::Session => sql.and_where_eq("session", quote(&context.session)),
- FilterMode::SessionPreload => {
- sql.and_where_eq("session", quote(&context.session));
- if let Some(session_start) = session_start {
- sql.or_where_lt("timestamp", session_start);
- }
- &mut sql
- }
- FilterMode::Directory => sql.and_where_eq("cwd", quote(&context.cwd)),
- FilterMode::Workspace => sql.and_where_like_left("cwd", git_root),
- };
-
- let orig_query = query;
-
- let mut regexes = Vec::new();
- if search_mode == SearchMode::Prefix {
- sql.and_where_like_left("command", query.replace('*', "%"))
- } else {
- let mut is_or = false;
- for token in QueryTokenizer::new(query) {
- // TODO smart case mode could be made configurable like in fzf
- let (is_glob, glob) = if token.has_uppercase() {
- (true, "*")
- } else {
- (false, "%")
- };
- let param = match token {
- QueryToken::Regex(r) => {
- regexes.push(String::from(r));
- continue;
- }
- QueryToken::Or => {
- if !is_or {
- is_or = true;
- continue;
- }
-
- format!("{glob}|{glob}")
- }
- QueryToken::MatchStart(term, _) => {
- format!("{term}{glob}")
- }
- QueryToken::MatchEnd(term, _) => {
- format!("{glob}{term}")
- }
- QueryToken::MatchFull(term, _) => {
- format!("{glob}{term}{glob}")
- }
- QueryToken::Match(term, _) => {
- if search_mode == SearchMode::FullText {
- format!("{glob}{term}{glob}")
- } else {
- term.split("").join(glob)
- }
- }
- };
-
- sql.fuzzy_condition("command", param, token.is_inverse(), is_glob, is_or);
- is_or = false;
- }
-
- &mut sql
- };
-
- for regex in regexes {
- sql.and_where("command regexp ?".bind(&regex));
- }
-
- filter_options
- .exit
- .map(|exit| sql.and_where_eq("exit", exit));
-
- filter_options
- .exclude_exit
- .map(|exclude_exit| sql.and_where_ne("exit", exclude_exit));
-
- filter_options
- .cwd
- .map(|cwd| sql.and_where_eq("cwd", quote(cwd)));
-
- filter_options
- .exclude_cwd
- .map(|exclude_cwd| sql.and_where_ne("cwd", quote(exclude_cwd)));
-
- filter_options.before.map(|before| {
- interim::parse_date_string(
- before.as_str(),
- OffsetDateTime::now_utc(),
- interim::Dialect::Uk,
- )
- .map(|before| {
- sql.and_where_lt("timestamp", quote(before.unix_timestamp_nanos() as i64))
- })
- });
-
- filter_options.after.map(|after| {
- interim::parse_date_string(
- after.as_str(),
- OffsetDateTime::now_utc(),
- interim::Dialect::Uk,
- )
- .map(|after| sql.and_where_gt("timestamp", quote(after.unix_timestamp_nanos() as i64)))
- });
-
- sql.and_where_is_null("deleted_at");
-
- let query = sql.sql().expect("bug in search query. please report");
-
- let res = sqlx::query(&query)
- .map(Self::query_history_inner)
- .fetch_all(&self.pool)
- .await?;
-
- Ok(ordering::reorder_fuzzy(search_mode, orig_query, res))
- }
-
- async fn query_history(&self, query: &str) -> Result<Vec<History>> {
- let res = sqlx::query(query)
- .map(Self::query_history_inner)
- .fetch_all(&self.pool)
- .await?;
-
- Ok(res)
- }
-
- async fn all_with_count(&self) -> Result<Vec<(History, i32)>> {
- debug!("listing history");
-
- let mut query = SqlBuilder::select_from(SqlName::new("history").alias("h").baquoted());
-
- query
- .fields(&[
- "id",
- "max(timestamp) as timestamp",
- "max(duration) as duration",
- "exit",
- "command",
- "deleted_at",
- "null as author",
- "null as intent",
- "group_concat(cwd, ':') as cwd",
- "group_concat(session) as session",
- "group_concat(hostname, ',') as hostname",
- "count(*) as count",
- ])
- .group_by("command")
- .group_by("exit")
- .and_where("deleted_at is null")
- .order_desc("timestamp");
-
- let query = query.sql().expect("bug in list query. please report");
-
- let res = sqlx::query(&query)
- .map(|row: SqliteRow| {
- let count: i32 = row.get("count");
- (Self::query_history_inner(row), count)
- })
- .fetch_all(&self.pool)
- .await?;
-
- Ok(res)
- }
-
- fn all_paged(&self, page_size: usize, include_deleted: bool, unique: bool) -> Paged {
- Paged::new(self.clone(), page_size, include_deleted, unique)
- }
-
pub(crate) async fn delete_rows(&self, ids: &[HistoryId]) -> Result<()> {
let mut tx = self.pool.begin().await?;
@@ -538,755 +197,4 @@ impl ClientSqlite {
Ok(())
}
-
- async fn stats(&self, h: &History) -> Result<HistoryStats> {
- // We select the previous in the session by time
- let mut prev = SqlBuilder::select_from("history");
- prev.field("*")
- .and_where("timestamp < ?1")
- .and_where("session = ?2")
- .order_by("timestamp", true)
- .limit(1);
-
- let mut next = SqlBuilder::select_from("history");
- next.field("*")
- .and_where("timestamp > ?1")
- .and_where("session = ?2")
- .order_by("timestamp", false)
- .limit(1);
-
- let mut total = SqlBuilder::select_from("history");
- total.field("count(1)").and_where("command = ?1");
-
- let mut average = SqlBuilder::select_from("history");
- average.field("avg(duration)").and_where("command = ?1");
-
- let mut exits = SqlBuilder::select_from("history");
- exits
- .fields(&["exit", "count(1) as count"])
- .and_where("command = ?1")
- .group_by("exit");
-
- // rewrite the following with sqlbuilder
- let mut day_of_week = SqlBuilder::select_from("history");
- day_of_week
- .fields(&[
- "strftime('%w', ROUND(timestamp / 1000000000), 'unixepoch') AS day_of_week",
- "count(1) as count",
- ])
- .and_where("command = ?1")
- .group_by("day_of_week");
-
- // Intentionally format the string with 01 hardcoded. We want the average runtime for the
- // _entire month_, but will later parse it as a datetime for sorting
- // Sqlite has no datetime so we cannot do it there, and otherwise sorting will just be a
- // string sort, which won't be correct.
- let mut duration_over_time = SqlBuilder::select_from("history");
- duration_over_time
- .fields(&[
- "strftime('01-%m-%Y', ROUND(timestamp / 1000000000), 'unixepoch') AS month_year",
- "avg(duration) as duration",
- ])
- .and_where("command = ?1")
- .group_by("month_year")
- .having("duration > 0");
-
- let prev = prev.sql().expect("issue in stats previous query");
- let next = next.sql().expect("issue in stats next query");
- let total = total.sql().expect("issue in stats average query");
- let average = average.sql().expect("issue in stats previous query");
- let exits = exits.sql().expect("issue in stats exits query");
- let day_of_week = day_of_week.sql().expect("issue in stats day of week query");
- let duration_over_time = duration_over_time
- .sql()
- .expect("issue in stats duration over time query");
-
- let prev = sqlx::query(&prev)
- .bind(h.timestamp.unix_timestamp_nanos() as i64)
- .bind(&h.session)
- .map(Self::query_history_inner)
- .fetch_optional(&self.pool)
- .await?;
-
- let next = sqlx::query(&next)
- .bind(h.timestamp.unix_timestamp_nanos() as i64)
- .bind(&h.session)
- .map(Self::query_history_inner)
- .fetch_optional(&self.pool)
- .await?;
-
- let total: (i64,) = sqlx::query_as(&total)
- .bind(&h.command)
- .fetch_one(&self.pool)
- .await?;
-
- let average: (f64,) = sqlx::query_as(&average)
- .bind(&h.command)
- .fetch_one(&self.pool)
- .await?;
-
- let exits: Vec<(i64, i64)> = sqlx::query_as(&exits)
- .bind(&h.command)
- .fetch_all(&self.pool)
- .await?;
-
- let day_of_week: Vec<(String, i64)> = sqlx::query_as(&day_of_week)
- .bind(&h.command)
- .fetch_all(&self.pool)
- .await?;
-
- let duration_over_time: Vec<(String, f64)> = sqlx::query_as(&duration_over_time)
- .bind(&h.command)
- .fetch_all(&self.pool)
- .await?;
-
- let duration_over_time = duration_over_time
- .iter()
- .map(|f| (f.0.clone(), f.1.round() as i64))
- .collect();
-
- Ok(HistoryStats {
- next,
- previous: prev,
- total: total.0 as u64,
- average_duration: average.0 as u64,
- exits,
- day_of_week,
- duration_over_time,
- })
- }
-
- async fn get_dups(&self, before: i64, dupkeep: u32) -> Result<Vec<History>> {
- let res = sqlx::query(
- "SELECT * FROM (
- SELECT *, ROW_NUMBER()
- OVER (PARTITION BY command, cwd, hostname ORDER BY timestamp DESC)
- AS rn
- FROM history
- ) sub
- WHERE rn > ?1 and timestamp < ?2;
- ",
- )
- .bind(dupkeep)
- .bind(before)
- .map(Self::query_history_inner)
- .fetch_all(&self.pool)
- .await?;
-
- Ok(res)
- }
-}
-
-struct Paged {
- database: ClientSqlite,
- page_size: usize,
- last_id: Option<String>,
- include_deleted: bool,
- unique: bool,
-}
-
-impl Paged {
- fn new(database: ClientSqlite, page_size: usize, include_deleted: bool, unique: bool) -> Self {
- Self {
- database,
- page_size,
- last_id: None,
- include_deleted,
- unique,
- }
- }
-
- async fn next(&mut self) -> Result<Option<Vec<History>>> {
- let mut query = SqlBuilder::select_from(SqlName::new("history").alias("h").baquoted());
-
- query.field("*").order_desc("id");
-
- if !self.include_deleted {
- query.and_where_is_null("deleted_at");
- }
-
- if self.unique {
- // We want to deduplicate on command, but the user can search via cwd, hostname, and session.
- // Without those fields, filter modes won't work right. With those fields, we get duplicates.
- // This must be handled upstream.
- query
- .group_by("command, cwd, hostname, session")
- .having("max(timestamp)");
- }
-
- query.limit(self.page_size);
-
- if let Some(last_id) = &self.last_id {
- query.and_where_lt("id", quote(last_id));
- }
-
- let query = query.sql().expect("bug in list query. please report");
- let res = self.database.query_history(&query).await?;
-
- if res.is_empty() {
- Ok(None)
- } else {
- self.last_id = Some(res.last().unwrap().id.0.clone());
- Ok(Some(res))
- }
- }
-}
-
-trait SqlBuilderExt {
- fn fuzzy_condition<S: ToString, T: ToString>(
- &mut self,
- field: S,
- mask: T,
- inverse: bool,
- glob: bool,
- is_or: bool,
- ) -> &mut Self;
-}
-
-impl SqlBuilderExt for SqlBuilder {
- /// adapted from the sql-builder *like functions
- fn fuzzy_condition<S: ToString, T: ToString>(
- &mut self,
- field: S,
- mask: T,
- inverse: bool,
- glob: bool,
- is_or: bool,
- ) -> &mut Self {
- let mut cond = field.to_string();
- if inverse {
- cond.push_str(" NOT");
- }
- if glob {
- cond.push_str(" GLOB '");
- } else {
- cond.push_str(" LIKE '");
- }
- cond.push_str(&esc(mask.to_string()));
- cond.push('\'');
- if is_or {
- self.or_where(cond)
- } else {
- self.and_where(cond)
- }
- }
-}
-
-#[cfg(test)]
-mod test {
- use crate::aclient::settings::test_local_timeout;
-
- use super::{
- ClientSqlite, Context, FilterMode, History, OffsetDateTime, OptFilters, Result, SearchMode,
- };
- use std::time::{Duration, Instant};
-
- async fn assert_search_eq(
- db: &ClientSqlite,
- mode: SearchMode,
- filter_mode: FilterMode,
- query: &str,
- expected: usize,
- ) -> Result<Vec<History>> {
- let context = Context {
- hostname: "test:host".to_string(),
- session: "beepboopiamasession".to_string(),
- cwd: "/home/ellie".to_string(),
- host_id: "test-host".to_string(),
- git_root: None,
- };
-
- let results = db
- .search(
- mode,
- filter_mode,
- &context,
- query,
- OptFilters {
- ..Default::default()
- },
- )
- .await?;
-
- assert_eq!(
- results.len(),
- expected,
- "query \"{}\", commands: {:?}",
- query,
- results.iter().map(|a| &a.command).collect::<Vec<&String>>()
- );
- Ok(results)
- }
-
- async fn assert_search_commands(
- db: &ClientSqlite,
- mode: SearchMode,
- filter_mode: FilterMode,
- query: &str,
- expected_commands: Vec<&str>,
- ) {
- let results = assert_search_eq(db, mode, filter_mode, query, expected_commands.len())
- .await
- .unwrap();
- let commands: Vec<&str> = results.iter().map(|a| a.command.as_str()).collect();
- assert_eq!(commands, expected_commands);
- }
-
- async fn new_history_item(db: &mut ClientSqlite, cmd: &str) -> Result<()> {
- const SESSION: &str = "test";
- const HOSTNAME: &str = "test.host";
-
- let mut captured: History = History::daemon()
- .timestamp(OffsetDateTime::now_utc())
- .command(cmd)
- .cwd("/home/ellie")
- .session(SESSION)
- .hostname(HOSTNAME)
- .build()
- .into();
-
- captured.exit = 0;
- captured.duration = 1;
- captured.session = "beep boop".to_string();
- captured.hostname = "booop".to_string();
-
- db.save(&captured).await
- }
-
- #[tokio::test(flavor = "multi_thread")]
- async fn test_search_prefix() {
- let mut db = ClientSqlite::new("sqlite::memory:", test_local_timeout())
- .await
- .unwrap();
- new_history_item(&mut db, "ls /home/ellie").await.unwrap();
-
- assert_search_eq(&db, SearchMode::Prefix, FilterMode::Global, "ls", 1)
- .await
- .unwrap();
- assert_search_eq(&db, SearchMode::Prefix, FilterMode::Global, "/home", 0)
- .await
- .unwrap();
- assert_search_eq(&db, SearchMode::Prefix, FilterMode::Global, "ls ", 0)
- .await
- .unwrap();
- }
-
- #[tokio::test(flavor = "multi_thread")]
- async fn test_search_fulltext() {
- let mut db = ClientSqlite::new("sqlite::memory:", test_local_timeout())
- .await
- .unwrap();
- new_history_item(&mut db, "ls /home/ellie").await.unwrap();
-
- assert_search_eq(&db, SearchMode::FullText, FilterMode::Global, "ls", 1)
- .await
- .unwrap();
- assert_search_eq(&db, SearchMode::FullText, FilterMode::Global, "/home", 1)
- .await
- .unwrap();
- assert_search_eq(&db, SearchMode::FullText, FilterMode::Global, "ls ho", 1)
- .await
- .unwrap();
- assert_search_eq(&db, SearchMode::FullText, FilterMode::Global, "hm", 0)
- .await
- .unwrap();
-
- // regex
- assert_search_eq(&db, SearchMode::FullText, FilterMode::Global, "r/^ls ", 1)
- .await
- .unwrap();
- assert_search_eq(
- &db,
- SearchMode::FullText,
- FilterMode::Global,
- "r/ls / ie$",
- 1,
- )
- .await
- .unwrap();
- assert_search_eq(
- &db,
- SearchMode::FullText,
- FilterMode::Global,
- "r/ls / !ie",
- 0,
- )
- .await
- .unwrap();
- assert_search_eq(
- &db,
- SearchMode::FullText,
- FilterMode::Global,
- "meow r/ls/",
- 0,
- )
- .await
- .unwrap();
- assert_search_eq(&db, SearchMode::FullText, FilterMode::Global, "r//hom/", 1)
- .await
- .unwrap();
- assert_search_eq(
- &db,
- SearchMode::FullText,
- FilterMode::Global,
- "r//home//",
- 1,
- )
- .await
- .unwrap();
- assert_search_eq(
- &db,
- SearchMode::FullText,
- FilterMode::Global,
- "r//home///",
- 0,
- )
- .await
- .unwrap();
- assert_search_eq(&db, SearchMode::FullText, FilterMode::Global, "/home.*e", 0)
- .await
- .unwrap();
- assert_search_eq(
- &db,
- SearchMode::FullText,
- FilterMode::Global,
- "r/home.*e",
- 1,
- )
- .await
- .unwrap();
- }
-
- #[tokio::test(flavor = "multi_thread")]
- async fn test_search_fuzzy() {
- let mut db = ClientSqlite::new("sqlite::memory:", test_local_timeout())
- .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();
-
- assert_search_eq(&db, SearchMode::Fuzzy, FilterMode::Global, "ls /", 3)
- .await
- .unwrap();
- assert_search_eq(&db, SearchMode::Fuzzy, FilterMode::Global, "ls/", 2)
- .await
- .unwrap();
- assert_search_eq(&db, SearchMode::Fuzzy, FilterMode::Global, "l/h/", 2)
- .await
- .unwrap();
- assert_search_eq(&db, SearchMode::Fuzzy, FilterMode::Global, "/h/e", 3)
- .await
- .unwrap();
- assert_search_eq(&db, SearchMode::Fuzzy, FilterMode::Global, "/hmoe/", 0)
- .await
- .unwrap();
- assert_search_eq(&db, SearchMode::Fuzzy, FilterMode::Global, "ellie/home", 0)
- .await
- .unwrap();
- assert_search_eq(&db, SearchMode::Fuzzy, FilterMode::Global, "lsellie", 1)
- .await
- .unwrap();
- assert_search_eq(&db, SearchMode::Fuzzy, FilterMode::Global, " ", 4)
- .await
- .unwrap();
-
- // single term operators
- assert_search_eq(&db, SearchMode::Fuzzy, FilterMode::Global, "^ls", 2)
- .await
- .unwrap();
- assert_search_eq(&db, SearchMode::Fuzzy, FilterMode::Global, "'ls", 2)
- .await
- .unwrap();
- assert_search_eq(&db, SearchMode::Fuzzy, FilterMode::Global, "ellie$", 2)
- .await
- .unwrap();
- assert_search_eq(&db, SearchMode::Fuzzy, FilterMode::Global, "!^ls", 2)
- .await
- .unwrap();
- assert_search_eq(&db, SearchMode::Fuzzy, FilterMode::Global, "!ellie", 1)
- .await
- .unwrap();
- assert_search_eq(&db, SearchMode::Fuzzy, FilterMode::Global, "!ellie$", 2)
- .await
- .unwrap();
-
- // multiple terms
- assert_search_eq(&db, SearchMode::Fuzzy, FilterMode::Global, "ls !ellie", 1)
- .await
- .unwrap();
- assert_search_eq(&db, SearchMode::Fuzzy, FilterMode::Global, "^ls !e$", 1)
- .await
- .unwrap();
- assert_search_eq(&db, SearchMode::Fuzzy, FilterMode::Global, "home !^ls", 2)
- .await
- .unwrap();
- assert_search_eq(
- &db,
- SearchMode::Fuzzy,
- FilterMode::Global,
- "'frank | 'rustup",
- 2,
- )
- .await
- .unwrap();
- assert_search_eq(
- &db,
- SearchMode::Fuzzy,
- FilterMode::Global,
- "'frank | 'rustup 'ls",
- 1,
- )
- .await
- .unwrap();
-
- // case matching
- assert_search_eq(&db, SearchMode::Fuzzy, FilterMode::Global, "Ellie", 1)
- .await
- .unwrap();
-
- // regex
- assert_search_eq(&db, SearchMode::Fuzzy, FilterMode::Global, "r/^ls ", 2)
- .await
- .unwrap();
- assert_search_eq(&db, SearchMode::Fuzzy, FilterMode::Global, "r/[Ee]llie", 3)
- .await
- .unwrap();
- assert_search_eq(&db, SearchMode::Fuzzy, FilterMode::Global, "/h/e r/^ls ", 1)
- .await
- .unwrap();
- }
-
- #[tokio::test(flavor = "multi_thread")]
- async fn test_search_reordered_fuzzy() {
- let mut db = ClientSqlite::new("sqlite::memory:", test_local_timeout())
- .await
- .unwrap();
- // test ordering of results: we should choose the first, even though it happened longer ago.
-
- new_history_item(&mut db, "curl").await.unwrap();
- new_history_item(&mut db, "corburl").await.unwrap();
-
- // if fuzzy reordering is on, it should come back in a more sensible order
- assert_search_commands(
- &db,
- SearchMode::Fuzzy,
- FilterMode::Global,
- "curl",
- vec!["curl", "corburl"],
- )
- .await;
-
- assert_search_eq(&db, SearchMode::Fuzzy, FilterMode::Global, "xxxx", 0)
- .await
- .unwrap();
- assert_search_eq(&db, SearchMode::Fuzzy, FilterMode::Global, "", 2)
- .await
- .unwrap();
- }
-
- #[tokio::test(flavor = "multi_thread")]
- #[expect(clippy::similar_names)]
- async fn test_paged_basic() {
- let mut db = ClientSqlite::new("sqlite::memory:", test_local_timeout())
- .await
- .unwrap();
-
- // Add 5 history items
- for i in 0..5 {
- new_history_item(&mut db, &format!("command{}", i))
- .await
- .unwrap();
- }
-
- // Create a paged iterator with page_size of 2
- let mut paged = db.all_paged(2, false, false);
-
- // First page should have 2 items
- let page1 = paged.next().await.unwrap();
- assert!(page1.is_some());
- assert_eq!(page1.unwrap().len(), 2);
-
- // Second page should have 2 items
- let page2 = paged.next().await.unwrap();
- assert!(page2.is_some());
- assert_eq!(page2.unwrap().len(), 2);
-
- // Third page should have 1 item
- let page3 = paged.next().await.unwrap();
- assert!(page3.is_some());
- assert_eq!(page3.unwrap().len(), 1);
-
- // Fourth page should be None (exhausted)
- let page4 = paged.next().await.unwrap();
- assert!(page4.is_none());
- }
-
- #[tokio::test(flavor = "multi_thread")]
- async fn test_paged_empty() {
- let db = ClientSqlite::new("sqlite::memory:", test_local_timeout())
- .await
- .unwrap();
-
- // Create a paged iterator on empty database
- let mut paged = db.all_paged(10, false, false);
-
- // Should return None immediately
- let page = paged.next().await.unwrap();
- assert!(page.is_none());
- }
-
- #[tokio::test(flavor = "multi_thread")]
- async fn test_paged_unique() {
- let mut db = ClientSqlite::new("sqlite::memory:", test_local_timeout())
- .await
- .unwrap();
-
- // Add duplicate commands
- new_history_item(&mut db, "duplicate").await.unwrap();
- new_history_item(&mut db, "duplicate").await.unwrap();
- new_history_item(&mut db, "unique1").await.unwrap();
- new_history_item(&mut db, "unique2").await.unwrap();
-
- // Without unique flag - should get all 4
- let mut paged = db.all_paged(10, false, false);
- let page = paged.next().await.unwrap().unwrap();
- assert_eq!(page.len(), 4);
-
- // With unique flag - should get 3 (duplicates collapsed)
- let mut paged_unique = db.all_paged(10, false, true);
- let paged_unique = paged_unique.next().await.unwrap().unwrap();
- assert_eq!(paged_unique.len(), 3);
- }
-
- #[tokio::test(flavor = "multi_thread")]
- async fn test_search_bench_dupes() {
- let context = Context {
- hostname: "test:host".to_string(),
- session: "beepboopiamasession".to_string(),
- cwd: "/home/ellie".to_string(),
- host_id: "test-host".to_string(),
- git_root: None,
- };
-
- let mut db = ClientSqlite::new("sqlite::memory:", test_local_timeout())
- .await
- .unwrap();
- for _i in 1..10000 {
- new_history_item(&mut db, "i am a duplicated command")
- .await
- .unwrap();
- }
- let start = Instant::now();
- let _results = db
- .search(
- SearchMode::Fuzzy,
- FilterMode::Global,
- &context,
- "",
- OptFilters {
- ..Default::default()
- },
- )
- .await
- .unwrap();
- let duration = start.elapsed();
-
- assert!(duration < Duration::from_secs(15));
- }
-}
-
-struct QueryTokenizer<'a> {
- query: &'a str,
- last_pos: usize,
-}
-
-enum QueryToken<'a> {
- Match(&'a str, bool),
- MatchStart(&'a str, bool),
- MatchEnd(&'a str, bool),
- MatchFull(&'a str, bool),
- Or,
- Regex(&'a str),
-}
-
-impl QueryToken<'_> {
- fn has_uppercase(&self) -> bool {
- match self {
- Self::Match(term, _)
- | Self::MatchStart(term, _)
- | Self::MatchEnd(term, _)
- | Self::MatchFull(term, _) => term.contains(char::is_uppercase),
- _ => false,
- }
- }
-
- fn is_inverse(&self) -> bool {
- match self {
- Self::Match(_, inv)
- | Self::MatchStart(_, inv)
- | Self::MatchEnd(_, inv)
- | Self::MatchFull(_, inv) => *inv,
- _ => false,
- }
- }
-}
-
-impl<'a> QueryTokenizer<'a> {
- fn new(query: &'a str) -> Self {
- Self { query, last_pos: 0 }
- }
-}
-
-impl<'a> Iterator for QueryTokenizer<'a> {
- type Item = QueryToken<'a>;
- fn next(&mut self) -> Option<Self::Item> {
- let remaining = &self.query[self.last_pos..];
- if remaining.is_empty() {
- return None;
- }
-
- if let Some(remaining) = remaining.strip_prefix("r/") {
- let (regex, next_pos) = if let Some(end) = remaining.find("/ ") {
- (&remaining[..end], self.last_pos + 2 + end + 2)
- } else if let Some(remaining) = remaining.strip_suffix('/') {
- (remaining, self.query.len())
- } else {
- (remaining, self.query.len())
- };
- self.last_pos = next_pos;
- Some(QueryToken::Regex(regex))
- } else {
- let (mut part, next_pos) = if let Some(sp) = remaining.find(' ') {
- (&remaining[..sp], self.last_pos + sp + 1)
- } else {
- (remaining, self.query.len())
- };
- self.last_pos = next_pos;
-
- if part == "|" {
- return Some(QueryToken::Or);
- }
-
- let is_inverse = part.strip_prefix('!').is_some_and(|s| {
- part = s;
- true
- });
-
- #[expect(clippy::option_if_let_else, reason = "It's too ugly")]
- let token = if let Some(s) = part.strip_prefix('^') {
- QueryToken::MatchStart(s, is_inverse)
- } else if let Some(s) = part.strip_suffix('$') {
- QueryToken::MatchEnd(s, is_inverse)
- } else if let Some(s) = part.strip_prefix('\'') {
- QueryToken::MatchFull(s, is_inverse)
- } else {
- QueryToken::Match(part, is_inverse)
- };
- Some(token)
- }
- }
}
diff --git a/crates/daemon/src/aclient/history/mod.rs b/crates/daemon/src/aclient/history/mod.rs
index f9afa5e9..ae7654a6 100644
--- a/crates/daemon/src/aclient/history/mod.rs
+++ b/crates/daemon/src/aclient/history/mod.rs
@@ -1,4 +1,3 @@
-use regex::RegexSet;
use rmp::decode::DecodeStringError;
use rmp::decode::ValueReadError;
use rmp::{Marker, decode::Bytes};
@@ -18,28 +17,6 @@ const HISTORY_RECORD_VERSION_V0: u16 = 0;
const HISTORY_RECORD_VERSION_V1: u16 = 1;
const HISTORY_VERSION: &str = HISTORY_VERSION_V1;
const HISTORY_TAG: &str = "history";
-const HISTORY_AUTHOR_ENV: &str = "ATUIN_HISTORY_AUTHOR";
-const HISTORY_INTENT_ENV: &str = "ATUIN_HISTORY_INTENT";
-
-#[derive(Debug, Clone, PartialEq, Eq, sqlx::FromRow)]
-pub(crate) struct HistoryStats {
- /// The command that was ran after this one in the session
- pub(crate) next: Option<History>,
-
- /// The command that was ran before this one in the session
- pub(crate) previous: Option<History>,
-
- /// How many times has this command been ran?
- pub(crate) total: u64,
-
- pub(crate) average_duration: u64,
-
- pub(crate) exits: Vec<(i64, i64)>,
-
- pub(crate) day_of_week: Vec<(String, i64)>,
-
- pub(crate) duration_over_time: Vec<(String, i64)>,
-}
trait HistoryExt: Sized {
fn serialize(&self) -> Result<DecryptedData>;
@@ -47,7 +24,6 @@ trait HistoryExt: Sized {
fn deserialize_v0(bytes: &[u8]) -> Result<Self>;
fn deserialize_v1(bytes: &[u8]) -> Result<Self>;
fn deserialize(bytes: &[u8], version: &str) -> Result<Self>;
- fn success(&self) -> bool;
}
impl HistoryExt for History {
@@ -248,18 +224,6 @@ impl HistoryExt for History {
_ => bail!("unknown version {version:?}"),
}
}
-
- #[expect(unused)]
- fn success(&self) -> bool {
- self.exit == 0 || self.duration == -1
- }
-}
-
-#[derive(Debug, Copy, Clone)]
-struct SettingsFilter<'a> {
- pub history: &'a RegexSet,
- pub cwd: &'a RegexSet,
- pub secrets: bool,
}
#[cfg(test)]
diff --git a/crates/daemon/src/aclient/history/store.rs b/crates/daemon/src/aclient/history/store.rs
index 244725eb..952f2070 100644
--- a/crates/daemon/src/aclient/history/store.rs
+++ b/crates/daemon/src/aclient/history/store.rs
@@ -1,5 +1,3 @@
-use std::collections::HashSet;
-
use eyre::{Result, bail, eyre};
use rmp::decode::Bytes;
use turtle::history::{History, HistoryId};
@@ -143,58 +141,6 @@ impl HistoryStore {
Ok((id, idx))
}
- async fn push_batch(&self, records: impl Iterator<Item = HistoryRecord>) -> Result<()> {
- let mut ret = Vec::new();
-
- let idx = self
- .store
- .last(self.host_id, HISTORY_TAG)
- .await?
- .map_or(0, |p| p.idx + 1);
-
- // Could probably _also_ do this as an iterator, but let's see how this is for now.
- // optimizing for minimal sqlite transactions, this code can be optimised later
- for (n, record) in records.enumerate() {
- let bytes = record.serialize()?;
-
- let record = Record::builder()
- .host(Host::new(self.host_id))
- .version(HISTORY_VERSION.to_string())
- .tag(HISTORY_TAG.to_string())
- .idx(idx + n as u64)
- .data(bytes)
- .build();
-
- let record = record.encrypt::<PASETO_V4>(&self.encryption_key);
-
- ret.push(record);
- }
-
- self.store.push_batch(ret.iter()).await?;
-
- Ok(())
- }
-
- async fn delete(&self, id: HistoryId) -> Result<(RecordId, RecordIdx)> {
- let record = HistoryRecord::Delete(id);
-
- self.push_record(record).await
- }
-
- /// Delete a batch of history entries via the record store.
- /// Returns the record IDs so the caller can run `incremental_build` when ready.
- async fn delete_entries(
- &self,
- entries: impl IntoIterator<Item = History>,
- ) -> Result<Vec<RecordId>> {
- let mut record_ids = Vec::new();
- for entry in entries {
- let (id, _) = self.delete(entry.id).await?;
- record_ids.push(id);
- }
- Ok(record_ids)
- }
-
pub(crate) async fn push(&self, history: History) -> Result<(RecordId, RecordIdx)> {
// TODO(ellie): move the history store to its own file
// it's tiny rn so fine as is
@@ -203,60 +149,37 @@ impl HistoryStore {
self.push_record(record).await
}
- async fn history(&self) -> Result<Vec<HistoryRecord>> {
- // Atm this loads all history into memory
- // Not ideal as that is potentially quite a lot, although history will be small.
- let records = self.store.all_tagged(HISTORY_TAG).await?;
- let mut ret = Vec::with_capacity(records.len());
-
- for record in records {
- let hist = match record.version.as_str() {
- HISTORY_VERSION_V0 | HISTORY_VERSION => {
- let version = record.version.clone();
- let decrypted = record.decrypt::<PASETO_V4>(&self.encryption_key)?;
-
- HistoryRecord::deserialize(&decrypted.data, version.as_str())
- }
- version => bail!("unknown history version {version:?}"),
- }?;
-
- ret.push(hist);
- }
-
- Ok(ret)
- }
-
- async fn build(&self, database: &ClientSqlite) -> Result<()> {
- // I'd like to change how we rebuild and not couple this with the database, but need to
- // consider the structure more deeply. This will be easy to change.
-
- // TODO(ellie): page or iterate this
- let history = self.history().await?;
-
- // In theory we could flatten this here
- // The current issue is that the database may have history in it already, from the old sync
- // This didn't actually delete old history
- // If we're sure we have a DB only maintained by the new store, we can flatten
- // create/delete before we even get to sqlite
- let mut creates = Vec::new();
- let mut deletes = Vec::new();
-
- for i in history {
- match i {
- HistoryRecord::Create(h) => {
- creates.push(h);
- }
- HistoryRecord::Delete(id) => {
- deletes.push(id);
- }
- }
- }
-
- database.save_bulk(&creates).await?;
- database.delete_rows(&deletes).await?;
-
- Ok(())
- }
+ // async fn build(&self, database: &ClientSqlite) -> Result<()> {
+ // // I'd like to change how we rebuild and not couple this with the database, but need to
+ // // consider the structure more deeply. This will be easy to change.
+ //
+ // // TODO(ellie): page or iterate this
+ // let history = self.history().await?;
+ //
+ // // In theory we could flatten this here
+ // // The current issue is that the database may have history in it already, from the old sync
+ // // This didn't actually delete old history
+ // // If we're sure we have a DB only maintained by the new store, we can flatten
+ // // create/delete before we even get to sqlite
+ // let mut creates = Vec::new();
+ // let mut deletes = Vec::new();
+ //
+ // for i in history {
+ // match i {
+ // HistoryRecord::Create(h) => {
+ // creates.push(h);
+ // }
+ // HistoryRecord::Delete(id) => {
+ // deletes.push(id);
+ // }
+ // }
+ // }
+ //
+ // database.save_bulk(&creates).await?;
+ // database.delete_rows(&deletes).await?;
+ //
+ // Ok(())
+ // }
pub(crate) async fn incremental_build(
&self,
@@ -294,74 +217,6 @@ impl HistoryStore {
Ok(())
}
-
- /// Get a list of history IDs that exist in the store
- /// Note: This currently involves loading all history into memory. This is not going to be a
- /// large amount in absolute terms, but do not all it in a hot loop.
- async fn history_ids(&self) -> Result<HashSet<HistoryId>> {
- let history = self.history().await?;
-
- let ret = history
- .iter()
- .map(|h| match h {
- HistoryRecord::Create(h) => h.id.clone(),
- HistoryRecord::Delete(id) => id.clone(),
- })
- .collect::<HashSet<_>>();
-
- Ok(ret)
- }
-
- async fn init_store(&self, db: &ClientSqlite) -> Result<()> {
- todo!();
-
- // let pb = ProgressBar::new_spinner();
- // pb.set_style(
- // ProgressStyle::with_template("{spinner:.blue} {msg}")
- // .unwrap()
- // .with_key("eta", |state: &ProgressState, w: &mut dyn Write| {
- // write!(w, "{:.1}s", state.eta().as_secs_f64()).unwrap();
- // })
- // .progress_chars("#>-"),
- // );
- // pb.enable_steady_tick(Duration::from_millis(500));
- //
- // pb.set_message("Fetching history from old database");
- //
- // let context = current_context().await?;
- // let history = db.list(&[], &context, None, false, true).await?;
- //
- // pb.set_message("Fetching history already in store");
- // let store_ids = self.history_ids().await?;
- //
- // pb.set_message("Converting old history to new store");
- // let mut records = Vec::new();
- //
- // for i in history {
- // debug!("loaded {}", i.id);
- //
- // if store_ids.contains(&i.id) {
- // debug!("skipping {} - already exists", i.id);
- // continue;
- // }
- //
- // if i.deleted_at.is_some() {
- // records.push(HistoryRecord::Delete(i.id));
- // } else {
- // records.push(HistoryRecord::Create(i));
- // }
- // }
- //
- // pb.set_message("Writing to db");
- //
- // if !records.is_empty() {
- // self.push_batch(records.into_iter()).await?;
- // }
- //
- // pb.finish_with_message("Import complete");
-
- Ok(())
- }
}
#[cfg(test)]
diff --git a/crates/daemon/src/aclient/mod.rs b/crates/daemon/src/aclient/mod.rs
index fdadb81b..2c445945 100644
--- a/crates/daemon/src/aclient/mod.rs
+++ b/crates/daemon/src/aclient/mod.rs
@@ -6,5 +6,4 @@ pub(crate) mod settings;
mod api_client;
mod meta;
-mod ordering;
mod utils;
diff --git a/crates/daemon/src/aclient/ordering.rs b/crates/daemon/src/aclient/ordering.rs
deleted file mode 100644
index 8fa6498e..00000000
--- a/crates/daemon/src/aclient/ordering.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-use minspan::minspan;
-use turtle::history::History;
-
-use super::settings::SearchMode;
-
-pub(crate) fn reorder_fuzzy(mode: SearchMode, query: &str, res: Vec<History>) -> Vec<History> {
- match mode {
- SearchMode::Fuzzy => reorder(query, |x| &x.command, res),
- _ => res,
- }
-}
-
-#[expect(clippy::needless_pass_by_value, reason = "makes things easier")]
-fn reorder<F, A>(query: &str, f: F, res: Vec<A>) -> Vec<A>
-where
- F: Fn(&A) -> &String,
- A: Clone,
-{
- let mut r = res.clone();
- let qvec = &query.chars().collect();
- r.sort_by_cached_key(|h| {
- // TODO for fzf search we should sum up scores for each matched term
- //
- // The fallback is a little unfortunate: when we are asked to match a query that is found nowhere,
- // we don't want to return a None, as the comparison behaviour would put the worst matches
- // at the front. Therefore, we'll return a set of indices that are one larger than the longest
- // possible legitimate match. This is meaningless except as a comparison.
- let (from, to) = minspan::span(qvec, &(f(h).chars().collect())).unwrap_or((0, res.len()));
- 1 + to - from
- });
- r
-}
diff --git a/crates/daemon/src/aclient/record/sqlite_store.rs b/crates/daemon/src/aclient/record/sqlite_store.rs
index 2186da54..0026690b 100644
--- a/crates/daemon/src/aclient/record/sqlite_store.rs
+++ b/crates/daemon/src/aclient/record/sqlite_store.rs
@@ -21,8 +21,6 @@ use turtle_common::record::{
use turtle_common::utils;
use uuid::Uuid;
-use super::encryption::PASETO_V4;
-
#[derive(Debug, Clone)]
pub(crate) struct SqliteStore {
pool: SqlitePool,
@@ -110,15 +108,6 @@ impl SqliteStore {
},
}
}
-
- async fn load_all(&self) -> Result<Vec<Record<EncryptedData>>> {
- let res = sqlx::query("select * from store ")
- .map(Self::query_row)
- .fetch_all(&self.pool)
- .await?;
-
- Ok(res)
- }
}
/// A record store stores records
@@ -157,21 +146,6 @@ impl SqliteStore {
Ok(res)
}
- async fn delete(&self, id: RecordId) -> Result<()> {
- sqlx::query("delete from store where id = ?1")
- .bind(id.0.as_hyphenated().to_string())
- .execute(&self.pool)
- .await?;
-
- Ok(())
- }
-
- async fn delete_all(&self) -> Result<()> {
- sqlx::query("delete from store").execute(&self.pool).await?;
-
- Ok(())
- }
-
pub(crate) async fn last(
&self,
host: HostId,
@@ -192,22 +166,6 @@ impl SqliteStore {
}
}
- async fn first(&self, host: HostId, tag: &str) -> Result<Option<Record<EncryptedData>>> {
- self.idx(host, tag, 0).await
- }
-
- async fn len_tag(&self, tag: &str) -> Result<u64> {
- let res: Result<(i64,), sqlx::Error> =
- sqlx::query_as("select count(*) from store where tag=?1")
- .bind(tag)
- .fetch_one(&self.pool)
- .await;
- match res {
- Err(e) => Err(eyre!("failed to fetch local store len: {}", e)),
- Ok(v) => Ok(v.0 as u64),
- }
- }
-
/// Get the next `limit` records, after and including the given index
pub(crate) async fn next(
&self,
@@ -230,28 +188,6 @@ impl SqliteStore {
Ok(res)
}
- /// Get the first record for a given host and tag
- async fn idx(
- &self,
- host: HostId,
- tag: &str,
- idx: RecordIdx,
- ) -> Result<Option<Record<EncryptedData>>> {
- let res = sqlx::query("select * from store where idx = ?1 and host = ?2 and tag = ?3")
- .bind(idx as i64)
- .bind(host.0.as_hyphenated().to_string())
- .bind(tag)
- .map(Self::query_row)
- .fetch_one(&self.pool)
- .await;
-
- match res {
- Err(sqlx::Error::RowNotFound) => Ok(None),
- Err(e) => Err(eyre!("an error occurred: {}", e)),
- Ok(v) => Ok(Some(v)),
- }
- }
-
pub(crate) async fn status(&self) -> Result<RecordStatus> {
let mut status = RecordStatus::new();
@@ -275,89 +211,6 @@ impl SqliteStore {
Ok(status)
}
-
- /// Get all records for a given tag
- pub(crate) async fn all_tagged(&self, tag: &str) -> Result<Vec<Record<EncryptedData>>> {
- let res = sqlx::query("select * from store where tag = ?1 order by timestamp asc")
- .bind(tag)
- .map(Self::query_row)
- .fetch_all(&self.pool)
- .await?;
-
- Ok(res)
- }
-
- /// Reencrypt every single item in this store with a new key
- /// Be careful - this may mess with sync.
- async fn re_encrypt(&self, old_key: &[u8; 32], new_key: &[u8; 32]) -> Result<()> {
- // Load all the records
- // In memory like some of the other code here
- // This will never be called in a hot loop, and only under the following circumstances
- // 1. The user has logged into a new account, with a new key. They are unlikely to have a
- // lot of data
- // 2. The user has encountered some sort of issue, and runs a maintenance command that
- // invokes this
- let all = self.load_all().await?;
-
- let re_encrypted = all
- .into_iter()
- .map(|record| record.re_encrypt::<PASETO_V4>(old_key, new_key))
- .collect::<Result<Vec<_>>>()?;
-
- // next up, we delete all the old data and reinsert the new stuff
- // do it in one transaction, so if anything fails we rollback OK
-
- let mut tx = self.pool.begin().await?;
-
- let res = sqlx::query("delete from store").execute(&mut *tx).await?;
-
- let rows = res.rows_affected();
- debug!("deleted {rows} rows");
-
- // don't call push_batch, as it will start its own transaction
- // call the underlying save_raw
-
- for record in re_encrypted {
- Self::save_raw(&mut tx, &record).await?;
- }
-
- tx.commit().await?;
-
- Ok(())
- }
-
- /// Verify that every record in this store can be decrypted with the current key
- /// Someday maybe also check each tag/record can be deserialized, but not for now.
- async fn verify(&self, key: &[u8; 32]) -> Result<()> {
- let all = self.load_all().await?;
-
- all.into_iter()
- .map(|record| record.decrypt::<PASETO_V4>(key))
- .collect::<Result<Vec<_>>>()?;
-
- Ok(())
- }
-
- /// Verify that every record in this store can be decrypted with the current key
- /// Someday maybe also check each tag/record can be deserialized, but not for now.
- async fn purge(&self, key: &[u8; 32]) -> Result<()> {
- let all = self.load_all().await?;
-
- for record in &all {
- if record.clone().decrypt::<PASETO_V4>(key).is_ok() {
- continue;
- }
-
- println!(
- "Failed to decrypt {}, deleting",
- record.id.0.as_hyphenated()
- );
-
- self.delete(record.id).await?;
- }
-
- Ok(())
- }
}
#[cfg(test)]
diff --git a/crates/daemon/src/aclient/settings/mod.rs b/crates/daemon/src/aclient/settings/mod.rs
index 10c84f50..ef3f1dd0 100644
--- a/crates/daemon/src/aclient/settings/mod.rs
+++ b/crates/daemon/src/aclient/settings/mod.rs
@@ -1,7 +1,5 @@
use crypto_secretbox::Key;
-use std::{
- collections::HashMap, fmt, fs::read_to_string, path::PathBuf, str::FromStr, sync::OnceLock,
-};
+use std::{collections::HashMap, fs::read_to_string, path::PathBuf, sync::OnceLock};
use tokio::sync::OnceCell;
use tracing::info;
use uuid::Uuid;
@@ -11,12 +9,10 @@ use clap::ValueEnum;
use config::{
Config, ConfigBuilder, Environment, File as ConfigFile, FileFormat, builder::DefaultState,
};
-use eyre::{Context, Error, Result, bail, eyre};
+use eyre::{Context, Result, eyre};
use fs_err::create_dir_all;
-use regex::RegexSet;
use serde::{Deserialize, Serialize};
-use serde_with::DeserializeFromStr;
-use time::{OffsetDateTime, UtcOffset, format_description::FormatItem, macros::format_description};
+use time::OffsetDateTime;
use turtle_common::record::HostId;
use turtle_common::utils;
@@ -26,93 +22,6 @@ static META_STORE: OnceCell<crate::aclient::meta::MetaStore> = OnceCell::const_n
mod meta;
-#[derive(Clone, Debug, Deserialize, Copy, ValueEnum, PartialEq, Serialize)]
-pub(crate) enum SearchMode {
- #[serde(rename = "prefix")]
- Prefix,
-
- #[serde(rename = "fulltext")]
- #[clap(aliases = &["fulltext"])]
- FullText,
-
- #[serde(rename = "fuzzy")]
- Fuzzy,
-
- #[serde(rename = "skim")]
- Skim,
-
- #[serde(rename = "daemon-fuzzy")]
- #[clap(aliases = &["daemon-fuzzy"])]
- DaemonFuzzy,
-}
-
-impl SearchMode {
- fn as_str(self) -> &'static str {
- match self {
- Self::Prefix => "PREFIX",
- Self::FullText => "FULLTXT",
- Self::Fuzzy => "FUZZY",
- Self::Skim => "SKIM",
- Self::DaemonFuzzy => "DAEMON",
- }
- }
- fn next(self, settings: &Settings) -> Self {
- match self {
- Self::Prefix => Self::FullText,
- // if the user is using skim, we go to skim
- Self::FullText if settings.search_mode == Self::Skim => Self::Skim,
- // if the user is using daemon-fuzzy, we go to daemon-fuzzy
- Self::FullText if settings.search_mode == Self::DaemonFuzzy => Self::DaemonFuzzy,
- // otherwise fuzzy.
- Self::FullText => Self::Fuzzy,
- Self::Fuzzy | Self::Skim | Self::DaemonFuzzy => Self::Prefix,
- }
- }
-}
-
-#[derive(Clone, Debug, Deserialize, Copy, PartialEq, Eq, ValueEnum, Serialize)]
-pub(crate) enum FilterMode {
- #[serde(rename = "global")]
- Global = 0,
-
- #[serde(rename = "host")]
- Host = 1,
-
- #[serde(rename = "session")]
- Session = 2,
-
- #[serde(rename = "directory")]
- Directory = 3,
-
- #[serde(rename = "workspace")]
- Workspace = 4,
-
- #[serde(rename = "session-preload")]
- SessionPreload = 5,
-}
-
-impl FilterMode {
- fn as_str(self) -> &'static str {
- match self {
- Self::Global => "GLOBAL",
- Self::Host => "HOST",
- Self::Session => "SESSION",
- Self::Directory => "DIRECTORY",
- Self::Workspace => "WORKSPACE",
- Self::SessionPreload => "SESSION+",
- }
- }
-}
-
-#[derive(Clone, Debug, Deserialize, Copy, Serialize)]
-enum ExitMode {
- #[serde(rename = "return-original")]
- ReturnOriginal,
-
- #[serde(rename = "return-query")]
- ReturnQuery,
-}
-
// FIXME: Can use upstream Dialect enum if https://github.com/stevedonovan/chrono-english/pull/16 is merged
// FIXME: Above PR was merged, but dependency was changed to interim (fork of chrono-english) in the ... interim
#[derive(Clone, Debug, Deserialize, Copy, Serialize)]
@@ -133,77 +42,6 @@ impl From<Dialect> for interim::Dialect {
}
}
-/// Type wrapper around `time::UtcOffset` to support a wider variety of timezone formats.
-///
-/// Note that the parsing of this struct needs to be done before starting any
-/// multithreaded runtime, otherwise it will fail on most Unix systems.
-///
-/// See: <https://github.com/atuinsh/atuin/pull/1517#discussion_r1447516426>
-#[derive(Clone, Copy, Debug, Eq, PartialEq, DeserializeFromStr, Serialize)]
-struct Timezone(UtcOffset);
-impl fmt::Display for Timezone {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- self.0.fmt(f)
- }
-}
-/// format: <+|-><hour>[:<minute>[:<second>]]
-static OFFSET_FMT: &[FormatItem<'_>] = format_description!(
- "[offset_hour sign:mandatory padding:none][optional [:[offset_minute padding:none][optional [:[offset_second padding:none]]]]]"
-);
-impl FromStr for Timezone {
- type Err = Error;
-
- fn from_str(s: &str) -> Result<Self> {
- // local timezone
- if matches!(s.to_lowercase().as_str(), "l" | "local") {
- // There have been some timezone issues, related to errors fetching it on some
- // platforms
- // Rather than fail to start, fallback to UTC. The user should still be able to specify
- // their timezone manually in the config file.
- let offset = UtcOffset::current_local_offset().unwrap_or(UtcOffset::UTC);
- return Ok(Self(offset));
- }
-
- if matches!(s.to_lowercase().as_str(), "0" | "utc") {
- let offset = UtcOffset::UTC;
- return Ok(Self(offset));
- }
-
- // offset from UTC
- if let Ok(offset) = UtcOffset::parse(s, OFFSET_FMT) {
- return Ok(Self(offset));
- }
-
- // IDEA: Currently named timezones are not supported, because the well-known crate
- // for this is `chrono_tz`, which is not really interoperable with the datetime crate
- // that we currently use - `time`. If ever we migrate to using `chrono`, this would
- // be a good feature to add.
-
- bail!(r#""{s}" is not a valid timezone spec"#)
- }
-}
-
-#[derive(Clone, Debug, Deserialize, Copy, Serialize)]
-enum Style {
- #[serde(rename = "auto")]
- Auto,
-
- #[serde(rename = "full")]
- Full,
-
- #[serde(rename = "compact")]
- Compact,
-}
-
-#[derive(Clone, Debug, Deserialize, Copy, Serialize)]
-enum WordJumpMode {
- #[serde(rename = "emacs")]
- Emacs,
-
- #[serde(rename = "subl")]
- Subl,
-}
-
#[derive(Clone, Debug, Deserialize, Copy, PartialEq, Eq, ValueEnum, Serialize)]
enum KeymapMode {
#[serde(rename = "emacs")]
@@ -249,144 +87,6 @@ enum CursorStyle {
}
#[derive(Clone, Debug, Deserialize, Serialize)]
-struct Stats {
- #[serde(default = "Stats::common_prefix_default")]
- common_prefix: Vec<String>, // sudo, etc. commands we want to strip off
- #[serde(default = "Stats::common_subcommands_default")]
- common_subcommands: Vec<String>, // kubectl, commands we should consider subcommands for
- #[serde(default = "Stats::ignored_commands_default")]
- ignored_commands: Vec<String>, // cd, ls, etc. commands we want to completely hide from stats
-}
-
-impl Stats {
- fn common_prefix_default() -> Vec<String> {
- vec!["sudo", "doas"].into_iter().map(String::from).collect()
- }
-
- fn common_subcommands_default() -> Vec<String> {
- vec![
- "apt",
- "cargo",
- "composer",
- "dnf",
- "docker",
- "dotnet",
- "git",
- "go",
- "ip",
- "jj",
- "kubectl",
- "nix",
- "nmcli",
- "npm",
- "pecl",
- "pnpm",
- "podman",
- "port",
- "systemctl",
- "tmux",
- "yarn",
- ]
- .into_iter()
- .map(String::from)
- .collect()
- }
-
- fn ignored_commands_default() -> Vec<String> {
- vec![]
- }
-}
-
-impl Default for Stats {
- fn default() -> Self {
- Self {
- common_prefix: Self::common_prefix_default(),
- common_subcommands: Self::common_subcommands_default(),
- ignored_commands: Self::ignored_commands_default(),
- }
- }
-}
-
-#[derive(Clone, Debug, Deserialize, Default, Serialize)]
-#[expect(clippy::struct_excessive_bools)]
-struct Keys {
- scroll_exits: bool,
- exit_past_line_start: bool,
- accept_past_line_end: bool,
- accept_past_line_start: bool,
- accept_with_backspace: bool,
- prefix: String,
-}
-
-impl Keys {
- /// The standard default values for all `[keys]` options.
- /// These match the config defaults set in `builder_with_data_dir()`.
- fn standard_defaults() -> Self {
- Self {
- scroll_exits: true,
- exit_past_line_start: true,
- accept_past_line_end: true,
- accept_past_line_start: false,
- accept_with_backspace: false,
- prefix: "a".to_string(),
- }
- }
-}
-
-/// A single rule within a conditional keybinding config.
-#[derive(Clone, Debug, Deserialize, Serialize)]
-struct KeyRuleConfig {
- /// Optional condition expression (e.g. "cursor-at-start", "input-empty && no-results").
- /// If absent, the rule always matches.
- #[serde(default)]
- when: Option<String>,
- /// The action to perform (e.g. "exit", "cursor-left", "accept").
- action: String,
-}
-
-/// A keybinding config value: either a simple action string or an ordered list of conditional rules.
-#[derive(Clone, Debug, Deserialize, Serialize)]
-#[serde(untagged)]
-enum KeyBindingConfig {
- /// Simple unconditional binding: `"ctrl-c" = "return-original"`
- Simple(String),
- /// Conditional binding: `"left" = [{ when = "cursor-at-start", action = "exit" }, { action = "cursor-left" }]`
- Rules(Vec<KeyRuleConfig>),
-}
-
-/// User-facing keymap configuration. Each mode maps key strings to bindings.
-/// Keys present here override the defaults for that key; unmentioned keys keep defaults.
-#[derive(Clone, Debug, Deserialize, Serialize, Default)]
-struct KeymapConfig {
- #[serde(default)]
- emacs: HashMap<String, KeyBindingConfig>,
- #[serde(default, rename = "vim-normal")]
- vim_normal: HashMap<String, KeyBindingConfig>,
- #[serde(default, rename = "vim-insert")]
- vim_insert: HashMap<String, KeyBindingConfig>,
- #[serde(default)]
- inspector: HashMap<String, KeyBindingConfig>,
- #[serde(default)]
- prefix: HashMap<String, KeyBindingConfig>,
-}
-
-impl KeymapConfig {
- /// Returns true if no keybinding overrides are configured in any mode.
- fn is_empty(&self) -> bool {
- self.emacs.is_empty()
- && self.vim_normal.is_empty()
- && self.vim_insert.is_empty()
- && self.inspector.is_empty()
- && self.prefix.is_empty()
- }
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-struct Preview {
- strategy: PreviewStrategy,
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
pub(crate) struct Daemon {
/// The daemon will handle sync on an interval. How often to sync, in seconds.
pub(crate) sync_frequency: u64,
@@ -404,100 +104,6 @@ pub(crate) struct Daemon {
tcp_port: u64,
}
-#[derive(Clone, Debug, Deserialize, Serialize)]
-struct Search {
- /// The list of enabled filter modes, in order of priority.
- filters: Vec<FilterMode>,
-
- /// The recency score multiplier for the search index (default: 1.0).
- /// Values < 1.0 reduce weight, > 1.0 increase weight, 0.0 disables.
- recency_score_multiplier: f64,
-
- /// The frequency score multiplier for the search index (default: 1.0).
- /// Values < 1.0 reduce weight, > 1.0 increase weight, 0.0 disables.
- frequency_score_multiplier: f64,
-
- /// The overall frecency score multiplier for the search index (default: 1.0).
- /// Applied after combining recency and frequency scores.
- frecency_score_multiplier: f64,
-}
-
-/// Log level for file logging. Maps to tracing's [`LevelFilter`].
-#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
-#[serde(rename_all = "lowercase")]
-enum LogLevel {
- Trace,
- Debug,
- #[default]
- Info,
- Warn,
- Error,
-}
-
-impl LogLevel {
- /// Convert to a tracing directive string for use with [`EnvFilter`].
- fn as_directive(self) -> &'static str {
- match self {
- Self::Trace => "trace",
- Self::Debug => "debug",
- Self::Info => "info",
- Self::Warn => "warn",
- Self::Error => "error",
- }
- }
-}
-
-/// Configuration for a specific log type (search or daemon).
-#[derive(Clone, Debug, Default, Deserialize, Serialize)]
-struct LogConfig {
- /// Log file name (relative to dir) or absolute path.
- file: String,
-
- /// Override global enabled setting for this log type.
- enabled: Option<bool>,
-
- /// Override global level setting for this log type.
- level: Option<LogLevel>,
-
- /// Override global retention days setting for this log type.
- retention: Option<u64>,
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-struct Logs {
- /// Enable file logging globally. Defaults to true.
- #[serde(default = "Logs::default_enabled")]
- enabled: bool,
-
- /// Directory for log files. Defaults to ~/.atuin/logs
- dir: String,
-
- /// Default log level for file logging. Defaults to "info".
- /// Note: [`ATUIN_LOG`] environment variable overrides this.
- #[serde(default)]
- level: LogLevel,
-
- /// Default retention days for log files. Defaults to 4.
- #[serde(default = "Logs::default_retention")]
- retention: u64,
-
- /// Search log settings
- #[serde(default)]
- search: LogConfig,
-
- /// Daemon log settings
- #[serde(default)]
- daemon: LogConfig,
-}
-
-impl Default for Preview {
- fn default() -> Self {
- Self {
- strategy: PreviewStrategy::Auto,
- }
- }
-}
-
impl Default for Daemon {
fn default() -> Self {
Self {
@@ -510,90 +116,6 @@ impl Default for Daemon {
}
}
-impl Default for Logs {
- fn default() -> Self {
- Self {
- enabled: true,
- dir: String::new(),
- level: LogLevel::default(),
- retention: Self::default_retention(),
- search: LogConfig {
- file: "search.log".to_string(),
- ..Default::default()
- },
- daemon: LogConfig {
- file: "daemon.log".to_string(),
- ..Default::default()
- },
- }
- }
-}
-
-impl Logs {
- fn default_enabled() -> bool {
- true
- }
-
- fn default_retention() -> u64 {
- 4
- }
-
- /// Returns whether search logging is enabled.
- /// Uses search-specific setting if set, otherwise falls back to global.
- fn search_enabled(&self) -> bool {
- self.search.enabled.unwrap_or(self.enabled)
- }
-
- /// Returns whether daemon logging is enabled.
- /// Uses daemon-specific setting if set, otherwise falls back to global.
- fn daemon_enabled(&self) -> bool {
- self.daemon.enabled.unwrap_or(self.enabled)
- }
-
- /// Returns the log level for search logging.
- /// Uses search-specific setting if set, otherwise falls back to global.
- fn search_level(&self) -> LogLevel {
- self.search.level.unwrap_or(self.level)
- }
-
- /// Returns the log level for daemon logging.
- /// Uses daemon-specific setting if set, otherwise falls back to global.
- fn daemon_level(&self) -> LogLevel {
- self.daemon.level.unwrap_or(self.level)
- }
-
- /// Returns the retention days for search logging.
- /// Uses search-specific setting if set, otherwise falls back to global.
- fn search_retention(&self) -> u64 {
- self.search.retention.unwrap_or(self.retention)
- }
-
- /// Returns the retention days for daemon logging.
- /// Uses daemon-specific setting if set, otherwise falls back to global.
- fn daemon_retention(&self) -> u64 {
- self.daemon.retention.unwrap_or(self.retention)
- }
-}
-
-impl Default for Search {
- fn default() -> Self {
- Self {
- filters: vec![
- FilterMode::Global,
- FilterMode::Host,
- FilterMode::Session,
- FilterMode::SessionPreload,
- FilterMode::Workspace,
- FilterMode::Directory,
- ],
-
- recency_score_multiplier: 1.0,
- frequency_score_multiplier: 1.0,
- frecency_score_multiplier: 1.0,
- }
- }
-}
-
// The preview height strategy also takes max_preview_height into account.
#[derive(Clone, Debug, Deserialize, Copy, PartialEq, Eq, ValueEnum, Serialize)]
enum PreviewStrategy {
@@ -610,182 +132,6 @@ enum PreviewStrategy {
Fixed,
}
-/// Column types available for the interactive search UI.
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Serialize)]
-#[serde(rename_all = "lowercase")]
-enum UiColumnType {
- /// Command execution duration (e.g., "123ms")
- Duration,
- /// Relative time since execution (e.g., "59s ago")
- Time,
- /// Absolute timestamp (e.g., "2025-01-22 14:35")
- Datetime,
- /// Working directory
- Directory,
- /// Hostname
- Host,
- /// Username
- User,
- /// Exit code
- Exit,
- /// The command itself (should be last, expands to fill)
- Command,
-}
-
-impl UiColumnType {
- /// Returns the default width for this column type (in characters).
- /// The Command column returns 0 as it expands to fill remaining space.
- fn default_width(self) -> u16 {
- match self {
- Self::Duration => 5, // "814ms"
- Self::Time => 9, // "459ms ago"
- Self::Datetime => 16, // "2025-01-22 14:35"
- Self::Directory => 20,
- Self::Host => 15,
- Self::User => 10,
- Self::Exit => {
- if cfg!(windows) {
- 11 // 32-bit integer on Windows: "-1978335212"
- } else {
- 3 // Usually a byte on Unix
- }
- }
- Self::Command => 0, // Expands to fill
- }
- }
-}
-
-/// A column configuration with type and optional custom width.
-/// Can be specified as just a string (uses default width) or as an object with type and width.
-#[derive(Clone, Debug, Serialize)]
-struct UiColumn {
- column_type: UiColumnType,
- width: u16,
- /// If true, this column expands to fill remaining space. Only one column should expand.
- expand: bool,
-}
-
-impl UiColumn {
- fn new(column_type: UiColumnType) -> Self {
- Self {
- width: column_type.default_width(),
- expand: column_type == UiColumnType::Command,
- column_type,
- }
- }
-}
-
-// Custom deserialize to handle both string and object formats:
-// "duration" or { type = "duration", width = 8, expand = true }
-impl<'de> Deserialize<'de> for UiColumn {
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: serde::Deserializer<'de>,
- {
- use serde::de::{self, MapAccess, Visitor};
-
- struct UiColumnVisitor;
-
- impl<'de> Visitor<'de> for UiColumnVisitor {
- type Value = UiColumn;
-
- fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
- formatter.write_str(
- "a column type string or an object with 'type' and optional 'width'/'expand'",
- )
- }
-
- fn visit_str<E>(self, value: &str) -> Result<UiColumn, E>
- where
- E: de::Error,
- {
- let column_type: UiColumnType =
- Deserialize::deserialize(de::value::StrDeserializer::new(value))?;
- Ok(UiColumn::new(column_type))
- }
-
- fn visit_map<M>(self, mut map: M) -> Result<UiColumn, M::Error>
- where
- M: MapAccess<'de>,
- {
- let mut column_type: Option<UiColumnType> = None;
- let mut width: Option<u16> = None;
- let mut expand: Option<bool> = None;
-
- while let Some(key) = map.next_key::<String>()? {
- match key.as_str() {
- "type" => {
- column_type = Some(map.next_value()?);
- }
- "width" => {
- width = Some(map.next_value()?);
- }
- "expand" => {
- expand = Some(map.next_value()?);
- }
- _ => {
- let _: de::IgnoredAny = map.next_value()?;
- }
- }
- }
-
- let column_type = column_type.ok_or_else(|| de::Error::missing_field("type"))?;
- let width = width.unwrap_or_else(|| column_type.default_width());
- let expand = expand.unwrap_or(column_type == UiColumnType::Command);
- Ok(UiColumn {
- column_type,
- width,
- expand,
- })
- }
- }
-
- deserializer.deserialize_any(UiColumnVisitor)
- }
-}
-
-/// UI-specific settings for the interactive search.
-#[derive(Clone, Debug, Deserialize, Serialize)]
-struct Ui {
- /// Columns to display in interactive search, from left to right.
- /// The indicator column (" > ") is always shown first implicitly.
- /// The "command" column should be last as it expands to fill remaining space.
- /// Can be simple strings or objects with type and width.
- #[serde(default = "Ui::default_columns")]
- columns: Vec<UiColumn>,
-}
-
-impl Ui {
- fn default_columns() -> Vec<UiColumn> {
- vec![
- UiColumn::new(UiColumnType::Duration),
- UiColumn::new(UiColumnType::Time),
- UiColumn::new(UiColumnType::Command),
- ]
- }
-
- /// Validate the UI configuration.
- /// Returns an error if more than one column has expand = true.
- fn validate(&self) -> Result<()> {
- let expand_count = self.columns.iter().filter(|c| c.expand).count();
- if expand_count > 1 {
- bail!(
- "Only one column can have expand = true, but {} columns are set to expand",
- expand_count
- );
- }
- Ok(())
- }
-}
-
-impl Default for Ui {
- fn default() -> Self {
- Self {
- columns: Self::default_columns(),
- }
- }
-}
-
/// Sync-specific settings.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub(crate) struct Sync {
@@ -851,87 +197,21 @@ impl Sync {
}
#[derive(Clone, Debug, Deserialize, Serialize)]
-#[expect(clippy::struct_excessive_bools)]
pub(crate) struct Settings {
- data_dir: Option<String>,
- dialect: Dialect,
- timezone: Timezone,
- style: Style,
-
pub(crate) db_path: String,
pub(crate) record_store_path: String,
- search_mode: SearchMode,
- filter_mode: Option<FilterMode>,
- filter_mode_shell_up_key_binding: Option<FilterMode>,
- search_mode_shell_up_key_binding: Option<SearchMode>,
- shell_up_key_binding: bool,
- inline_height: u16,
- inline_height_shell_up_key_binding: Option<u16>,
- invert: bool,
- show_preview: bool,
- max_preview_height: u16,
- show_help: bool,
- show_tabs: bool,
- show_numeric_shortcuts: bool,
- auto_hide_height: u16,
- exit_mode: ExitMode,
- keymap_mode: KeymapMode,
- keymap_mode_shell: KeymapMode,
- keymap_cursor: HashMap<String, CursorStyle>,
- word_jump_mode: WordJumpMode,
- word_chars: String,
- scroll_context_lines: usize,
- history_format: String,
- strip_trailing_whitespace: bool,
- prefers_reduced_motion: bool,
- store_failed: bool,
- no_mouse: bool,
-
- #[serde(with = "serde_regex", default = "RegexSet::empty", skip_serializing)]
- history_filter: RegexSet,
-
- #[serde(with = "serde_regex", default = "RegexSet::empty", skip_serializing)]
- cwd_filter: RegexSet,
-
- secrets_filter: bool,
- workspaces: bool,
- ctrl_n_shortcuts: bool,
pub(crate) network_connect_timeout: u64,
pub(crate) network_timeout: u64,
pub(crate) local_timeout: f64,
- enter_accept: bool,
- smart_sort: bool,
- command_chaining: bool,
#[serde(default)]
pub(crate) sync: Sync,
#[serde(default)]
- stats: Stats,
-
- #[serde(default)]
- keys: Keys,
-
- #[serde(default)]
- keymap: KeymapConfig,
-
- #[serde(default)]
- preview: Preview,
-
- #[serde(default)]
pub(crate) daemon: Daemon,
#[serde(default)]
- search: Search,
-
- #[serde(default)]
- ui: Ui,
-
- #[serde(default)]
- logs: Logs,
-
- #[serde(default)]
meta: meta::Settings,
}
@@ -961,23 +241,6 @@ impl Settings {
Self::meta_store().await?.save_sync_time().await
}
- fn default_filter_mode(&self, git_root: bool) -> FilterMode {
- self.filter_mode
- .filter(|x| self.search.filters.contains(x))
- .or_else(|| {
- self.search
- .filters
- .iter()
- .find(|x| match (x, git_root, self.workspaces) {
- (FilterMode::Workspace, true, true) => true,
- (FilterMode::Workspace, _, _) => false,
- (_, _, _) => true,
- })
- .copied()
- })
- .unwrap_or(FilterMode::Global)
- }
-
fn builder() -> Result<ConfigBuilder<DefaultState>> {
Self::builder_with_data_dir(&utils::data_dir())
}
@@ -1224,79 +487,12 @@ impl Settings {
config_builder.build().map_err(Into::into)
}
- /// Look up a single config value by dotted key (e.g. `"daemon.sync_frequency"`).
- ///
- /// Returns the effective value after merging defaults, config file, and
- /// environment — without the side-effects of full `Settings` construction
- /// (meta store init, path expansion, etc.).
- fn get_config_value(key: &str) -> Result<String> {
- let config = Self::build_config()?;
- let value: config::Value = config
- .get(key)
- .map_err(|e| eyre!("failed to get config value '{}': {}", key, e))?;
- Ok(Self::format_resolved_value(&value, key))
- }
-
- fn format_resolved_value(value: &config::Value, prefix: &str) -> String {
- use config::ValueKind;
-
- match &value.kind {
- ValueKind::Nil => String::new(),
- ValueKind::Boolean(b) => b.to_string(),
- ValueKind::I64(i) => i.to_string(),
- ValueKind::I128(i) => i.to_string(),
- ValueKind::U64(u) => u.to_string(),
- ValueKind::U128(u) => u.to_string(),
- ValueKind::Float(f) => f.to_string(),
- ValueKind::String(s) => s.clone(),
- ValueKind::Array(arr) => {
- let items: Vec<String> = arr
- .iter()
- .map(|v| Self::format_resolved_value(v, ""))
- .collect();
- format!("[{}]", items.join(", "))
- }
- ValueKind::Table(map) => {
- let mut lines = Vec::new();
- let mut keys: Vec<_> = map.keys().collect();
- keys.sort();
-
- for k in keys {
- let v = &map[k];
- let full_key = if prefix.is_empty() {
- k.clone()
- } else {
- format!("{prefix}.{k}")
- };
-
- match &v.kind {
- ValueKind::Table(_) => {
- lines.push(Self::format_resolved_value(v, &full_key));
- }
- _ => {
- lines.push(format!(
- "{} = {}",
- full_key,
- Self::format_resolved_value(v, "")
- ));
- }
- }
- }
-
- lines.join("\n")
- }
- }
- }
-
pub(crate) fn new() -> Result<Self> {
let config = Self::build_config()?;
let settings: Self = config
.try_deserialize()
.map_err(|e| eyre!("failed to deserialize: {}", e))?;
- // Validate UI settings
- settings.ui.validate()?;
-
// Register meta store config for lazy initialization on first access
META_CONFIG
.set((settings.meta.db_path.clone(), settings.local_timeout))
@@ -1311,9 +507,21 @@ impl Settings {
.map_err(|e| eyre!("failed to expand path: {}", e))
}
- fn paths_ok(&self) -> bool {
- // TODO(@bpeetz): Add the `sync.*` paths <2026-06-11>
- let paths = [&self.db_path, &self.record_store_path, &self.meta.db_path];
+ pub(crate) fn paths_ok(&self) -> bool {
+ let mut paths: Vec<&str> = vec![
+ &self.db_path,
+ &self.record_store_path,
+ &self.meta.db_path,
+ &self.daemon.socket_path,
+ ];
+
+ if let Some(path) = &self.sync.encryption_key_path {
+ paths.push(path.to_str().unwrap());
+ }
+ if let Some(path) = &self.sync.user_id_path {
+ paths.push(path.to_str().unwrap());
+ }
+
paths.iter().all(|p| !utils::broken_symlink(p))
}
}
@@ -1343,96 +551,8 @@ pub(crate) fn test_local_timeout() -> f64 {
#[cfg(test)]
mod tests {
- use std::str::FromStr;
-
use eyre::Result;
- use super::Timezone;
-
- #[test]
- fn can_parse_offset_timezone_spec() -> Result<()> {
- assert_eq!(Timezone::from_str("+02")?.0.as_hms(), (2, 0, 0));
- assert_eq!(Timezone::from_str("-04")?.0.as_hms(), (-4, 0, 0));
- assert_eq!(Timezone::from_str("+05:30")?.0.as_hms(), (5, 30, 0));
- assert_eq!(Timezone::from_str("-09:30")?.0.as_hms(), (-9, -30, 0));
-
- // single digit hours are allowed
- assert_eq!(Timezone::from_str("+2")?.0.as_hms(), (2, 0, 0));
- assert_eq!(Timezone::from_str("-4")?.0.as_hms(), (-4, 0, 0));
- assert_eq!(Timezone::from_str("+5:30")?.0.as_hms(), (5, 30, 0));
- assert_eq!(Timezone::from_str("-9:30")?.0.as_hms(), (-9, -30, 0));
-
- // fully qualified form
- assert_eq!(Timezone::from_str("+09:30:00")?.0.as_hms(), (9, 30, 0));
- assert_eq!(Timezone::from_str("-09:30:00")?.0.as_hms(), (-9, -30, 0));
-
- // these offsets don't really exist but are supported anyway
- assert_eq!(Timezone::from_str("+0:5")?.0.as_hms(), (0, 5, 0));
- assert_eq!(Timezone::from_str("-0:5")?.0.as_hms(), (0, -5, 0));
- assert_eq!(Timezone::from_str("+01:23:45")?.0.as_hms(), (1, 23, 45));
- assert_eq!(Timezone::from_str("-01:23:45")?.0.as_hms(), (-1, -23, -45));
-
- // require a leading sign for clarity
- assert!(Timezone::from_str("5").is_err());
- assert!(Timezone::from_str("10:30").is_err());
-
- Ok(())
- }
-
- #[test]
- fn can_choose_workspace_filters_when_in_git_context() -> Result<()> {
- let mut settings = super::Settings::default();
- settings.search.filters = vec![
- super::FilterMode::Workspace,
- super::FilterMode::Host,
- super::FilterMode::Directory,
- super::FilterMode::Session,
- super::FilterMode::Global,
- ];
- settings.workspaces = true;
-
- assert_eq!(
- settings.default_filter_mode(true),
- super::FilterMode::Workspace,
- );
-
- Ok(())
- }
-
- #[test]
- fn wont_choose_workspace_filters_when_not_in_git_context() -> Result<()> {
- let mut settings = super::Settings::default();
- settings.search.filters = vec![
- super::FilterMode::Workspace,
- super::FilterMode::Host,
- super::FilterMode::Directory,
- super::FilterMode::Session,
- super::FilterMode::Global,
- ];
- settings.workspaces = true;
-
- assert_eq!(settings.default_filter_mode(false), super::FilterMode::Host,);
-
- Ok(())
- }
-
- #[test]
- fn wont_choose_workspace_filters_when_workspaces_disabled() -> Result<()> {
- let mut settings = super::Settings::default();
- settings.search.filters = vec![
- super::FilterMode::Workspace,
- super::FilterMode::Host,
- super::FilterMode::Directory,
- super::FilterMode::Session,
- super::FilterMode::Global,
- ];
- settings.workspaces = false;
-
- assert_eq!(settings.default_filter_mode(true), super::FilterMode::Host,);
-
- Ok(())
- }
-
#[test]
fn builder_with_data_dir_uses_custom_paths() -> Result<()> {
use std::path::PathBuf;
@@ -1476,68 +596,4 @@ mod tests {
Ok(())
}
-
- #[test]
- fn keymap_config_deserializes_simple_binding() {
- let json = r#"{"emacs": {"ctrl-c": "exit"}}"#;
- let config: super::KeymapConfig = serde_json::from_str(json).unwrap();
- assert_eq!(config.emacs.len(), 1);
- match &config.emacs["ctrl-c"] {
- super::KeyBindingConfig::Simple(s) => assert_eq!(s, "exit"),
- _ => panic!("expected Simple variant"),
- }
- }
-
- #[test]
- fn keymap_config_deserializes_conditional_binding() {
- let json = r#"{
- "emacs": {
- "left": [
- {"when": "cursor-at-start", "action": "exit"},
- {"action": "cursor-left"}
- ]
- }
- }"#;
- let config: super::KeymapConfig = serde_json::from_str(json).unwrap();
- match &config.emacs["left"] {
- super::KeyBindingConfig::Rules(rules) => {
- assert_eq!(rules.len(), 2);
- assert_eq!(rules[0].when.as_deref(), Some("cursor-at-start"));
- assert_eq!(rules[0].action, "exit");
- assert!(rules[1].when.is_none());
- assert_eq!(rules[1].action, "cursor-left");
- }
- _ => panic!("expected Rules variant"),
- }
- }
-
- #[test]
- fn keymap_config_deserializes_vim_normal() {
- let json = r#"{"vim-normal": {"j": "select-next", "k": "select-previous"}}"#;
- let config: super::KeymapConfig = serde_json::from_str(json).unwrap();
- assert_eq!(config.vim_normal.len(), 2);
- assert!(config.emacs.is_empty());
- }
-
- #[test]
- fn keymap_config_is_empty_when_default() {
- let config = super::KeymapConfig::default();
- assert!(config.is_empty());
- }
-
- #[test]
- fn keymap_config_mixed_modes() {
- let json = r#"{
- "emacs": {"ctrl-c": "exit"},
- "vim-normal": {"q": "exit"},
- "inspector": {"d": "delete"}
- }"#;
- let config: super::KeymapConfig = serde_json::from_str(json).unwrap();
- assert!(!config.is_empty());
- assert_eq!(config.emacs.len(), 1);
- assert_eq!(config.vim_normal.len(), 1);
- assert_eq!(config.inspector.len(), 1);
- assert!(config.vim_insert.is_empty());
- assert!(config.prefix.is_empty());
- }
}
diff --git a/crates/daemon/src/api/control.rs b/crates/daemon/src/api/control.rs
index 94fb7ce9..ff19f593 100644
--- a/crates/daemon/src/api/control.rs
+++ b/crates/daemon/src/api/control.rs
@@ -37,18 +37,14 @@ enum SyncState {
/// It's not a component - it's part of the daemon's core infrastructure.
pub(crate) struct ControlService {
handle: DaemonHandle,
- task_handle: tokio::task::JoinHandle<()>,
}
impl ControlService {
/// Create a new control service with the given daemon handle.
pub(crate) fn new(handle: DaemonHandle) -> Self {
- let task_handle = tokio::spawn(sync_loop(handle.clone()));
+ tokio::spawn(sync_loop(handle.clone()));
- Self {
- handle,
- task_handle,
- }
+ Self { handle }
}
/// Get a tonic server for this service.
diff --git a/crates/daemon/src/api/history.rs b/crates/daemon/src/api/history.rs
index d024ea92..6165464d 100644
--- a/crates/daemon/src/api/history.rs
+++ b/crates/daemon/src/api/history.rs
@@ -90,10 +90,7 @@ impl HistorySvc for HistoryService {
self.handle.history_db().range(from, to).await
} else {
- self.handle
- .history_db()
- .list(None, None, false, false)
- .await
+ self.handle.history_db().list(None, false, false).await
}
.map_err(|e| Status::internal(format!("failed to read db: {e:?}")))?
.into_iter()
diff --git a/crates/daemon/src/main.rs b/crates/daemon/src/main.rs
index 1d94b5fa..9d5a5333 100644
--- a/crates/daemon/src/main.rs
+++ b/crates/daemon/src/main.rs
@@ -1,4 +1,8 @@
-#![expect(unused_crate_dependencies)]
+#![expect(
+ clippy::cast_possible_truncation,
+ clippy::cast_possible_wrap,
+ clippy::cast_sign_loss
+)]
use std::{
fs::{self, File, OpenOptions},
@@ -44,6 +48,10 @@ async fn main() -> Result<()> {
}
let settings = Settings::new().wrap_err("could not load client settings")?;
+ if !settings.paths_ok() {
+ bail!("Failed to verify all paths :(");
+ }
+
let db_path = PathBuf::from(settings.db_path.as_str());
let record_store_path = PathBuf::from(settings.record_store_path.as_str());
diff --git a/crates/server/Cargo.toml b/crates/server/Cargo.toml
index 7343c0fc..81aabbf8 100644
--- a/crates/server/Cargo.toml
+++ b/crates/server/Cargo.toml
@@ -12,25 +12,25 @@ homepage = { workspace = true }
repository = { workspace = true }
[dependencies]
-tracing-subscriber = { version = "0.3", features = ["ansi", "fmt", "registry", "env-filter", "json"] }
-axum = "0.8"
-config = { version = "0.15.8", default-features = false, features = ["toml"] }
+tracing-subscriber = { workspace = true }
+axum = { workspace = true }
+config = { workspace = true }
clap = { workspace = true }
-eyre = "0.6"
-fs-err = "3.1"
-metrics = "0.24"
-metrics-exporter-prometheus = { version = "0.18", default-features = false }
-rand = { version = "0.8.5", features = ["std"] }
-serde = { version = "1.0.202", features = ["derive"] }
-sqlx = { version = "0.8", features = ["runtime-tokio-rustls", "time", "postgres", "uuid", "sqlite", "regexp"] }
-time = { version = "0.3.47", features = [ "serde-human-readable", "macros", "local-offset", "macros", "formatting", "parsing"] }
-tokio = { version = "1", features = ["full"] }
-tower = "0.5"
-tower-http = { version = "0.6", features = ["trace"] }
-tracing = "0.1"
+eyre = { workspace = true }
+fs-err = { workspace = true }
+metrics = { workspace = true }
+metrics-exporter-prometheus = { workspace = true }
+rand = { workspace = true }
+serde = { workspace = true }
+sqlx = { workspace = true }
+time = { workspace = true }
+tokio = { workspace = true }
+tower = { workspace = true }
+tower-http = { workspace = true }
+tracing = { workspace = true }
turtle-common = { workspace = true }
-url = "2.5.2"
-uuid = { version = "1.9", features = ["v4", "v7", "serde"] }
+url = { workspace = true }
+uuid = { workspace = true }
[package.metadata.docs.rs]
all-features = true
diff --git a/crates/turtle/Cargo.toml b/crates/turtle/Cargo.toml
index 547f8530..d5397f4e 100644
--- a/crates/turtle/Cargo.toml
+++ b/crates/turtle/Cargo.toml
@@ -12,18 +12,18 @@ homepage = { workspace = true }
repository = { workspace = true }
[dependencies]
-eyre = "0.6"
-hyper-util = "0.1"
-prost = "0.14"
-regex = "1.10.5"
-sqlx = { version = "0.8", features = ["runtime-tokio-rustls", "time", "postgres", "uuid", "sqlite", "regexp"] }
-time = { version = "0.3.47", features = [ "serde-human-readable", "macros", "local-offset", "macros", "formatting", "parsing"] }
-tokio = { version = "1", features = ["full"] }
-tonic = "0.14"
-tonic-prost = "0.14"
-tower = "0.5"
-turtle-common = {workspace = true}
-typed-builder = "0.18.2"
+eyre = { workspace = true }
+hyper-util = { workspace = true }
+prost = { workspace = true }
+regex = { workspace = true }
+sqlx = { workspace = true }
+time = { workspace = true }
+tokio = { workspace = true }
+tonic = { workspace = true }
+tonic-prost = { workspace = true }
+tower = { workspace = true }
+turtle-common = { workspace = true }
+typed-builder = { workspace = true }
[build-dependencies]
protox = "0.9"