forked from extern/nushell
nu-path crate refactor (#3730)
* Resolve rebase artifacts * Remove leftover dependencies on removed feature * Remove unnecessary 'pub' * Start taking notes and fooling around * Split canonicalize to two versions; Add TODOs One that takes `relative_to` and one that doesn't. More TODO notes. * Merge absolutize to and rename resolve_dots * Add custom absolutize fn and use it in path expand * Convert a couple of dunce::canonicalize to ours * Update nu-path description * Replace all canonicalize with nu-path version * Remove leftover dunce dependencies * Fix broken autocd with trailing slash Trailing slash is preserved *only* in paths that do not contain "." or "..". This should be fixed in the future to cover all paths but for now it at least covers basic cases. * Use dunce::canonicalize for canonicalizing * Alow cd recovery from non-existent cwd * Disable removed canonicalize functionality tests Remove unused import * Break down nu-path into separate modules * Remove unused public imports * Remove abundant cow mapping * Fix clippy warning * Reformulate old canonicalize tests to expand_path They wouldn't work with the new canonicalize. * Canonicalize also ~ and ndots; Unify path joining Also, add doc comments in nu_path::expansions. * Add comment * Avoid expanding ndots if path is not valid UTF-8 With this change, no lossy path->string conversion should happen in the nu-path crate. * Fmt * Slight expand_tilde refactor; Add doc comments * Start nu-path integration tests * Add tests TODO * Fix docstring typo * Fix some doc strings * Add README for nu-path crate * Add a couple of canonicalize tests * Add nu-path integration tests * Add trim trailing slashes tests * Update nu-path dependency * Remove unused import * Regenerate lockfile
This commit is contained in:
parent
1c1c58e802
commit
d95375d494
125
Cargo.lock
generated
125
Cargo.lock
generated
@ -191,9 +191,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-std"
|
name = "async-std"
|
||||||
version = "1.9.0"
|
version = "1.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d9f06685bad74e0570f5213741bea82158279a4103d988e57bfada11ad230341"
|
checksum = "f8056f1455169ab86dd47b47391e4ab0cbd25410a70e9fe675544f49bafaf952"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-trait"
|
name = "async-trait"
|
||||||
@ -451,9 +451,9 @@ checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.0.1"
|
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 = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040"
|
checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bzip2"
|
name = "bzip2"
|
||||||
@ -688,9 +688,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
version = "0.1.5"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "66c99696f6c9dd7f35d486b9d04d7e6e202aa3e8c40d553f2fdf5e7e0c6a71ef"
|
checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
@ -1614,7 +1614,7 @@ version = "0.3.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d7f3675cfef6a30c8031cf9e6493ebdc3bb3272a3fea3923c4210d1830e6a472"
|
checksum = "d7f3675cfef6a30c8031cf9e6493ebdc3bb3272a3fea3923c4210d1830e6a472"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 1.0.1",
|
"bytes 1.1.0",
|
||||||
"fnv",
|
"fnv",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
@ -1743,7 +1743,7 @@ version = "0.2.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "527e8c9ac747e28542699a951517aa9a6945af506cd1f2e1b53a576c17b6cc11"
|
checksum = "527e8c9ac747e28542699a951517aa9a6945af506cd1f2e1b53a576c17b6cc11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 1.0.1",
|
"bytes 1.1.0",
|
||||||
"fnv",
|
"fnv",
|
||||||
"itoa",
|
"itoa",
|
||||||
]
|
]
|
||||||
@ -1754,7 +1754,7 @@ version = "0.4.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "399c583b2979440c60be0821a6199eca73bc3c8dcd9d070d75ac726e2c6186e5"
|
checksum = "399c583b2979440c60be0821a6199eca73bc3c8dcd9d070d75ac726e2c6186e5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 1.0.1",
|
"bytes 1.1.0",
|
||||||
"http",
|
"http",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
@ -1782,11 +1782,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "0.14.11"
|
version = "0.14.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b61cf2d1aebcf6e6352c97b81dc2244ca29194be1b276f5d8ad5c6330fffb11"
|
checksum = "13f67199e765030fa08fe0bd581af683f0d5bc04ea09c2b1102012c5fb90e7fd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 1.0.1",
|
"bytes 1.1.0",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@ -1810,7 +1810,7 @@ version = "0.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
|
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 1.0.1",
|
"bytes 1.1.0",
|
||||||
"hyper",
|
"hyper",
|
||||||
"native-tls",
|
"native-tls",
|
||||||
"tokio",
|
"tokio",
|
||||||
@ -2017,9 +2017,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.100"
|
version = "0.2.101"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a1fa8cddc8fbbee11227ef194b5317ed014b8acbf15139bd716a18ad3fe99ec5"
|
checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libgit2-sys"
|
name = "libgit2-sys"
|
||||||
@ -2083,9 +2083,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
version = "0.4.4"
|
version = "0.4.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb"
|
checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"scopeguard",
|
"scopeguard",
|
||||||
]
|
]
|
||||||
@ -2426,7 +2426,6 @@ name = "nu"
|
|||||||
version = "0.36.1"
|
version = "0.36.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ctrlc",
|
"ctrlc",
|
||||||
"dunce",
|
|
||||||
"futures 0.3.16",
|
"futures 0.3.16",
|
||||||
"hamcrest2",
|
"hamcrest2",
|
||||||
"itertools",
|
"itertools",
|
||||||
@ -2514,7 +2513,7 @@ dependencies = [
|
|||||||
"base64",
|
"base64",
|
||||||
"bigdecimal-rs",
|
"bigdecimal-rs",
|
||||||
"byte-unit",
|
"byte-unit",
|
||||||
"bytes 1.0.1",
|
"bytes 1.1.0",
|
||||||
"calamine",
|
"calamine",
|
||||||
"chrono",
|
"chrono",
|
||||||
"chrono-tz",
|
"chrono-tz",
|
||||||
@ -2527,7 +2526,6 @@ dependencies = [
|
|||||||
"directories-next",
|
"directories-next",
|
||||||
"dirs-next",
|
"dirs-next",
|
||||||
"dtparse",
|
"dtparse",
|
||||||
"dunce",
|
|
||||||
"eml-parser",
|
"eml-parser",
|
||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
"filesize",
|
"filesize",
|
||||||
@ -2640,6 +2638,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"nu-ansi-term",
|
"nu-ansi-term",
|
||||||
"nu-errors",
|
"nu-errors",
|
||||||
|
"nu-path",
|
||||||
"nu-protocol",
|
"nu-protocol",
|
||||||
"nu-source",
|
"nu-source",
|
||||||
"nu-table",
|
"nu-table",
|
||||||
@ -2669,7 +2668,6 @@ dependencies = [
|
|||||||
"codespan-reporting",
|
"codespan-reporting",
|
||||||
"derive-new",
|
"derive-new",
|
||||||
"dirs-next",
|
"dirs-next",
|
||||||
"dunce",
|
|
||||||
"dyn-clone",
|
"dyn-clone",
|
||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
"filesize",
|
"filesize",
|
||||||
@ -2734,9 +2732,9 @@ dependencies = [
|
|||||||
name = "nu-json"
|
name = "nu-json"
|
||||||
version = "0.36.1"
|
version = "0.36.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dunce",
|
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"linked-hash-map",
|
"linked-hash-map",
|
||||||
|
"nu-path",
|
||||||
"nu-test-support",
|
"nu-test-support",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"regex",
|
"regex",
|
||||||
@ -2751,7 +2749,6 @@ dependencies = [
|
|||||||
"bigdecimal-rs",
|
"bigdecimal-rs",
|
||||||
"codespan-reporting",
|
"codespan-reporting",
|
||||||
"derive-new",
|
"derive-new",
|
||||||
"dunce",
|
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"itertools",
|
"itertools",
|
||||||
"log",
|
"log",
|
||||||
@ -2773,6 +2770,7 @@ version = "0.36.1"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"dirs-next",
|
"dirs-next",
|
||||||
"dunce",
|
"dunce",
|
||||||
|
"nu-test-support",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2879,6 +2877,7 @@ dependencies = [
|
|||||||
"hamcrest2",
|
"hamcrest2",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"nu-errors",
|
"nu-errors",
|
||||||
|
"nu-path",
|
||||||
"nu-protocol",
|
"nu-protocol",
|
||||||
"nu-source",
|
"nu-source",
|
||||||
"nu-value-ext",
|
"nu-value-ext",
|
||||||
@ -3195,7 +3194,7 @@ 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 = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606"
|
checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-bigint 0.4.0",
|
"num-bigint 0.4.1",
|
||||||
"num-complex 0.4.0",
|
"num-complex 0.4.0",
|
||||||
"num-integer",
|
"num-integer",
|
||||||
"num-iter",
|
"num-iter",
|
||||||
@ -3228,9 +3227,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-bigint"
|
name = "num-bigint"
|
||||||
version = "0.4.0"
|
version = "0.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4e0d047c1062aa51e256408c560894e5251f08925980e53cf1aa5bd00eec6512"
|
checksum = "76e97c412795abf6c24ba30055a8f20642ea57ca12875220b854cfa501bf1e48"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"num-integer",
|
"num-integer",
|
||||||
@ -3320,7 +3319,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a"
|
checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"num-bigint 0.4.0",
|
"num-bigint 0.4.1",
|
||||||
"num-integer",
|
"num-integer",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
@ -3376,9 +3375,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.26.1"
|
version = "0.26.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ee2766204889d09937d00bfbb7fec56bb2a199e2ade963cab19185d8a6104c7c"
|
checksum = "39f37e50073ccad23b6d09bcb5b263f4e76d3bb6038e4a3c08e52162ffa8abc2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
@ -3477,9 +3476,9 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.11.1"
|
version = "0.11.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb"
|
checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"instant",
|
"instant",
|
||||||
"lock_api",
|
"lock_api",
|
||||||
@ -3488,9 +3487,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot_core"
|
name = "parking_lot_core"
|
||||||
version = "0.8.3"
|
version = "0.8.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018"
|
checksum = "467fce6df07c66afb48b6284f13c724a1d5a13cbfcfdf5d5ce572c8313e0c722"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"instant",
|
"instant",
|
||||||
@ -3513,7 +3512,7 @@ dependencies = [
|
|||||||
"chrono",
|
"chrono",
|
||||||
"flate2",
|
"flate2",
|
||||||
"lz4",
|
"lz4",
|
||||||
"num-bigint 0.4.0",
|
"num-bigint 0.4.1",
|
||||||
"parquet-format",
|
"parquet-format",
|
||||||
"rand 0.8.4",
|
"rand 0.8.4",
|
||||||
"snap",
|
"snap",
|
||||||
@ -4231,7 +4230,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "246e9f61b9bb77df069a947682be06e31ac43ea37862e244a69f177694ea6d22"
|
checksum = "246e9f61b9bb77df069a947682be06e31ac43ea37862e244a69f177694ea6d22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
"bytes 1.0.1",
|
"bytes 1.1.0",
|
||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@ -4373,9 +4372,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
version = "0.1.20"
|
version = "0.1.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dead70b0b5e03e9c814bcb6b01e03e68f7c57a80aa48c72ec92152ab3e818d49"
|
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-serialize"
|
name = "rustc-serialize"
|
||||||
@ -4418,7 +4417,7 @@ dependencies = [
|
|||||||
"smallvec",
|
"smallvec",
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
"utf8parse 0.2.0",
|
"utf8parse",
|
||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -4436,7 +4435,7 @@ checksum = "6b7327334dd66eab764647b4df9331a46487e933812351baf8fffdeb8a022711"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"base64",
|
"base64",
|
||||||
"bytes 1.0.1",
|
"bytes 1.1.0",
|
||||||
"chrono",
|
"chrono",
|
||||||
"dyn-clone",
|
"dyn-clone",
|
||||||
"failure",
|
"failure",
|
||||||
@ -4506,9 +4505,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "security-framework-sys"
|
name = "security-framework-sys"
|
||||||
version = "2.3.0"
|
version = "2.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7e4effb91b4b8b6fb7732e670b6cee160278ff8e6bf485c7805d9e319d76e284"
|
checksum = "19133a286e494cc3311c165c4676ccb1fd47bed45b55f9d71fbd784ad4cea6f8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"core-foundation-sys",
|
"core-foundation-sys",
|
||||||
"libc",
|
"libc",
|
||||||
@ -4627,9 +4626,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_yaml"
|
name = "serde_yaml"
|
||||||
version = "0.8.19"
|
version = "0.8.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6375dbd828ed6964c3748e4ef6d18e7a175d408ffe184bca01698d0c73f915a9"
|
checksum = "ad104641f3c958dab30eb3010e834c2622d1f3f4c530fef1dee20ad9485f3c09"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dtoa",
|
"dtoa",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
@ -4677,9 +4676,9 @@ checksum = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha2"
|
name = "sha2"
|
||||||
version = "0.9.5"
|
version = "0.9.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b362ae5752fd2137731f9fa25fd4d9058af34666ca1966fb969119cc35719f12"
|
checksum = "9204c41a1597a8c5af23c82d1c921cb01ec0a4c59e07a9c7306062829a3903f3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"block-buffer",
|
"block-buffer",
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
@ -4838,9 +4837,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strip-ansi-escapes"
|
name = "strip-ansi-escapes"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9d63676e2abafa709460982ddc02a3bb586b6d15a49b75c212e06edd3933acee"
|
checksum = "011cbb39cf7c1f62871aea3cc46e5817b0937b49e9447370c93cacbe93a766d8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"vte",
|
"vte",
|
||||||
]
|
]
|
||||||
@ -5162,12 +5161,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.10.0"
|
version = "1.10.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "01cf844b23c6131f624accf65ce0e4e9956a8bb329400ea5bcc26ae3a5c20b0b"
|
checksum = "92036be488bb6594459f2e03b60e42df6f937fe6ca5c5ffdcb539c6b84dc40f5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"bytes 1.0.1",
|
"bytes 1.1.0",
|
||||||
"libc",
|
"libc",
|
||||||
"memchr",
|
"memchr",
|
||||||
"mio",
|
"mio",
|
||||||
@ -5215,7 +5214,7 @@ version = "0.6.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1caa0b0c8d94a049db56b5acf8cba99dc0623aab1b26d5b5f5e2d945846b3592"
|
checksum = "1caa0b0c8d94a049db56b5acf8cba99dc0623aab1b26d5b5f5e2d945846b3592"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 1.0.1",
|
"bytes 1.1.0",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
"log",
|
"log",
|
||||||
@ -5402,12 +5401,6 @@ version = "0.1.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7cf7d77f457ef8dfa11e4cd5933c5ddb5dc52a94664071951219a97710f0a32b"
|
checksum = "7cf7d77f457ef8dfa11e4cd5933c5ddb5dc52a94664071951219a97710f0a32b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "utf8parse"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8772a4ccbb4e89959023bc5b7cb8623a795caa7092d99f3aa9501b9484d4557d"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8parse"
|
name = "utf8parse"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@ -5449,11 +5442,23 @@ checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vte"
|
name = "vte"
|
||||||
version = "0.3.3"
|
version = "0.10.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4f42f536e22f7fcbb407639765c8fd78707a33109301f834a594758bedd6e8cf"
|
checksum = "6cbce692ab4ca2f1f3047fcf732430249c0e971bfdd2b234cf2c47ad93af5983"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"utf8parse 0.1.1",
|
"arrayvec 0.5.2",
|
||||||
|
"utf8parse",
|
||||||
|
"vte_generate_state_changes",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vte_generate_state_changes"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -58,7 +58,6 @@ itertools = "0.10.0"
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-test-support = { version = "0.36.1", path="./crates/nu-test-support" }
|
nu-test-support = { version = "0.36.1", path="./crates/nu-test-support" }
|
||||||
dunce = "1.0.1"
|
|
||||||
serial_test = "0.5.1"
|
serial_test = "0.5.1"
|
||||||
hamcrest2 = "0.3.0"
|
hamcrest2 = "0.3.0"
|
||||||
rstest = "0.10.0"
|
rstest = "0.10.0"
|
||||||
|
@ -45,7 +45,6 @@ derive-new = "0.5.8"
|
|||||||
directories-next = "2.0.0"
|
directories-next = "2.0.0"
|
||||||
dirs-next = "2.0.0"
|
dirs-next = "2.0.0"
|
||||||
dtparse = "1.2.0"
|
dtparse = "1.2.0"
|
||||||
dunce = "1.0.1"
|
|
||||||
eml-parser = "0.1.0"
|
eml-parser = "0.1.0"
|
||||||
encoding_rs = "0.8.28"
|
encoding_rs = "0.8.28"
|
||||||
filesize = "0.2.0"
|
filesize = "0.2.0"
|
||||||
|
@ -104,7 +104,7 @@ fn run_with_stdin(
|
|||||||
let process_args = command_args
|
let process_args = command_args
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(arg, _is_literal)| {
|
.map(|(arg, _is_literal)| {
|
||||||
let arg = nu_path::expand_tilde_string(Cow::Borrowed(arg));
|
let arg = nu_path::expand_tilde(arg).to_string_lossy().to_string();
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
{
|
{
|
||||||
|
@ -4,7 +4,7 @@ use crate::prelude::*;
|
|||||||
use nu_engine::WholeStreamCommand;
|
use nu_engine::WholeStreamCommand;
|
||||||
|
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_path::canonicalize;
|
use nu_path::canonicalize_with;
|
||||||
use nu_protocol::{CommandAction, ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
|
use nu_protocol::{CommandAction, ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
|
||||||
use nu_source::Tagged;
|
use nu_source::Tagged;
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ impl WholeStreamCommand for SubCommand {
|
|||||||
tag,
|
tag,
|
||||||
}) = load_path
|
}) = load_path
|
||||||
{
|
{
|
||||||
let path = canonicalize(shell_manager.path(), load_path).map_err(|_| {
|
let path = canonicalize_with(load_path, shell_manager.path()).map_err(|_| {
|
||||||
ShellError::labeled_error(
|
ShellError::labeled_error(
|
||||||
"Cannot load plugins from directory",
|
"Cannot load plugins from directory",
|
||||||
"directory not found",
|
"directory not found",
|
||||||
|
@ -31,12 +31,12 @@ impl WholeStreamCommand for AutoenvTrust {
|
|||||||
value: UntaggedValue::Primitive(Primitive::String(ref path)),
|
value: UntaggedValue::Primitive(Primitive::String(ref path)),
|
||||||
tag: _,
|
tag: _,
|
||||||
}) => {
|
}) => {
|
||||||
let mut dir = fs::canonicalize(path)?;
|
let mut dir = nu_path::canonicalize(path)?;
|
||||||
dir.push(".nu-env");
|
dir.push(".nu-env");
|
||||||
dir
|
dir
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let mut dir = fs::canonicalize(std::env::current_dir()?)?;
|
let mut dir = nu_path::canonicalize(std::env::current_dir()?)?;
|
||||||
dir.push(".nu-env");
|
dir.push(".nu-env");
|
||||||
dir
|
dir
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ impl WholeStreamCommand for AutoenvUntrust {
|
|||||||
value: UntaggedValue::Primitive(Primitive::String(ref path)),
|
value: UntaggedValue::Primitive(Primitive::String(ref path)),
|
||||||
tag: _,
|
tag: _,
|
||||||
}) => {
|
}) => {
|
||||||
let mut dir = fs::canonicalize(path)?;
|
let mut dir = nu_path::canonicalize(path)?;
|
||||||
dir.push(".nu-env");
|
dir.push(".nu-env");
|
||||||
dir
|
dir
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ use log::debug;
|
|||||||
use nu_engine::StringOrBinary;
|
use nu_engine::StringOrBinary;
|
||||||
use nu_engine::WholeStreamCommand;
|
use nu_engine::WholeStreamCommand;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
|
use nu_path::canonicalize;
|
||||||
use nu_protocol::{CommandAction, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
use nu_protocol::{CommandAction, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||||
use nu_source::{AnchorLocation, Span, Tagged};
|
use nu_source::{AnchorLocation, Span, Tagged};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
@ -193,7 +194,7 @@ pub fn fetch(
|
|||||||
// TODO: I don't understand the point of this? Maybe for better error reporting
|
// TODO: I don't understand the point of this? Maybe for better error reporting
|
||||||
let mut cwd = PathBuf::from(cwd);
|
let mut cwd = PathBuf::from(cwd);
|
||||||
cwd.push(location);
|
cwd.push(location);
|
||||||
let nice_location = dunce::canonicalize(&cwd).map_err(|e| match e.kind() {
|
let nice_location = canonicalize(&cwd).map_err(|e| match e.kind() {
|
||||||
std::io::ErrorKind::NotFound => ShellError::labeled_error(
|
std::io::ErrorKind::NotFound => ShellError::labeled_error(
|
||||||
format!("Cannot find file {:?}", cwd),
|
format!("Cannot find file {:?}", cwd),
|
||||||
"cannot find file",
|
"cannot find file",
|
||||||
|
@ -2,7 +2,7 @@ use super::{operate, PathSubcommandArguments};
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use nu_engine::WholeStreamCommand;
|
use nu_engine::WholeStreamCommand;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_path::expand_path;
|
use nu_path::{canonicalize, expand_path};
|
||||||
use nu_protocol::{ColumnPath, Signature, SyntaxShape, UntaggedValue, Value};
|
use nu_protocol::{ColumnPath, Signature, SyntaxShape, UntaggedValue, Value};
|
||||||
use nu_source::Span;
|
use nu_source::Span;
|
||||||
use std::{borrow::Cow, path::Path};
|
use std::{borrow::Cow, path::Path};
|
||||||
@ -95,7 +95,7 @@ impl WholeStreamCommand for PathExpand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn action(path: &Path, tag: Tag, args: &PathExpandArguments) -> Value {
|
fn action(path: &Path, tag: Tag, args: &PathExpandArguments) -> Value {
|
||||||
if let Ok(p) = dunce::canonicalize(path) {
|
if let Ok(p) = canonicalize(path) {
|
||||||
UntaggedValue::filepath(p).into_value(tag)
|
UntaggedValue::filepath(p).into_value(tag)
|
||||||
} else if args.strict {
|
} else if args.strict {
|
||||||
Value::error(ShellError::labeled_error(
|
Value::error(ShellError::labeled_error(
|
||||||
|
@ -7,6 +7,7 @@ use std::path::PathBuf;
|
|||||||
use nu_engine::WholeStreamCommand;
|
use nu_engine::WholeStreamCommand;
|
||||||
use nu_engine::{evaluate_baseline_expr, shell::CdArgs};
|
use nu_engine::{evaluate_baseline_expr, shell::CdArgs};
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
|
use nu_path::{canonicalize, trim_trailing_slash};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
hir::{ExternalArgs, ExternalCommand, SpannedExpression},
|
hir::{ExternalArgs, ExternalCommand, SpannedExpression},
|
||||||
Primitive, UntaggedValue,
|
Primitive, UntaggedValue,
|
||||||
@ -137,10 +138,10 @@ fn maybe_autocd_dir(cmd: &ExternalCommand, ctx: &mut EvaluationContext) -> Optio
|
|||||||
let path_name = if name.ends_with(std::path::is_separator)
|
let path_name = if name.ends_with(std::path::is_separator)
|
||||||
|| (cmd.args.is_empty()
|
|| (cmd.args.is_empty()
|
||||||
&& PathBuf::from(name).is_dir()
|
&& PathBuf::from(name).is_dir()
|
||||||
&& dunce::canonicalize(name).is_ok()
|
&& canonicalize(name).is_ok()
|
||||||
&& !ctx.host().lock().is_external_cmd(name))
|
&& !ctx.host().lock().is_external_cmd(name))
|
||||||
{
|
{
|
||||||
Some(name)
|
Some(trim_trailing_slash(name))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
@ -30,6 +30,7 @@ sys-locale = "0.1.0"
|
|||||||
toml = "0.5.8"
|
toml = "0.5.8"
|
||||||
|
|
||||||
nu-errors = { version = "0.36.1", path="../nu-errors" }
|
nu-errors = { version = "0.36.1", path="../nu-errors" }
|
||||||
|
nu-path = { version = "0.36.1", path="../nu-path" }
|
||||||
nu-protocol = { version = "0.36.1", path="../nu-protocol" }
|
nu-protocol = { version = "0.36.1", path="../nu-protocol" }
|
||||||
nu-source = { version = "0.36.1", path="../nu-source" }
|
nu-source = { version = "0.36.1", path="../nu-source" }
|
||||||
nu-table = { version = "0.36.1", path="../nu-table" }
|
nu-table = { version = "0.36.1", path="../nu-table" }
|
||||||
|
@ -21,7 +21,7 @@ impl Trusted {
|
|||||||
|
|
||||||
pub fn is_file_trusted(nu_env_file: &Path, content: &[u8]) -> Result<bool, ShellError> {
|
pub fn is_file_trusted(nu_env_file: &Path, content: &[u8]) -> Result<bool, ShellError> {
|
||||||
let contentdigest = Sha256::digest(content).as_slice().to_vec();
|
let contentdigest = Sha256::digest(content).as_slice().to_vec();
|
||||||
let nufile = std::fs::canonicalize(nu_env_file)?;
|
let nufile = nu_path::canonicalize(nu_env_file)?;
|
||||||
|
|
||||||
let trusted = read_trusted()?;
|
let trusted = read_trusted()?;
|
||||||
Ok(trusted.files.get(&nufile.to_string_lossy().to_string()) == Some(&contentdigest))
|
Ok(trusted.files.get(&nufile.to_string_lossy().to_string()) == Some(&contentdigest))
|
||||||
|
@ -31,7 +31,6 @@ bytes = "0.5.6"
|
|||||||
chrono = { version="0.4.19", features=["serde"] }
|
chrono = { version="0.4.19", features=["serde"] }
|
||||||
derive-new = "0.5.8"
|
derive-new = "0.5.8"
|
||||||
dirs-next = "2.0.0"
|
dirs-next = "2.0.0"
|
||||||
dunce = "1.0.1"
|
|
||||||
encoding_rs = "0.8.28"
|
encoding_rs = "0.8.28"
|
||||||
filesize = "0.2.0"
|
filesize = "0.2.0"
|
||||||
fs_extra = "1.2.0"
|
fs_extra = "1.2.0"
|
||||||
|
@ -9,7 +9,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use encoding_rs::Encoding;
|
use encoding_rs::Encoding;
|
||||||
use nu_data::config::LocalConfigDiff;
|
use nu_data::config::LocalConfigDiff;
|
||||||
use nu_path::canonicalize;
|
use nu_path::{canonicalize, canonicalize_with, expand_path_with};
|
||||||
use nu_protocol::{CommandAction, ConfigPath, TaggedDictBuilder, Value};
|
use nu_protocol::{CommandAction, ConfigPath, TaggedDictBuilder, Value};
|
||||||
use nu_source::{Span, Tag};
|
use nu_source::{Span, Tag};
|
||||||
use nu_stream::{ActionStream, Interruptible, IntoActionStream, OutputStream};
|
use nu_stream::{ActionStream, Interruptible, IntoActionStream, OutputStream};
|
||||||
@ -77,7 +77,7 @@ impl FilesystemShell {
|
|||||||
path: String,
|
path: String,
|
||||||
mode: FilesystemShellMode,
|
mode: FilesystemShellMode,
|
||||||
) -> Result<FilesystemShell, std::io::Error> {
|
) -> Result<FilesystemShell, std::io::Error> {
|
||||||
let path = canonicalize(std::env::current_dir()?, &path)?;
|
let path = canonicalize_with(&path, std::env::current_dir()?)?;
|
||||||
let path = path.display().to_string();
|
let path = path.display().to_string();
|
||||||
let last_path = path.clone();
|
let last_path = path.clone();
|
||||||
|
|
||||||
@ -237,13 +237,20 @@ impl Shell for FilesystemShell {
|
|||||||
if target == Path::new("-") {
|
if target == Path::new("-") {
|
||||||
PathBuf::from(&self.last_path)
|
PathBuf::from(&self.last_path)
|
||||||
} else {
|
} else {
|
||||||
let path = canonicalize(self.path(), target).map_err(|_| {
|
// Extra expand attempt allows cd from /home/user/non-existent-dir/..
|
||||||
ShellError::labeled_error(
|
// to /home/user
|
||||||
|
let path = match canonicalize_with(&target, self.path()) {
|
||||||
|
Ok(p) => p,
|
||||||
|
_ => expand_path_with(&target, self.path()),
|
||||||
|
};
|
||||||
|
|
||||||
|
if !path.exists() {
|
||||||
|
return Err(ShellError::labeled_error(
|
||||||
"Cannot change to directory",
|
"Cannot change to directory",
|
||||||
"directory not found",
|
"directory not found",
|
||||||
&tag,
|
&tag,
|
||||||
)
|
));
|
||||||
})?;
|
}
|
||||||
|
|
||||||
if !path.is_dir() {
|
if !path.is_dir() {
|
||||||
return Err(ShellError::labeled_error(
|
return Err(ShellError::labeled_error(
|
||||||
@ -291,7 +298,7 @@ impl Shell for FilesystemShell {
|
|||||||
//Loading local configs in script mode, makes scripts behave different on different
|
//Loading local configs in script mode, makes scripts behave different on different
|
||||||
//filesystems and might therefore surprise users. That's why we only load them in cli mode.
|
//filesystems and might therefore surprise users. That's why we only load them in cli mode.
|
||||||
if self.is_cli() {
|
if self.is_cli() {
|
||||||
match dunce::canonicalize(self.path()) {
|
match canonicalize(self.path()) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let err = ShellError::untagged_runtime_error(format!(
|
let err = ShellError::untagged_runtime_error(format!(
|
||||||
"Could not get absolute path from current fs shell. The error was: {:?}",
|
"Could not get absolute path from current fs shell. The error was: {:?}",
|
||||||
@ -388,7 +395,7 @@ impl Shell for FilesystemShell {
|
|||||||
if entry.is_file() {
|
if entry.is_file() {
|
||||||
let sources = sources.paths_applying_with(|(source_file, _depth_level)| {
|
let sources = sources.paths_applying_with(|(source_file, _depth_level)| {
|
||||||
if destination.is_dir() {
|
if destination.is_dir() {
|
||||||
let mut dest = canonicalize(&path, &dst.item)?;
|
let mut dest = canonicalize_with(&dst.item, &path)?;
|
||||||
if let Some(name) = entry.file_name() {
|
if let Some(name) = entry.file_name() {
|
||||||
dest.push(name);
|
dest.push(name);
|
||||||
}
|
}
|
||||||
@ -427,7 +434,7 @@ impl Shell for FilesystemShell {
|
|||||||
|
|
||||||
let sources = sources.paths_applying_with(|(source_file, depth_level)| {
|
let sources = sources.paths_applying_with(|(source_file, depth_level)| {
|
||||||
let mut dest = destination.clone();
|
let mut dest = destination.clone();
|
||||||
let path = canonicalize(&path, &source_file)?;
|
let path = canonicalize_with(&source_file, &path)?;
|
||||||
|
|
||||||
let comps: Vec<_> = path
|
let comps: Vec<_> = path
|
||||||
.components()
|
.components()
|
||||||
@ -773,7 +780,7 @@ impl Shell for FilesystemShell {
|
|||||||
|
|
||||||
fn pwd(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
fn pwd(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||||
let path = PathBuf::from(self.path());
|
let path = PathBuf::from(self.path());
|
||||||
let p = match dunce::canonicalize(path.as_path()) {
|
let p = match canonicalize(path.as_path()) {
|
||||||
Ok(p) => p,
|
Ok(p) => p,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
return Err(ShellError::labeled_error(
|
return Err(ShellError::labeled_error(
|
||||||
@ -792,7 +799,7 @@ impl Shell for FilesystemShell {
|
|||||||
|
|
||||||
fn set_path(&mut self, path: String) {
|
fn set_path(&mut self, path: String) {
|
||||||
let pathbuf = PathBuf::from(&path);
|
let pathbuf = PathBuf::from(&path);
|
||||||
let path = match canonicalize(self.path(), pathbuf.as_path()) {
|
let path = match canonicalize_with(pathbuf.as_path(), self.path()) {
|
||||||
Ok(path) => {
|
Ok(path) => {
|
||||||
let _ = std::env::set_current_dir(&path);
|
let _ = std::env::set_current_dir(&path);
|
||||||
std::env::set_var("PWD", &path);
|
std::env::set_var("PWD", &path);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_path::canonicalize;
|
use nu_path::canonicalize_with;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@ -47,7 +47,7 @@ impl FileStructure {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn build(&mut self, src: &Path, lvl: usize) -> Result<(), ShellError> {
|
fn build(&mut self, src: &Path, lvl: usize) -> Result<(), ShellError> {
|
||||||
let source = canonicalize(std::env::current_dir()?, src)?;
|
let source = canonicalize_with(src, std::env::current_dir()?)?;
|
||||||
|
|
||||||
if source.is_dir() {
|
if source.is_dir() {
|
||||||
for entry in std::fs::read_dir(src)? {
|
for entry in std::fs::read_dir(src)? {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use crate::plugin::run_plugin::PluginCommandBuilder;
|
use crate::plugin::run_plugin::PluginCommandBuilder;
|
||||||
use log::trace;
|
use log::trace;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
|
use nu_path::canonicalize;
|
||||||
use nu_plugin::jsonrpc::JsonRpc;
|
use nu_plugin::jsonrpc::JsonRpc;
|
||||||
use nu_protocol::{Signature, Value};
|
use nu_protocol::{Signature, Value};
|
||||||
use std::io::{BufRead, BufReader, Write};
|
use std::io::{BufRead, BufReader, Write};
|
||||||
@ -48,7 +49,7 @@ pub fn build_plugin_command(
|
|||||||
let request_raw = serde_json::to_string(&request)?;
|
let request_raw = serde_json::to_string(&request)?;
|
||||||
trace!(target: "nu::load", "plugin infrastructure config -> path {:#?}, request {:?}", &path, &request_raw);
|
trace!(target: "nu::load", "plugin infrastructure config -> path {:#?}, request {:?}", &path, &request_raw);
|
||||||
stdin.write_all(format!("{}\n", request_raw).as_bytes())?;
|
stdin.write_all(format!("{}\n", request_raw).as_bytes())?;
|
||||||
let path = dunce::canonicalize(path)?;
|
let path = canonicalize(path)?;
|
||||||
|
|
||||||
let mut input = String::new();
|
let mut input = String::new();
|
||||||
let result = match reader.read_line(&mut input) {
|
let result = match reader.read_line(&mut input) {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::{evaluate::internal::InternalIterator, maybe_print_errors, run_block, shell::CdArgs};
|
use crate::{evaluate::internal::InternalIterator, maybe_print_errors, run_block, shell::CdArgs};
|
||||||
use crate::{BufCodecReader, MaybeTextCodec, StringOrBinary};
|
use crate::{BufCodecReader, MaybeTextCodec, StringOrBinary};
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_path::canonicalize;
|
use nu_path::{canonicalize_with, trim_trailing_slash};
|
||||||
use nu_protocol::hir::{
|
use nu_protocol::hir::{
|
||||||
Call, ClassifiedCommand, Expression, ExternalRedirection, InternalCommand, Literal,
|
Call, ClassifiedCommand, Expression, ExternalRedirection, InternalCommand, Literal,
|
||||||
NamedArguments, SpannedExpression,
|
NamedArguments, SpannedExpression,
|
||||||
@ -66,7 +66,6 @@ pub fn process_script(
|
|||||||
let (block, err) = nu_parser::parse(line, span_offset, &ctx.scope);
|
let (block, err) = nu_parser::parse(line, span_offset, &ctx.scope);
|
||||||
|
|
||||||
debug!("{:#?}", block);
|
debug!("{:#?}", block);
|
||||||
//println!("{:#?}", pipeline);
|
|
||||||
|
|
||||||
if let Some(failure) = err {
|
if let Some(failure) = err {
|
||||||
return LineResult::Error(line.to_string(), failure.into());
|
return LineResult::Error(line.to_string(), failure.into());
|
||||||
@ -116,7 +115,7 @@ pub fn process_script(
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.map(NamedArguments::is_empty)
|
.map(NamedArguments::is_empty)
|
||||||
.unwrap_or(true)
|
.unwrap_or(true)
|
||||||
&& canonicalize(ctx.shell_manager().path(), name).is_ok()
|
&& canonicalize_with(name, ctx.shell_manager().path()).is_ok()
|
||||||
&& Path::new(&name).is_dir()
|
&& Path::new(&name).is_dir()
|
||||||
&& !ctx.host().lock().is_external_cmd(name)
|
&& !ctx.host().lock().is_external_cmd(name)
|
||||||
{
|
{
|
||||||
@ -160,6 +159,8 @@ pub fn process_script(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let path = trim_trailing_slash(&path);
|
||||||
|
|
||||||
let cd_args = CdArgs {
|
let cd_args = CdArgs {
|
||||||
path: Some(Tagged {
|
path: Some(Tagged {
|
||||||
item: PathBuf::from(path),
|
item: PathBuf::from(path),
|
||||||
|
@ -20,6 +20,6 @@ lazy_static = "1"
|
|||||||
linked-hash-map = { version="0.5", optional=true }
|
linked-hash-map = { version="0.5", optional=true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
nu-path = { version = "0.36.1", path="../nu-path" }
|
||||||
nu-test-support = { version = "0.36.1", path="../nu-test-support" }
|
nu-test-support = { version = "0.36.1", path="../nu-test-support" }
|
||||||
serde_json = "1.0.39"
|
serde_json = "1.0.39"
|
||||||
dunce = "1.0.1"
|
|
||||||
|
@ -26,7 +26,7 @@ fn txt(text: &str) -> String {
|
|||||||
fn hjson_expectations() -> PathBuf {
|
fn hjson_expectations() -> PathBuf {
|
||||||
let assets = nu_test_support::fs::assets().join("nu_json");
|
let assets = nu_test_support::fs::assets().join("nu_json");
|
||||||
|
|
||||||
dunce::canonicalize(assets.clone()).unwrap_or_else(|e| {
|
nu_path::canonicalize(assets.clone()).unwrap_or_else(|e| {
|
||||||
panic!(
|
panic!(
|
||||||
"Couldn't canonicalize hjson assets path {}: {:?}",
|
"Couldn't canonicalize hjson assets path {}: {:?}",
|
||||||
assets.display(),
|
assets.display(),
|
||||||
|
@ -18,8 +18,6 @@ serde = "1.0"
|
|||||||
itertools = "0.10.0"
|
itertools = "0.10.0"
|
||||||
smart-default = "0.6.0"
|
smart-default = "0.6.0"
|
||||||
|
|
||||||
dunce = "1.0.1"
|
|
||||||
|
|
||||||
nu-errors = { version = "0.36.1", path="../nu-errors" }
|
nu-errors = { version = "0.36.1", path="../nu-errors" }
|
||||||
nu-data = { version = "0.36.1", path="../nu-data" }
|
nu-data = { version = "0.36.1", path="../nu-data" }
|
||||||
nu-path = { version = "0.36.1", path="../nu-path" }
|
nu-path = { version = "0.36.1", path="../nu-path" }
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
use std::borrow::Cow;
|
|
||||||
use std::{path::PathBuf, sync::Arc};
|
use std::{path::PathBuf, sync::Arc};
|
||||||
|
|
||||||
use bigdecimal::BigDecimal;
|
use bigdecimal::BigDecimal;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use log::trace;
|
use log::trace;
|
||||||
use nu_errors::{ArgumentError, ParseError};
|
use nu_errors::{ArgumentError, ParseError};
|
||||||
use nu_path::{expand_path, expand_path_string};
|
use nu_path::expand_path;
|
||||||
use nu_protocol::hir::{
|
use nu_protocol::hir::{
|
||||||
self, Binary, Block, Call, ClassifiedCommand, Expression, ExternalRedirection, Flag, FlagKind,
|
self, Binary, Block, Call, ClassifiedCommand, Expression, ExternalRedirection, Flag, FlagKind,
|
||||||
Group, InternalCommand, Member, NamedArguments, Operator, Pipeline, RangeOperator,
|
Group, InternalCommand, Member, NamedArguments, Operator, Pipeline, RangeOperator,
|
||||||
@ -954,8 +953,8 @@ fn parse_arg(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
SyntaxShape::GlobPattern => {
|
SyntaxShape::GlobPattern => {
|
||||||
let trimmed = Cow::Owned(trim_quotes(&lite_arg.item));
|
let trimmed = trim_quotes(&lite_arg.item);
|
||||||
let expanded = expand_path_string(trimmed).to_string();
|
let expanded = expand_path(trimmed).to_string_lossy().to_string();
|
||||||
(
|
(
|
||||||
SpannedExpression::new(Expression::glob_pattern(expanded), lite_arg.span),
|
SpannedExpression::new(Expression::glob_pattern(expanded), lite_arg.span),
|
||||||
None,
|
None,
|
||||||
@ -972,7 +971,7 @@ fn parse_arg(
|
|||||||
SyntaxShape::FilePath => {
|
SyntaxShape::FilePath => {
|
||||||
let trimmed = trim_quotes(&lite_arg.item);
|
let trimmed = trim_quotes(&lite_arg.item);
|
||||||
let path = PathBuf::from(trimmed);
|
let path = PathBuf::from(trimmed);
|
||||||
let expanded = expand_path(Cow::Owned(path)).to_path_buf();
|
let expanded = expand_path(path);
|
||||||
(
|
(
|
||||||
SpannedExpression::new(Expression::FilePath(expanded), lite_arg.span),
|
SpannedExpression::new(Expression::FilePath(expanded), lite_arg.span),
|
||||||
None,
|
None,
|
||||||
@ -1659,8 +1658,8 @@ fn parse_external_call(
|
|||||||
) -> (Option<ClassifiedCommand>, Option<ParseError>) {
|
) -> (Option<ClassifiedCommand>, Option<ParseError>) {
|
||||||
let mut error = None;
|
let mut error = None;
|
||||||
let name = lite_cmd.parts[0].clone().map(|v| {
|
let name = lite_cmd.parts[0].clone().map(|v| {
|
||||||
let trimmed = Cow::Owned(trim_quotes(&v));
|
let trimmed = trim_quotes(&v);
|
||||||
expand_path_string(trimmed).to_string()
|
expand_path(trimmed).to_string_lossy().to_string()
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut args = vec![];
|
let mut args = vec![];
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
authors = ["The Nu Project Contributors"]
|
authors = ["The Nu Project Contributors"]
|
||||||
description = "Nushell parser"
|
description = "Path handling library for Nushell"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-path"
|
name = "nu-path"
|
||||||
@ -9,3 +9,6 @@ version = "0.36.1"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
dirs-next = "2.0.0"
|
dirs-next = "2.0.0"
|
||||||
dunce = "1.0.1"
|
dunce = "1.0.1"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
nu-test-support = { version = "0.36.1", path="../nu-test-support" }
|
||||||
|
3
crates/nu-path/README.md
Normal file
3
crates/nu-path/README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# nu-path
|
||||||
|
|
||||||
|
This crate takes care of path handling in Nushell, such as canonicalization and component expansion, as well as other path-related utilities.
|
259
crates/nu-path/src/dots.rs
Normal file
259
crates/nu-path/src/dots.rs
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
use std::path::{is_separator, Component, Path, PathBuf};
|
||||||
|
|
||||||
|
const EXPAND_STR: &str = if cfg!(windows) { r"..\" } else { "../" };
|
||||||
|
|
||||||
|
fn handle_dots_push(string: &mut String, count: u8) {
|
||||||
|
if count < 1 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if count == 1 {
|
||||||
|
string.push('.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for _ in 0..(count - 1) {
|
||||||
|
string.push_str(EXPAND_STR);
|
||||||
|
}
|
||||||
|
|
||||||
|
string.pop(); // remove last '/'
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Expands any occurence of more than two dots into a sequence of ../ (or ..\ on windows), e.g.,
|
||||||
|
/// "..." into "../..", "...." into "../../../", etc.
|
||||||
|
pub fn expand_ndots(path: impl AsRef<Path>) -> PathBuf {
|
||||||
|
// Check if path is valid UTF-8 and if not, return it as it is to avoid breaking it via string
|
||||||
|
// conversion.
|
||||||
|
let path_str = match path.as_ref().to_str() {
|
||||||
|
Some(s) => s,
|
||||||
|
None => return path.as_ref().into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// find if we need to expand any >2 dot paths and early exit if not
|
||||||
|
let mut dots_count = 0u8;
|
||||||
|
let ndots_present = {
|
||||||
|
for chr in path_str.chars() {
|
||||||
|
if chr == '.' {
|
||||||
|
dots_count += 1;
|
||||||
|
} else {
|
||||||
|
if is_separator(chr) && (dots_count > 2) {
|
||||||
|
// this path component had >2 dots
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
dots_count = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dots_count > 2
|
||||||
|
};
|
||||||
|
|
||||||
|
if !ndots_present {
|
||||||
|
return path.as_ref().into();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut dots_count = 0u8;
|
||||||
|
let mut expanded = String::new();
|
||||||
|
for chr in path_str.chars() {
|
||||||
|
if chr == '.' {
|
||||||
|
dots_count += 1;
|
||||||
|
} else {
|
||||||
|
if is_separator(chr) {
|
||||||
|
// check for dots expansion only at path component boundaries
|
||||||
|
handle_dots_push(&mut expanded, dots_count);
|
||||||
|
dots_count = 0;
|
||||||
|
} else {
|
||||||
|
// got non-dot within path component => do not expand any dots
|
||||||
|
while dots_count > 0 {
|
||||||
|
expanded.push('.');
|
||||||
|
dots_count -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expanded.push(chr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_dots_push(&mut expanded, dots_count);
|
||||||
|
|
||||||
|
expanded.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Expand "." and ".." into nothing and parent directory, respectively.
|
||||||
|
pub fn expand_dots(path: impl AsRef<Path>) -> PathBuf {
|
||||||
|
let path = path.as_ref();
|
||||||
|
|
||||||
|
// Early-exit if path does not contain '.' or '..'
|
||||||
|
if !path
|
||||||
|
.components()
|
||||||
|
.any(|c| std::matches!(c, Component::CurDir | Component::ParentDir))
|
||||||
|
{
|
||||||
|
return path.into();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut result = PathBuf::with_capacity(path.as_os_str().len());
|
||||||
|
|
||||||
|
// Only pop/skip path elements if the previous one was an actual path element
|
||||||
|
let prev_is_normal = |p: &Path| -> bool {
|
||||||
|
p.components()
|
||||||
|
.next_back()
|
||||||
|
.map(|c| std::matches!(c, Component::Normal(_)))
|
||||||
|
.unwrap_or(false)
|
||||||
|
};
|
||||||
|
|
||||||
|
path.components().for_each(|component| match component {
|
||||||
|
Component::ParentDir if prev_is_normal(&result) => {
|
||||||
|
result.pop();
|
||||||
|
}
|
||||||
|
Component::CurDir if prev_is_normal(&result) => {}
|
||||||
|
_ => result.push(component),
|
||||||
|
});
|
||||||
|
|
||||||
|
dunce::simplified(&result).to_path_buf()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expand_two_dots() {
|
||||||
|
let path = Path::new("/foo/bar/..");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
PathBuf::from("/foo"), // missing path
|
||||||
|
expand_dots(path)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expand_dots_with_curdir() {
|
||||||
|
let path = Path::new("/foo/./bar/./baz");
|
||||||
|
|
||||||
|
assert_eq!(PathBuf::from("/foo/bar/baz"), expand_dots(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_ndots_expansion(expected: &str, s: &str) {
|
||||||
|
let expanded = expand_ndots(Path::new(s));
|
||||||
|
assert_eq!(Path::new(expected), &expanded);
|
||||||
|
}
|
||||||
|
|
||||||
|
// common tests
|
||||||
|
#[test]
|
||||||
|
fn string_without_ndots() {
|
||||||
|
check_ndots_expansion("../hola", "../hola");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn string_with_three_ndots_and_chars() {
|
||||||
|
check_ndots_expansion("a...b", "a...b");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn string_with_two_ndots_and_chars() {
|
||||||
|
check_ndots_expansion("a..b", "a..b");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn string_with_one_dot_and_chars() {
|
||||||
|
check_ndots_expansion("a.b", "a.b");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expand_dots_double_dots_no_change() {
|
||||||
|
// Can't resolve this as we don't know our parent dir
|
||||||
|
assert_eq!(Path::new(".."), expand_dots(Path::new("..")));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expand_dots_single_dot_no_change() {
|
||||||
|
// Can't resolve this as we don't know our current dir
|
||||||
|
assert_eq!(Path::new("."), expand_dots(Path::new(".")));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expand_dots_multi_single_dots_no_change() {
|
||||||
|
assert_eq!(Path::new("././."), expand_dots(Path::new("././.")));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expand_multi_double_dots_no_change() {
|
||||||
|
assert_eq!(Path::new("../../../"), expand_dots(Path::new("../../../")));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expand_dots_no_change_with_dirs() {
|
||||||
|
// Can't resolve this as we don't know our parent dir
|
||||||
|
assert_eq!(
|
||||||
|
Path::new("../../../dir1/dir2/"),
|
||||||
|
expand_dots(Path::new("../../../dir1/dir2"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expand_dots_simple() {
|
||||||
|
assert_eq!(Path::new("/foo"), expand_dots(Path::new("/foo/bar/..")));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expand_dots_complex() {
|
||||||
|
assert_eq!(
|
||||||
|
Path::new("/test"),
|
||||||
|
expand_dots(Path::new("/foo/./bar/../../test/././test2/../"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
mod windows {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn string_with_three_ndots() {
|
||||||
|
check_ndots_expansion(r"..\..", "...");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn string_with_mixed_ndots_and_chars() {
|
||||||
|
check_ndots_expansion(
|
||||||
|
r"a...b/./c..d/../e.f/..\..\..//.",
|
||||||
|
"a...b/./c..d/../e.f/....//.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn string_with_three_ndots_and_final_slash() {
|
||||||
|
check_ndots_expansion(r"..\../", ".../");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn string_with_three_ndots_and_garbage() {
|
||||||
|
check_ndots_expansion(r"ls ..\../ garbage.*[", "ls .../ garbage.*[");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
mod non_windows {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn string_with_three_ndots() {
|
||||||
|
check_ndots_expansion(r"../..", "...");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn string_with_mixed_ndots_and_chars() {
|
||||||
|
check_ndots_expansion(
|
||||||
|
"a...b/./c..d/../e.f/../../..//.",
|
||||||
|
"a...b/./c..d/../e.f/....//.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn string_with_three_ndots_and_final_slash() {
|
||||||
|
check_ndots_expansion("../../", ".../");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn string_with_three_ndots_and_garbage() {
|
||||||
|
check_ndots_expansion("ls ../../ garbage.*[", "ls .../ garbage.*[");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
75
crates/nu-path/src/expansions.rs
Normal file
75
crates/nu-path/src/expansions.rs
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
use std::io;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use super::dots::{expand_dots, expand_ndots};
|
||||||
|
use super::tilde::expand_tilde;
|
||||||
|
|
||||||
|
// Join a path relative to another path. Paths starting with tilde are considered as absolute.
|
||||||
|
fn join_path_relative<P, Q>(path: P, relative_to: Q) -> PathBuf
|
||||||
|
where
|
||||||
|
P: AsRef<Path>,
|
||||||
|
Q: AsRef<Path>,
|
||||||
|
{
|
||||||
|
let path = path.as_ref();
|
||||||
|
let relative_to = relative_to.as_ref();
|
||||||
|
|
||||||
|
if path == Path::new(".") {
|
||||||
|
// Joining a Path with '.' appends a '.' at the end, making the prompt
|
||||||
|
// more ugly - so we don't do anything, which should result in an equal
|
||||||
|
// path on all supported systems.
|
||||||
|
relative_to.into()
|
||||||
|
} else if path.starts_with("~") {
|
||||||
|
// do not end up with "/some/path/~"
|
||||||
|
path.into()
|
||||||
|
} else {
|
||||||
|
relative_to.join(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolve all symbolic links and all components (tilde, ., .., ...+) and return the path in its
|
||||||
|
/// absolute form.
|
||||||
|
///
|
||||||
|
/// Fails under the same conditions as
|
||||||
|
/// [std::fs::canonicalize](https://doc.rust-lang.org/std/fs/fn.canonicalize.html).
|
||||||
|
pub fn canonicalize(path: impl AsRef<Path>) -> io::Result<PathBuf> {
|
||||||
|
let path = expand_tilde(path);
|
||||||
|
let path = expand_ndots(path);
|
||||||
|
|
||||||
|
dunce::canonicalize(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Same as canonicalize() but the input path is specified relative to another path
|
||||||
|
pub fn canonicalize_with<P, Q>(path: P, relative_to: Q) -> io::Result<PathBuf>
|
||||||
|
where
|
||||||
|
P: AsRef<Path>,
|
||||||
|
Q: AsRef<Path>,
|
||||||
|
{
|
||||||
|
let path = join_path_relative(path, relative_to);
|
||||||
|
|
||||||
|
canonicalize(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolve only path components (tilde, ., .., ...+), if possible.
|
||||||
|
///
|
||||||
|
/// The function works in a "best effort" mode: It does not fail but rather returns the unexpanded
|
||||||
|
/// version if the expansion is not possible.
|
||||||
|
///
|
||||||
|
/// Furthermore, unlike canonicalize(), it does not use sys calls (such as readlink).
|
||||||
|
///
|
||||||
|
/// Does not convert to absolute form nor does it resolve symlinks.
|
||||||
|
pub fn expand_path(path: impl AsRef<Path>) -> PathBuf {
|
||||||
|
let path = expand_tilde(path);
|
||||||
|
let path = expand_ndots(path);
|
||||||
|
expand_dots(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Same as expand_path() but the input path is specified relative to another path
|
||||||
|
pub fn expand_path_with<P, Q>(path: P, relative_to: Q) -> PathBuf
|
||||||
|
where
|
||||||
|
P: AsRef<Path>,
|
||||||
|
Q: AsRef<Path>,
|
||||||
|
{
|
||||||
|
let path = join_path_relative(path, relative_to);
|
||||||
|
|
||||||
|
expand_path(path)
|
||||||
|
}
|
@ -1,530 +1,8 @@
|
|||||||
use std::borrow::Cow;
|
mod dots;
|
||||||
use std::io;
|
mod expansions;
|
||||||
use std::path::{Component, Path, PathBuf};
|
mod tilde;
|
||||||
|
mod util;
|
||||||
// Utility for applying a function that can only be called on the borrowed type of the Cow
|
|
||||||
// and also returns a ref. If the Cow is a borrow, we can return the same borrow but an
|
pub use expansions::{canonicalize, canonicalize_with, expand_path, expand_path_with};
|
||||||
// owned value needs extra handling because the returned valued has to be owned as well
|
pub use tilde::expand_tilde;
|
||||||
pub fn cow_map_by_ref<B, O, F>(c: Cow<'_, B>, f: F) -> Cow<'_, B>
|
pub use util::trim_trailing_slash;
|
||||||
where
|
|
||||||
B: ToOwned<Owned = O> + ?Sized,
|
|
||||||
O: AsRef<B>,
|
|
||||||
F: FnOnce(&B) -> &B,
|
|
||||||
{
|
|
||||||
match c {
|
|
||||||
Cow::Borrowed(b) => Cow::Borrowed(f(b)),
|
|
||||||
Cow::Owned(o) => Cow::Owned(f(o.as_ref()).to_owned()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Utility for applying a function over Cow<'a, Path> over a Cow<'a, str> while avoiding unnecessary conversions
|
|
||||||
fn cow_map_str_path<'a, F>(c: Cow<'a, str>, f: F) -> Cow<'a, str>
|
|
||||||
where
|
|
||||||
F: FnOnce(Cow<'a, Path>) -> Cow<'a, Path>,
|
|
||||||
{
|
|
||||||
let ret = match c {
|
|
||||||
Cow::Borrowed(b) => f(Cow::Borrowed(Path::new(b))),
|
|
||||||
Cow::Owned(o) => f(Cow::Owned(PathBuf::from(o))),
|
|
||||||
};
|
|
||||||
|
|
||||||
match ret {
|
|
||||||
Cow::Borrowed(expanded) => expanded.to_string_lossy(),
|
|
||||||
Cow::Owned(expanded) => Cow::Owned(expanded.to_string_lossy().to_string()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Utility for applying a function over Cow<'a, str> over a Cow<'a, Path> while avoiding unnecessary conversions
|
|
||||||
fn cow_map_path_str<'a, F>(c: Cow<'a, Path>, f: F) -> Cow<'a, Path>
|
|
||||||
where
|
|
||||||
F: FnOnce(Cow<'a, str>) -> Cow<'a, str>,
|
|
||||||
{
|
|
||||||
let ret = match c {
|
|
||||||
Cow::Borrowed(path) => f(path.to_string_lossy()),
|
|
||||||
Cow::Owned(buf) => f(Cow::Owned(buf.to_string_lossy().to_string())),
|
|
||||||
};
|
|
||||||
|
|
||||||
match ret {
|
|
||||||
Cow::Borrowed(expanded) => Cow::Borrowed(Path::new(expanded)),
|
|
||||||
Cow::Owned(expanded) => Cow::Owned(PathBuf::from(expanded)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const EXPAND_STR: &str = if cfg!(windows) { r"..\" } else { "../" };
|
|
||||||
fn handle_dots_push(string: &mut String, count: u8) {
|
|
||||||
if count < 1 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if count == 1 {
|
|
||||||
string.push('.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for _ in 0..(count - 1) {
|
|
||||||
string.push_str(EXPAND_STR);
|
|
||||||
}
|
|
||||||
|
|
||||||
string.pop(); // remove last '/'
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expands any occurence of more than two dots into a sequence of ../ (or ..\ on windows), e.g.
|
|
||||||
// ... into ../..
|
|
||||||
// .... into ../../../
|
|
||||||
fn expand_ndots_string(path: Cow<'_, str>) -> Cow<'_, str> {
|
|
||||||
use std::path::is_separator;
|
|
||||||
// find if we need to expand any >2 dot paths and early exit if not
|
|
||||||
let mut dots_count = 0u8;
|
|
||||||
let ndots_present = {
|
|
||||||
for chr in path.chars() {
|
|
||||||
if chr == '.' {
|
|
||||||
dots_count += 1;
|
|
||||||
} else {
|
|
||||||
if is_separator(chr) && (dots_count > 2) {
|
|
||||||
// this path component had >2 dots
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
dots_count = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dots_count > 2
|
|
||||||
};
|
|
||||||
|
|
||||||
if !ndots_present {
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut dots_count = 0u8;
|
|
||||||
let mut expanded = String::new();
|
|
||||||
for chr in path.chars() {
|
|
||||||
if chr == '.' {
|
|
||||||
dots_count += 1;
|
|
||||||
} else {
|
|
||||||
if is_separator(chr) {
|
|
||||||
// check for dots expansion only at path component boundaries
|
|
||||||
handle_dots_push(&mut expanded, dots_count);
|
|
||||||
dots_count = 0;
|
|
||||||
} else {
|
|
||||||
// got non-dot within path component => do not expand any dots
|
|
||||||
while dots_count > 0 {
|
|
||||||
expanded.push('.');
|
|
||||||
dots_count -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
expanded.push(chr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handle_dots_push(&mut expanded, dots_count);
|
|
||||||
|
|
||||||
expanded.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expands any occurence of more than two dots into a sequence of ../ (or ..\ on windows), e.g.
|
|
||||||
// ... into ../..
|
|
||||||
// .... into ../../../
|
|
||||||
fn expand_ndots(path: Cow<'_, Path>) -> Cow<'_, Path> {
|
|
||||||
cow_map_path_str(path, expand_ndots_string)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn absolutize<P, Q>(relative_to: P, path: Q) -> PathBuf
|
|
||||||
where
|
|
||||||
P: AsRef<Path>,
|
|
||||||
Q: AsRef<Path>,
|
|
||||||
{
|
|
||||||
let path = if path.as_ref() == Path::new(".") {
|
|
||||||
// Joining a Path with '.' appends a '.' at the end, making the prompt
|
|
||||||
// more ugly - so we don't do anything, which should result in an equal
|
|
||||||
// path on all supported systems.
|
|
||||||
relative_to.as_ref().to_owned()
|
|
||||||
} else if path.as_ref().starts_with("~") {
|
|
||||||
expand_tilde(Cow::Borrowed(path.as_ref())).to_path_buf()
|
|
||||||
} else {
|
|
||||||
relative_to.as_ref().join(path)
|
|
||||||
};
|
|
||||||
|
|
||||||
let (relative_to, path) = {
|
|
||||||
let components: Vec<_> = path.components().collect();
|
|
||||||
let separator = components
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.find(|(_, c)| c == &&Component::CurDir || c == &&Component::ParentDir);
|
|
||||||
|
|
||||||
if let Some((index, _)) = separator {
|
|
||||||
let (absolute, relative) = components.split_at(index);
|
|
||||||
let absolute: PathBuf = absolute.iter().collect();
|
|
||||||
let relative: PathBuf = relative.iter().collect();
|
|
||||||
|
|
||||||
(absolute, relative)
|
|
||||||
} else {
|
|
||||||
(
|
|
||||||
relative_to.as_ref().to_path_buf(),
|
|
||||||
components.iter().collect::<PathBuf>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let path = if path.is_relative() {
|
|
||||||
let mut result = relative_to;
|
|
||||||
path.components().for_each(|component| match component {
|
|
||||||
Component::ParentDir => {
|
|
||||||
result.pop();
|
|
||||||
}
|
|
||||||
Component::Normal(normal) => result.push(normal),
|
|
||||||
_ => {}
|
|
||||||
});
|
|
||||||
|
|
||||||
result
|
|
||||||
} else {
|
|
||||||
path
|
|
||||||
};
|
|
||||||
|
|
||||||
dunce::simplified(&path).to_path_buf()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn canonicalize<P, Q>(relative_to: P, path: Q) -> io::Result<PathBuf>
|
|
||||||
where
|
|
||||||
P: AsRef<Path>,
|
|
||||||
Q: AsRef<Path>,
|
|
||||||
{
|
|
||||||
let absolutized = absolutize(&relative_to, path);
|
|
||||||
let path = match std::fs::read_link(&absolutized) {
|
|
||||||
Ok(resolved) => {
|
|
||||||
let parent = absolutized.parent().unwrap_or(&absolutized);
|
|
||||||
absolutize(parent, resolved)
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(e) => {
|
|
||||||
if absolutized.exists() {
|
|
||||||
absolutized
|
|
||||||
} else {
|
|
||||||
return Err(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(dunce::simplified(&path).to_path_buf())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expansion logic lives here to enable testing without depending on dirs-next
|
|
||||||
fn expand_tilde_with(path: Cow<'_, Path>, home: Option<PathBuf>) -> Cow<'_, Path> {
|
|
||||||
if !path.starts_with("~") {
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
match home {
|
|
||||||
None => path,
|
|
||||||
Some(mut h) => {
|
|
||||||
if h == Path::new("/") {
|
|
||||||
// Corner case: `h` root directory;
|
|
||||||
// don't prepend extra `/`, just drop the tilde.
|
|
||||||
cow_map_by_ref(path, |p: &Path| {
|
|
||||||
p.strip_prefix("~").expect("cannot strip ~ prefix")
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
h.push(path.strip_prefix("~/").expect("cannot strip ~/ prefix"));
|
|
||||||
Cow::Owned(h)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn expand_tilde(path: Cow<'_, Path>) -> Cow<'_, Path> {
|
|
||||||
expand_tilde_with(path, dirs_next::home_dir())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn expand_tilde_string(path: Cow<'_, str>) -> Cow<'_, str> {
|
|
||||||
cow_map_str_path(path, expand_tilde)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove "." and ".." in a path. Prefix ".." are not removed as we don't have access to the
|
|
||||||
// current dir. This is merely 'string manipulation'. Does not handle "...+", see expand_ndots for that
|
|
||||||
pub fn resolve_dots(path: Cow<'_, Path>) -> Cow<'_, Path> {
|
|
||||||
debug_assert!(!path.components().any(|c| std::matches!(c, Component::Normal(os_str) if os_str.to_string_lossy().starts_with("..."))), "Unexpected ndots!");
|
|
||||||
if !path
|
|
||||||
.components()
|
|
||||||
.any(|c| std::matches!(c, Component::CurDir | Component::ParentDir))
|
|
||||||
{
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut result = PathBuf::with_capacity(path.as_os_str().len());
|
|
||||||
|
|
||||||
// Only pop/skip path elements if the previous one was an actual path element
|
|
||||||
let prev_is_normal = |p: &Path| -> bool {
|
|
||||||
p.components()
|
|
||||||
.next_back()
|
|
||||||
.map(|c| std::matches!(c, Component::Normal(_)))
|
|
||||||
.unwrap_or(false)
|
|
||||||
};
|
|
||||||
path.as_ref()
|
|
||||||
.components()
|
|
||||||
.for_each(|component| match component {
|
|
||||||
Component::ParentDir if prev_is_normal(&result) => {
|
|
||||||
result.pop();
|
|
||||||
}
|
|
||||||
Component::CurDir if prev_is_normal(&result) => {}
|
|
||||||
_ => result.push(component),
|
|
||||||
});
|
|
||||||
|
|
||||||
Cow::Owned(dunce::simplified(&result).to_path_buf())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expands ~ to home and shortens paths by removing unecessary ".." and "."
|
|
||||||
// where possible. Also expands "...+" appropriately.
|
|
||||||
pub fn expand_path(path: Cow<'_, Path>) -> Cow<'_, Path> {
|
|
||||||
let path = expand_tilde(path);
|
|
||||||
let path = expand_ndots(path);
|
|
||||||
resolve_dots(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn expand_path_string(path: Cow<'_, str>) -> Cow<'_, str> {
|
|
||||||
cow_map_str_path(path, expand_path)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use std::io;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn absolutize_two_dots() {
|
|
||||||
let relative_to = Path::new("/foo/bar");
|
|
||||||
let path = Path::new("..");
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
PathBuf::from("/foo"), // missing path
|
|
||||||
absolutize(relative_to, path)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn absolutize_with_curdir() {
|
|
||||||
let relative_to = Path::new("/foo");
|
|
||||||
let path = Path::new("./bar/./baz");
|
|
||||||
|
|
||||||
assert!(!absolutize(relative_to, path)
|
|
||||||
.to_str()
|
|
||||||
.unwrap()
|
|
||||||
.contains('.'));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn canonicalize_should_succeed() -> io::Result<()> {
|
|
||||||
let relative_to = Path::new("/foo/bar");
|
|
||||||
let path = Path::new("../..");
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
PathBuf::from("/"), // existing path
|
|
||||||
canonicalize(relative_to, path)?,
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn canonicalize_should_fail() {
|
|
||||||
let relative_to = Path::new("/foo/bar/baz"); // '/foo' is missing
|
|
||||||
let path = Path::new("../..");
|
|
||||||
|
|
||||||
assert!(canonicalize(relative_to, path).is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_ndots_expansion(expected: &str, s: &str) {
|
|
||||||
let expanded = expand_ndots(Cow::Borrowed(Path::new(s)));
|
|
||||||
// If we don't expect expansion, verify that we get a borrow back and no PathBuf creation has been made
|
|
||||||
if expected == s {
|
|
||||||
assert!(
|
|
||||||
std::matches!(expanded, Cow::Borrowed(_)),
|
|
||||||
"No PathBuf should be needed here (unnecessary allocation)"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
assert_eq!(Path::new(expected), &expanded);
|
|
||||||
}
|
|
||||||
|
|
||||||
// common tests
|
|
||||||
#[test]
|
|
||||||
fn string_without_ndots() {
|
|
||||||
check_ndots_expansion("../hola", "../hola");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn string_with_three_ndots_and_chars() {
|
|
||||||
check_ndots_expansion("a...b", "a...b");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn string_with_two_ndots_and_chars() {
|
|
||||||
check_ndots_expansion("a..b", "a..b");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn string_with_one_dot_and_chars() {
|
|
||||||
check_ndots_expansion("a.b", "a.b");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn resolve_dots_double_dots_no_change() {
|
|
||||||
// Can't resolve this as we don't know our parent dir
|
|
||||||
assert_eq!(Path::new(".."), resolve_dots(Path::new("..").into()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn resolve_dots_single_dot_no_change() {
|
|
||||||
// Can't resolve this as we don't know our current dir
|
|
||||||
assert_eq!(Path::new("."), resolve_dots(Path::new(".").into()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn resolve_dots_multi_single_dots_no_change() {
|
|
||||||
assert_eq!(Path::new("././."), resolve_dots(Path::new("././.").into()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn resolve_multi_double_dots_no_change() {
|
|
||||||
assert_eq!(
|
|
||||||
Path::new("../../../"),
|
|
||||||
resolve_dots(Path::new("../../../").into())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn resolve_dots_no_change_with_dirs() {
|
|
||||||
// Can't resolve this as we don't know our parent dir
|
|
||||||
assert_eq!(
|
|
||||||
Path::new("../../../dir1/dir2/"),
|
|
||||||
resolve_dots(Path::new("../../../dir1/dir2").into())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn resolve_dots_simple() {
|
|
||||||
assert_eq!(
|
|
||||||
Path::new("/foo"),
|
|
||||||
resolve_dots(Path::new("/foo/bar/..").into())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn resolve_dots_complex() {
|
|
||||||
assert_eq!(
|
|
||||||
Path::new("/test"),
|
|
||||||
resolve_dots(Path::new("/foo/./bar/../../test/././test2/../").into())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Windows tests
|
|
||||||
#[cfg(windows)]
|
|
||||||
mod windows {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn string_with_three_ndots() {
|
|
||||||
check_ndots_expansion(r"..\..", "...");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn string_with_mixed_ndots_and_chars() {
|
|
||||||
check_ndots_expansion(
|
|
||||||
r"a...b/./c..d/../e.f/..\..\..//.",
|
|
||||||
"a...b/./c..d/../e.f/....//.",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn string_with_three_ndots_and_final_slash() {
|
|
||||||
check_ndots_expansion(r"..\../", ".../");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn string_with_three_ndots_and_garbage() {
|
|
||||||
check_ndots_expansion(r"ls ..\../ garbage.*[", "ls .../ garbage.*[");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// non-Windows tests
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
mod non_windows {
|
|
||||||
use super::*;
|
|
||||||
#[test]
|
|
||||||
fn string_with_three_ndots() {
|
|
||||||
check_ndots_expansion(r"../..", "...");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn string_with_mixed_ndots_and_chars() {
|
|
||||||
check_ndots_expansion(
|
|
||||||
"a...b/./c..d/../e.f/../../..//.",
|
|
||||||
"a...b/./c..d/../e.f/....//.",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn string_with_three_ndots_and_final_slash() {
|
|
||||||
check_ndots_expansion("../../", ".../");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn string_with_three_ndots_and_garbage() {
|
|
||||||
check_ndots_expansion("ls ../../ garbage.*[", "ls .../ garbage.*[");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mod tilde {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
fn check_expanded(s: &str) {
|
|
||||||
let home = Path::new("/home");
|
|
||||||
let buf = Some(PathBuf::from(home));
|
|
||||||
assert!(expand_tilde_with(Cow::Borrowed(Path::new(s)), buf).starts_with(&home));
|
|
||||||
|
|
||||||
// Tests the special case in expand_tilde for "/" as home
|
|
||||||
let home = Path::new("/");
|
|
||||||
let buf = Some(PathBuf::from(home));
|
|
||||||
assert!(!expand_tilde_with(Cow::Borrowed(Path::new(s)), buf).starts_with("//"));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_not_expanded(s: &str) {
|
|
||||||
let home = PathBuf::from("/home");
|
|
||||||
let expanded = expand_tilde_with(Cow::Borrowed(Path::new(s)), Some(home));
|
|
||||||
assert!(
|
|
||||||
std::matches!(expanded, Cow::Borrowed(_)),
|
|
||||||
"No PathBuf should be needed here (unecessary allocation)"
|
|
||||||
);
|
|
||||||
assert!(expanded == Path::new(s));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn string_with_tilde() {
|
|
||||||
check_expanded("~");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn string_with_tilde_forward_slash() {
|
|
||||||
check_expanded("~/test/");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn string_with_tilde_double_forward_slash() {
|
|
||||||
check_expanded("~//test/");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn does_not_expand_tilde_if_tilde_is_not_first_character() {
|
|
||||||
check_not_expanded("1~1");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
#[test]
|
|
||||||
fn string_with_tilde_backslash() {
|
|
||||||
check_expanded("~\\test/test2/test3");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
#[test]
|
|
||||||
fn string_with_double_tilde_backslash() {
|
|
||||||
check_expanded("~\\\\test\\test2/test3");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
85
crates/nu-path/src/tilde.rs
Normal file
85
crates/nu-path/src/tilde.rs
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
fn expand_tilde_with(path: impl AsRef<Path>, home: Option<PathBuf>) -> PathBuf {
|
||||||
|
let path = path.as_ref();
|
||||||
|
|
||||||
|
if !path.starts_with("~") {
|
||||||
|
return path.into();
|
||||||
|
}
|
||||||
|
|
||||||
|
match home {
|
||||||
|
None => path.into(),
|
||||||
|
Some(mut h) => {
|
||||||
|
if h == Path::new("/") {
|
||||||
|
// Corner case: `h` is a root directory;
|
||||||
|
// don't prepend extra `/`, just drop the tilde.
|
||||||
|
path.strip_prefix("~").unwrap_or(path).into()
|
||||||
|
} else {
|
||||||
|
if let Ok(p) = path.strip_prefix("~/") {
|
||||||
|
h.push(p)
|
||||||
|
}
|
||||||
|
h
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Expand tilde ("~") into a home directory if it is the first path component
|
||||||
|
pub fn expand_tilde(path: impl AsRef<Path>) -> PathBuf {
|
||||||
|
// TODO: Extend this to work with "~user" style of home paths
|
||||||
|
expand_tilde_with(path, dirs_next::home_dir())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn check_expanded(s: &str) {
|
||||||
|
let home = Path::new("/home");
|
||||||
|
let buf = Some(PathBuf::from(home));
|
||||||
|
assert!(expand_tilde_with(Path::new(s), buf).starts_with(&home));
|
||||||
|
|
||||||
|
// Tests the special case in expand_tilde for "/" as home
|
||||||
|
let home = Path::new("/");
|
||||||
|
let buf = Some(PathBuf::from(home));
|
||||||
|
assert!(!expand_tilde_with(Path::new(s), buf).starts_with("//"));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_not_expanded(s: &str) {
|
||||||
|
let home = PathBuf::from("/home");
|
||||||
|
let expanded = expand_tilde_with(Path::new(s), Some(home));
|
||||||
|
assert!(expanded == Path::new(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn string_with_tilde() {
|
||||||
|
check_expanded("~");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn string_with_tilde_forward_slash() {
|
||||||
|
check_expanded("~/test/");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn string_with_tilde_double_forward_slash() {
|
||||||
|
check_expanded("~//test/");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn does_not_expand_tilde_if_tilde_is_not_first_character() {
|
||||||
|
check_not_expanded("1~1");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
#[test]
|
||||||
|
fn string_with_tilde_backslash() {
|
||||||
|
check_expanded("~\\test/test2/test3");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
#[test]
|
||||||
|
fn string_with_double_tilde_backslash() {
|
||||||
|
check_expanded("~\\\\test\\test2/test3");
|
||||||
|
}
|
||||||
|
}
|
4
crates/nu-path/src/util.rs
Normal file
4
crates/nu-path/src/util.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/// Trim trailing path separator from a string
|
||||||
|
pub fn trim_trailing_slash(s: &str) -> &str {
|
||||||
|
s.trim_end_matches(std::path::is_separator)
|
||||||
|
}
|
412
crates/nu-path/tests/canonicalize.rs
Normal file
412
crates/nu-path/tests/canonicalize.rs
Normal file
@ -0,0 +1,412 @@
|
|||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use nu_test_support::fs::Stub::EmptyFile;
|
||||||
|
use nu_test_support::playground::Playground;
|
||||||
|
|
||||||
|
use nu_path::{canonicalize, canonicalize_with};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canonicalize_path() {
|
||||||
|
Playground::setup("nu_path_test_1", |dirs, sandbox| {
|
||||||
|
sandbox.with_files(vec![EmptyFile("spam.txt")]);
|
||||||
|
|
||||||
|
let mut spam = dirs.test().clone();
|
||||||
|
spam.push("spam.txt");
|
||||||
|
|
||||||
|
let actual = canonicalize(spam).expect("Failed to canonicalize");
|
||||||
|
|
||||||
|
assert!(actual.ends_with("spam.txt"));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canonicalize_unicode_path() {
|
||||||
|
Playground::setup("nu_path_test_1", |dirs, sandbox| {
|
||||||
|
sandbox.with_files(vec![EmptyFile("🚒.txt")]);
|
||||||
|
|
||||||
|
let mut spam = dirs.test().clone();
|
||||||
|
spam.push("🚒.txt");
|
||||||
|
|
||||||
|
let actual = canonicalize(spam).expect("Failed to canonicalize");
|
||||||
|
|
||||||
|
assert!(actual.ends_with("🚒.txt"));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[ignore]
|
||||||
|
#[test]
|
||||||
|
fn canonicalize_non_utf8_path() {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canonicalize_path_relative_to() {
|
||||||
|
Playground::setup("nu_path_test_1", |dirs, sandbox| {
|
||||||
|
sandbox.with_files(vec![EmptyFile("spam.txt")]);
|
||||||
|
|
||||||
|
let actual = canonicalize_with("spam.txt", dirs.test()).expect("Failed to canonicalize");
|
||||||
|
let mut expected = dirs.test().clone();
|
||||||
|
expected.push("spam.txt");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canonicalize_unicode_path_relative_to_unicode_path_with_spaces() {
|
||||||
|
Playground::setup("nu_path_test_1", |dirs, sandbox| {
|
||||||
|
sandbox.mkdir("e-$ èрт🚒♞中片-j");
|
||||||
|
sandbox.with_files(vec![EmptyFile("e-$ èрт🚒♞中片-j/🚒.txt")]);
|
||||||
|
|
||||||
|
let mut relative_to = dirs.test().clone();
|
||||||
|
relative_to.push("e-$ èрт🚒♞中片-j");
|
||||||
|
|
||||||
|
let actual = canonicalize_with("🚒.txt", relative_to).expect("Failed to canonicalize");
|
||||||
|
let mut expected = dirs.test().clone();
|
||||||
|
expected.push("e-$ èрт🚒♞中片-j/🚒.txt");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[ignore]
|
||||||
|
#[test]
|
||||||
|
fn canonicalize_non_utf8_path_relative_to_non_utf8_path_with_spaces() {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canonicalize_absolute_path_relative_to() {
|
||||||
|
Playground::setup("nu_path_test_1", |dirs, sandbox| {
|
||||||
|
sandbox.with_files(vec![EmptyFile("spam.txt")]);
|
||||||
|
|
||||||
|
let mut absolute_path = dirs.test().clone();
|
||||||
|
absolute_path.push("spam.txt");
|
||||||
|
|
||||||
|
let actual = canonicalize_with(&absolute_path, "non/existent/directory")
|
||||||
|
.expect("Failed to canonicalize");
|
||||||
|
let expected = absolute_path;
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canonicalize_dot() {
|
||||||
|
let actual = canonicalize(".").expect("Failed to canonicalize");
|
||||||
|
let expected = std::env::current_dir().expect("Could not get current directory");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canonicalize_many_dots() {
|
||||||
|
let actual = canonicalize("././/.//////./././//.///").expect("Failed to canonicalize");
|
||||||
|
let expected = std::env::current_dir().expect("Could not get current directory");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canonicalize_path_with_dot_relative_to() {
|
||||||
|
Playground::setup("nu_path_test_1", |dirs, sandbox| {
|
||||||
|
sandbox.with_files(vec![EmptyFile("spam.txt")]);
|
||||||
|
|
||||||
|
let actual = canonicalize_with("./spam.txt", dirs.test()).expect("Failed to canonicalize");
|
||||||
|
let mut expected = dirs.test().clone();
|
||||||
|
expected.push("spam.txt");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canonicalize_path_with_many_dots_relative_to() {
|
||||||
|
Playground::setup("nu_path_test_1", |dirs, sandbox| {
|
||||||
|
sandbox.with_files(vec![EmptyFile("spam.txt")]);
|
||||||
|
|
||||||
|
let actual = canonicalize_with("././/.//////./././//.////spam.txt", dirs.test())
|
||||||
|
.expect("Failed to canonicalize");
|
||||||
|
let mut expected = dirs.test().clone();
|
||||||
|
expected.push("spam.txt");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canonicalize_double_dot() {
|
||||||
|
let actual = canonicalize("..").expect("Failed to canonicalize");
|
||||||
|
let cwd = std::env::current_dir().expect("Could not get current directory");
|
||||||
|
let expected = cwd
|
||||||
|
.parent()
|
||||||
|
.expect("Could not get parent of current directory");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canonicalize_path_with_double_dot_relative_to() {
|
||||||
|
Playground::setup("nu_path_test_1", |dirs, sandbox| {
|
||||||
|
sandbox.mkdir("foo");
|
||||||
|
sandbox.with_files(vec![EmptyFile("spam.txt")]);
|
||||||
|
|
||||||
|
let actual =
|
||||||
|
canonicalize_with("foo/../spam.txt", dirs.test()).expect("Failed to canonicalize");
|
||||||
|
let mut expected = dirs.test().clone();
|
||||||
|
expected.push("spam.txt");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canonicalize_path_with_many_double_dots_relative_to() {
|
||||||
|
Playground::setup("nu_path_test_1", |dirs, sandbox| {
|
||||||
|
sandbox.mkdir("foo/bar/baz");
|
||||||
|
sandbox.with_files(vec![EmptyFile("spam.txt")]);
|
||||||
|
|
||||||
|
let actual = canonicalize_with("foo/bar/baz/../../../spam.txt", dirs.test())
|
||||||
|
.expect("Failed to canonicalize");
|
||||||
|
let mut expected = dirs.test().clone();
|
||||||
|
expected.push("spam.txt");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canonicalize_ndots() {
|
||||||
|
let actual = canonicalize("...").expect("Failed to canonicalize");
|
||||||
|
let cwd = std::env::current_dir().expect("Could not get current directory");
|
||||||
|
let expected = cwd
|
||||||
|
.parent()
|
||||||
|
.expect("Could not get parent of current directory")
|
||||||
|
.parent()
|
||||||
|
.expect("Could not get parent of a parent of current directory");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canonicalize_path_with_3_ndots_relative_to() {
|
||||||
|
Playground::setup("nu_path_test_1", |dirs, sandbox| {
|
||||||
|
sandbox.mkdir("foo/bar");
|
||||||
|
sandbox.with_files(vec![EmptyFile("spam.txt")]);
|
||||||
|
|
||||||
|
let actual =
|
||||||
|
canonicalize_with("foo/bar/.../spam.txt", dirs.test()).expect("Failed to canonicalize");
|
||||||
|
let mut expected = dirs.test().clone();
|
||||||
|
expected.push("spam.txt");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canonicalize_path_with_many_3_ndots_relative_to() {
|
||||||
|
Playground::setup("nu_path_test_1", |dirs, sandbox| {
|
||||||
|
sandbox.mkdir("foo/bar/baz/eggs/sausage/bacon");
|
||||||
|
sandbox.with_files(vec![EmptyFile("spam.txt")]);
|
||||||
|
|
||||||
|
let actual = canonicalize_with(
|
||||||
|
"foo/bar/baz/eggs/sausage/bacon/.../.../.../spam.txt",
|
||||||
|
dirs.test(),
|
||||||
|
)
|
||||||
|
.expect("Failed to canonicalize");
|
||||||
|
let mut expected = dirs.test().clone();
|
||||||
|
expected.push("spam.txt");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canonicalize_path_with_4_ndots_relative_to() {
|
||||||
|
Playground::setup("nu_path_test_1", |dirs, sandbox| {
|
||||||
|
sandbox.mkdir("foo/bar/baz");
|
||||||
|
sandbox.with_files(vec![EmptyFile("spam.txt")]);
|
||||||
|
|
||||||
|
let actual = canonicalize_with("foo/bar/baz/..../spam.txt", dirs.test())
|
||||||
|
.expect("Failed to canonicalize");
|
||||||
|
let mut expected = dirs.test().clone();
|
||||||
|
expected.push("spam.txt");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canonicalize_path_with_many_4_ndots_relative_to() {
|
||||||
|
Playground::setup("nu_path_test_1", |dirs, sandbox| {
|
||||||
|
sandbox.mkdir("foo/bar/baz/eggs/sausage/bacon");
|
||||||
|
sandbox.with_files(vec![EmptyFile("spam.txt")]);
|
||||||
|
|
||||||
|
let actual = canonicalize_with(
|
||||||
|
"foo/bar/baz/eggs/sausage/bacon/..../..../spam.txt",
|
||||||
|
dirs.test(),
|
||||||
|
)
|
||||||
|
.expect("Failed to canonicalize");
|
||||||
|
let mut expected = dirs.test().clone();
|
||||||
|
expected.push("spam.txt");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canonicalize_path_with_way_too_many_dots_relative_to() {
|
||||||
|
Playground::setup("nu_path_test_1", |dirs, sandbox| {
|
||||||
|
sandbox.mkdir("foo/bar/baz/eggs/sausage/bacon/vikings");
|
||||||
|
sandbox.with_files(vec![EmptyFile("spam.txt")]);
|
||||||
|
|
||||||
|
let mut relative_to = dirs.test().clone();
|
||||||
|
relative_to.push("foo/bar/baz/eggs/sausage/bacon/vikings");
|
||||||
|
|
||||||
|
let actual = canonicalize_with("././..////././...///././.....///spam.txt", relative_to)
|
||||||
|
.expect("Failed to canonicalize");
|
||||||
|
let mut expected = dirs.test().clone();
|
||||||
|
expected.push("spam.txt");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canonicalize_unicode_path_with_way_too_many_dots_relative_to_unicode_path_with_spaces() {
|
||||||
|
Playground::setup("nu_path_test_1", |dirs, sandbox| {
|
||||||
|
sandbox.mkdir("foo/áčěéí +šř=é/baz/eggs/e-$ èрт🚒♞中片-j/bacon/öäöä öäöä");
|
||||||
|
sandbox.with_files(vec![EmptyFile("🚒.txt")]);
|
||||||
|
|
||||||
|
let mut relative_to = dirs.test().clone();
|
||||||
|
relative_to.push("foo/áčěéí +šř=é/baz/eggs/e-$ èрт🚒♞中片-j/bacon/öäöä öäöä");
|
||||||
|
|
||||||
|
let actual = canonicalize_with("././..////././...///././.....///🚒.txt", relative_to)
|
||||||
|
.expect("Failed to canonicalize");
|
||||||
|
let mut expected = dirs.test().clone();
|
||||||
|
expected.push("🚒.txt");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canonicalize_tilde() {
|
||||||
|
let tilde_path = "~";
|
||||||
|
|
||||||
|
let actual = canonicalize(tilde_path).expect("Failed to canonicalize");
|
||||||
|
|
||||||
|
assert!(actual.is_absolute());
|
||||||
|
assert!(!actual.starts_with("~"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canonicalize_tilde_relative_to() {
|
||||||
|
let tilde_path = "~";
|
||||||
|
|
||||||
|
let actual =
|
||||||
|
canonicalize_with(tilde_path, "non/existent/path").expect("Failed to canonicalize");
|
||||||
|
|
||||||
|
assert!(actual.is_absolute());
|
||||||
|
assert!(!actual.starts_with("~"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
#[test]
|
||||||
|
fn canonicalize_symlink() {
|
||||||
|
Playground::setup("nu_path_test_1", |dirs, sandbox| {
|
||||||
|
sandbox.with_files(vec![EmptyFile("spam.txt")]);
|
||||||
|
sandbox.symlink("spam.txt", "link_to_spam.txt");
|
||||||
|
|
||||||
|
let mut symlink_path = dirs.test().clone();
|
||||||
|
symlink_path.push("link_to_spam.txt");
|
||||||
|
|
||||||
|
let actual = canonicalize(symlink_path).expect("Failed to canonicalize");
|
||||||
|
let mut expected = dirs.test().clone();
|
||||||
|
expected.push("spam.txt");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
#[test]
|
||||||
|
fn canonicalize_symlink_relative_to() {
|
||||||
|
Playground::setup("nu_path_test_1", |dirs, sandbox| {
|
||||||
|
sandbox.with_files(vec![EmptyFile("spam.txt")]);
|
||||||
|
sandbox.symlink("spam.txt", "link_to_spam.txt");
|
||||||
|
|
||||||
|
let actual =
|
||||||
|
canonicalize_with("link_to_spam.txt", dirs.test()).expect("Failed to canonicalize");
|
||||||
|
let mut expected = dirs.test().clone();
|
||||||
|
expected.push("spam.txt");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
#[cfg(not(windows))] // seems like Windows symlink requires existing file or dir
|
||||||
|
#[test]
|
||||||
|
fn canonicalize_symlink_loop_relative_to_should_fail() {
|
||||||
|
Playground::setup("nu_path_test_1", |dirs, sandbox| {
|
||||||
|
// sandbox.with_files(vec![EmptyFile("spam.txt")]);
|
||||||
|
sandbox.symlink("spam.txt", "link_to_spam.txt");
|
||||||
|
sandbox.symlink("link_to_spam.txt", "spam.txt");
|
||||||
|
|
||||||
|
let actual = canonicalize_with("link_to_spam.txt", dirs.test());
|
||||||
|
|
||||||
|
assert!(actual.is_err());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
#[test]
|
||||||
|
fn canonicalize_nested_symlink_relative_to() {
|
||||||
|
Playground::setup("nu_path_test_1", |dirs, sandbox| {
|
||||||
|
sandbox.with_files(vec![EmptyFile("spam.txt")]);
|
||||||
|
sandbox.symlink("spam.txt", "link_to_spam.txt");
|
||||||
|
sandbox.symlink("link_to_spam.txt", "link_to_link_to_spam.txt");
|
||||||
|
|
||||||
|
let actual = canonicalize_with("link_to_link_to_spam.txt", dirs.test())
|
||||||
|
.expect("Failed to canonicalize");
|
||||||
|
let mut expected = dirs.test().clone();
|
||||||
|
expected.push("spam.txt");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
#[test]
|
||||||
|
fn canonicalize_nested_symlink_within_symlink_dir_relative_to() {
|
||||||
|
Playground::setup("nu_path_test_1", |dirs, sandbox| {
|
||||||
|
sandbox.mkdir("foo/bar/baz");
|
||||||
|
sandbox.with_files(vec![EmptyFile("foo/bar/baz/spam.txt")]);
|
||||||
|
sandbox.symlink("foo/bar/baz/spam.txt", "foo/bar/link_to_spam.txt");
|
||||||
|
sandbox.symlink("foo/bar/link_to_spam.txt", "foo/link_to_link_to_spam.txt");
|
||||||
|
sandbox.symlink("foo", "link_to_foo");
|
||||||
|
|
||||||
|
let actual = canonicalize_with("link_to_foo/link_to_link_to_spam.txt", dirs.test())
|
||||||
|
.expect("Failed to canonicalize");
|
||||||
|
let mut expected = dirs.test().clone();
|
||||||
|
expected.push("foo/bar/baz/spam.txt");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canonicalize_should_fail() {
|
||||||
|
let path = Path::new("/foo/bar/baz"); // hopefully, this path does not exist
|
||||||
|
|
||||||
|
assert!(canonicalize(path).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canonicalize_with_should_fail() {
|
||||||
|
let relative_to = "/foo";
|
||||||
|
let path = "bar/baz";
|
||||||
|
|
||||||
|
assert!(canonicalize_with(path, relative_to).is_err());
|
||||||
|
}
|
294
crates/nu-path/tests/expand_path.rs
Normal file
294
crates/nu-path/tests/expand_path.rs
Normal file
@ -0,0 +1,294 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use nu_test_support::playground::Playground;
|
||||||
|
|
||||||
|
use nu_path::{expand_path, expand_path_with};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expand_path_with_and_without_relative() {
|
||||||
|
let relative_to = "/foo/bar";
|
||||||
|
let path = "../..";
|
||||||
|
let full_path = "/foo/bar/../..";
|
||||||
|
|
||||||
|
assert_eq!(expand_path(full_path), expand_path_with(path, relative_to),);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expand_path_with_relative() {
|
||||||
|
let relative_to = "/foo/bar";
|
||||||
|
let path = "../..";
|
||||||
|
|
||||||
|
assert_eq!(PathBuf::from("/"), expand_path_with(path, relative_to),);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expand_path_no_change() {
|
||||||
|
let path = "/foo/bar";
|
||||||
|
|
||||||
|
let actual = expand_path(&path);
|
||||||
|
|
||||||
|
assert_eq!(actual, PathBuf::from(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expand_unicode_path_no_change() {
|
||||||
|
Playground::setup("nu_path_test_1", |dirs, _| {
|
||||||
|
let mut spam = dirs.test().clone();
|
||||||
|
spam.push("🚒.txt");
|
||||||
|
|
||||||
|
let actual = expand_path(spam);
|
||||||
|
let mut expected = dirs.test().clone();
|
||||||
|
expected.push("🚒.txt");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[ignore]
|
||||||
|
#[test]
|
||||||
|
fn expand_non_utf8_path() {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expand_path_relative_to() {
|
||||||
|
Playground::setup("nu_path_test_1", |dirs, _| {
|
||||||
|
let actual = expand_path_with("spam.txt", dirs.test());
|
||||||
|
let mut expected = dirs.test().clone();
|
||||||
|
expected.push("spam.txt");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expand_unicode_path_relative_to_unicode_path_with_spaces() {
|
||||||
|
Playground::setup("nu_path_test_1", |dirs, _| {
|
||||||
|
let mut relative_to = dirs.test().clone();
|
||||||
|
relative_to.push("e-$ èрт🚒♞中片-j");
|
||||||
|
|
||||||
|
let actual = expand_path_with("🚒.txt", relative_to);
|
||||||
|
let mut expected = dirs.test().clone();
|
||||||
|
expected.push("e-$ èрт🚒♞中片-j/🚒.txt");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[ignore]
|
||||||
|
#[test]
|
||||||
|
fn expand_non_utf8_path_relative_to_non_utf8_path_with_spaces() {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expand_absolute_path_relative_to() {
|
||||||
|
Playground::setup("nu_path_test_1", |dirs, _| {
|
||||||
|
let mut absolute_path = dirs.test().clone();
|
||||||
|
absolute_path.push("spam.txt");
|
||||||
|
|
||||||
|
let actual = expand_path_with(&absolute_path, "non/existent/directory");
|
||||||
|
let expected = absolute_path;
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expand_path_dot() {
|
||||||
|
let actual = expand_path(".");
|
||||||
|
let expected = PathBuf::from(".");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expand_path_many_dots() {
|
||||||
|
let actual = expand_path("././/.//////./././//.///");
|
||||||
|
let expected = PathBuf::from("././././././.");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expand_path_with_dot_relative_to() {
|
||||||
|
Playground::setup("nu_path_test_1", |dirs, _| {
|
||||||
|
let actual = expand_path_with("./spam.txt", dirs.test());
|
||||||
|
let mut expected = dirs.test().clone();
|
||||||
|
expected.push("spam.txt");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expand_path_double_dot() {
|
||||||
|
let actual = expand_path("..");
|
||||||
|
let expected = PathBuf::from("..");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expand_path_dot_double_dot() {
|
||||||
|
let actual = expand_path("./..");
|
||||||
|
let expected = PathBuf::from("./..");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expand_path_double_dot_dot() {
|
||||||
|
let actual = expand_path("../.");
|
||||||
|
let expected = PathBuf::from("..");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expand_path_with_many_dots_relative_to() {
|
||||||
|
Playground::setup("nu_path_test_1", |dirs, _| {
|
||||||
|
let actual = expand_path_with("././/.//////./././//.////spam.txt", dirs.test());
|
||||||
|
let mut expected = dirs.test().clone();
|
||||||
|
expected.push("spam.txt");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expand_path_with_double_dot_relative_to() {
|
||||||
|
Playground::setup("nu_path_test_1", |dirs, _| {
|
||||||
|
let actual = expand_path_with("foo/../spam.txt", dirs.test());
|
||||||
|
let mut expected = dirs.test().clone();
|
||||||
|
expected.push("spam.txt");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expand_path_with_many_double_dots_relative_to() {
|
||||||
|
Playground::setup("nu_path_test_1", |dirs, _| {
|
||||||
|
let actual = expand_path_with("foo/bar/baz/../../../spam.txt", dirs.test());
|
||||||
|
let mut expected = dirs.test().clone();
|
||||||
|
expected.push("spam.txt");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expand_path_ndots() {
|
||||||
|
let actual = expand_path("...");
|
||||||
|
let mut expected = PathBuf::from("..");
|
||||||
|
expected.push("..");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expand_normal_path_ndots() {
|
||||||
|
let actual = expand_path("foo/bar/baz/...");
|
||||||
|
let expected = PathBuf::from("foo");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expand_path_with_3_ndots_relative_to() {
|
||||||
|
Playground::setup("nu_path_test_1", |dirs, _| {
|
||||||
|
let actual = expand_path_with("foo/bar/.../spam.txt", dirs.test());
|
||||||
|
let mut expected = dirs.test().clone();
|
||||||
|
expected.push("spam.txt");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expand_path_with_many_3_ndots_relative_to() {
|
||||||
|
Playground::setup("nu_path_test_1", |dirs, _| {
|
||||||
|
let actual = expand_path_with(
|
||||||
|
"foo/bar/baz/eggs/sausage/bacon/.../.../.../spam.txt",
|
||||||
|
dirs.test(),
|
||||||
|
);
|
||||||
|
let mut expected = dirs.test().clone();
|
||||||
|
expected.push("spam.txt");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expand_path_with_4_ndots_relative_to() {
|
||||||
|
Playground::setup("nu_path_test_1", |dirs, _| {
|
||||||
|
let actual = expand_path_with("foo/bar/baz/..../spam.txt", dirs.test());
|
||||||
|
let mut expected = dirs.test().clone();
|
||||||
|
expected.push("spam.txt");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expand_path_with_many_4_ndots_relative_to() {
|
||||||
|
Playground::setup("nu_path_test_1", |dirs, _| {
|
||||||
|
let actual = expand_path_with(
|
||||||
|
"foo/bar/baz/eggs/sausage/bacon/..../..../spam.txt",
|
||||||
|
dirs.test(),
|
||||||
|
);
|
||||||
|
let mut expected = dirs.test().clone();
|
||||||
|
expected.push("spam.txt");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expand_path_with_way_too_many_dots_relative_to() {
|
||||||
|
Playground::setup("nu_path_test_1", |dirs, _| {
|
||||||
|
let mut relative_to = dirs.test().clone();
|
||||||
|
relative_to.push("foo/bar/baz/eggs/sausage/bacon/vikings");
|
||||||
|
|
||||||
|
let actual = expand_path_with("././..////././...///././.....///spam.txt", relative_to);
|
||||||
|
let mut expected = dirs.test().clone();
|
||||||
|
expected.push("spam.txt");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expand_unicode_path_with_way_too_many_dots_relative_to_unicode_path_with_spaces() {
|
||||||
|
Playground::setup("nu_path_test_1", |dirs, _| {
|
||||||
|
let mut relative_to = dirs.test().clone();
|
||||||
|
relative_to.push("foo/áčěéí +šř=é/baz/eggs/e-$ èрт🚒♞中片-j/bacon/öäöä öäöä");
|
||||||
|
|
||||||
|
let actual = expand_path_with("././..////././...///././.....///🚒.txt", relative_to);
|
||||||
|
let mut expected = dirs.test().clone();
|
||||||
|
expected.push("🚒.txt");
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expand_path_tilde() {
|
||||||
|
let tilde_path = "~";
|
||||||
|
|
||||||
|
let actual = expand_path(tilde_path);
|
||||||
|
|
||||||
|
assert!(actual.is_absolute());
|
||||||
|
assert!(!actual.starts_with("~"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expand_path_tilde_relative_to() {
|
||||||
|
let tilde_path = "~";
|
||||||
|
|
||||||
|
let actual = expand_path_with(tilde_path, "non/existent/path");
|
||||||
|
|
||||||
|
assert!(actual.is_absolute());
|
||||||
|
assert!(!actual.starts_with("~"));
|
||||||
|
}
|
3
crates/nu-path/tests/mod.rs
Normal file
3
crates/nu-path/tests/mod.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
mod canonicalize;
|
||||||
|
mod expand_path;
|
||||||
|
mod util;
|
45
crates/nu-path/tests/util.rs
Normal file
45
crates/nu-path/tests/util.rs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
use nu_path::trim_trailing_slash;
|
||||||
|
use std::path::MAIN_SEPARATOR;
|
||||||
|
|
||||||
|
/// Helper function that joins string literals with '/' or '\', based on the host OS
|
||||||
|
fn join_path_sep(pieces: &[&str]) -> String {
|
||||||
|
let sep_string = String::from(MAIN_SEPARATOR);
|
||||||
|
pieces.join(&sep_string)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn trims_trailing_slash_without_trailing_slash() {
|
||||||
|
let path = join_path_sep(&["some", "path"]);
|
||||||
|
|
||||||
|
let actual = trim_trailing_slash(&path);
|
||||||
|
|
||||||
|
assert_eq!(actual, &path)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn trims_trailing_slash() {
|
||||||
|
let path = join_path_sep(&["some", "path", ""]);
|
||||||
|
|
||||||
|
let actual = trim_trailing_slash(&path);
|
||||||
|
let expected = join_path_sep(&["some", "path"]);
|
||||||
|
|
||||||
|
assert_eq!(actual, &expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn trims_many_trailing_slashes() {
|
||||||
|
let path = join_path_sep(&["some", "path", "", "", "", ""]);
|
||||||
|
|
||||||
|
let actual = trim_trailing_slash(&path);
|
||||||
|
let expected = join_path_sep(&["some", "path"]);
|
||||||
|
|
||||||
|
assert_eq!(actual, &expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn trims_trailing_slash_empty() {
|
||||||
|
let path = String::from(MAIN_SEPARATOR);
|
||||||
|
let actual = trim_trailing_slash(&path);
|
||||||
|
|
||||||
|
assert_eq!(actual, "")
|
||||||
|
}
|
@ -11,6 +11,7 @@ doctest = false
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-errors = { version = "0.36.1", path="../nu-errors" }
|
nu-errors = { version = "0.36.1", path="../nu-errors" }
|
||||||
|
nu-path = { version="0.36.1", path="../nu-path" }
|
||||||
nu-protocol = { path="../nu-protocol", version = "0.36.1" }
|
nu-protocol = { path="../nu-protocol", version = "0.36.1" }
|
||||||
nu-source = { path="../nu-source", version = "0.36.1" }
|
nu-source = { path="../nu-source", version = "0.36.1" }
|
||||||
nu-value-ext = { version = "0.36.1", path="../nu-value-ext" }
|
nu-value-ext = { version = "0.36.1", path="../nu-value-ext" }
|
||||||
|
@ -30,7 +30,7 @@ macro_rules! nu {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let test_bins = $crate::fs::binaries();
|
let test_bins = $crate::fs::binaries();
|
||||||
let test_bins = dunce::canonicalize(&test_bins).unwrap_or_else(|e| {
|
let test_bins = nu_path::canonicalize(&test_bins).unwrap_or_else(|e| {
|
||||||
panic!(
|
panic!(
|
||||||
"Couldn't canonicalize dummy binaries path {}: {:?}",
|
"Couldn't canonicalize dummy binaries path {}: {:?}",
|
||||||
test_bins.display(),
|
test_bins.display(),
|
||||||
@ -111,7 +111,7 @@ macro_rules! nu_with_plugins {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let test_bins = $crate::fs::binaries();
|
let test_bins = $crate::fs::binaries();
|
||||||
let test_bins = dunce::canonicalize(&test_bins).unwrap_or_else(|e| {
|
let test_bins = nu_path::canonicalize(&test_bins).unwrap_or_else(|e| {
|
||||||
panic!(
|
panic!(
|
||||||
"Couldn't canonicalize dummy binaries path {}: {:?}",
|
"Couldn't canonicalize dummy binaries path {}: {:?}",
|
||||||
test_bins.display(),
|
test_bins.display(),
|
||||||
|
@ -78,7 +78,7 @@ impl<'a> Playground<'a> {
|
|||||||
std::fs::create_dir(PathBuf::from(&nuplay_dir)).expect("can not create directory");
|
std::fs::create_dir(PathBuf::from(&nuplay_dir)).expect("can not create directory");
|
||||||
|
|
||||||
let fixtures = fs::fixtures();
|
let fixtures = fs::fixtures();
|
||||||
let fixtures = dunce::canonicalize(fixtures.clone()).unwrap_or_else(|e| {
|
let fixtures = nu_path::canonicalize(fixtures.clone()).unwrap_or_else(|e| {
|
||||||
panic!(
|
panic!(
|
||||||
"Couldn't canonicalize fixtures path {}: {:?}",
|
"Couldn't canonicalize fixtures path {}: {:?}",
|
||||||
fixtures.display(),
|
fixtures.display(),
|
||||||
@ -97,7 +97,7 @@ impl<'a> Playground<'a> {
|
|||||||
|
|
||||||
let playground_root = playground.root.path();
|
let playground_root = playground.root.path();
|
||||||
|
|
||||||
let test = dunce::canonicalize(playground_root.join(topic)).unwrap_or_else(|e| {
|
let test = nu_path::canonicalize(playground_root.join(topic)).unwrap_or_else(|e| {
|
||||||
panic!(
|
panic!(
|
||||||
"Couldn't canonicalize test path {}: {:?}",
|
"Couldn't canonicalize test path {}: {:?}",
|
||||||
playground_root.join(topic).display(),
|
playground_root.join(topic).display(),
|
||||||
@ -105,7 +105,7 @@ impl<'a> Playground<'a> {
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
let root = dunce::canonicalize(playground_root).unwrap_or_else(|e| {
|
let root = nu_path::canonicalize(playground_root).unwrap_or_else(|e| {
|
||||||
panic!(
|
panic!(
|
||||||
"Couldn't canonicalize tests root path {}: {:?}",
|
"Couldn't canonicalize tests root path {}: {:?}",
|
||||||
playground_root.display(),
|
playground_root.display(),
|
||||||
|
@ -6,7 +6,7 @@ use hamcrest2::assert_that;
|
|||||||
use hamcrest2::prelude::*;
|
use hamcrest2::prelude::*;
|
||||||
|
|
||||||
fn path(p: &Path) -> PathBuf {
|
fn path(p: &Path) -> PathBuf {
|
||||||
dunce::canonicalize(p)
|
nu_path::canonicalize(p)
|
||||||
.unwrap_or_else(|e| panic!("Couldn't canonicalize path {}: {:?}", p.display(), e))
|
.unwrap_or_else(|e| panic!("Couldn't canonicalize path {}: {:?}", p.display(), e))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,6 @@ fn picks_up_env_keys_when_entering_trusted_directory() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "directories-support")]
|
|
||||||
#[cfg(feature = "which-support")]
|
#[cfg(feature = "which-support")]
|
||||||
#[test]
|
#[test]
|
||||||
#[serial]
|
#[serial]
|
||||||
@ -433,7 +432,6 @@ fn given_a_hierachy_of_trusted_directories_going_back_restores_overwritten_varia
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "directories-support")]
|
|
||||||
#[cfg(feature = "which-support")]
|
#[cfg(feature = "which-support")]
|
||||||
#[test]
|
#[test]
|
||||||
#[serial]
|
#[serial]
|
||||||
@ -488,7 +486,6 @@ fn local_config_env_var_present_and_removed_correctly() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "directories-support")]
|
|
||||||
#[cfg(feature = "which-support")]
|
#[cfg(feature = "which-support")]
|
||||||
#[test]
|
#[test]
|
||||||
#[serial]
|
#[serial]
|
||||||
@ -555,7 +552,6 @@ fn local_config_env_var_gets_overwritten() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "directories-support")]
|
|
||||||
#[cfg(feature = "which-support")]
|
#[cfg(feature = "which-support")]
|
||||||
#[test]
|
#[test]
|
||||||
#[serial]
|
#[serial]
|
||||||
@ -604,7 +600,6 @@ fn autoenv_test_entry_scripts() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "directories-support")]
|
|
||||||
#[cfg(feature = "which-support")]
|
#[cfg(feature = "which-support")]
|
||||||
#[test]
|
#[test]
|
||||||
#[serial]
|
#[serial]
|
||||||
|
@ -5,7 +5,6 @@ use nu_test_support::{nu, pipeline};
|
|||||||
use hamcrest2::assert_that;
|
use hamcrest2::assert_that;
|
||||||
use hamcrest2::prelude::*;
|
use hamcrest2::prelude::*;
|
||||||
|
|
||||||
#[cfg(feature = "directories-support")]
|
|
||||||
#[cfg(feature = "which-support")]
|
#[cfg(feature = "which-support")]
|
||||||
mod environment;
|
mod environment;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user