Refactor
This commit is contained in:
parent
31116ce4dc
commit
c2ee3fe3e9
|
|
@ -10,9 +10,9 @@ checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "0.7.19"
|
version = "0.7.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
|
checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
@ -25,9 +25,9 @@ checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.66"
|
version = "1.0.68"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
|
checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "askama"
|
name = "askama"
|
||||||
|
|
@ -74,9 +74,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "assert_cmd"
|
name = "assert_cmd"
|
||||||
version = "2.0.5"
|
version = "2.0.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d5c2ca00549910ec251e3bd15f87aeeb206c9456b9a77b43ff6c97c54042a472"
|
checksum = "fa3d466004a8b4cb1bc34044240a2fd29d17607e2e3bd613eb44fd48e8100da3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bstr",
|
"bstr",
|
||||||
"doc-comment",
|
"doc-comment",
|
||||||
|
|
@ -86,23 +86,6 @@ dependencies = [
|
||||||
"wait-timeout",
|
"wait-timeout",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "atty"
|
|
||||||
version = "0.2.14"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
|
||||||
dependencies = [
|
|
||||||
"hermit-abi",
|
|
||||||
"libc",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "autocfg"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bincode"
|
name = "bincode"
|
||||||
version = "1.3.3"
|
version = "1.3.3"
|
||||||
|
|
@ -120,15 +103,22 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bstr"
|
name = "bstr"
|
||||||
version = "0.2.17"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
|
checksum = "b45ea9b00a7b3f2988e9a65ad3917e62123c38dba709b666506207be96d1790b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static",
|
|
||||||
"memchr",
|
"memchr",
|
||||||
|
"once_cell",
|
||||||
"regex-automata",
|
"regex-automata",
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.78"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
|
@ -137,14 +127,14 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.0.18"
|
version = "4.0.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "335867764ed2de42325fafe6d18b8af74ba97ee0c590fa016f157535b42ab04b"
|
checksum = "a7db700bc935f9e43e88d00b0850dae18a63773cfbec6d8e070fccf7fef89a39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atty",
|
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
"clap_lex",
|
"clap_lex",
|
||||||
|
"is-terminal",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"strsim",
|
"strsim",
|
||||||
"termcolor",
|
"termcolor",
|
||||||
|
|
@ -152,18 +142,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_complete"
|
name = "clap_complete"
|
||||||
version = "4.0.3"
|
version = "4.0.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dfe581a2035db4174cdbdc91265e1aba50f381577f0510d0ad36c7bc59cc84a3"
|
checksum = "10861370d2ba66b0f5989f83ebf35db6421713fd92351790e7fdd6c36774c56b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_complete_fig"
|
name = "clap_complete_fig"
|
||||||
version = "4.0.1"
|
version = "4.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b36d1abc7184a737efc9f589e6e783e8b56c72e71fca748cf9947ed0a6f46d44"
|
checksum = "46b30e010e669cd021e5004f3be26cff6b7c08d2a8a0d65b48d43a8cc0efd6c3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"clap_complete",
|
"clap_complete",
|
||||||
|
|
@ -171,9 +161,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_derive"
|
name = "clap_derive"
|
||||||
version = "4.0.18"
|
version = "4.0.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "16a1b0f6422af32d5da0c58e2703320f379216ee70198241c84173a8c5ac28f3"
|
checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"proc-macro-error",
|
"proc-macro-error",
|
||||||
|
|
@ -191,15 +181,6 @@ dependencies = [
|
||||||
"os_str_bytes",
|
"os_str_bytes",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam-utils"
|
|
||||||
version = "0.8.12"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "difflib"
|
name = "difflib"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
|
@ -244,6 +225,27 @@ version = "1.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
|
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno"
|
||||||
|
version = "0.2.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
|
||||||
|
dependencies = [
|
||||||
|
"errno-dragonfly",
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno-dragonfly"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastrand"
|
name = "fastrand"
|
||||||
version = "1.8.0"
|
version = "1.8.0"
|
||||||
|
|
@ -272,15 +274,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glob"
|
name = "glob"
|
||||||
version = "0.3.0"
|
version = "0.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "globset"
|
name = "globset"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a"
|
checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"bstr",
|
"bstr",
|
||||||
|
|
@ -297,20 +299,19 @@ checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.1.19"
|
version = "0.2.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ignore"
|
name = "ignore"
|
||||||
version = "0.4.18"
|
version = "0.4.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d"
|
checksum = "a05705bc64e0b66a806c3740bd6578ea66051b157ec42dc219c785cbf185aef3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-utils",
|
|
||||||
"globset",
|
"globset",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
|
|
@ -331,6 +332,28 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "io-lifetimes"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is-terminal"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi",
|
||||||
|
"io-lifetimes",
|
||||||
|
"rustix",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.10.5"
|
version = "0.10.5"
|
||||||
|
|
@ -348,9 +371,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.137"
|
version = "0.2.139"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
|
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linux-raw-sys"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
|
|
@ -391,21 +420,21 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nix"
|
name = "nix"
|
||||||
version = "0.25.0"
|
version = "0.26.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e322c04a9e3440c327fca7b6c8a63e6890a32fa2ad689db972425f07e0d22abb"
|
checksum = "46a58d1d356c6597d08cde02c2f09d785b09e28711837b1ed667dc652c08a694"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
|
"static_assertions",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nom"
|
name = "nom"
|
||||||
version = "7.1.1"
|
version = "7.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36"
|
checksum = "e5507769c4919c998e69e49c839d9dc6e693ede4cc4290d6ad8b41d4f09c548c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
"minimal-lexical",
|
"minimal-lexical",
|
||||||
|
|
@ -413,15 +442,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.16.0"
|
version = "1.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
|
checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "os_str_bytes"
|
name = "os_str_bytes"
|
||||||
version = "6.3.1"
|
version = "6.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3baf96e39c5359d2eb0dd6ccb42c62b91d9678aa68160d261b9e0ccbf9e9dea9"
|
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ouroboros"
|
name = "ouroboros"
|
||||||
|
|
@ -448,9 +477,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "predicates"
|
name = "predicates"
|
||||||
version = "2.1.1"
|
version = "2.1.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a5aab5be6e4732b473071984b3164dbbfb7a3674d30ea5ff44410b6bcd960c3c"
|
checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"difflib",
|
"difflib",
|
||||||
"itertools",
|
"itertools",
|
||||||
|
|
@ -459,15 +488,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "predicates-core"
|
name = "predicates-core"
|
||||||
version = "1.0.3"
|
version = "1.0.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "da1c2388b1513e1b605fcec39a95e0a9e8ef088f71443ef37099fa9ae6673fcb"
|
checksum = "72f883590242d3c6fc5bf50299011695fa6590c2c70eac95ee1bdb9a733ad1a2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "predicates-tree"
|
name = "predicates-tree"
|
||||||
version = "1.0.5"
|
version = "1.0.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4d86de6de25020a36c6d3643a86d9a6a9f552107c0559c60ea03551b5e16c032"
|
checksum = "54ff541861505aabf6ea722d2131ee980b8276e10a1297b94e896dd8b621850d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"predicates-core",
|
"predicates-core",
|
||||||
"termtree",
|
"termtree",
|
||||||
|
|
@ -499,18 +528,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.47"
|
version = "1.0.49"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
|
checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.21"
|
version = "1.0.23"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
@ -537,9 +566,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
|
checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
|
@ -554,9 +583,9 @@ checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.6.27"
|
version = "0.6.28"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
|
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "remove_dir_all"
|
name = "remove_dir_all"
|
||||||
|
|
@ -569,9 +598,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rstest"
|
name = "rstest"
|
||||||
version = "0.15.0"
|
version = "0.16.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e9c9dc66cc29792b663ffb5269be669f1613664e69ad56441fdb895c2347b930"
|
checksum = "b07f2d176c472198ec1e6551dc7da28f1c089652f66a7b722676c2238ebc0edf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rstest_macros",
|
"rstest_macros",
|
||||||
"rustc_version",
|
"rustc_version",
|
||||||
|
|
@ -579,15 +608,16 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rstest_macros"
|
name = "rstest_macros"
|
||||||
version = "0.14.0"
|
version = "0.16.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5015e68a0685a95ade3eee617ff7101ab6a3fc689203101ca16ebc16f2b89c66"
|
checksum = "7229b505ae0706e64f37ffc54a9c163e11022a6636d58fe1f3f52018257ff9f7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"rustc_version",
|
"rustc_version",
|
||||||
"syn",
|
"syn",
|
||||||
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -610,6 +640,20 @@ dependencies = [
|
||||||
"semver",
|
"semver",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustix"
|
||||||
|
version = "0.36.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4feacf7db682c6c329c4ede12649cd36ecab0f3be5b7d74e6a20304725db4549"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"errno",
|
||||||
|
"io-lifetimes",
|
||||||
|
"libc",
|
||||||
|
"linux-raw-sys",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "same-file"
|
name = "same-file"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
|
|
@ -621,24 +665,24 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver"
|
name = "semver"
|
||||||
version = "1.0.14"
|
version = "1.0.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4"
|
checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.147"
|
version = "1.0.152"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965"
|
checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.147"
|
version = "1.0.152"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852"
|
checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
@ -651,6 +695,12 @@ version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
|
checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "static_assertions"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
|
|
@ -659,9 +709,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.103"
|
version = "1.0.107"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d"
|
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
@ -693,24 +743,24 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "termtree"
|
name = "termtree"
|
||||||
version = "0.2.4"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b"
|
checksum = "95059e91184749cb66be6dc994f67f182b6d897cb3df74a5bf66b5e709295fd8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.37"
|
version = "1.0.38"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
|
checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.37"
|
version = "1.0.38"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
|
checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
@ -737,9 +787,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.5"
|
version = "1.0.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
|
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
|
|
@ -815,6 +865,63 @@ version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xtask"
|
name = "xtask"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
|
||||||
|
|
@ -31,12 +31,12 @@ dunce = "1.0.1"
|
||||||
fastrand = "1.7.0"
|
fastrand = "1.7.0"
|
||||||
glob = "0.3.0"
|
glob = "0.3.0"
|
||||||
ignore = "0.4.18"
|
ignore = "0.4.18"
|
||||||
nix = { version = "0.25.0", default-features = false, features = [
|
nix = { version = "0.26.1", default-features = false, features = [
|
||||||
"fs",
|
"fs",
|
||||||
"user",
|
"user",
|
||||||
] }
|
] }
|
||||||
ouroboros = "0.15.5"
|
ouroboros = "0.15.5"
|
||||||
rstest = { version = "0.15.0", default-features = false }
|
rstest = { version = "0.16.0", default-features = false }
|
||||||
rstest_reuse = "0.4.0"
|
rstest_reuse = "0.4.0"
|
||||||
serde = { version = "1.0.116", features = ["derive"] }
|
serde = { version = "1.0.116", features = ["derive"] }
|
||||||
shell-words = "1.0.0"
|
shell-words = "1.0.0"
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
comment_width = 100
|
|
||||||
group_imports = "StdExternalCrate"
|
group_imports = "StdExternalCrate"
|
||||||
imports_granularity = "Module"
|
imports_granularity = "Module"
|
||||||
max_width = 120
|
max_width = 120
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ use crate::{config, util};
|
||||||
impl Run for Add {
|
impl Run for Add {
|
||||||
fn run(&self) -> Result<()> {
|
fn run(&self) -> Result<()> {
|
||||||
// These characters can't be printed cleanly to a single line, so they can cause confusion
|
// These characters can't be printed cleanly to a single line, so they can cause confusion
|
||||||
// when writing to fzf / stdout.
|
// when writing to stdout.
|
||||||
const EXCLUDE_CHARS: &[char] = &['\n', '\r'];
|
const EXCLUDE_CHARS: &[char] = &['\n', '\r'];
|
||||||
|
|
||||||
let exclude_dirs = config::exclude_dirs()?;
|
let exclude_dirs = config::exclude_dirs()?;
|
||||||
|
|
|
||||||
113
src/cmd/edit.rs
113
src/cmd/edit.rs
|
|
@ -3,9 +3,9 @@ use std::io::{self, Write};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
use crate::cmd::{Edit, EditCommand, Run};
|
use crate::cmd::{Edit, EditCommand, Run};
|
||||||
use crate::db::{Database, Epoch};
|
use crate::db::Database;
|
||||||
use crate::error::BrokenPipeHandler;
|
use crate::error::BrokenPipeHandler;
|
||||||
use crate::util::{self, Fz};
|
use crate::util::{self, Fzf, FzfChild};
|
||||||
|
|
||||||
impl Run for Edit {
|
impl Run for Edit {
|
||||||
fn run(&self) -> Result<()> {
|
fn run(&self) -> Result<()> {
|
||||||
|
|
@ -23,76 +23,61 @@ impl Run for Edit {
|
||||||
EditCommand::Reload => {}
|
EditCommand::Reload => {}
|
||||||
}
|
}
|
||||||
db.save()?;
|
db.save()?;
|
||||||
print_dirs(db, now)
|
|
||||||
|
let stdout = &mut io::stdout().lock();
|
||||||
|
for dir in db.dirs().iter().rev() {
|
||||||
|
write!(stdout, "{}\0", dir.display().with_score(now).with_separator('\t')).pipe_exit("fzf")?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
db.sort_by_score(now);
|
db.sort_by_score(now);
|
||||||
db.save()?;
|
db.save()?;
|
||||||
|
Self::get_fzf()?.wait()?;
|
||||||
let mut fzf = Fz::new()?;
|
Ok(())
|
||||||
fzf.args([
|
|
||||||
// Search mode
|
|
||||||
"--delimiter=\t",
|
|
||||||
"--nth=2",
|
|
||||||
"--scheme=path",
|
|
||||||
// Search result
|
|
||||||
"--tiebreak=end,chunk,index",
|
|
||||||
// Interface
|
|
||||||
"--bind=\
|
|
||||||
ctrl-r:reload(zoxide edit reload),\
|
|
||||||
ctrl-w:reload(zoxide edit delete {2..}),\
|
|
||||||
ctrl-a:reload(zoxide edit increment {2..}),\
|
|
||||||
ctrl-d:reload(zoxide edit decrement {2..}),\
|
|
||||||
ctrl-z:ignore,\
|
|
||||||
double-click:ignore,\
|
|
||||||
enter:abort",
|
|
||||||
"--cycle",
|
|
||||||
"--keep-right",
|
|
||||||
// Layout
|
|
||||||
"--border=rounded",
|
|
||||||
"--border-label= zoxide-edit ",
|
|
||||||
"--header=\
|
|
||||||
ctrl-r:reload \tctrl-w:delete
|
|
||||||
ctrl-a:increment\tctrl-d:decrement
|
|
||||||
|
|
||||||
SCORE\tPATH",
|
|
||||||
"--info=inline",
|
|
||||||
"--layout=reverse",
|
|
||||||
"--padding=1,0,0,0",
|
|
||||||
// Display
|
|
||||||
"--color=label:bold",
|
|
||||||
"--tabstop=1",
|
|
||||||
// Scripting
|
|
||||||
"--read0",
|
|
||||||
])
|
|
||||||
.envs([
|
|
||||||
("CLICOLOR", "1"),
|
|
||||||
("CLICOLOR_FORCE", "1"),
|
|
||||||
("FZF_DEFAULT_COMMAND", "zoxide edit reload"),
|
|
||||||
]);
|
|
||||||
|
|
||||||
if cfg!(unix) {
|
|
||||||
// Non-POSIX args are only available on certain operating systems.
|
|
||||||
const PREVIEW_ARG: &str = if cfg!(target_os = "linux") {
|
|
||||||
r"--preview=\command -p ls -Cp --color=always --group-directories-first {2..}"
|
|
||||||
} else {
|
|
||||||
r"--preview=\command -p ls -Cp {2..}"
|
|
||||||
};
|
|
||||||
fzf.args([PREVIEW_ARG, "--preview-window=down,30%"]).env("SHELL", "sh");
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut fzf = fzf.spawn()?;
|
|
||||||
fzf.wait()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_dirs(db: &Database, now: Epoch) -> Result<()> {
|
impl Edit {
|
||||||
let stdout = &mut io::stdout().lock();
|
fn get_fzf() -> Result<FzfChild> {
|
||||||
for dir in db.dirs().iter().rev() {
|
Fzf::new()?
|
||||||
let score = dir.score(now).clamp(0.0, 9999.0);
|
.args([
|
||||||
write!(stdout, "{:>6.1}\t{}\x00", score, &dir.path).pipe_exit("fzf")?;
|
// Search mode
|
||||||
|
"--scheme=path",
|
||||||
|
// Search result
|
||||||
|
"--tiebreak=end,chunk,index",
|
||||||
|
// Interface
|
||||||
|
"--bind=\
|
||||||
|
btab:up,\
|
||||||
|
ctrl-r:reload(zoxide edit reload),\
|
||||||
|
ctrl-d:reload(zoxide edit delete {2..}),\
|
||||||
|
ctrl-w:reload(zoxide edit increment {2..}),\
|
||||||
|
ctrl-s:reload(zoxide edit decrement {2..}),\
|
||||||
|
ctrl-z:ignore,\
|
||||||
|
double-click:ignore,\
|
||||||
|
enter:abort,\
|
||||||
|
start:reload(zoxide edit reload),\
|
||||||
|
tab:down",
|
||||||
|
"--cycle",
|
||||||
|
"--keep-right",
|
||||||
|
// Layout
|
||||||
|
"--border=sharp",
|
||||||
|
"--border-label= zoxide-edit ",
|
||||||
|
"--header=\
|
||||||
|
ctrl-r:reload \tctrl-d:delete
|
||||||
|
ctrl-w:increment\tctrl-s:decrement
|
||||||
|
|
||||||
|
SCORE\tPATH",
|
||||||
|
"--info=inline",
|
||||||
|
"--layout=reverse",
|
||||||
|
"--padding=1,0,0,0",
|
||||||
|
// Display
|
||||||
|
"--color=label:bold",
|
||||||
|
"--tabstop=1",
|
||||||
|
])
|
||||||
|
.enable_preview()
|
||||||
|
.spawn()
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,8 @@ use crate::shell::{self, Opts};
|
||||||
impl Run for Init {
|
impl Run for Init {
|
||||||
fn run(&self) -> Result<()> {
|
fn run(&self) -> Result<()> {
|
||||||
let cmd = if self.no_cmd { None } else { Some(self.cmd.as_str()) };
|
let cmd = if self.no_cmd { None } else { Some(self.cmd.as_str()) };
|
||||||
|
|
||||||
let echo = config::echo();
|
let echo = config::echo();
|
||||||
let resolve_symlinks = config::resolve_symlinks();
|
let resolve_symlinks = config::resolve_symlinks();
|
||||||
|
|
||||||
let opts = &Opts { cmd, hook: self.hook, echo, resolve_symlinks };
|
let opts = &Opts { cmd, hook: self.hook, echo, resolve_symlinks };
|
||||||
|
|
||||||
let source = match self.shell {
|
let source = match self.shell {
|
||||||
|
|
|
||||||
111
src/cmd/query.rs
111
src/cmd/query.rs
|
|
@ -4,15 +4,13 @@ use anyhow::{Context, Result};
|
||||||
|
|
||||||
use crate::cmd::{Query, Run};
|
use crate::cmd::{Query, Run};
|
||||||
use crate::config;
|
use crate::config;
|
||||||
use crate::db2::{Database, DatabaseFile};
|
use crate::db::{Database, Epoch, Stream};
|
||||||
use crate::error::BrokenPipeHandler;
|
use crate::error::BrokenPipeHandler;
|
||||||
use crate::util::{self, Fzf};
|
use crate::util::{self, Fzf, FzfChild};
|
||||||
|
|
||||||
impl Run for Query {
|
impl Run for Query {
|
||||||
fn run(&self) -> Result<()> {
|
fn run(&self) -> Result<()> {
|
||||||
let data_dir = config::data_dir()?;
|
let mut db = crate::db::Database::open()?;
|
||||||
let mut db = DatabaseFile::new(data_dir);
|
|
||||||
let mut db = db.open()?;
|
|
||||||
self.query(&mut db).and(db.save())
|
self.query(&mut db).and(db.save())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -20,7 +18,44 @@ impl Run for Query {
|
||||||
impl Query {
|
impl Query {
|
||||||
fn query(&self, db: &mut Database) -> Result<()> {
|
fn query(&self, db: &mut Database) -> Result<()> {
|
||||||
let now = util::current_time()?;
|
let now = util::current_time()?;
|
||||||
|
let mut stream = self.get_stream(db, now);
|
||||||
|
|
||||||
|
if self.interactive {
|
||||||
|
let mut fzf = Self::get_fzf()?;
|
||||||
|
let selection = loop {
|
||||||
|
match stream.next() {
|
||||||
|
Some(dir) => {
|
||||||
|
if let Some(selection) = fzf.write(dir, now)? {
|
||||||
|
break selection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => break fzf.wait()?,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.score {
|
||||||
|
print!("{selection}");
|
||||||
|
} else {
|
||||||
|
let path = selection.get(7..).context("could not read selection from fzf")?;
|
||||||
|
print!("{path}");
|
||||||
|
}
|
||||||
|
} else if self.list {
|
||||||
|
let handle = &mut io::stdout().lock();
|
||||||
|
while let Some(dir) = stream.next() {
|
||||||
|
let dir = if self.score { dir.display().with_score(now) } else { dir.display() };
|
||||||
|
writeln!(handle, "{dir}").pipe_exit("stdout")?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let handle = &mut io::stdout();
|
||||||
|
let dir = stream.next().context("no match found")?;
|
||||||
|
let dir = if self.score { dir.display().with_score(now) } else { dir.display() };
|
||||||
|
writeln!(handle, "{dir}").pipe_exit("stdout")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_stream<'a>(&self, db: &'a mut Database, now: Epoch) -> Stream<'a> {
|
||||||
let mut stream = db.stream(now).with_keywords(&self.keywords);
|
let mut stream = db.stream(now).with_keywords(&self.keywords);
|
||||||
if !self.all {
|
if !self.all {
|
||||||
let resolve_symlinks = config::resolve_symlinks();
|
let resolve_symlinks = config::resolve_symlinks();
|
||||||
|
|
@ -29,46 +64,36 @@ impl Query {
|
||||||
if let Some(path) = &self.exclude {
|
if let Some(path) = &self.exclude {
|
||||||
stream = stream.with_exclude(path);
|
stream = stream.with_exclude(path);
|
||||||
}
|
}
|
||||||
|
stream
|
||||||
|
}
|
||||||
|
|
||||||
if self.interactive {
|
fn get_fzf() -> Result<FzfChild> {
|
||||||
let mut fzf = Fzf::new(false)?;
|
let mut fzf = Fzf::new()?;
|
||||||
let stdin = fzf.stdin();
|
if let Some(fzf_opts) = config::fzf_opts() {
|
||||||
|
fzf.env("FZF_DEFAULT_OPTS", fzf_opts)
|
||||||
let selection = loop {
|
|
||||||
let Some(dir) = stream.next() else { break fzf.select()? };
|
|
||||||
match writeln!(stdin, "{}", dir.display_score(now)) {
|
|
||||||
Err(e) if e.kind() == io::ErrorKind::BrokenPipe => break fzf.select()?,
|
|
||||||
result => result.context("could not write to fzf")?,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if self.score {
|
|
||||||
print!("{selection}");
|
|
||||||
} else {
|
|
||||||
let path = selection.get(5..).context("could not read selection from fzf")?;
|
|
||||||
print!("{path}");
|
|
||||||
}
|
|
||||||
} else if self.list {
|
|
||||||
let handle = &mut io::stdout().lock();
|
|
||||||
while let Some(dir) = stream.next() {
|
|
||||||
if self.score {
|
|
||||||
writeln!(handle, "{}", dir.display_score(now))
|
|
||||||
} else {
|
|
||||||
writeln!(handle, "{}", dir.display())
|
|
||||||
}
|
|
||||||
.pipe_exit("stdout")?;
|
|
||||||
}
|
|
||||||
handle.flush().pipe_exit("stdout")?;
|
|
||||||
} else {
|
} else {
|
||||||
let dir = stream.next().context("no match found")?;
|
fzf.args([
|
||||||
if self.score {
|
// Search mode
|
||||||
writeln!(io::stdout(), "{}", dir.display_score(now))
|
"--scheme=path",
|
||||||
} else {
|
// Search result
|
||||||
writeln!(io::stdout(), "{}", dir.display())
|
"--tiebreak=end,chunk,index",
|
||||||
}
|
// Interface
|
||||||
.pipe_exit("stdout")?;
|
"--bind=ctrl-z:ignore,btab:up,tab:down",
|
||||||
|
"--cycle",
|
||||||
|
"--keep-right",
|
||||||
|
// Layout
|
||||||
|
"--border=sharp", // rounded edges don't display correctly on some terminals
|
||||||
|
"--height=45%",
|
||||||
|
"--info=inline",
|
||||||
|
"--layout=reverse",
|
||||||
|
// Display
|
||||||
|
"--tabstop=1",
|
||||||
|
// Scripting
|
||||||
|
"--exit-0",
|
||||||
|
"--select-1",
|
||||||
|
])
|
||||||
|
.enable_preview()
|
||||||
}
|
}
|
||||||
|
.spawn()
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
use std::borrow::Cow;
|
use std::{
|
||||||
|
borrow::Cow,
|
||||||
|
fmt::{self, Display, Formatter},
|
||||||
|
};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
|
@ -13,6 +16,10 @@ pub struct Dir<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dir<'_> {
|
impl Dir<'_> {
|
||||||
|
pub fn display(&self) -> DirDisplay<'_> {
|
||||||
|
DirDisplay::new(self)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn score(&self, now: Epoch) -> Rank {
|
pub fn score(&self, now: Epoch) -> Rank {
|
||||||
// The older the entry, the lesser its importance.
|
// The older the entry, the lesser its importance.
|
||||||
let duration = now.saturating_sub(self.last_accessed);
|
let duration = now.saturating_sub(self.last_accessed);
|
||||||
|
|
@ -28,5 +35,37 @@ impl Dir<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct DirDisplay<'a> {
|
||||||
|
dir: &'a Dir<'a>,
|
||||||
|
now: Option<Epoch>,
|
||||||
|
separator: char,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> DirDisplay<'a> {
|
||||||
|
fn new(dir: &'a Dir) -> Self {
|
||||||
|
Self { dir, separator: ' ', now: None }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_score(mut self, now: Epoch) -> Self {
|
||||||
|
self.now = Some(now);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_separator(mut self, separator: char) -> Self {
|
||||||
|
self.separator = separator;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for DirDisplay<'_> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
if let Some(now) = self.now {
|
||||||
|
let score = self.dir.score(now).clamp(0.0, 9999.0);
|
||||||
|
write!(f, "{score:>6.1}{}", self.separator)?;
|
||||||
|
}
|
||||||
|
write!(f, "{}", self.dir.path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub type Rank = f64;
|
pub type Rank = f64;
|
||||||
pub type Epoch = u64;
|
pub type Epoch = u64;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
mod dir;
|
mod dir;
|
||||||
|
mod stream;
|
||||||
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::{fs, io};
|
use std::{fs, io};
|
||||||
|
|
@ -8,6 +9,7 @@ use bincode::Options;
|
||||||
use ouroboros::self_referencing;
|
use ouroboros::self_referencing;
|
||||||
|
|
||||||
pub use crate::db::dir::{Dir, Epoch, Rank};
|
pub use crate::db::dir::{Dir, Epoch, Rank};
|
||||||
|
pub use crate::db::stream::Stream;
|
||||||
use crate::{config, util};
|
use crate::{config, util};
|
||||||
|
|
||||||
#[self_referencing]
|
#[self_referencing]
|
||||||
|
|
@ -16,7 +18,7 @@ pub struct Database {
|
||||||
bytes: Vec<u8>,
|
bytes: Vec<u8>,
|
||||||
#[borrows(bytes)]
|
#[borrows(bytes)]
|
||||||
#[covariant]
|
#[covariant]
|
||||||
dirs: Vec<Dir<'this>>,
|
pub dirs: Vec<Dir<'this>>,
|
||||||
dirty: bool,
|
dirty: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -25,10 +27,11 @@ impl Database {
|
||||||
|
|
||||||
pub fn open() -> Result<Self> {
|
pub fn open() -> Result<Self> {
|
||||||
let data_dir = config::data_dir()?;
|
let data_dir = config::data_dir()?;
|
||||||
Self::open_dir(&data_dir)
|
Self::open_dir(data_dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_dir(data_dir: &Path) -> Result<Self> {
|
pub fn open_dir(data_dir: impl AsRef<Path>) -> Result<Self> {
|
||||||
|
let data_dir = data_dir.as_ref();
|
||||||
let path = data_dir.join("db.zo");
|
let path = data_dir.join("db.zo");
|
||||||
|
|
||||||
match fs::read(&path) {
|
match fs::read(&path) {
|
||||||
|
|
@ -90,15 +93,18 @@ impl Database {
|
||||||
/// Removes the directory with `path` from the store. This does not preserve
|
/// Removes the directory with `path` from the store. This does not preserve
|
||||||
/// ordering, but is O(1).
|
/// ordering, but is O(1).
|
||||||
pub fn remove(&mut self, path: impl AsRef<str>) -> bool {
|
pub fn remove(&mut self, path: impl AsRef<str>) -> bool {
|
||||||
let deleted = self.with_dirs_mut(|dirs| match dirs.iter().position(|dir| dir.path == path.as_ref()) {
|
match self.dirs().iter().position(|dir| dir.path == path.as_ref()) {
|
||||||
Some(idx) => {
|
Some(idx) => {
|
||||||
dirs.swap_remove(idx);
|
self.swap_remove(idx);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
None => false,
|
None => false,
|
||||||
});
|
}
|
||||||
self.with_dirty_mut(|dirty| *dirty |= deleted);
|
}
|
||||||
deleted
|
|
||||||
|
pub fn swap_remove(&mut self, idx: usize) {
|
||||||
|
self.with_dirs_mut(|dirs| dirs.swap_remove(idx));
|
||||||
|
self.with_dirty_mut(|dirty| *dirty = true);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn age(&mut self, max_age: Rank) {
|
pub fn age(&mut self, max_age: Rank) {
|
||||||
|
|
@ -120,6 +126,10 @@ impl Database {
|
||||||
self.with_dirty_mut(|dirty_prev| *dirty_prev |= dirty);
|
self.with_dirty_mut(|dirty_prev| *dirty_prev |= dirty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn stream(&mut self, now: Epoch) -> Stream {
|
||||||
|
Stream::new(self, now)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn dedup(&mut self) {
|
pub fn dedup(&mut self) {
|
||||||
// Sort by path, so that equal paths are next to each other.
|
// Sort by path, so that equal paths are next to each other.
|
||||||
self.sort_by_path();
|
self.sort_by_path();
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,11 @@ use std::iter::Rev;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use std::{fs, path};
|
use std::{fs, path};
|
||||||
|
|
||||||
use crate::db2::{Database, Dir, Epoch};
|
use crate::db::{Database, Dir, Epoch};
|
||||||
use crate::util::{self, MONTH};
|
use crate::util::{self, MONTH};
|
||||||
|
|
||||||
pub struct Stream<'db, 'file> {
|
pub struct Stream<'a> {
|
||||||
db: &'db mut Database<'file>,
|
db: &'a mut Database,
|
||||||
idxs: Rev<Range<usize>>,
|
idxs: Rev<Range<usize>>,
|
||||||
|
|
||||||
keywords: Vec<String>,
|
keywords: Vec<String>,
|
||||||
|
|
@ -18,11 +18,10 @@ pub struct Stream<'db, 'file> {
|
||||||
exclude_path: Option<String>,
|
exclude_path: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db, 'file> Stream<'db, 'file> {
|
impl<'a> Stream<'a> {
|
||||||
pub fn new(db: &'db mut Database<'file>, now: Epoch) -> Self {
|
pub fn new(db: &'a mut Database, now: Epoch) -> Self {
|
||||||
// Iterate in descending order of score.
|
db.sort_by_score(now);
|
||||||
db.dirs.sort_unstable_by(|dir1, dir2| dir1.score(now).total_cmp(&dir2.score(now)));
|
let idxs = (0..db.dirs().len()).rev();
|
||||||
let idxs = (0..db.dirs.len()).rev();
|
|
||||||
|
|
||||||
// If a directory is deleted and hasn't been used for 3 months, delete
|
// If a directory is deleted and hasn't been used for 3 months, delete
|
||||||
// it from the database.
|
// it from the database.
|
||||||
|
|
@ -55,9 +54,9 @@ impl<'db, 'file> Stream<'db, 'file> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next(&mut self) -> Option<&Dir<'file>> {
|
pub fn next(&mut self) -> Option<&Dir> {
|
||||||
while let Some(idx) = self.idxs.next() {
|
while let Some(idx) = self.idxs.next() {
|
||||||
let dir = &self.db.dirs[idx];
|
let dir = &self.db.dirs()[idx];
|
||||||
|
|
||||||
if !self.matches_keywords(&dir.path) {
|
if !self.matches_keywords(&dir.path) {
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -65,8 +64,7 @@ impl<'db, 'file> Stream<'db, 'file> {
|
||||||
|
|
||||||
if !self.matches_exists(&dir.path) {
|
if !self.matches_exists(&dir.path) {
|
||||||
if dir.last_accessed < self.expire_below {
|
if dir.last_accessed < self.expire_below {
|
||||||
self.db.dirs.swap_remove(idx);
|
self.db.swap_remove(idx);
|
||||||
self.db.modified = true;
|
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -75,7 +73,7 @@ impl<'db, 'file> Stream<'db, 'file> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let dir = &self.db.dirs[idx];
|
let dir = &self.db.dirs()[idx];
|
||||||
return Some(dir);
|
return Some(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -148,8 +146,8 @@ mod tests {
|
||||||
#[case(&["/foo/", "/bar"], "/foo/bar", false)]
|
#[case(&["/foo/", "/bar"], "/foo/bar", false)]
|
||||||
#[case(&["/foo/", "/bar"], "/foo/baz/bar", true)]
|
#[case(&["/foo/", "/bar"], "/foo/baz/bar", true)]
|
||||||
fn query(#[case] keywords: &[&str], #[case] path: &str, #[case] is_match: bool) {
|
fn query(#[case] keywords: &[&str], #[case] path: &str, #[case] is_match: bool) {
|
||||||
let mut db = Database { dirs: Vec::new().into(), modified: false, data_dir: &PathBuf::new() };
|
let db = &mut Database::new(PathBuf::new(), Vec::new(), |_| Vec::new(), false);
|
||||||
let stream = db.stream(0).with_keywords(keywords);
|
let stream = Stream::new(db, 0).with_keywords(keywords);
|
||||||
assert_eq!(is_match, stream.matches_keywords(path));
|
assert_eq!(is_match, stream.matches_keywords(path));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
160
src/db2/dir.rs
160
src/db2/dir.rs
|
|
@ -1,160 +0,0 @@
|
||||||
use std::borrow::Cow;
|
|
||||||
use std::fmt::{self, Display, Formatter};
|
|
||||||
use std::ops::{Deref, DerefMut};
|
|
||||||
|
|
||||||
use anyhow::{bail, Context, Result};
|
|
||||||
use bincode::Options as _;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
|
||||||
pub struct DirList<'a>(#[serde(borrow)] pub Vec<Dir<'a>>);
|
|
||||||
|
|
||||||
impl DirList<'_> {
|
|
||||||
const VERSION: u32 = 3;
|
|
||||||
|
|
||||||
pub fn new() -> DirList<'static> {
|
|
||||||
DirList(Vec::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_bytes(bytes: &[u8]) -> Result<DirList> {
|
|
||||||
// Assume a maximum size for the database. This prevents bincode from throwing strange
|
|
||||||
// errors when it encounters invalid data.
|
|
||||||
const MAX_SIZE: u64 = 32 << 20; // 32 MiB
|
|
||||||
let deserializer = &mut bincode::options().with_fixint_encoding().with_limit(MAX_SIZE);
|
|
||||||
|
|
||||||
// Split bytes into sections.
|
|
||||||
let version_size = deserializer.serialized_size(&Self::VERSION).unwrap() as _;
|
|
||||||
if bytes.len() < version_size {
|
|
||||||
bail!("could not deserialize database: corrupted data");
|
|
||||||
}
|
|
||||||
let (bytes_version, bytes_dirs) = bytes.split_at(version_size);
|
|
||||||
|
|
||||||
// Deserialize sections.
|
|
||||||
(|| {
|
|
||||||
let version = deserializer.deserialize(bytes_version)?;
|
|
||||||
match version {
|
|
||||||
Self::VERSION => Ok(deserializer.deserialize(bytes_dirs)?),
|
|
||||||
version => {
|
|
||||||
bail!("unsupported version (got {version}, supports {})", Self::VERSION)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})()
|
|
||||||
.context("could not deserialize database")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_bytes(&self) -> Result<Vec<u8>> {
|
|
||||||
(|| -> bincode::Result<_> {
|
|
||||||
// Preallocate buffer with combined size of sections.
|
|
||||||
let version_size = bincode::serialized_size(&Self::VERSION)?;
|
|
||||||
let dirs_size = bincode::serialized_size(&self)?;
|
|
||||||
let buffer_size = version_size + dirs_size;
|
|
||||||
let mut buffer = Vec::with_capacity(buffer_size as _);
|
|
||||||
|
|
||||||
// Serialize sections into buffer.
|
|
||||||
bincode::serialize_into(&mut buffer, &Self::VERSION)?;
|
|
||||||
bincode::serialize_into(&mut buffer, &self)?;
|
|
||||||
Ok(buffer)
|
|
||||||
})()
|
|
||||||
.context("could not serialize database")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Deref for DirList<'a> {
|
|
||||||
type Target = Vec<Dir<'a>>;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> DerefMut for DirList<'a> {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
&mut self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<Vec<Dir<'a>>> for DirList<'a> {
|
|
||||||
fn from(dirs: Vec<Dir<'a>>) -> Self {
|
|
||||||
DirList(dirs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
|
||||||
pub struct Dir<'a> {
|
|
||||||
#[serde(borrow)]
|
|
||||||
pub path: Cow<'a, str>,
|
|
||||||
pub rank: Rank,
|
|
||||||
pub last_accessed: Epoch,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Dir<'_> {
|
|
||||||
pub fn score(&self, now: Epoch) -> Rank {
|
|
||||||
const HOUR: Epoch = 60 * 60;
|
|
||||||
const DAY: Epoch = 24 * HOUR;
|
|
||||||
const WEEK: Epoch = 7 * DAY;
|
|
||||||
|
|
||||||
// The older the entry, the lesser its importance.
|
|
||||||
let duration = now.saturating_sub(self.last_accessed);
|
|
||||||
if duration < HOUR {
|
|
||||||
self.rank * 4.0
|
|
||||||
} else if duration < DAY {
|
|
||||||
self.rank * 2.0
|
|
||||||
} else if duration < WEEK {
|
|
||||||
self.rank * 0.5
|
|
||||||
} else {
|
|
||||||
self.rank * 0.25
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn display(&self) -> DirDisplay {
|
|
||||||
DirDisplay { dir: self }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn display_score(&self, now: Epoch) -> DirDisplayScore {
|
|
||||||
DirDisplayScore { dir: self, now }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct DirDisplay<'a> {
|
|
||||||
dir: &'a Dir<'a>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for DirDisplay<'_> {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "{}", self.dir.path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct DirDisplayScore<'a> {
|
|
||||||
dir: &'a Dir<'a>,
|
|
||||||
now: Epoch,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for DirDisplayScore<'_> {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
||||||
let score = self.dir.score(self.now).clamp(0.0, 9999.0) as u64;
|
|
||||||
write!(f, "{:>4} {}", score, self.dir.path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Rank = f64;
|
|
||||||
pub type Epoch = u64;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use std::borrow::Cow;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn zero_copy() {
|
|
||||||
let dirs = DirList(vec![Dir { path: "/".into(), rank: 0.0, last_accessed: 0 }]);
|
|
||||||
|
|
||||||
let bytes = dirs.to_bytes().unwrap();
|
|
||||||
let dirs = DirList::from_bytes(&bytes).unwrap();
|
|
||||||
|
|
||||||
for dir in dirs.iter() {
|
|
||||||
assert!(matches!(dir.path, Cow::Borrowed(_)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
mod dir;
|
|
||||||
mod stream;
|
|
||||||
|
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
use std::{fs, io};
|
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
|
||||||
pub use dir::{Dir, DirList, Epoch, Rank};
|
|
||||||
pub use stream::Stream;
|
|
||||||
|
|
||||||
use crate::util;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Database<'file> {
|
|
||||||
pub dirs: DirList<'file>,
|
|
||||||
pub modified: bool,
|
|
||||||
pub data_dir: &'file Path,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'file> Database<'file> {
|
|
||||||
pub fn save(&mut self) -> Result<()> {
|
|
||||||
if !self.modified {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let buffer = self.dirs.to_bytes()?;
|
|
||||||
let path = db_path(self.data_dir);
|
|
||||||
util::write(path, buffer).context("could not write to database")?;
|
|
||||||
self.modified = false;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Streaming iterator for directories.
|
|
||||||
pub fn stream(&mut self, now: Epoch) -> Stream<'_, 'file> {
|
|
||||||
Stream::new(self, now)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct DatabaseFile {
|
|
||||||
buffer: Vec<u8>,
|
|
||||||
data_dir: PathBuf,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DatabaseFile {
|
|
||||||
pub fn new<P: Into<PathBuf>>(data_dir: P) -> Self {
|
|
||||||
DatabaseFile { buffer: Vec::new(), data_dir: data_dir.into() }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn open(&mut self) -> Result<Database> {
|
|
||||||
// Read the entire database to memory. For smaller files, this is faster than
|
|
||||||
// mmap / streaming, and allows for zero-copy deserialization.
|
|
||||||
let path = db_path(&self.data_dir);
|
|
||||||
match fs::read(&path) {
|
|
||||||
Ok(buffer) => {
|
|
||||||
self.buffer = buffer;
|
|
||||||
let dirs = DirList::from_bytes(&self.buffer)
|
|
||||||
.with_context(|| format!("could not deserialize database: {}", path.display()))?;
|
|
||||||
Ok(Database { dirs, modified: false, data_dir: &self.data_dir })
|
|
||||||
}
|
|
||||||
Err(e) if e.kind() == io::ErrorKind::NotFound => {
|
|
||||||
// Create data directory, but don't create any file yet. The file will be created
|
|
||||||
// later by [`Database::save`] if any data is modified.
|
|
||||||
fs::create_dir_all(&self.data_dir)
|
|
||||||
.with_context(|| format!("unable to create data directory: {}", self.data_dir.display()))?;
|
|
||||||
Ok(Database { dirs: DirList::new(), modified: false, data_dir: &self.data_dir })
|
|
||||||
}
|
|
||||||
Err(e) => Err(e).with_context(|| format!("could not read from database: {}", path.display())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn db_path<P: AsRef<Path>>(data_dir: P) -> PathBuf {
|
|
||||||
const DB_FILENAME: &str = "db.zo";
|
|
||||||
data_dir.as_ref().join(DB_FILENAME)
|
|
||||||
}
|
|
||||||
|
|
@ -7,7 +7,6 @@ use rstest_reuse;
|
||||||
mod cmd;
|
mod cmd;
|
||||||
mod config;
|
mod config;
|
||||||
mod db;
|
mod db;
|
||||||
mod db2;
|
|
||||||
mod error;
|
mod error;
|
||||||
mod shell;
|
mod shell;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
|
||||||
253
src/util.rs
253
src/util.rs
|
|
@ -1,8 +1,8 @@
|
||||||
|
use std::ffi::OsStr;
|
||||||
use std::fs::{self, File, OpenOptions};
|
use std::fs::{self, File, OpenOptions};
|
||||||
use std::io::{self, Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
use std::ops::{Deref, DerefMut};
|
|
||||||
use std::path::{Component, Path, PathBuf};
|
use std::path::{Component, Path, PathBuf};
|
||||||
use std::process::{Child, ChildStdin, Command, Stdio};
|
use std::process::{Child, Command, Stdio};
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
use std::{env, mem};
|
use std::{env, mem};
|
||||||
|
|
||||||
|
|
@ -10,8 +10,7 @@ use std::{env, mem};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use anyhow::{bail, Context, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
|
|
||||||
use crate::config;
|
use crate::db::{Dir, Epoch};
|
||||||
use crate::db2::Epoch;
|
|
||||||
use crate::error::SilentExit;
|
use crate::error::SilentExit;
|
||||||
|
|
||||||
pub const SECOND: Epoch = 1;
|
pub const SECOND: Epoch = 1;
|
||||||
|
|
@ -21,9 +20,9 @@ pub const DAY: Epoch = 24 * HOUR;
|
||||||
pub const WEEK: Epoch = 7 * DAY;
|
pub const WEEK: Epoch = 7 * DAY;
|
||||||
pub const MONTH: Epoch = 30 * DAY;
|
pub const MONTH: Epoch = 30 * DAY;
|
||||||
|
|
||||||
pub struct Fz(Command);
|
pub struct Fzf(Command);
|
||||||
|
|
||||||
impl Fz {
|
impl Fzf {
|
||||||
const ERR_FZF_NOT_FOUND: &str = "could not find fzf, is it installed?";
|
const ERR_FZF_NOT_FOUND: &str = "could not find fzf, is it installed?";
|
||||||
|
|
||||||
pub fn new() -> Result<Self> {
|
pub fn new() -> Result<Self> {
|
||||||
|
|
@ -35,7 +34,77 @@ impl Fz {
|
||||||
let program = which::which("fzf.exe").map_err(|_| anyhow!(Self::ERR_FZF_NOT_FOUND))?;
|
let program = which::which("fzf.exe").map_err(|_| anyhow!(Self::ERR_FZF_NOT_FOUND))?;
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
let program = "fzf";
|
let program = "fzf";
|
||||||
Ok(Fz(Command::new(program)))
|
|
||||||
|
let mut cmd = Command::new(program);
|
||||||
|
cmd.args([
|
||||||
|
// Search mode
|
||||||
|
"--delimiter=\t",
|
||||||
|
"--nth=2",
|
||||||
|
// Scripting
|
||||||
|
"--read0",
|
||||||
|
])
|
||||||
|
.stdin(Stdio::piped())
|
||||||
|
.stdout(Stdio::piped());
|
||||||
|
|
||||||
|
Ok(Fzf(cmd))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enable_preview(&mut self) -> &mut Self {
|
||||||
|
// Previews are only supported on UNIX.
|
||||||
|
if !cfg!(unix) {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.args([
|
||||||
|
// Non-POSIX args are only available on certain operating systems.
|
||||||
|
if cfg!(target_os = "linux") {
|
||||||
|
r"--preview=\command -p ls -Cp --color=always --group-directories-first {2..}"
|
||||||
|
} else {
|
||||||
|
r"--preview=\command -p ls -Cp {2..}"
|
||||||
|
},
|
||||||
|
// Rounded edges don't display correctly on some terminals.
|
||||||
|
"--preview-window=down,30%,sharp",
|
||||||
|
])
|
||||||
|
.envs([
|
||||||
|
// Enables colorized `ls` output on macOS / FreeBSD.
|
||||||
|
("CLICOLOR", "1"),
|
||||||
|
// Forces colorized `ls` output when the output is not a
|
||||||
|
// TTY (like in fzf's preview window) on macOS /
|
||||||
|
// FreeBSD.
|
||||||
|
("CLICOLOR_FORCE", "1"),
|
||||||
|
// Ensures that the preview command is run in a
|
||||||
|
// POSIX-compliant shell, regardless of what shell the
|
||||||
|
// user has selected.
|
||||||
|
("SHELL", "sh"),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn args<I, S>(&mut self, args: I) -> &mut Self
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = S>,
|
||||||
|
S: AsRef<OsStr>,
|
||||||
|
{
|
||||||
|
self.0.args(args);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn env<K, V>(&mut self, key: K, val: V) -> &mut Self
|
||||||
|
where
|
||||||
|
K: AsRef<OsStr>,
|
||||||
|
V: AsRef<OsStr>,
|
||||||
|
{
|
||||||
|
self.0.env(key, val);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn envs<I, K, V>(&mut self, vars: I) -> &mut Self
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = (K, V)>,
|
||||||
|
K: AsRef<OsStr>,
|
||||||
|
V: AsRef<OsStr>,
|
||||||
|
{
|
||||||
|
self.0.envs(vars);
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn spawn(&mut self) -> Result<FzfChild> {
|
pub fn spawn(&mut self) -> Result<FzfChild> {
|
||||||
|
|
@ -47,140 +116,27 @@ impl Fz {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for Fz {
|
|
||||||
type Target = Command;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for Fz {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
&mut self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct FzfChild(Child);
|
pub struct FzfChild(Child);
|
||||||
|
|
||||||
impl FzfChild {
|
impl FzfChild {
|
||||||
pub fn select(&mut self) -> Result<String> {
|
pub fn write(&mut self, dir: &Dir, now: Epoch) -> Result<Option<String>> {
|
||||||
// Drop stdin to prevent deadlock.
|
let handle = self.0.stdin.as_mut().unwrap();
|
||||||
mem::drop(self.stdin.take());
|
match write!(handle, "{}\0", dir.display().with_score(now).with_separator('\t')) {
|
||||||
|
Ok(()) => Ok(None),
|
||||||
|
Err(e) if e.kind() == io::ErrorKind::BrokenPipe => self.wait().map(Some),
|
||||||
|
Err(e) => Err(e).context("could not write to fzf"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut stdout = self.stdout.take().unwrap();
|
pub fn wait(&mut self) -> Result<String> {
|
||||||
|
// Drop stdin to prevent deadlock.
|
||||||
|
mem::drop(self.0.stdin.take());
|
||||||
|
|
||||||
|
let mut stdout = self.0.stdout.take().unwrap();
|
||||||
let mut output = String::new();
|
let mut output = String::new();
|
||||||
stdout.read_to_string(&mut output).context("failed to read from fzf")?;
|
stdout.read_to_string(&mut output).context("failed to read from fzf")?;
|
||||||
|
|
||||||
self.wait()?;
|
|
||||||
Ok(output)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn wait(&mut self) -> Result<()> {
|
|
||||||
let status = self.0.wait().context("wait failed on fzf")?;
|
let status = self.0.wait().context("wait failed on fzf")?;
|
||||||
match status.code() {
|
|
||||||
Some(0) => Ok(()),
|
|
||||||
Some(1) => bail!("no match found"),
|
|
||||||
Some(2) => bail!("fzf returned an error"),
|
|
||||||
Some(130) => bail!(SilentExit { code: 130 }),
|
|
||||||
Some(128..=254) | None => bail!("fzf was terminated"),
|
|
||||||
_ => bail!("fzf returned an unknown error"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for FzfChild {
|
|
||||||
type Target = Child;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for FzfChild {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
&mut self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Fzf {
|
|
||||||
child: Child,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Fzf {
|
|
||||||
pub fn new(multiple: bool) -> Result<Self> {
|
|
||||||
const ERR_FZF_NOT_FOUND: &str = "could not find fzf, is it installed?";
|
|
||||||
|
|
||||||
// On Windows, CreateProcess implicitly searches the current working
|
|
||||||
// directory for the executable, which is a potential security issue.
|
|
||||||
// Instead, we resolve the path to the executable and then pass it to
|
|
||||||
// CreateProcess.
|
|
||||||
#[cfg(windows)]
|
|
||||||
let mut command = Command::new(which::which("fzf.exe").map_err(|_| anyhow!(ERR_FZF_NOT_FOUND))?);
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
let mut command = Command::new("fzf");
|
|
||||||
if multiple {
|
|
||||||
command.arg("--multi");
|
|
||||||
} else {
|
|
||||||
command.arg("--bind=tab:down,btab:up");
|
|
||||||
}
|
|
||||||
command.arg("--nth=2..").stdin(Stdio::piped()).stdout(Stdio::piped());
|
|
||||||
if let Some(fzf_opts) = config::fzf_opts() {
|
|
||||||
command.env("FZF_DEFAULT_OPTS", fzf_opts);
|
|
||||||
} else {
|
|
||||||
command.args([
|
|
||||||
// Search result
|
|
||||||
"--no-sort",
|
|
||||||
// Interface
|
|
||||||
"--cycle",
|
|
||||||
"--keep-right",
|
|
||||||
// Layout
|
|
||||||
"--height=50%",
|
|
||||||
"--info=inline",
|
|
||||||
"--layout=reverse",
|
|
||||||
// Scripting
|
|
||||||
"--exit-0",
|
|
||||||
"--select-1",
|
|
||||||
// Key/Event bindings
|
|
||||||
"--bind=ctrl-z:ignore",
|
|
||||||
]);
|
|
||||||
if cfg!(unix) {
|
|
||||||
// Non-POSIX args are only available on certain operating systems.
|
|
||||||
const PREVIEW_CMD: &str = if cfg!(target_os = "linux") {
|
|
||||||
r"\command -p ls -Cp --color=always --group-directories-first {2..}"
|
|
||||||
} else {
|
|
||||||
r"\command -p ls -Cp {2..}"
|
|
||||||
};
|
|
||||||
command.args(["--preview", PREVIEW_CMD, "--preview-window=down,30%"]).envs([
|
|
||||||
("CLICOLOR", "1"),
|
|
||||||
("CLICOLOR_FORCE", "1"),
|
|
||||||
("SHELL", "sh"),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let child = match command.spawn() {
|
|
||||||
Ok(child) => child,
|
|
||||||
Err(e) if e.kind() == io::ErrorKind::NotFound => bail!(ERR_FZF_NOT_FOUND),
|
|
||||||
Err(e) => Err(e).context("could not launch fzf")?,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Fzf { child })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn stdin(&mut self) -> &mut ChildStdin {
|
|
||||||
self.child.stdin.as_mut().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn select(mut self) -> Result<String> {
|
|
||||||
// Drop stdin to prevent deadlock.
|
|
||||||
mem::drop(self.child.stdin.take());
|
|
||||||
|
|
||||||
let mut stdout = self.child.stdout.take().unwrap();
|
|
||||||
let mut output = String::new();
|
|
||||||
stdout.read_to_string(&mut output).context("failed to read from fzf")?;
|
|
||||||
|
|
||||||
let status = self.child.wait().context("wait failed on fzf")?;
|
|
||||||
match status.code() {
|
match status.code() {
|
||||||
Some(0) => Ok(output),
|
Some(0) => Ok(output),
|
||||||
Some(1) => bail!("no match found"),
|
Some(1) => bail!("no match found"),
|
||||||
|
|
@ -250,7 +206,7 @@ fn tmpfile<P: AsRef<Path>>(dir: P) -> Result<(File, PathBuf)> {
|
||||||
// Atomically create the tmpfile.
|
// Atomically create the tmpfile.
|
||||||
match OpenOptions::new().write(true).create_new(true).open(&path) {
|
match OpenOptions::new().write(true).create_new(true).open(&path) {
|
||||||
Ok(file) => break Ok((file, path)),
|
Ok(file) => break Ok((file, path)),
|
||||||
Err(e) if e.kind() == io::ErrorKind::AlreadyExists && attempts < MAX_ATTEMPTS => (),
|
Err(e) if e.kind() == io::ErrorKind::AlreadyExists && attempts < MAX_ATTEMPTS => {}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
break Err(e).with_context(|| format!("could not create file: {}", path.display()));
|
break Err(e).with_context(|| format!("could not create file: {}", path.display()));
|
||||||
}
|
}
|
||||||
|
|
@ -258,25 +214,22 @@ fn tmpfile<P: AsRef<Path>>(dir: P) -> Result<(File, PathBuf)> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Similar to [`fs::rename`], but retries on Windows.
|
/// Similar to [`fs::rename`], but with retries on Windows.
|
||||||
fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> Result<()> {
|
fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> Result<()> {
|
||||||
const MAX_ATTEMPTS: usize = 5;
|
|
||||||
let from = from.as_ref();
|
let from = from.as_ref();
|
||||||
let to = to.as_ref();
|
let to = to.as_ref();
|
||||||
|
|
||||||
if cfg!(windows) {
|
const MAX_ATTEMPTS: usize = if cfg!(windows) { 5 } else { 1 };
|
||||||
let mut attempts = 0;
|
let mut attempts = 0;
|
||||||
loop {
|
|
||||||
attempts += 1;
|
loop {
|
||||||
match fs::rename(from, to) {
|
match fs::rename(from, to) {
|
||||||
Err(e) if e.kind() == io::ErrorKind::PermissionDenied && attempts < MAX_ATTEMPTS => (),
|
Err(e) if e.kind() == io::ErrorKind::PermissionDenied && attempts < MAX_ATTEMPTS => attempts += 1,
|
||||||
result => break result,
|
result => {
|
||||||
|
break result.with_context(|| format!("could not rename file: {} -> {}", from.display(), to.display()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
fs::rename(from, to)
|
|
||||||
}
|
}
|
||||||
.with_context(|| format!("could not rename file: {} -> {}", from.display(), to.display()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn canonicalize<P: AsRef<Path>>(path: &P) -> Result<PathBuf> {
|
pub fn canonicalize<P: AsRef<Path>>(path: &P) -> Result<PathBuf> {
|
||||||
|
|
@ -392,7 +345,7 @@ pub fn resolve_path<P: AsRef<Path>>(path: &P) -> Result<PathBuf> {
|
||||||
for component in components {
|
for component in components {
|
||||||
match component {
|
match component {
|
||||||
Component::Normal(_) => stack.push(component),
|
Component::Normal(_) => stack.push(component),
|
||||||
Component::CurDir => (),
|
Component::CurDir => {}
|
||||||
Component::ParentDir => {
|
Component::ParentDir => {
|
||||||
if stack.last() != Some(&Component::RootDir) {
|
if stack.last() != Some(&Component::RootDir) {
|
||||||
stack.pop();
|
stack.pop();
|
||||||
|
|
@ -408,5 +361,9 @@ pub fn resolve_path<P: AsRef<Path>>(path: &P) -> Result<PathBuf> {
|
||||||
/// Convert a string to lowercase, with a fast path for ASCII strings.
|
/// Convert a string to lowercase, with a fast path for ASCII strings.
|
||||||
pub fn to_lowercase<S: AsRef<str>>(s: S) -> String {
|
pub fn to_lowercase<S: AsRef<str>>(s: S) -> String {
|
||||||
let s = s.as_ref();
|
let s = s.as_ref();
|
||||||
if s.is_ascii() { s.to_ascii_lowercase() } else { s.to_lowercase() }
|
if s.is_ascii() {
|
||||||
|
s.to_ascii_lowercase()
|
||||||
|
} else {
|
||||||
|
s.to_lowercase()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue