From c7beaf261fc205af98ee2df8afde820a264fd81a Mon Sep 17 00:00:00 2001 From: Robert Collins Date: Tue, 27 Apr 2021 08:02:24 +0200 Subject: [PATCH 1/4] Require retry 1.2.1 to fix use of Display with Error types --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index da639c31b5..4483e26429 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1663,9 +1663,9 @@ dependencies = [ [[package]] name = "retry" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c15ef4789108d066d7fd85dcec330eab9b8e51244275922a9b7161afc4f46dda" +checksum = "d0ee4a654b43dd7e3768be7a1c0fc20e90f0a84b72a60ffb6c11e1cae2545c2e" dependencies = [ "rand 0.7.3", ] diff --git a/Cargo.toml b/Cargo.toml index 336746e221..483ef0987c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,7 +68,7 @@ zstd = "0.6" [dependencies.retry] default-features = false features = ["random"] -version = "1" +version = "1.2.1" [dependencies.rs_tracing] features = ["rs_tracing"] From fdb85c4f659d53ba7d3638950447237f63934fdd Mon Sep 17 00:00:00 2001 From: Robert Collins Date: Sun, 15 Mar 2020 22:41:10 +1300 Subject: [PATCH 2/4] Add anyhow and thiserror anyhow provides significantly better error tooling than error_chain, without the derivation support, thiserror fills that gap. --- Cargo.lock | 309 +++++++++++++++++++++++------------------------------ Cargo.toml | 5 +- 2 files changed, 139 insertions(+), 175 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4483e26429..2389c70483 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,9 +11,9 @@ dependencies = [ [[package]] name = "adler" -version = "0.2.3" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aes" @@ -67,9 +67,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" +checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b" [[package]] name = "async-compression" @@ -209,9 +209,9 @@ checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe" [[package]] name = "byteorder" -version = "1.4.2" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" @@ -303,12 +303,6 @@ dependencies = [ "cc", ] -[[package]] -name = "const_fn" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6" - [[package]] name = "core-foundation" version = "0.9.1" @@ -369,12 +363,11 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.1" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1aaa739f95311c2c7887a76863f500026092fb1dce0161dab577e559ef3569d" +checksum = "2584f639eb95fea8c798496315b297cf81b9b58b6d30ab066a75455333cf4b12" dependencies = [ "cfg-if 1.0.0", - "const_fn", "crossbeam-utils", "lazy_static", "memoffset", @@ -383,9 +376,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d" +checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49" dependencies = [ "autocfg 1.0.1", "cfg-if 1.0.0", @@ -394,24 +387,24 @@ dependencies = [ [[package]] name = "curl" -version = "0.4.34" +version = "0.4.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e268162af1a5fe89917ae25ba3b0a77c8da752bdc58e7dbb4f15b91fbd33756e" +checksum = "5a872858e9cb9e3b96c80dd78774ad9e32e44d3b05dc31e142b858d14aebc82c" dependencies = [ "curl-sys", "libc", "openssl-probe", "openssl-sys", "schannel", - "socket2", + "socket2 0.3.19", "winapi", ] [[package]] name = "curl-sys" -version = "0.4.40+curl-7.75.0" +version = "0.4.41+curl-7.75.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffafc1c35958318bd7fdd0582995ce4c72f4f461a8e70499ccee83a619fd562" +checksum = "0ec466abd277c7cab2905948f3e94d10bc4963f1f5d47921c1cc4ffd2028fe65" dependencies = [ "cc", "libc", @@ -598,7 +591,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" dependencies = [ "backtrace", - "version_check 0.9.2", + "version_check 0.9.3", ] [[package]] @@ -658,30 +651,30 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2d31b7ec7efab6eefc7c57233bb10b847986139d88cc2f5a02a1ae6871a1846" +checksum = "8c2dd2df839b57db9ab69c2c9d8f3e8c81984781937fe2807dc6dcf3b2ad2939" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79e5145dde8da7d1b3892dad07a9c98fc04bc39892b1ecc9692cf53e2b780a65" +checksum = "15496a72fabf0e62bdc3df11a59a3787429221dd0710ba8ef163d6f7a9112c94" [[package]] name = "futures-io" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28be053525281ad8259d47e4de5de657b25e7bac113458555bb4b70bc6870500" +checksum = "d71c2c65c57704c32f5241c1223167c2c3294fd34ac020c807ddbe6db287ba59" [[package]] name = "futures-macro" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c287d25add322d9f9abdcdc5927ca398917996600182178774032e9f8258fedd" +checksum = "ea405816a5139fb39af82c2beb921d52143f556038378d6db21183a5c37fbfb7" dependencies = [ "proc-macro-hack", "proc-macro2", @@ -691,24 +684,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf5c69029bda2e743fddd0582d1083951d65cc9539aebf8812f36c3491342d6" +checksum = "85754d98985841b7d4f5e8e6fbfa4a4ac847916893ec511a2917ccd8525b8bb3" [[package]] name = "futures-task" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13de07eb8ea81ae445aca7b69f5f7bf15d7bf4912d8ca37d6645c77ae8a58d86" -dependencies = [ - "once_cell", -] +checksum = "fa189ef211c15ee602667a6fcfe1c1fd9e07d42250d2156382820fba33c9df80" [[package]] name = "futures-util" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "632a8cd0f2a4b3fdea1657f08bde063848c3bd00f9bbf6e256b8be78802e624b" +checksum = "1812c7ab8aedf8d6f2701a43e1243acdbcc2b36ab26e2ad421eb99ac963d96d1" dependencies = [ "futures-core", "futures-io", @@ -729,7 +719,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" dependencies = [ "typenum", - "version_check 0.9.2", + "version_check 0.9.3", ] [[package]] @@ -782,17 +772,11 @@ dependencies = [ "syn", ] -[[package]] -name = "glob" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" - [[package]] name = "h2" -version = "0.3.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b67e66362108efccd8ac053abafc8b7a8d86a37e6e48fc4f6f7485eb5e9e6a5" +checksum = "fc018e188373e2777d0ef2467ebff62a08e66c3f5857b23c8fbec3018210dc00" dependencies = [ "bytes", "fnv", @@ -805,7 +789,6 @@ dependencies = [ "tokio", "tokio-util", "tracing", - "tracing-futures", ] [[package]] @@ -825,9 +808,9 @@ dependencies = [ [[package]] name = "hex" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "home" @@ -850,12 +833,13 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2861bd27ee074e5ee891e8b539837a9430012e249d7f0ca2d795650f579c1994" +checksum = "5dfb77c123b4e2f72a2069aeae0b4b4949cc7e966df277813fc16347e7549737" dependencies = [ "bytes", "http", + "pin-project-lite", ] [[package]] @@ -872,9 +856,9 @@ checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" [[package]] name = "hyper" -version = "0.14.4" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8e946c2b1349055e0b72ae281b238baf1a3ea7307c7e9f9d64673bdd9c26ac7" +checksum = "8bf09f61b52cfcf4c00de50df88ae423d6c02354e385a86341133b5338630ad1" dependencies = [ "bytes", "futures-channel", @@ -887,7 +871,7 @@ dependencies = [ "httpdate", "itoa", "pin-project", - "socket2", + "socket2 0.4.0", "tokio", "tower-service", "tracing", @@ -941,9 +925,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.6.1" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b" +checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" dependencies = [ "autocfg 1.0.1", "hashbrown", @@ -955,15 +939,6 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135" -[[package]] -name = "itertools" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "0.4.7" @@ -981,9 +956,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.47" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cfb73131c35423a367daf8cbd24100af0d077668c8c2943f0e7dd775fef0f65" +checksum = "2d99f9e3e84b8f67f846ef5b4cbbc3b1c29f6c759fcbce6f01aa0e73d932a24c" dependencies = [ "wasm-bindgen", ] @@ -1005,9 +980,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.86" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c" +checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41" [[package]] name = "libm" @@ -1072,9 +1047,9 @@ checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" [[package]] name = "memoffset" -version = "0.6.1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87" +checksum = "f83fb6581e8ed1f85fd45c116db8405483899489e38406156c25eb743554361d" dependencies = [ "autocfg 1.0.1", ] @@ -1087,9 +1062,9 @@ checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" [[package]] name = "miniz_oxide" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" dependencies = [ "adler", "autocfg 1.0.1", @@ -1097,9 +1072,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.7.8" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc250d6848c90d719ea2ce34546fb5df7af1d3fd189d10bf7bad80bfcebecd95" +checksum = "cf80d3e903b34e0bd7282b218398aec54e082c840d9baf8339e0080a0c542956" dependencies = [ "libc", "log", @@ -1110,11 +1085,10 @@ dependencies = [ [[package]] name = "miow" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" dependencies = [ - "socket2", "winapi", ] @@ -1244,9 +1218,9 @@ checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4" [[package]] name = "once_cell" -version = "1.5.2" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" +checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" [[package]] name = "opaque-debug" @@ -1265,15 +1239,15 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.32" +version = "0.10.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "038d43985d1ddca7a9900630d8cd031b56e4794eecc2e9ea39dd17aa04399a70" +checksum = "a61075b62a23fef5a29815de7536d940aa35ce96d18ce0cc5076272db678a577" dependencies = [ "bitflags", "cfg-if 1.0.0", "foreign-types", - "lazy_static", "libc", + "once_cell", "openssl-sys", ] @@ -1285,18 +1259,18 @@ checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" [[package]] name = "openssl-src" -version = "111.14.0+1.1.1j" +version = "111.15.0+1.1.1k" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "055b569b5bd7e5462a1700f595c7c7d487691d73b5ce064176af7f9f0cbb80a9" +checksum = "b1a5f6ae2ac04393b217ea9f700cd04fa9bf3d93fae2872069f3d15d908af70a" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.60" +version = "0.9.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "921fc71883267538946025deffb622905ecad223c28efbfdef9bb59a0175f3e6" +checksum = "313752393519e876837e09e1fa183ddef0be7735868dced3196f4472d536277f" dependencies = [ "autocfg 1.0.1", "cc", @@ -1383,18 +1357,18 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96fa8ebb90271c4477f144354485b8068bd8f6b78b428b01ba892ca26caf0b63" +checksum = "bc174859768806e91ae575187ada95c91a29e96a98dc5d2cd9a1fed039501ba6" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "758669ae3558c6f74bd2a18b41f7ac0b5a195aea6639d6a9b5e5d1ad5ba24c0b" +checksum = "a490329918e856ed1b083f244e3bfe2d8c4f336407e4ea9e1a9f479ff09049e5" dependencies = [ "proc-macro2", "quote", @@ -1403,9 +1377,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.4" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439697af366c49a6d0a010c56a0d97685bc140ce0d377b13a2ea2aa42d64a827" +checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905" [[package]] name = "pin-utils" @@ -1439,9 +1413,9 @@ checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" [[package]] name = "proc-macro2" -version = "1.0.24" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" dependencies = [ "unicode-xid", ] @@ -1583,21 +1557,20 @@ dependencies = [ [[package]] name = "regex" -version = "1.4.3" +version = "1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a" +checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19" dependencies = [ "aho-corasick", "memchr", "regex-syntax", - "thread_local", ] [[package]] name = "regex-syntax" -version = "0.6.22" +version = "0.6.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581" +checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548" [[package]] name = "remove_dir_all" @@ -1622,9 +1595,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0460542b551950620a3648c6aa23318ac6b3cd779114bd873209e6e8b5eb1c34" +checksum = "bf12057f289428dbf5c591c74bf10392e4a8003f993405a902f20117019022d4" dependencies = [ "async-compression", "base64 0.13.0", @@ -1796,6 +1769,7 @@ dependencies = [ "tar", "tempfile", "term", + "thiserror", "threadpool", "toml", "url", @@ -1856,9 +1830,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.0.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1759c2e3c8580017a484a7ac56d3abc5a6c1feadf88db2f3633f12ae4268c69" +checksum = "3670b1d2fdf6084d192bc71ead7aabe6c06aa2ea3fbd9cc3ac111fa5c2b1bd84" dependencies = [ "bitflags", "core-foundation", @@ -1869,9 +1843,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.0.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f99b9d5e26d2a71633cc4f2ebae7cc9f874044e0c351a27e17892d76dce5678b" +checksum = "3676258fd3cfe2c9a0ec99ce3038798d847ce3e4bb17746373eb9f0f1ac16339" dependencies = [ "core-foundation-sys", "libc", @@ -1897,18 +1871,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.123" +version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae" +checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.123" +version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31" +checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" dependencies = [ "proc-macro2", "quote", @@ -1917,9 +1891,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.62" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea1c6153794552ea7cf7cf63b1231a25de00ec90db326ba6264440fa08e31486" +checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" dependencies = [ "itoa", "ryu", @@ -2016,6 +1990,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "socket2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e3dfc207c526015c632472a77be09cf1b6e46866581aecae5cc38fb4235dea2" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "spin" version = "0.5.2" @@ -2058,9 +2042,9 @@ checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2" [[package]] name = "syn" -version = "1.0.60" +version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" +checksum = "3ce15dd3ed8aa2f8eeac4716d6ef5ab58b6b9256db41d7e1a0224c2788e8fd87" dependencies = [ "proc-macro2", "quote", @@ -2153,15 +2137,6 @@ dependencies = [ "syn", ] -[[package]] -name = "thread_local" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" -dependencies = [ - "once_cell", -] - [[package]] name = "threadpool" version = "1.8.1" @@ -2183,9 +2158,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317cca572a0e89c3ce0ca1f1bdc9369547fe318a683418e42ac8f59d14701023" +checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342" dependencies = [ "tinyvec_macros", ] @@ -2198,9 +2173,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.2.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8190d04c665ea9e6b6a0dc45523ade572c088d2e6566244c1122671dbf4ae3a" +checksum = "134af885d758d645f0f0505c9a8b3f9bf8a348fd822e112ab5248138348f1722" dependencies = [ "autocfg 1.0.1", "bytes", @@ -2246,9 +2221,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.6.3" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebb7cb2f00c5ae8df755b252306272cd1790d39728363936e01827e11f0b017b" +checksum = "5143d049e85af7fbc36f5454d990e62c2df705b3589f123b71f441b6b59f443f" dependencies = [ "bytes", "futures-core", @@ -2275,9 +2250,9 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" [[package]] name = "tracing" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f77d3842f76ca899ff2dbcf231c5c65813dea431301d6eb686279c15c4464f12" +checksum = "01ebdc2bb4498ab1ab5f5b73c5803825e60199229ccba0698170e3be0e7f959f" dependencies = [ "cfg-if 1.0.0", "pin-project-lite", @@ -2293,16 +2268,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "tracing-futures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" -dependencies = [ - "pin-project", - "tracing", -] - [[package]] name = "try-lock" version = "0.2.3" @@ -2331,9 +2296,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" +checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" [[package]] name = "ucd-trie" @@ -2347,7 +2312,7 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" dependencies = [ - "version_check 0.9.2", + "version_check 0.9.3", ] [[package]] @@ -2418,9 +2383,9 @@ checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" [[package]] name = "version_check" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" [[package]] name = "wait-timeout" @@ -2433,9 +2398,9 @@ dependencies = [ [[package]] name = "walkdir" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" dependencies = [ "same-file", "winapi", @@ -2466,9 +2431,9 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "wasm-bindgen" -version = "0.2.70" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55c0f7123de74f0dab9b7d00fd614e7b19349cd1e2f5252bbe9b1754b59433be" +checksum = "83240549659d187488f91f33c0f8547cbfef0b2088bc470c116d1d260ef623d9" dependencies = [ "cfg-if 1.0.0", "serde", @@ -2478,9 +2443,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.70" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bc45447f0d4573f3d65720f636bbcc3dd6ce920ed704670118650bcd47764c7" +checksum = "ae70622411ca953215ca6d06d3ebeb1e915f0f6613e3b495122878d7ebec7dae" dependencies = [ "bumpalo", "lazy_static", @@ -2493,9 +2458,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.20" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3de431a2910c86679c34283a33f66f4e4abd7e0aec27b6669060148872aadf94" +checksum = "81b8b767af23de6ac18bf2168b690bed2902743ddf0fb39252e36f9e2bfc63ea" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -2505,9 +2470,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.70" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b8853882eef39593ad4174dd26fc9865a64e84026d223f63bb2c42affcbba2c" +checksum = "3e734d91443f177bfdb41969de821e15c516931c3c3db3d318fa1b68975d0f6f" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2515,9 +2480,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.70" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4133b5e7f2a531fa413b3a1695e925038a05a71cf67e87dafa295cb645a01385" +checksum = "d53739ff08c8a68b0fdbcd54c372b8ab800b1449ab3c9d706503bc7dd1621b2c" dependencies = [ "proc-macro2", "quote", @@ -2528,15 +2493,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.70" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd4945e4943ae02d15c13962b38a5b1e81eadd4b71214eee75af64a4d6a4fd64" +checksum = "d9a543ae66aa233d14bb765ed9af4a33e81b8b58d1584cf1b47ff8cd0b9e4489" [[package]] name = "web-sys" -version = "0.3.47" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c40dc691fc48003eba817c38da7113c15698142da971298003cac3ef175680b3" +checksum = "a905d57e488fec8861446d3393670fb50d27a262344013181c2cdf9fff5481be" dependencies = [ "js-sys", "wasm-bindgen", @@ -2653,18 +2618,18 @@ dependencies = [ [[package]] name = "zstd" -version = "0.6.0+zstd.1.4.8" +version = "0.6.1+zstd.1.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4e44664feba7f2f1a9f300c1f6157f2d1bfc3c15c6f3cf4beabf3f5abe9c237" +checksum = "5de55e77f798f205d8561b8fe2ef57abfb6e0ff2abe7fd3c089e119cdb5631a3" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" -version = "3.0.0+zstd.1.4.8" +version = "3.0.1+zstd.1.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9447afcd795693ad59918c7bbffe42fdd6e467d708f3537e3dc14dc598c573f" +checksum = "1387cabcd938127b30ce78c4bf00b30387dddf704e3f0881dbc4ff62b5566f8c" dependencies = [ "libc", "zstd-sys", @@ -2672,12 +2637,10 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "1.4.19+zstd.1.4.8" +version = "1.4.20+zstd.1.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec24a9273d24437afb8e71b16f3d9a5d569193cccdb7896213b59f552f387674" +checksum = "ebd5b733d7cf2d9447e2c3e76a5589b4f5e5ae065c22a2bc0b023cbc331b6c8e" dependencies = [ "cc", - "glob", - "itertools", "libc", ] diff --git a/Cargo.toml b/Cargo.toml index 483ef0987c..5fb4ff6626 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ anyhow = "1.0.31" cfg-if = "1.0" chrono = "0.4" clap = "2" -download = { path = "download", default-features = false } +download = {path = "download", default-features = false} effective-limits = "0.5.2" error-chain = "0.12" flate2 = "1" @@ -43,6 +43,7 @@ opener = "0.4.0" # Used by `curl` or `reqwest` backend although it isn't imported # by our rustup. openssl = {version = "0.10", optional = true} +pgp = {version = "0.7", default-features = false} pulldown-cmark = {version = "0.8", default-features = false} rand = "0.8" regex = "1" @@ -55,9 +56,9 @@ sha2 = "0.9" strsim = "0.10" tar = "0.4.26" tempfile = "3.1" -pgp = {version = "0.7", default-features = false} # FIXME(issue #1818, #1826, and friends) term = "=0.5.1" +thiserror = "1.0" threadpool = "1" toml = "0.5" url = "2.1" From aa84b40559ea9d40e85b720fba214c77aac1f5dd Mon Sep 17 00:00:00 2001 From: Robert Collins Date: Mon, 16 Mar 2020 23:03:57 +1300 Subject: [PATCH 3/4] Introduce SyncError SyncError provides adaption to wrap around error_chain, both where we use it, and if any libraries rustup is using use it. Once introduced we can start incrementally migrating function and error at at time to anyhow without requiring a single big bang commit. --- src/errors.rs | 145 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 140 insertions(+), 5 deletions(-) diff --git a/src/errors.rs b/src/errors.rs index d9ee516e65..b867eda33c 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,16 +1,20 @@ #![allow(clippy::large_enum_variant)] #![allow(deprecated)] // because of `Error::description` deprecation in `error_chain` -use crate::dist::dist::Profile; -use crate::dist::manifest::{Component, Manifest}; -use crate::dist::temp; -use crate::{component_for_bin, Toolchain}; -use error_chain::error_chain; use std::ffi::OsString; +use std::fmt::{self, Debug, Display}; use std::io::{self, Write}; use std::path::PathBuf; +use std::sync::{Arc, Mutex, MutexGuard, Weak}; + +use error_chain::error_chain; use url::Url; +use crate::dist::dist::Profile; +use crate::dist::manifest::{Component, Manifest}; +use crate::dist::temp; +use crate::{component_for_bin, Toolchain}; + pub const TOOLSTATE_MSG: &str = "If you require these components, please install and use the latest successful build version,\n\ which you can find at .\n\nAfter determining \ @@ -415,6 +419,137 @@ error_chain! { } } +/// Inspired by failure::SyncFailure, but not identical. +/// +/// SyncError does not grant full safety: it will panic when errors are used +/// across threads (e.g. by threaded error logging libraries). This could be +/// fixed, but as we don't do that within rustup, it is not needed. If using +/// this code elsewhere, just hunt down and remove the unchecked unwraps. +pub struct SyncError { + inner: Arc>, + proxy: Option>, +} + +impl SyncError { + pub fn new(err: T) -> Self { + let arc = Arc::new(Mutex::new(err)); + let proxy = match arc.lock().unwrap().source() { + None => None, + Some(source) => Some(CauseProxy::new(source, Arc::downgrade(&arc), 0)), + }; + + SyncError { inner: arc, proxy } + } + + pub fn maybe(r: std::result::Result) -> std::result::Result { + match r { + Ok(v) => Ok(v), + Err(e) => Err(SyncError::new(e)), + } + } + + pub fn unwrap(self) -> T { + Arc::try_unwrap(self.inner).unwrap().into_inner().unwrap() + } + + pub fn as_ref(&self) -> MutexGuard<'_, T> { + Arc::as_ref(&self.inner).lock().unwrap() + } +} + +impl std::error::Error for SyncError { + #[cfg(backtrace)] + fn backtrace(&self) -> Option<&Backtrace> {} + + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.proxy.as_ref().map(|x| x as _) + } +} + +impl Display for SyncError +where + T: Display, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.inner.lock().unwrap().fmt(f) + } +} + +impl Debug for SyncError +where + T: Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.inner.lock().unwrap().fmt(f) + } +} + +struct CauseProxy { + inner: Weak>, + next: Option>>, + depth: u32, +} + +impl CauseProxy { + fn new(err: &dyn std::error::Error, weak: Weak>, depth: u32) -> Self { + // Can't allocate an object, or mutate the proxy safely during source(), + // so we just take the hit at construction, recursively. We can't hold + // references outside the mutex at all, so instead we remember how many + // steps to get to this proxy. And if some error chain plays tricks, the + // user gets both pieces. + CauseProxy { + inner: weak.clone(), + depth, + next: match err.source() { + None => None, + Some(source) => Some(Box::new(CauseProxy::new(source, weak, depth + 1))), + }, + } + } + + fn with_instance(&self, f: F) -> R + where + F: FnOnce(&(dyn std::error::Error + 'static)) -> R, + { + let arc = self.inner.upgrade().unwrap(); + { + let e = arc.lock().unwrap(); + let mut source = e.source().unwrap(); + for _ in 0..self.depth { + source = source.source().unwrap(); + } + f(source) + } + } +} + +impl std::error::Error for CauseProxy { + #[cfg(backtrace)] + fn backtrace(&self) -> Option<&Backtrace> {} + + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.next.as_ref().map(|x| x as _) + } +} + +impl Display for CauseProxy +where + T: Display + std::error::Error, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.with_instance(|i| std::fmt::Display::fmt(&i, f)) + } +} + +impl Debug for CauseProxy +where + T: Debug + std::error::Error, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.with_instance(|i| std::fmt::Debug::fmt(&i, f)) + } +} + fn valid_profile_names() -> String { Profile::names() .iter() From cf6c9d62124a48224e19708d0581eaa99b378bea Mon Sep 17 00:00:00 2001 From: Robert Collins Date: Mon, 16 Mar 2020 23:13:15 +1300 Subject: [PATCH 4/4] Convert code to anyhow The main UI impact is that using the anyhow formatters removes some custom code at a slight UI change - rather than repeating the err! macro output around each item in the backtrace, we get it just once (see the comment in common.rs). --- Cargo.lock | 55 +-- Cargo.toml | 1 - download/Cargo.toml | 22 +- download/src/errors.rs | 41 +- download/src/lib.rs | 130 +++--- src/bin/rustup-init.rs | 21 +- src/cli/common.rs | 66 ++- src/cli/errors.rs | 95 +--- src/cli/proxy_mode.rs | 4 +- src/cli/rustup_mode.rs | 118 +++-- src/cli/self_update.rs | 75 ++-- src/cli/self_update/shell.rs | 5 +- src/cli/self_update/unix.rs | 18 +- src/cli/self_update/windows.rs | 50 +-- src/cli/setup_mode.rs | 2 +- src/cli/topical_doc.rs | 12 +- src/command.rs | 4 +- src/config.rs | 112 +++-- src/diskio/mod.rs | 5 +- src/diskio/test.rs | 3 +- src/dist/component/components.rs | 35 +- src/dist/component/package.rs | 40 +- src/dist/component/transaction.rs | 19 +- src/dist/config.rs | 6 +- src/dist/dist.rs | 330 +++++++++----- src/dist/download.rs | 48 +- src/dist/manifest.rs | 33 +- src/dist/manifestation.rs | 60 +-- src/dist/notifications.rs | 3 +- src/dist/signatures.rs | 14 +- src/dist/temp.rs | 75 +--- src/errors.rs | 699 ++++-------------------------- src/fallback_settings.rs | 20 +- src/install.rs | 8 +- src/notifications.rs | 4 +- src/settings.rs | 13 +- src/test.rs | 6 +- src/toolchain.rs | 136 +++--- src/utils/raw.rs | 71 +-- src/utils/toml_utils.rs | 29 +- src/utils/utils.rs | 193 ++++----- tests/cli-inst-interactive.rs | 2 + tests/cli-v2.rs | 4 +- tests/dist.rs | 48 +- tests/dist_install.rs | 9 +- tests/dist_manifest.rs | 6 +- tests/dist_transactions.rs | 38 +- 47 files changed, 1115 insertions(+), 1673 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2389c70483..04716feafd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,14 +1,5 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -[[package]] -name = "addr2line" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a55f82cfe485775d02112886f4169bde0c5894d75e79ead7eafe7e40a25e45f7" -dependencies = [ - "gimli", -] - [[package]] name = "adler" version = "1.0.2" @@ -107,20 +98,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" -[[package]] -name = "backtrace" -version = "0.3.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d117600f438b1707d4e4ae15d3595657288f8235a0eb593e80ecc98ab34e1bc" -dependencies = [ - "addr2line", - "cfg-if 1.0.0", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - [[package]] name = "base64" version = "0.12.3" @@ -512,13 +489,14 @@ dependencies = [ name = "download" version = "0.6.9" dependencies = [ + "anyhow", "curl", "env_proxy", - "error-chain", "hyper", "lazy_static", "reqwest", "tempfile", + "thiserror", "tokio", "url", ] @@ -584,16 +562,6 @@ dependencies = [ "url", ] -[[package]] -name = "error-chain" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" -dependencies = [ - "backtrace", - "version_check 0.9.3", -] - [[package]] name = "filetime" version = "0.2.14" @@ -744,12 +712,6 @@ dependencies = [ "wasi 0.10.2+wasi-snapshot-preview1", ] -[[package]] -name = "gimli" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce" - [[package]] name = "git-testament" version = "0.1.9" @@ -1210,12 +1172,6 @@ dependencies = [ "libc", ] -[[package]] -name = "object" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4" - [[package]] name = "once_cell" version = "1.7.2" @@ -1702,12 +1658,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rustc-demangle" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232" - [[package]] name = "rustls" version = "0.19.0" @@ -1744,7 +1694,6 @@ dependencies = [ "clap", "download", "effective-limits", - "error-chain", "flate2", "git-testament", "home", diff --git a/Cargo.toml b/Cargo.toml index 5fb4ff6626..13e3b5012c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,6 @@ chrono = "0.4" clap = "2" download = {path = "download", default-features = false} effective-limits = "0.5.2" -error-chain = "0.12" flate2 = "1" git-testament = "0.1.4" home = {git = "https://github.com/rbtcollins/home", rev = "a243ee2fbee6022c57d56f5aa79aefe194eabe53"} diff --git a/download/Cargo.toml b/download/Cargo.toml index 55bedfdc1a..bfba6a171d 100644 --- a/download/Cargo.toml +++ b/download/Cargo.toml @@ -1,11 +1,10 @@ [package] -name = "download" -version = "0.6.9" +authors = ["Brian Anderson "] edition = "2018" -authors = [ "Brian Anderson " ] - license = "MIT/Apache-2.0" +name = "download" +version = "0.6.9" [features] @@ -17,14 +16,15 @@ reqwest-default-tls = ["reqwest/default-tls"] reqwest-rustls-tls = ["reqwest/rustls-tls-native-roots"] [dependencies] -error-chain = "0.12" +anyhow = "1.0.31" +curl = {version = "0.4.11", optional = true} +env_proxy = {version = "0.4.1", optional = true} +lazy_static = {version = "1.0", optional = true} +reqwest = {version = "0.11", default-features = false, features = ["blocking", "gzip", "socks"], optional = true} +thiserror = "1.0" url = "2.1" -curl = { version = "0.4.11", optional = true } -env_proxy = { version = "0.4.1", optional = true } -lazy_static = { version = "1.0", optional = true } -reqwest = { version = "0.11", default-features = false, features = ["blocking", "gzip", "socks"], optional = true } [dev-dependencies] -hyper = { version = "0.14", default-features = false, features = ["tcp", "server"] } +hyper = {version = "0.14", default-features = false, features = ["tcp", "server"]} tempfile = "3" -tokio = { version = "1", default-features = false, features = ["sync"] } +tokio = {version = "1", default-features = false, features = ["sync"]} diff --git a/download/src/errors.rs b/download/src/errors.rs index 8a006b9978..63dcd4d98e 100644 --- a/download/src/errors.rs +++ b/download/src/errors.rs @@ -1,25 +1,20 @@ -#![allow(deprecated)] // because of `Error::description` deprecation in `error_chain` -use error_chain::error_chain; +use thiserror::Error; -error_chain! { - links { } - - foreign_links { - Io(std::io::Error); - Reqwest(::reqwest::Error) #[cfg(feature = "reqwest-backend")]; - } - - errors { - HttpStatus(e: u32) { - description("http request returned an unsuccessful status code") - display("http request returned an unsuccessful status code: {}", e) - } - FileNotFound { - description("file not found") - } - BackendUnavailable(be: &'static str) { - description("download backend unavailable") - display("download backend '{}' unavailable", be) - } - } +#[derive(Debug, Error)] +pub enum DownloadError { + #[error("http request returned an unsuccessful status code: {0}")] + HttpStatus(u32), + #[error("file not found")] + FileNotFound, + #[error("download backend '{0}' unavailable")] + BackendUnavailable(&'static str), + #[error("{0}")] + Message(String), + #[error(transparent)] + IoError(#[from] std::io::Error), + #[cfg(feature = "reqwest-backend")] + #[error(transparent)] + Reqwest(#[from] ::reqwest::Error), + #[error(transparent)] + CurlError(#[from] curl::Error), } diff --git a/download/src/lib.rs b/download/src/lib.rs index 90ca04e816..b1046ea93e 100644 --- a/download/src/lib.rs +++ b/download/src/lib.rs @@ -2,6 +2,9 @@ #![deny(rust_2018_idioms)] use std::path::Path; + +use anyhow::Context; +pub use anyhow::Result; use url::Url; mod errors; @@ -84,7 +87,7 @@ pub fn download_to_path_with_backend( .write(true) .create(true) .open(&path) - .chain_err(|| "error opening file for download")?; + .context("error opening file for download")?; possible_partial.seek(SeekFrom::End(0))?; @@ -95,7 +98,7 @@ pub fn download_to_path_with_backend( .write(true) .create(true) .open(&path) - .chain_err(|| "error creating file for download")?, + .context("error creating file for download")?, 0, ) }; @@ -106,7 +109,7 @@ pub fn download_to_path_with_backend( if let Event::DownloadDataReceived(data) = event { file.borrow_mut() .write_all(data) - .chain_err(|| "unable to write download to disk")?; + .context("unable to write download to disk")?; } match callback { Some(cb) => cb(event), @@ -116,14 +119,14 @@ pub fn download_to_path_with_backend( file.borrow_mut() .sync_data() - .chain_err(|| "unable to sync download to disk")?; + .context("unable to sync download to disk")?; Ok(()) }() .map_err(|e| { // TODO: We currently clear up the cached download on any error, should we restrict it to a subset? remove_file(path) - .chain_err(|| "cleaning up cached downloads") + .context("cleaning up cached downloads") .unwrap(); e }) @@ -133,14 +136,17 @@ pub fn download_to_path_with_backend( /// stack via libcurl #[cfg(feature = "curl-backend")] pub mod curl { - use super::Event; - use crate::errors::*; - use curl::easy::Easy; use std::cell::RefCell; use std::str; use std::time::Duration; + + use anyhow::{Context, Result}; + use curl::easy::Easy; use url::Url; + use super::Event; + use crate::errors::*; + pub fn download( url: &Url, resume_from: u64, @@ -155,17 +161,11 @@ pub mod curl { EASY.with(|handle| { let mut handle = handle.borrow_mut(); - handle - .url(&url.to_string()) - .chain_err(|| "failed to set url")?; - handle - .follow_location(true) - .chain_err(|| "failed to set follow redirects")?; + handle.url(&url.to_string())?; + handle.follow_location(true)?; if resume_from > 0 { - handle - .resume_from(resume_from) - .chain_err(|| "setting the range header for download resumption")?; + handle.resume_from(resume_from)?; } else { // an error here indicates that the range header isn't supported by underlying curl, // so there's nothing to "clear" - safe to ignore this error. @@ -173,9 +173,7 @@ pub mod curl { } // Take at most 30s to connect - handle - .connect_timeout(Duration::new(30, 0)) - .chain_err(|| "failed to set connect timeout")?; + handle.connect_timeout(Duration::new(30, 0))?; { let cberr = RefCell::new(None); @@ -184,38 +182,36 @@ pub mod curl { // Data callback for libcurl which is called with data that's // downloaded. We just feed it into our hasher and also write it out // to disk. - transfer - .write_function(|data| match callback(Event::DownloadDataReceived(data)) { + transfer.write_function(|data| { + match callback(Event::DownloadDataReceived(data)) { Ok(()) => Ok(data.len()), Err(e) => { *cberr.borrow_mut() = Some(e); Ok(0) } - }) - .chain_err(|| "failed to set write")?; + } + })?; // Listen for headers and parse out a `Content-Length` (case-insensitive) if it // comes so we know how much we're downloading. - transfer - .header_function(|header| { - if let Ok(data) = str::from_utf8(header) { - let prefix = "content-length: "; - if data.to_ascii_lowercase().starts_with(prefix) { - if let Ok(s) = data[prefix.len()..].trim().parse::() { - let msg = Event::DownloadContentLengthReceived(s + resume_from); - match callback(msg) { - Ok(()) => (), - Err(e) => { - *cberr.borrow_mut() = Some(e); - return false; - } + transfer.header_function(|header| { + if let Ok(data) = str::from_utf8(header) { + let prefix = "content-length: "; + if data.to_ascii_lowercase().starts_with(prefix) { + if let Ok(s) = data[prefix.len()..].trim().parse::() { + let msg = Event::DownloadContentLengthReceived(s + resume_from); + match callback(msg) { + Ok(()) => (), + Err(e) => { + *cberr.borrow_mut() = Some(e); + return false; } } } } - true - }) - .chain_err(|| "failed to set header")?; + } + true + })?; // If an error happens check to see if we had a filesystem error up // in `cberr`, but we always want to punt it up. @@ -227,9 +223,9 @@ pub mod curl { None => { // Otherwise, return the error from curl if e.is_file_couldnt_read_file() { - Err(e).chain_err(|| ErrorKind::FileNotFound) + Err(e).context(DownloadError::FileNotFound) } else { - Err(e).chain_err(|| "error during download") + Err(e).context("error during download")? } } } @@ -237,13 +233,11 @@ pub mod curl { } // If we didn't get a 20x or 0 ("OK" for files) then return an error - let code = handle - .response_code() - .chain_err(|| "failed to get response code")?; + let code = handle.response_code()?; match code { 0 | 200..=299 => {} _ => { - return Err(ErrorKind::HttpStatus(code).into()); + return Err(DownloadError::HttpStatus(code).into()); } }; @@ -254,16 +248,19 @@ pub mod curl { #[cfg(feature = "reqwest-backend")] pub mod reqwest_be { - use super::Event; - use super::TlsBackend; - use crate::errors::*; + use std::io; + use std::time::Duration; + + use anyhow::{anyhow, Context, Result}; use lazy_static::lazy_static; use reqwest::blocking::{Client, ClientBuilder, Response}; use reqwest::{header, Proxy}; - use std::io; - use std::time::Duration; use url::Url; + use super::Event; + use super::TlsBackend; + use crate::errors::*; + pub fn download( url: &Url, resume_from: u64, @@ -275,12 +272,11 @@ pub mod reqwest_be { return Ok(()); } - let mut res = - request(url, resume_from, tls).chain_err(|| "failed to make network request")?; + let mut res = request(url, resume_from, tls).context("failed to make network request")?; if !res.status().is_success() { let code: u16 = res.status().into(); - return Err(ErrorKind::HttpStatus(u32::from(code)).into()); + return Err(anyhow!(DownloadError::HttpStatus(u32::from(code)))); } let buffer_size = 0x10000; @@ -293,8 +289,7 @@ pub mod reqwest_be { } loop { - let bytes_read = - io::Read::read(&mut res, &mut buffer).chain_err(|| "error reading from socket")?; + let bytes_read = io::Read::read(&mut res, &mut buffer)?; if bytes_read != 0 { callback(Event::DownloadDataReceived(&buffer[0..bytes_read]))?; @@ -349,7 +344,11 @@ pub mod reqwest_be { env_proxy::for_url(url).to_url() } - fn request(url: &Url, resume_from: u64, backend: TlsBackend) -> Result { + fn request( + url: &Url, + resume_from: u64, + backend: TlsBackend, + ) -> Result { let client: &Client = match backend { #[cfg(feature = "reqwest-rustls-tls")] TlsBackend::Rustls => &CLIENT_RUSTLS_TLS, @@ -384,22 +383,21 @@ pub mod reqwest_be { if url.scheme() == "file" { let src = url .to_file_path() - .map_err(|_| Error::from(format!("bogus file url: '{}'", url)))?; + .map_err(|_| DownloadError::Message(format!("bogus file url: '{}'", url)))?; if !src.is_file() { // Because some of rustup's logic depends on checking // the error when a downloaded file doesn't exist, make // the file case return the same error value as the // network case. - return Err(ErrorKind::FileNotFound.into()); + return Err(anyhow!(DownloadError::FileNotFound)); } - let mut f = fs::File::open(src).chain_err(|| "unable to open downloaded file")?; + let mut f = fs::File::open(src).context("unable to open downloaded file")?; io::Seek::seek(&mut f, io::SeekFrom::Start(resume_from))?; let mut buffer = vec![0u8; 0x10000]; loop { - let bytes_read = io::Read::read(&mut f, &mut buffer) - .chain_err(|| "unable to read downloaded file")?; + let bytes_read = io::Read::read(&mut f, &mut buffer)?; if bytes_read == 0 { break; } @@ -423,8 +421,8 @@ pub mod curl { pub fn download( _url: &Url, _resume_from: u64, - _callback: &dyn Fn(Event<'_>) -> Result<()>, - ) -> Result<()> { + _callback: &dyn Fn(Event<'_>) -> Result<(), DownloadError>, + ) -> Result<(), DownloadError> { Err(ErrorKind::BackendUnavailable("curl").into()) } } @@ -440,9 +438,9 @@ pub mod reqwest_be { pub fn download( _url: &Url, _resume_from: u64, - _callback: &dyn Fn(Event<'_>) -> Result<()>, + _callback: &dyn Fn(Event<'_>) -> Result<(), DownloadError>, _tls: TlsBackend, - ) -> Result<()> { + ) -> Result<(), DownloadError> { Err(ErrorKind::BackendUnavailable("reqwest").into()) } } diff --git a/src/bin/rustup-init.rs b/src/bin/rustup-init.rs index 2b658bc19e..037cfcbfce 100644 --- a/src/bin/rustup-init.rs +++ b/src/bin/rustup-init.rs @@ -15,11 +15,11 @@ use std::path::PathBuf; +use anyhow::{anyhow, Result}; use cfg_if::cfg_if; use rs_tracing::*; use rustup::cli::common; -use rustup::cli::errors::*; use rustup::cli::proxy_mode; use rustup::cli::rustup_mode; #[cfg(windows)] @@ -33,8 +33,8 @@ use rustup::{DUP_TOOLS, TOOLS}; fn main() { let process = OSProcess::default(); with(Box::new(process), || match run_rustup() { - Err(ref e) => { - common::report_error(e); + Err(e) => { + common::report_error(&e); std::process::exit(1); } Ok(utils::ExitCode(c)) => std::process::exit(c), @@ -102,12 +102,21 @@ fn run_rustup_inner() -> Result { { proxy_mode::main() } else { - Err(ErrorKind::UnknownProxyName(n.to_string()).into()) + Err(anyhow!(format!( + "unknown proxy name: '{}'; valid proxy names are {}", + n, + TOOLS + .iter() + .chain(DUP_TOOLS.iter()) + .map(|s| format!("'{}'", s)) + .collect::>() + .join(", ") + ))) } } None => { // Weird case. No arg0, or it's unparsable. - Err(ErrorKind::NoExeName.into()) + Err(rustup::cli::errors::CLIError::NoExeName.into()) } } } @@ -119,7 +128,7 @@ fn do_recursion_guard() -> Result<()> { .and_then(|s| s.parse().ok()) .unwrap_or(0); if recursion_count > RUST_RECURSION_COUNT_MAX { - return Err(ErrorKind::InfiniteRecursion.into()); + return Err(anyhow!("infinite recursion detected")); } Ok(()) diff --git a/src/cli/common.rs b/src/cli/common.rs index b7e6fb66a4..97983ad8d9 100644 --- a/src/cli/common.rs +++ b/src/cli/common.rs @@ -6,11 +6,11 @@ use std::path::Path; use std::sync::Arc; use std::{cmp, env, iter}; +use anyhow::{anyhow, Context, Result}; use git_testament::{git_testament, render_testament}; use lazy_static::lazy_static; use term2::Terminal; -use super::errors::*; use super::self_update; use super::term2; use crate::dist::notifications as dist_notifications; @@ -105,10 +105,12 @@ pub fn read_line() -> Result { let stdin = process().stdin(); let stdin = stdin.lock(); let mut lines = stdin.lines(); - lines - .next() - .and_then(std::result::Result::ok) - .ok_or_else(|| "unable to read from stdin for confirmation".into()) + let lines = lines.next().transpose()?; + match lines { + None => Err(anyhow!("no lines found from stdin")), + Some(v) => Ok(v), + } + .context("unable to read from stdin for confirmation") } #[derive(Default)] @@ -170,18 +172,11 @@ pub fn set_globals(verbose: bool, quiet: bool) -> Result { }))?) } -pub fn show_channel_update( - cfg: &Cfg, - name: &str, - updated: crate::Result, -) -> Result<()> { +pub fn show_channel_update(cfg: &Cfg, name: &str, updated: Result) -> Result<()> { show_channel_updates(cfg, vec![(name.to_string(), updated)]) } -fn show_channel_updates( - cfg: &Cfg, - toolchains: Vec<(String, crate::Result)>, -) -> Result<()> { +fn show_channel_updates(cfg: &Cfg, toolchains: Vec<(String, Result)>) -> Result<()> { let data = toolchains.into_iter().map(|(name, result)| { let toolchain = cfg.get_toolchain(&name, false)?; let mut version: String = toolchain.rustc_version(); @@ -305,11 +300,7 @@ pub fn self_update_permitted(explicit: bool) -> Result { } } Err(env::VarError::NotPresent) => {} - Err(e) => { - return Err( - format!("Could not interrogate SNAP environment variable: {}", e).into(), - ) - } + Err(e) => return Err(e).context("Could not interrogate SNAP environment variable"), } let current_exe = env::current_exe()?; let current_exe_dir = current_exe.parent().expect("Rustup isn't in a directory‽"); @@ -362,8 +353,7 @@ where pub fn list_targets(toolchain: &Toolchain<'_>) -> Result { let mut t = term2::stdout(); - let distributable = DistributableToolchain::new(&toolchain) - .chain_err(|| crate::ErrorKind::ComponentsUnsupported(toolchain.name().to_string()))?; + let distributable = DistributableToolchain::new_for_components(&toolchain)?; let components = distributable.list_components()?; for component in components { if component.component.short_name_in_manifest() == "rust-std" { @@ -387,8 +377,7 @@ pub fn list_targets(toolchain: &Toolchain<'_>) -> Result { pub fn list_installed_targets(toolchain: &Toolchain<'_>) -> Result { let mut t = term2::stdout(); - let distributable = DistributableToolchain::new(&toolchain) - .chain_err(|| crate::ErrorKind::ComponentsUnsupported(toolchain.name().to_string()))?; + let distributable = DistributableToolchain::new_for_components(&toolchain)?; let components = distributable.list_components()?; for component in components { if component.component.short_name_in_manifest() == "rust-std" { @@ -407,8 +396,7 @@ pub fn list_installed_targets(toolchain: &Toolchain<'_>) -> Result) -> Result { let mut t = term2::stdout(); - let distributable = DistributableToolchain::new(&toolchain) - .chain_err(|| crate::ErrorKind::ComponentsUnsupported(toolchain.name().to_string()))?; + let distributable = DistributableToolchain::new_for_components(&toolchain)?; let components = distributable.list_components()?; for component in components { let name = component.name; @@ -426,8 +414,7 @@ pub fn list_components(toolchain: &Toolchain<'_>) -> Result { pub fn list_installed_components(toolchain: &Toolchain<'_>) -> Result { let mut t = term2::stdout(); - let distributable = DistributableToolchain::new(&toolchain) - .chain_err(|| crate::ErrorKind::ComponentsUnsupported(toolchain.name().to_string()))?; + let distributable = DistributableToolchain::new_for_components(&toolchain)?; let components = distributable.list_components()?; for component in components { if component.installed { @@ -499,7 +486,7 @@ pub fn list_toolchains(cfg: &Cfg, verbose: bool) -> Result { }; print_toolchain_path(cfg, &toolchain, if_default, if_override, verbose) - .expect("Failed to list toolchains' directories"); + .context("Failed to list toolchains' directories")?; } } Ok(utils::ExitCode(0)) @@ -615,22 +602,21 @@ fn show_backtrace() -> bool { false } -pub fn report_error(e: &Error) { - err!("{}", e); - - for e in e.iter().skip(1) { - err!("caused by: {}", e); - } - +pub fn report_error(e: &anyhow::Error) { + // NB: This shows one error: even for multiple causes and backtraces etc, + // rather than one per cause, and one for the backtrace. This seems like a + // reasonable tradeoff, but if we want to do differently, this is the code + // hunk to revisit, that and a similar build.rs auto-detect glue as anyhow + // has to detect when backtrace is available. if show_backtrace() { - if let Some(backtrace) = e.backtrace() { - err!("backtrace:"); - err!("{:?}", backtrace); - } + err!("{:?}", e); + } else { + err!("{:#}", e); } } -pub fn ignorable_error(error: super::errors::Error, no_prompt: bool) -> Result<()> { +pub fn ignorable_error(error: &'static str, no_prompt: bool) -> Result<()> { + let error = anyhow!(error); report_error(&error); if no_prompt { warn!("continuing (because the -y flag is set and the error is ignorable)"); diff --git a/src/cli/errors.rs b/src/cli/errors.rs index 2ee78e9e2f..9fd24271e5 100644 --- a/src/cli/errors.rs +++ b/src/cli/errors.rs @@ -1,95 +1,24 @@ #![allow(clippy::large_enum_variant)] #![allow(dead_code)] -#![allow(deprecated)] // because of `Error::description` deprecation in `error_chain` use std::io; use std::path::PathBuf; -use clap::Shell; -use error_chain::error_chain; use lazy_static::lazy_static; use regex::Regex; use strsim::damerau_levenshtein; - -use super::rustup_mode::CompletionCommand; -use crate::dist::temp; -use crate::{DUP_TOOLS, TOOLS}; - -error_chain! { - links { - Rustup(crate::Error, crate::ErrorKind); - } - - foreign_links { - Clap(clap::Error); - Temp(temp::Error); - Io(io::Error); - Term(term::Error); - } - - errors { - InvalidCustomToolchainName(t: String) { - display("invalid custom toolchain name: '{}'", t) - } - PermissionDenied { - description("permission denied") - } - ToolchainNotInstalled(t: String) { - description("toolchain is not installed") - display("toolchain '{}' is not installed", t) - } - InvalidToolchainName(t: String) { - description("invalid toolchain name") - display("invalid toolchain name: '{}'{}", t, maybe_suggest_toolchain(t)) - } - InfiniteRecursion { - description("infinite recursion detected") - } - NoExeName { - description("couldn't determine self executable name") - } - UnknownProxyName(n: String) { - description("unknown proxy name") - display( - "unknown proxy name: '{}'; valid proxy names are {}", - n, - valid_proxy_names(), - ) - } - NotSelfInstalled(p: PathBuf) { - description("rustup is not installed") - display("rustup is not installed at '{}'", p.display()) - } - WindowsUninstallMadness { - description("failure during windows uninstall") - } - UnsupportedCompletionShell(shell: Shell, cmd: CompletionCommand) { - description("completion script for shell not yet supported for tool") - display("{} does not currently support completions for {}", cmd, shell) - } - TargetAllSpecifiedWithTargets(t: Vec) { - description( - "the `all` target, which installs all available targets, \ - cannot be combined with other targets" - ) - display("`rustup target add {}` includes `all`", t.join(" ")) - } - WritingShellProfile { - path: PathBuf, - } { - description("could not amend shell profile") - display("could not amend shell profile: '{}'", path.display()) - } - } -} - -fn valid_proxy_names() -> String { - TOOLS - .iter() - .chain(DUP_TOOLS.iter()) - .map(|s| format!("'{}'", s)) - .collect::>() - .join(", ") +use thiserror::Error as ThisError; + +#[derive(ThisError, Debug)] +pub enum CLIError { + #[error("couldn't determine self executable name")] + NoExeName, + #[error("rustup is not installed at '{}'", .p.display())] + NotSelfInstalled { p: PathBuf }, + #[error("failure reading directory {}", .p.display())] + ReadDirError { p: PathBuf, source: io::Error }, + #[error("failure during windows uninstall")] + WindowsUninstallMadness, } fn maybe_suggest_toolchain(bad_name: &str) -> String { diff --git a/src/cli/proxy_mode.rs b/src/cli/proxy_mode.rs index f7c3aee8c3..5912421d78 100644 --- a/src/cli/proxy_mode.rs +++ b/src/cli/proxy_mode.rs @@ -2,6 +2,8 @@ use std::ffi::OsString; use std::path::PathBuf; use std::process; +use anyhow::Result; + use super::common::set_globals; use super::errors::*; use super::job; @@ -23,7 +25,7 @@ pub fn main() -> Result { .as_ref() .and_then(|a| a.file_name()) .and_then(std::ffi::OsStr::to_str); - let arg0 = arg0.ok_or(ErrorKind::NoExeName)?; + let arg0 = arg0.ok_or(CLIError::NoExeName)?; // Check for a toolchain specifier. let arg1 = args.next(); diff --git a/src/cli/rustup_mode.rs b/src/cli/rustup_mode.rs index 6ba0e365d0..1d7105300a 100644 --- a/src/cli/rustup_mode.rs +++ b/src/cli/rustup_mode.rs @@ -1,4 +1,3 @@ -use std::error::Error; use std::fmt; use std::io::Write; use std::iter; @@ -6,9 +5,9 @@ use std::path::{Path, PathBuf}; use std::process::Command; use std::str::FromStr; +use anyhow::{anyhow, bail, Error, Result}; use clap::{App, AppSettings, Arg, ArgGroup, ArgMatches, Shell, SubCommand}; -use super::errors::*; use super::help::*; use super::self_update; use super::term2; @@ -19,6 +18,7 @@ use crate::dist::dist::{ PartialTargetTriple, PartialToolchainDesc, Profile, TargetTriple, ToolchainDesc, }; use crate::dist::manifest::Component; +use crate::errors::RustupError; use crate::process; use crate::toolchain::{CustomToolchain, DistributableToolchain}; use crate::utils::utils; @@ -27,16 +27,22 @@ use crate::{command, Cfg, ComponentStatus, Toolchain}; fn handle_epipe(res: Result) -> Result { match res { - Err(Error(ErrorKind::Io(ref err), _)) if err.kind() == std::io::ErrorKind::BrokenPipe => { - Ok(utils::ExitCode(0)) + Err(e) => { + let root = e.root_cause(); + if let Some(io_err) = root.downcast_ref::() { + if io_err.kind() == std::io::ErrorKind::BrokenPipe { + return Ok(utils::ExitCode(0)); + } + } + Err(e) } res => res, } } -fn deprecated(instead: &str, cfg: &mut Cfg, matches: B, callee: F) -> Result +fn deprecated(instead: &str, cfg: &mut Cfg, matches: B, callee: F) -> R where - F: FnOnce(&mut Cfg, B) -> Result, + F: FnOnce(&mut Cfg, B) -> R, { (cfg.notify_handler)(Notification::PlainVerboseMessage( "Use of (currently) unmaintained command line interface.", @@ -183,12 +189,12 @@ pub fn main() -> Result { }, ("completions", Some(c)) => { if let Some(shell) = c.value_of("shell") { - output_completion_script( + (output_completion_script( shell.parse::().unwrap(), c.value_of("command") .and_then(|cmd| cmd.parse::().ok()) .unwrap_or(CompletionCommand::Rustup), - )? + ))? } else { unreachable!() } @@ -792,7 +798,7 @@ fn update_bare_triple_check(cfg: &Cfg, name: &str) -> Result<()> { writeln!(process().stdout(),)?; } } - return Err(ErrorKind::ToolchainNotInstalled(name.to_string()).into()); + bail!(RustupError::ToolchainNotInstalled(name.to_string())); } Ok(()) } @@ -818,7 +824,7 @@ fn default_bare_triple_check(cfg: &Cfg, name: &str) -> Result<()> { toolchain.name() )?; } - return Err(ErrorKind::ToolchainNotInstalled(name.to_string()).into()); + return Err(RustupError::ToolchainNotInstalled(name.to_string()).into()); } } Ok(()) @@ -834,7 +840,7 @@ fn default_(cfg: &Cfg, m: &ArgMatches<'_>) -> Result { let distributable = DistributableToolchain::new(&toolchain)?; Some(distributable.install_from_dist_if_not_installed()?) } else if !toolchain.exists() { - return Err(ErrorKind::ToolchainNotInstalled(toolchain.name().to_string()).into()); + return Err(RustupError::ToolchainNotInstalled(toolchain.name().to_string()).into()); } else { None }; @@ -857,7 +863,7 @@ fn default_(cfg: &Cfg, m: &ArgMatches<'_>) -> Result { } else { let default_toolchain: Result = cfg .get_default()? - .ok_or_else(|| "no default toolchain configured".into()); + .ok_or(anyhow!("no default toolchain configured")); writeln!(process().stdout(), "{} (default)", default_toolchain?)?; } @@ -901,7 +907,7 @@ fn check_updates(cfg: &Cfg) -> Result { } } } - (_, Err(err)) => return Err(err.into()), + (_, Err(err)) => return Err(err), } } @@ -986,7 +992,9 @@ fn update(cfg: &mut Cfg, m: &ArgMatches<'_>) -> Result { None, )?) } else if !toolchain.exists() { - return Err(ErrorKind::InvalidToolchainName(toolchain.name().to_string()).into()); + bail!(RustupError::InvalidToolchainName( + toolchain.name().to_string() + )); } else { None }; @@ -1031,11 +1039,10 @@ fn which(cfg: &Cfg, m: &ArgMatches<'_>) -> Result { let binary_path = if m.is_present("toolchain") { let toolchain = m.value_of("toolchain").unwrap(); cfg.which_binary_by_toolchain(toolchain, binary)? - .expect("binary not found") } else { cfg.which_binary(&utils::current_dir()?, binary)? - .expect("binary not found") - }; + } + .expect("binary not found"); utils::assert_is_file(&binary_path)?; @@ -1105,11 +1112,11 @@ fn show(cfg: &Cfg) -> Result { if show_installed_toolchains { let mut t = term2::stdout(); if show_headers { - print_header(&mut t, "installed toolchains")?; + print_header::(&mut t, "installed toolchains")?; } let default_name: Result = cfg .get_default()? - .ok_or_else(|| "no default toolchain configured".into()); + .ok_or(anyhow!("no default toolchain configured")); let default_name = default_name?; for it in installed_toolchains { if default_name == it { @@ -1126,7 +1133,7 @@ fn show(cfg: &Cfg) -> Result { if show_active_targets { let mut t = term2::stdout(); if show_headers { - print_header(&mut t, "installed targets for active toolchain")?; + print_header::(&mut t, "installed targets for active toolchain")?; } for at in active_targets { writeln!( @@ -1146,7 +1153,7 @@ fn show(cfg: &Cfg) -> Result { if show_active_toolchain { let mut t = term2::stdout(); if show_headers { - print_header(&mut t, "active toolchain")?; + print_header::(&mut t, "active toolchain")?; } match active_toolchain { @@ -1160,11 +1167,13 @@ fn show(cfg: &Cfg) -> Result { writeln!(t, "{}", toolchain.rustc_version())?; } }, - Err(crate::Error(crate::ErrorKind::ToolchainNotSelected, _)) => { - writeln!(t, "no active toolchain")?; - } Err(err) => { - if let Some(cause) = err.source() { + let root_cause = err.root_cause(); + if let Some(RustupError::ToolchainNotSelected) = + root_cause.downcast_ref::() + { + writeln!(t, "no active toolchain")?; + } else if let Some(cause) = err.source() { writeln!(t, "(error: {}, {})", err, cause)?; } else { writeln!(t, "(error: {})", err)?; @@ -1177,7 +1186,10 @@ fn show(cfg: &Cfg) -> Result { } } - fn print_header(t: &mut term2::StdoutTerminal, s: &str) -> Result<()> { + fn print_header(t: &mut term2::StdoutTerminal, s: &str) -> std::result::Result<(), E> + where + E: From + From, + { t.attr(term2::Attr::Bold)?; writeln!(t, "{}", s)?; writeln!(t, "{}", iter::repeat("-").take(s.len()).collect::())?; @@ -1193,8 +1205,15 @@ fn show_active_toolchain(cfg: &Cfg, m: &ArgMatches<'_>) -> Result {} - Err(e) => return Err(e.into()), + Err(e) => { + let root_cause = e.root_cause(); + if let Some(RustupError::ToolchainNotSelected) = + root_cause.downcast_ref::() + { + } else { + return Err(e); + } + } Ok((toolchain, reason)) => { if let Some(reason) = reason { writeln!(process().stdout(), "{} ({})", toolchain.name(), reason)?; @@ -1231,13 +1250,7 @@ fn target_add(cfg: &Cfg, m: &ArgMatches<'_>) -> Result { // isn't a feature yet. // list_components *and* add_component would both be inappropriate for // custom toolchains. - if toolchain.is_custom() { - return Err(crate::Error( - crate::ErrorKind::ComponentsUnsupported(toolchain.name().to_string()), - error_chain::State::default(), - ) - .into()); - } + let distributable = DistributableToolchain::new_for_components(&toolchain)?; let mut targets: Vec = m .values_of("target") @@ -1247,11 +1260,13 @@ fn target_add(cfg: &Cfg, m: &ArgMatches<'_>) -> Result { if targets.contains(&"all".to_string()) { if targets.len() != 1 { - return Err(ErrorKind::TargetAllSpecifiedWithTargets(targets).into()); + return Err(anyhow!( + "`rustup target add {}` includes `all`", + targets.join(" ") + )); } targets.clear(); - let distributable = DistributableToolchain::new(&toolchain)?; for component in distributable.list_components()? { if component.component.short_name_in_manifest() == "rust-std" && component.available @@ -1273,7 +1288,6 @@ fn target_add(cfg: &Cfg, m: &ArgMatches<'_>) -> Result { Some(TargetTriple::new(target)), false, ); - let distributable = DistributableToolchain::new(&toolchain)?; distributable.add_component(new_component)?; } @@ -1289,8 +1303,7 @@ fn target_remove(cfg: &Cfg, m: &ArgMatches<'_>) -> Result { Some(TargetTriple::new(target)), false, ); - let distributable = DistributableToolchain::new(&toolchain) - .chain_err(|| crate::ErrorKind::ComponentsUnsupported(toolchain.name().to_string()))?; + let distributable = DistributableToolchain::new_for_components(&toolchain)?; distributable.remove_component(new_component)?; } @@ -1303,7 +1316,8 @@ fn component_list(cfg: &Cfg, m: &ArgMatches<'_>) -> Result { if m.is_present("installed") { common::list_installed_components(&toolchain) } else { - common::list_components(&toolchain) + common::list_components(&toolchain)?; + Ok(utils::ExitCode(0)) } } @@ -1329,8 +1343,7 @@ fn component_add(cfg: &Cfg, m: &ArgMatches<'_>) -> Result { fn component_remove(cfg: &Cfg, m: &ArgMatches<'_>) -> Result { let toolchain = explicit_or_dir_toolchain(cfg, m)?; - let distributable = DistributableToolchain::new(&toolchain) - .chain_err(|| crate::ErrorKind::ComponentsUnsupported(toolchain.name().to_string()))?; + let distributable = DistributableToolchain::new_for_components(&toolchain)?; let target = m.value_of("target").map(TargetTriple::new).or_else(|| { distributable .desc() @@ -1374,7 +1387,10 @@ fn toolchain_link(cfg: &Cfg, m: &ArgMatches<'_>) -> Result { custom.install_from_dir(Path::new(path), true)?; Ok(utils::ExitCode(0)) } else { - Err(ErrorKind::InvalidCustomToolchainName(toolchain.name().to_string()).into()) + Err(anyhow!( + "invalid custom toolchain name: '{}'", + toolchain.name().to_string() + )) } } @@ -1394,7 +1410,7 @@ fn override_add(cfg: &Cfg, m: &ArgMatches<'_>) -> Result { let distributable = DistributableToolchain::new(&toolchain)?; Some(distributable.install_from_dist_if_not_installed()?) } else if !toolchain.exists() { - return Err(ErrorKind::ToolchainNotInstalled(toolchain.name().to_string()).into()); + return Err(RustupError::ToolchainNotInstalled(toolchain.name().to_string()).into()); } else { None }; @@ -1498,7 +1514,9 @@ fn doc(cfg: &Cfg, m: &ArgMatches<'_>) -> Result { "To install, try `rustup component add --toolchain {} rust-docs`", toolchain.name() ); - return Err("unable to view documentation which is not installed".into()); + return Err(anyhow!( + "unable to view documentation which is not installed" + )); } } let topical_path: PathBuf; @@ -1636,7 +1654,13 @@ fn output_completion_script(shell: Shell, command: CompletionCommand) -> Result< let script = match shell { Shell::Bash => "/etc/bash_completion.d/cargo", Shell::Zsh => "/share/zsh/site-functions/_cargo", - _ => return Err(ErrorKind::UnsupportedCompletionShell(shell, command).into()), + _ => { + return Err(anyhow!( + "{} does not currently support completions for {}", + command, + shell + )) + } }; writeln!( diff --git a/src/cli/self_update.rs b/src/cli/self_update.rs index 46015f28b5..5b41a0ccbf 100644 --- a/src/cli/self_update.rs +++ b/src/cli/self_update.rs @@ -51,6 +51,7 @@ use std::fs; use std::path::{Component, Path, PathBuf, MAIN_SEPARATOR}; use std::process::Command; +use anyhow::{anyhow, Context, Result}; use cfg_if::cfg_if; use same_file::Handle; @@ -303,7 +304,7 @@ pub fn install( let mut term = term2::stdout(); #[cfg(windows)] - if !do_msvc_check(&opts)? { + if !do_msvc_check(&opts) { if no_prompt { warn!("installing msvc toolchain without its prerequisites"); } else { @@ -362,8 +363,8 @@ pub fn install( Ok(utils::ExitCode(0)) })(); - if let Err(ref e) = install_res { - common::report_error(e); + if let Err(e) = install_res { + common::report_error(&e); // On windows, where installation happens in a console // that may have opened just for this purpose, give @@ -428,7 +429,7 @@ fn rustc_or_cargo_exists_in_path() -> Result<()> { let cargo = path.join(format!("cargo{}", EXE_SUFFIX)); if rustc.exists() || cargo.exists() { - return Err(path.to_str().unwrap().into()); + return Err(anyhow!("{}", path.to_str().unwrap().to_owned())); } } } @@ -452,7 +453,7 @@ fn check_existence_of_rustc_or_cargo_in_path(no_prompt: bool) -> Result<()> { warn!("If you are sure that you want both rustup and your already installed Rust"); warn!("then please reply `y' or `yes' or set RUSTUP_INIT_SKIP_PATH_CHECK to yes"); warn!("or pass `-y' to ignore all ignorable checks."); - ignorable_error("cannot install while Rust is installed".into(), no_prompt)?; + ignorable_error("cannot install while Rust is installed", no_prompt)?; } Ok(()) } @@ -473,7 +474,7 @@ fn do_pre_install_sanity_checks(no_prompt: bool) -> Result<()> { "run `{}` as root to uninstall Rust", uninstaller_path.display() ); - ignorable_error("cannot install while Rust is installed".into(), no_prompt)?; + ignorable_error("cannot install while Rust is installed", no_prompt)?; } if rustup_sh_exists { @@ -483,10 +484,7 @@ fn do_pre_install_sanity_checks(no_prompt: bool) -> Result<()> { warn!("or, if you already have rustup installed, you can run"); warn!("`rustup self update` and `rustup toolchain list` to upgrade"); warn!("your directory structure"); - ignorable_error( - "cannot install while rustup.sh is installed".into(), - no_prompt, - )?; + ignorable_error("cannot install while rustup.sh is installed", no_prompt)?; } Ok(()) @@ -515,7 +513,7 @@ fn do_pre_install_options_sanity_checks(opts: &InstallOpts<'_>) -> Result<()> { Ok(()) })() .map_err(|e: Box| { - format!( + anyhow!( "Pre-checks for host and toolchain failed: {}\n\ If you are unsure of suitable values, the 'stable' toolchain is the default.\n\ Valid host triples look something like: {}", @@ -529,7 +527,7 @@ fn do_pre_install_options_sanity_checks(opts: &InstallOpts<'_>) -> Result<()> { fn pre_install_msg(no_modify_path: bool) -> Result { let cargo_home = utils::cargo_home()?; let cargo_home_bin = cargo_home.join("bin"); - let rustup_home = utils::rustup_home()?; + let rustup_home = home::rustup_home()?; if !no_modify_path { // Brittle code warning: some duplication in unix::do_add_to_path @@ -693,7 +691,7 @@ pub fn install_proxies() -> Result<()> { // preexisting tools we found, then we're going to assume that it // was preinstalled and actually pointing to a totally different // binary. This is intended for cases where historically users - // rand `cargo install rustfmt` and so they had custom `rustfmt` + // ran `cargo install rustfmt` and so they had custom `rustfmt` // and `cargo-fmt` executables lying around, but we as rustup have // since started managing these tools. // @@ -830,7 +828,7 @@ pub fn uninstall(no_prompt: bool) -> Result { .join(&format!("bin/rustup{}", EXE_SUFFIX)) .exists() { - return Err(ErrorKind::NotSelfInstalled(cargo_home).into()); + return Err(CLIError::NotSelfInstalled { p: cargo_home }.into()); } if !no_prompt { @@ -846,13 +844,11 @@ pub fn uninstall(no_prompt: bool) -> Result { info!("removing rustup home"); // Delete RUSTUP_HOME - let rustup_dir = utils::rustup_home()?; + let rustup_dir = home::rustup_home()?; if rustup_dir.exists() { utils::remove_dir("rustup_home", &rustup_dir, &|_: Notification<'_>| {})?; } - let read_dir_err = "failure reading directory"; - info!("removing cargo home"); // Remove CARGO_HOME/bin from PATH @@ -862,9 +858,15 @@ pub fn uninstall(no_prompt: bool) -> Result { // Delete everything in CARGO_HOME *except* the rustup bin // First everything except the bin directory - let diriter = fs::read_dir(&cargo_home).chain_err(|| read_dir_err)?; + let diriter = fs::read_dir(&cargo_home).map_err(|e| CLIError::ReadDirError { + p: cargo_home.clone(), + source: e, + })?; for dirent in diriter { - let dirent = dirent.chain_err(|| read_dir_err)?; + let dirent = dirent.map_err(|e| CLIError::ReadDirError { + p: cargo_home.clone(), + source: e, + })?; if dirent.file_name().to_str() != Some("bin") { if dirent.path().is_dir() { utils::remove_dir("cargo_home", &dirent.path(), &|_: Notification<'_>| {})?; @@ -881,9 +883,16 @@ pub fn uninstall(no_prompt: bool) -> Result { .chain(DUP_TOOLS.iter()) .map(|t| format!("{}{}", t, EXE_SUFFIX)); let tools: Vec<_> = tools.chain(vec![format!("rustup{}", EXE_SUFFIX)]).collect(); - let diriter = fs::read_dir(&cargo_home.join("bin")).chain_err(|| read_dir_err)?; + let bin_dir = cargo_home.join("bin"); + let diriter = fs::read_dir(&bin_dir).map_err(|e| CLIError::ReadDirError { + p: bin_dir.clone(), + source: e, + })?; for dirent in diriter { - let dirent = dirent.chain_err(|| read_dir_err)?; + let dirent = dirent.map_err(|e| CLIError::ReadDirError { + p: bin_dir.clone(), + source: e, + })?; let name = dirent.file_name(); let file_is_tool = name.to_str().map(|n| tools.iter().any(|t| *t == n)); if file_is_tool == Some(false) { @@ -998,7 +1007,7 @@ pub fn prepare_update() -> Result> { let setup_path = cargo_home.join(&format!("bin{}rustup-init{}", MAIN_SEPARATOR, EXE_SUFFIX)); if !rustup_path.exists() { - return Err(ErrorKind::NotSelfInstalled(cargo_home).into()); + return Err(CLIError::NotSelfInstalled { p: cargo_home }.into()); } if setup_path.exists() { @@ -1060,7 +1069,7 @@ pub fn get_available_rustup_version() -> Result { let tempdir = tempfile::Builder::new() .prefix("rustup-update") .tempdir() - .chain_err(|| "error creating temp directory")?; + .context("error creating temp directory")?; // Parse the release file. let release_file_url = format!("{}/release-stable.toml", update_root); @@ -1068,17 +1077,17 @@ pub fn get_available_rustup_version() -> Result { let release_file = tempdir.path().join("release-stable.toml"); utils::download_file(&release_file_url, &release_file, None, &|_| ())?; let release_toml_str = utils::read_file("rustup release", &release_file)?; - let release_toml: toml::Value = toml::from_str(&release_toml_str) - .map_err(|_| Error::from("unable to parse rustup release file"))?; + let release_toml: toml::Value = + toml::from_str(&release_toml_str).context("unable to parse rustup release file")?; // Check the release file schema. let schema = release_toml .get("schema-version") - .ok_or_else(|| Error::from("no schema key in rustup release file"))? + .ok_or_else(|| anyhow!("no schema key in rustup release file"))? .as_str() - .ok_or_else(|| Error::from("invalid schema key in rustup release file"))?; + .ok_or_else(|| anyhow!("invalid schema key in rustup release file"))?; if schema != "1" { - return Err(Error::from(&*format!( + return Err(anyhow!(format!( "unknown schema version '{}' in rustup release file", schema ))); @@ -1087,9 +1096,9 @@ pub fn get_available_rustup_version() -> Result { // Get the version. let available_version = release_toml .get("version") - .ok_or_else(|| Error::from("no version key in rustup release file"))? + .ok_or_else(|| anyhow!("no version key in rustup release file"))? .as_str() - .ok_or_else(|| Error::from("invalid version key in rustup release file"))?; + .ok_or_else(|| anyhow!("invalid version key in rustup release file"))?; Ok(String::from(available_version)) } @@ -1109,6 +1118,8 @@ pub fn cleanup_self_updater() -> Result<()> { mod tests { use std::collections::HashMap; + use anyhow::Result; + use crate::cli::common; use crate::dist::dist::ToolchainDesc; use crate::test::{test_dir, with_rustup_home, Env}; @@ -1123,7 +1134,7 @@ mod tests { vars, ..Default::default() }); - currentprocess::with(tp.clone(), || -> anyhow::Result<()> { + currentprocess::with(tp.clone(), || -> Result<()> { // TODO: we could pass in a custom cfg to get notification // callbacks rather than output to the tp sink. let mut cfg = common::set_globals(false, false).unwrap(); @@ -1170,7 +1181,7 @@ info: default host triple is {0} vars, ..Default::default() }); - currentprocess::with(tp, || -> anyhow::Result<()> { + currentprocess::with(tp, || -> Result<()> { super::install_bins().unwrap(); Ok(()) }) diff --git a/src/cli/self_update/shell.rs b/src/cli/self_update/shell.rs index 33f3088bf7..a77066f222 100644 --- a/src/cli/self_update/shell.rs +++ b/src/cli/self_update/shell.rs @@ -23,11 +23,12 @@ //! 1) using a shell script that updates PATH if the path is not in PATH //! 2) sourcing this script (`. /path/to/script`) in any appropriate rc file +use std::borrow::Cow; use std::path::PathBuf; -use error_chain::bail; +use anyhow::{bail, Result}; -use super::*; +use super::utils; use crate::process; pub type Shell = Box; diff --git a/src/cli/self_update/unix.rs b/src/cli/self_update/unix.rs index 390a16e60e..d7f9a5a2b6 100644 --- a/src/cli/self_update/unix.rs +++ b/src/cli/self_update/unix.rs @@ -1,7 +1,8 @@ use std::path::{Path, PathBuf}; use std::process::Command; -use super::super::errors::*; +use anyhow::{bail, Context, Result}; + use super::install_bins; use super::shell; use crate::process; @@ -48,9 +49,7 @@ pub fn do_anti_sudo_check(no_prompt: bool) -> Result { pub fn delete_rustup_and_cargo_home() -> Result<()> { let cargo_home = utils::cargo_home()?; - utils::remove_dir("cargo_home", &cargo_home, &|_: Notification<'_>| ())?; - - Ok(()) + utils::remove_dir("cargo_home", &cargo_home, &|_: Notification<'_>| ()) } pub fn do_remove_from_path() -> Result<()> { @@ -92,11 +91,8 @@ pub fn do_add_to_path() -> Result<()> { _ => &source_cmd, }; - utils::append_file("rcfile", &rc, &cmd_to_write).chain_err(|| { - ErrorKind::WritingShellProfile { - path: rc.to_path_buf(), - } - })?; + utils::append_file("rcfile", &rc, &cmd_to_write) + .with_context(|| format!("could not amend shell profile: '{}'", rc.display()))?; } } @@ -134,10 +130,10 @@ pub fn run_update(setup_path: &Path) -> Result { let status = Command::new(setup_path) .arg("--self-replace") .status() - .chain_err(|| "unable to run updater")?; + .context("unable to run updater")?; if !status.success() { - return Err("self-updated failed to replace rustup executable".into()); + bail!("self-updated failed to replace rustup executable"); } Ok(utils::ExitCode(0)) diff --git a/src/cli/self_update/windows.rs b/src/cli/self_update/windows.rs index f2ddce5dd7..d71a9ee18b 100644 --- a/src/cli/self_update/windows.rs +++ b/src/cli/self_update/windows.rs @@ -4,6 +4,8 @@ use std::os::windows::ffi::{OsStrExt, OsStringExt}; use std::path::Path; use std::process::Command; +use anyhow::{anyhow, Context, Result}; + use super::super::errors::*; use super::common; use super::{install_bins, InstallOpts}; @@ -24,10 +26,10 @@ pub fn ensure_prompt() -> Result<()> { // Provide guidance about setting up MSVC if it doesn't appear to be // installed -pub fn do_msvc_check(opts: &InstallOpts<'_>) -> Result { +pub fn do_msvc_check(opts: &InstallOpts<'_>) -> bool { // Test suite skips this since it's env dependent if process().var("RUSTUP_INIT_SKIP_MSVC_CHECK").is_ok() { - return Ok(true); + return true; } use cc::windows_registry; @@ -39,10 +41,10 @@ pub fn do_msvc_check(opts: &InstallOpts<'_>) -> Result { let installing_msvc = host_triple.contains("msvc"); let have_msvc = windows_registry::find_tool(&host_triple, "cl.exe").is_some(); if installing_msvc && !have_msvc { - return Ok(false); + return false; } - Ok(true) + true } /// Run by rustup-gc-$num.exe to delete CARGO_HOME @@ -65,7 +67,7 @@ pub fn complete_windows_uninstall() -> Result { .stdout(Stdio::null()) .stderr(Stdio::null()) .spawn() - .chain_err(|| ErrorKind::WindowsUninstallMadness)?; + .context(CLIError::WindowsUninstallMadness)?; Ok(utils::ExitCode(0)) } @@ -89,7 +91,7 @@ pub fn wait_for_parent() -> Result<()> { let snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if snapshot == INVALID_HANDLE_VALUE { let err = io::Error::last_os_error(); - return Err(err).chain_err(|| ErrorKind::WindowsUninstallMadness); + return Err(err).context(CLIError::WindowsUninstallMadness); } let snapshot = scopeguard::guard(snapshot, |h| { @@ -103,7 +105,7 @@ pub fn wait_for_parent() -> Result<()> { let success = Process32First(*snapshot, &mut entry); if success == 0 { let err = io::Error::last_os_error(); - return Err(err).chain_err(|| ErrorKind::WindowsUninstallMadness); + return Err(err).context(CLIError::WindowsUninstallMadness); } let this_pid = GetCurrentProcessId(); @@ -111,7 +113,7 @@ pub fn wait_for_parent() -> Result<()> { let success = Process32Next(*snapshot, &mut entry); if success == 0 { let err = io::Error::last_os_error(); - return Err(err).chain_err(|| ErrorKind::WindowsUninstallMadness); + return Err(err).context(CLIError::WindowsUninstallMadness); } } @@ -136,7 +138,7 @@ pub fn wait_for_parent() -> Result<()> { if res != WAIT_OBJECT_0 { let err = io::Error::last_os_error(); - return Err(err).chain_err(|| ErrorKind::WindowsUninstallMadness); + return Err(err).context(CLIError::WindowsUninstallMadness); } } @@ -161,22 +163,16 @@ fn _apply_new_path(new_path: Option>) -> Result<()> { }; let root = RegKey::predef(HKEY_CURRENT_USER); - let environment = root - .open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE) - .chain_err(|| ErrorKind::PermissionDenied)?; + let environment = root.open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE)?; if new_path.is_empty() { - environment - .delete_value("PATH") - .chain_err(|| ErrorKind::PermissionDenied)?; + environment.delete_value("PATH")?; } else { let reg_value = RegValue { bytes: to_winreg_bytes(new_path), vtype: RegType::REG_EXPAND_SZ, }; - environment - .set_raw_value("PATH", ®_value) - .chain_err(|| ErrorKind::PermissionDenied)?; + environment.set_raw_value("PATH", ®_value)?; } // Tell other processes to update their environment @@ -205,7 +201,7 @@ fn get_windows_path_var() -> Result>> { let root = RegKey::predef(HKEY_CURRENT_USER); let environment = root .open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE) - .chain_err(|| ErrorKind::PermissionDenied)?; + .context("Failed opening Environment key")?; let reg_value = environment.get_raw_value("PATH"); match reg_value { @@ -221,7 +217,7 @@ fn get_windows_path_var() -> Result>> { } } Err(ref e) if e.kind() == io::ErrorKind::NotFound => Ok(Some(Vec::new())), - Err(e) => Err(e).chain_err(|| ErrorKind::WindowsUninstallMadness), + Err(e) => Err(e).context(CLIError::WindowsUninstallMadness), } } @@ -290,7 +286,7 @@ pub fn do_add_to_programs() -> Result<()> { let key = RegKey::predef(HKEY_CURRENT_USER) .create_subkey(RUSTUP_UNINSTALL_ENTRY) - .chain_err(|| ErrorKind::PermissionDenied)? + .context("Failed creating uninstall key")? .0; // Don't overwrite registry if Rustup is already installed @@ -317,9 +313,9 @@ pub fn do_add_to_programs() -> Result<()> { }; key.set_raw_value("UninstallString", ®_value) - .chain_err(|| ErrorKind::PermissionDenied)?; + .context("Failed to set uninstall string")?; key.set_value("DisplayName", &"Rustup: the Rust toolchain installer") - .chain_err(|| ErrorKind::PermissionDenied)?; + .context("Failed to set display name")?; Ok(()) } @@ -328,7 +324,7 @@ pub fn do_remove_from_programs() -> Result<()> { match RegKey::predef(HKEY_CURRENT_USER).delete_subkey_all(RUSTUP_UNINSTALL_ENTRY) { Ok(()) => Ok(()), Err(ref e) if e.kind() == std::io::ErrorKind::NotFound => Ok(()), - Err(e) => Err(e).chain_err(|| ErrorKind::PermissionDenied), + Err(e) => Err(anyhow!(e)), } } @@ -365,7 +361,7 @@ pub fn run_update(setup_path: &Path) -> Result { Command::new(setup_path) .arg("--self-replace") .spawn() - .chain_err(|| "unable to run updater")?; + .context("unable to run updater")?; Ok(utils::ExitCode(0)) } @@ -460,7 +456,7 @@ pub fn delete_rustup_and_cargo_home() -> Result<()> { if gc_handle == INVALID_HANDLE_VALUE { let err = io::Error::last_os_error(); - return Err(err).chain_err(|| ErrorKind::WindowsUninstallMadness); + return Err(err).context(CLIError::WindowsUninstallMadness); } scopeguard::guard(gc_handle, |h| { @@ -470,7 +466,7 @@ pub fn delete_rustup_and_cargo_home() -> Result<()> { Command::new(gc_exe) .spawn() - .chain_err(|| ErrorKind::WindowsUninstallMadness)?; + .context(CLIError::WindowsUninstallMadness)?; // The catch 22 article says we must sleep here to give // Windows a chance to bump the processes file reference diff --git a/src/cli/setup_mode.rs b/src/cli/setup_mode.rs index 9bc47ed802..f54accbd80 100644 --- a/src/cli/setup_mode.rs +++ b/src/cli/setup_mode.rs @@ -1,7 +1,7 @@ +use anyhow::Result; use clap::{App, AppSettings, Arg}; use super::common; -use super::errors::*; use super::self_update::{self, InstallOpts}; use crate::dist::dist::Profile; use crate::process; diff --git a/src/cli/topical_doc.rs b/src/cli/topical_doc.rs index cabf8f34b2..eec029221b 100644 --- a/src/cli/topical_doc.rs +++ b/src/cli/topical_doc.rs @@ -2,7 +2,7 @@ use std::ffi::OsString; use std::fs; use std::path::{Path, PathBuf}; -use super::errors::*; +use anyhow::{anyhow, bail, Context, Result}; struct DocData<'a> { topic: &'a str, @@ -10,10 +10,6 @@ struct DocData<'a> { root: &'a Path, } -fn no_document(topic: &str) -> Result { - Err(format!("No document for '{}'", topic).into()) -} - fn index_html(doc: &DocData<'_>, wpath: &Path) -> Option { let indexhtml = wpath.join("index.html"); match &doc.root.join(&indexhtml).exists() { @@ -23,7 +19,7 @@ fn index_html(doc: &DocData<'_>, wpath: &Path) -> Option { } fn dir_into_vec(dir: &Path) -> Result> { - let entries = fs::read_dir(dir).chain_err(|| format!("Opening directory {:?}", dir))?; + let entries = fs::read_dir(dir).with_context(|| format!("Failed to read_dir {:?}", dir))?; let mut v = Vec::new(); for entry in entries { let entry = entry?; @@ -43,7 +39,7 @@ fn search_path(doc: &DocData<'_>, wpath: &Path, keywords: &[&str]) -> Result Result { @@ -124,7 +120,7 @@ pub fn local_path(root: &Path, topic: &str) -> Result { 1 => match topic { "std" | "core" | "alloc" => match index_html(&doc, &work_path) { Some(f) => f, - None => no_document(doc.topic)?, + None => bail!(format!("No document for '{}'", doc.topic)), }, _ => { let std = PathBuf::from("std"); diff --git a/src/command.rs b/src/command.rs index 7992884194..1efd1e1a47 100644 --- a/src/command.rs +++ b/src/command.rs @@ -2,6 +2,8 @@ use std::ffi::OsStr; use std::io; use std::process::{self, Command}; +use anyhow::{Context, Result}; + use crate::errors::*; use crate::utils::utils::ExitCode; @@ -18,7 +20,7 @@ pub fn run_command_for_dir>( // then tests that depend on rustups stdin being inherited won't work in-process. cmd.stdin(process::Stdio::inherit()); - return exec(&mut cmd).chain_err(|| crate::ErrorKind::RunningCommand { + return exec(&mut cmd).with_context(|| RustupError::RunningCommand { name: OsStr::new(arg0).to_owned(), }); diff --git a/src/config.rs b/src/config.rs index e589ae0425..551a1a3bdb 100644 --- a/src/config.rs +++ b/src/config.rs @@ -6,12 +6,17 @@ use std::process::Command; use std::str::FromStr; use std::sync::Arc; +use anyhow::{anyhow, bail, Context, Result}; use pgp::{Deserializable, SignedPublicKey}; use serde::Deserialize; +use thiserror::Error as ThisError; use crate::dist::download::DownloadCfg; -use crate::dist::{dist, temp}; -use crate::errors::*; +use crate::dist::{ + dist::{self, valid_profile_names}, + temp, +}; +use crate::errors::RustupError; use crate::fallback_settings::FallbackSettings; use crate::notifications::*; use crate::process; @@ -19,6 +24,16 @@ use crate::settings::{Settings, SettingsFile, DEFAULT_METADATA_VERSION}; use crate::toolchain::{DistributableToolchain, Toolchain, UpdateStatus}; use crate::utils::utils; +#[derive(Debug, ThisError)] +enum ConfigError { + #[error("empty toolchain override file detected. Please remove it, or else specify the desired toolchain properties in the file")] + EmptyOverrideFile, + #[error("missing toolchain properties in toolchain override file")] + InvalidOverrideFile, + #[error("error parsing override file")] + ParsingOverrideFile, +} + #[derive(Debug, Default, Deserialize, PartialEq, Eq)] struct OverrideFile { toolchain: ToolchainSection, @@ -110,12 +125,19 @@ impl<'a> OverrideCfg<'a> { || file.toolchain.components.is_some() || file.toolchain.profile.is_some() { - return Err(ErrorKind::CannotSpecifyPathAndOptions(path).into()); + bail!( + "toolchain options are ignored for path toolchain ({})", + path.display() + ) } Some(Toolchain::from_path(cfg, cfg_path, &path)?) } (Some(channel), Some(path)) => { - return Err(ErrorKind::CannotSpecifyChannelAndPath(channel, path).into()) + bail!( + "cannot specify both channel ({}) and path ({}) simultaneously", + channel, + path.display() + ) } (None, None) => None, }, @@ -167,7 +189,7 @@ impl PgpPublicKey { wait = every; } wait -= 1; - write!(ret, "{:02X}", b).map_err(|e| format!("{:?}", e))?; + write!(ret, "{:02X}", b)?; } Ok(ret) } @@ -255,8 +277,12 @@ impl Cfg { if let Some(ref s_path) = process().var_os("RUSTUP_PGP_KEY") { let path = PathBuf::from(s_path); let file = utils::open_file("RUSTUP_PGP_KEY", &path)?; - let (key, _) = SignedPublicKey::from_armor_single(file) - .map_err(|error| ErrorKind::InvalidPgpKey(PathBuf::from(s_path), error))?; + let (key, _) = SignedPublicKey::from_armor_single(file).map_err(|error| { + RustupError::InvalidPgpKey { + path: s_path.into(), + source: error, + } + })?; pgp_keys.push(PgpPublicKey::FromEnvironment(path, key)); } @@ -264,8 +290,12 @@ impl Cfg { if let Some(s) = &s.pgp_keys { let path = PathBuf::from(s); let file = utils::open_file("PGP Key from config", &path)?; - let (key, _) = SignedPublicKey::from_armor_single(file) - .map_err(|error| ErrorKind::InvalidPgpKey(PathBuf::from(s), error))?; + let (key, _) = SignedPublicKey::from_armor_single(file).map_err(|error| { + anyhow!(RustupError::InvalidPgpKey { + path: s.into(), + source: error, + }) + })?; pgp_keys.push(PgpPublicKey::FromConfiguration(path, key)); } @@ -322,7 +352,7 @@ impl Cfg { // For now, that means simply checking that 'stable' can resolve // for the current configuration. cfg.resolve_toolchain("stable") - .map_err(|e| format!("Unable parse configuration: {}", e))?; + .context("Unable parse configuration")?; Ok(cfg) } @@ -360,7 +390,11 @@ impl Cfg { pub fn set_profile(&mut self, profile: &str) -> Result<()> { if !dist::Profile::names().contains(&profile) { - return Err(ErrorKind::UnknownProfile(profile.to_owned()).into()); + return Err(anyhow!( + "unknown profile name: '{}'; valid profile names are {}", + profile.to_owned(), + valid_profile_names(), + )); } self.profile_override = None; self.settings_file.with_mut(|s| { @@ -406,12 +440,6 @@ impl Cfg { Toolchain::from(self, name) } - pub fn verify_toolchain(&self, name: &str) -> Result> { - let toolchain = self.get_toolchain(name, false)?; - toolchain.verify()?; - Ok(toolchain) - } - pub fn get_hash_file(&self, toolchain: &str, create_parent: bool) -> Result { if create_parent { utils::ensure_dir_exists( @@ -462,14 +490,14 @@ impl Cfg { let dirs = utils::read_dir("toolchains", &self.toolchains_dir)?; for dir in dirs { - let dir = dir.chain_err(|| ErrorKind::UpgradeIoError)?; + let dir = dir.context("IO Error reading toolchains")?; utils::remove_dir("toolchain", &dir.path(), self.notify_handler.as_ref())?; } // Also delete the update hashes let files = utils::read_dir("update hashes", &self.update_hash_dir)?; for file in files { - let file = file.chain_err(|| ErrorKind::UpgradeIoError)?; + let file = file.context("IO Error reading update hashes")?; utils::remove_file("update hash", &file.path())?; } @@ -478,15 +506,7 @@ impl Cfg { Ok(()) }) } - _ => Err(ErrorKind::UnknownMetadataVersion(current_version).into()), - } - } - - pub fn delete_data(&self) -> Result<()> { - if utils::path_exists(&self.rustup_dir) { - utils::remove_dir("home", &self.rustup_dir, self.notify_handler.as_ref()) - } else { - Ok(()) + _ => Err(RustupError::UnknownMetadataVersion(current_version).into()), } } @@ -574,8 +594,8 @@ impl Cfg { if !toolchain.exists() && toolchain.is_custom() { // Strip the confusing NotADirectory error and only mention that the // override toolchain is not installed. - return Err(Error::from(reason_err)).chain_err(|| { - ErrorKind::OverrideToolchainNotInstalled(toolchain.name().into()) + return Err(anyhow!(reason_err)).with_context(|| { + format!("override toolchain '{}' is not installed", toolchain.name()) }); } } @@ -657,22 +677,22 @@ impl Cfg { let contents = contents.as_ref(); match (contents.lines().count(), parse_mode) { - (0, _) => Err(ErrorKind::EmptyOverrideFile.into()), + (0, _) => Err(anyhow!(ConfigError::EmptyOverrideFile)), (1, ParseMode::Both) => { let channel = contents.trim(); if channel.is_empty() { - Err(ErrorKind::EmptyOverrideFile.into()) + Err(anyhow!(ConfigError::EmptyOverrideFile)) } else { Ok(channel.into()) } } _ => { let override_file = toml::from_str::(contents) - .map_err(ErrorKind::ParsingOverrideFile)?; + .context(ConfigError::ParsingOverrideFile)?; if override_file.is_empty() { - Err(ErrorKind::InvalidOverrideFile.into()) + Err(anyhow!(ConfigError::InvalidOverrideFile)) } else { Ok(override_file) } @@ -754,7 +774,7 @@ impl Cfg { if toolchain.is_custom() { if !toolchain.exists() { return Err( - ErrorKind::ToolchainNotInstalled(toolchain.name().to_string()).into(), + RustupError::ToolchainNotInstalled(toolchain.name().to_string()).into(), ); } } else { @@ -771,7 +791,7 @@ impl Cfg { Ok((toolchain, reason)) } else { // No override and no default set - Err(ErrorKind::ToolchainNotSelected.into()) + Err(RustupError::ToolchainNotSelected.into()) } } @@ -847,7 +867,9 @@ impl Cfg { if s.version == DEFAULT_METADATA_VERSION { Ok(()) } else { - Err(ErrorKind::NeedMetadataUpgrade.into()) + Err(anyhow!( + "rustup's metadata is out of date. run `rustup self upgrade-data`" + )) } }) } @@ -1140,8 +1162,8 @@ components = [ "rustfmt" ] let result = Cfg::parse_override_file(contents, ParseMode::Both); assert!(matches!( - result.unwrap_err().kind(), - ErrorKind::InvalidOverrideFile + result.unwrap_err().downcast::(), + Ok(ConfigError::InvalidOverrideFile) )); } @@ -1151,8 +1173,8 @@ components = [ "rustfmt" ] let result = Cfg::parse_override_file(contents, ParseMode::Both); assert!(matches!( - result.unwrap_err().kind(), - ErrorKind::EmptyOverrideFile + result.unwrap_err().downcast::(), + Ok(ConfigError::EmptyOverrideFile) )); } @@ -1162,8 +1184,8 @@ components = [ "rustfmt" ] let result = Cfg::parse_override_file(contents, ParseMode::Both); assert!(matches!( - result.unwrap_err().kind(), - ErrorKind::EmptyOverrideFile + result.unwrap_err().downcast::(), + Ok(ConfigError::EmptyOverrideFile) )); } @@ -1175,8 +1197,8 @@ channel = nightly let result = Cfg::parse_override_file(contents, ParseMode::Both); assert!(matches!( - result.unwrap_err().kind(), - ErrorKind::ParsingOverrideFile(..) + result.unwrap_err().downcast::(), + Ok(ConfigError::ParsingOverrideFile) )); } } diff --git a/src/diskio/mod.rs b/src/diskio/mod.rs index 09ccdc1a66..d82d31196d 100644 --- a/src/diskio/mod.rs +++ b/src/diskio/mod.rs @@ -62,7 +62,8 @@ use std::sync::mpsc::Receiver; use std::time::{Duration, Instant}; use std::{fmt::Debug, fs::OpenOptions}; -use crate::errors::{Result, ResultExt}; +use anyhow::{Context, Result}; + use crate::process; use crate::utils::notifications::Notification; @@ -371,7 +372,7 @@ pub fn get_executor<'a>( Err(_) => num_cpus::get(), Ok(n) => n .parse::() - .chain_err(|| "invalid value in RUSTUP_IO_THREADS. Must be a natural number")?, + .context("invalid value in RUSTUP_IO_THREADS. Must be a natural number")?, }; Ok(match thread_count { 0 | 1 => Box::new(immediate::ImmediateUnpacker::new()), diff --git a/src/diskio/test.rs b/src/diskio/test.rs index bed268c588..f7933dde79 100644 --- a/src/diskio/test.rs +++ b/src/diskio/test.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; -use crate::errors::Result; +use anyhow::Result; + use crate::test::test_dir; use super::{get_executor, Executor, Item}; diff --git a/src/dist/component/components.rs b/src/dist/component/components.rs index ae707e0c57..12da6f0822 100644 --- a/src/dist/component/components.rs +++ b/src/dist/component/components.rs @@ -1,15 +1,16 @@ -use crate::dist::prefix::InstallPrefix; -use crate::errors::*; -/// The representation of the installed toolchain and its components. -/// `Components` and `DirectoryPackage` are the two sides of the -/// installation / uninstallation process. -use crate::utils::utils; +//! The representation of the installed toolchain and its components. +//! `Components` and `DirectoryPackage` are the two sides of the +//! installation / uninstallation process. + +use std::path::{Path, PathBuf}; + +use anyhow::{bail, Result}; use crate::dist::component::package::{INSTALLER_VERSION, VERSION_FILE}; use crate::dist::component::transaction::Transaction; - -use std::fs::File; -use std::path::{Path, PathBuf}; +use crate::dist::prefix::InstallPrefix; +use crate::errors::RustupError; +use crate::utils::utils; const COMPONENTS_FILE: &str = "components"; @@ -25,7 +26,10 @@ impl Components { // Validate that the metadata uses a format we know if let Some(v) = c.read_version()? { if v != INSTALLER_VERSION { - return Err(ErrorKind::BadInstalledMetadataVersion(v).into()); + bail!( + "unsupported metadata version in existing installation: {}", + v + ); } } @@ -94,11 +98,6 @@ pub struct ComponentBuilder<'a> { } impl<'a> ComponentBuilder<'a> { - pub fn add_file(&mut self, path: PathBuf) -> Result { - self.parts - .push(ComponentPart("file".to_owned(), path.clone())); - self.tx.add_file(&self.name, path) - } pub fn copy_file(&mut self, path: PathBuf, src: &Path) -> Result<()> { self.parts .push(ComponentPart("file".to_owned(), path.clone())); @@ -182,7 +181,7 @@ impl Component { for line in utils::read_file("component", &self.manifest_file())?.lines() { result.push( ComponentPart::decode(line) - .ok_or_else(|| ErrorKind::CorruptComponent(self.name.clone()))?, + .ok_or_else(|| RustupError::CorruptComponent(self.name.clone()))?, ); } Ok(result) @@ -192,7 +191,7 @@ impl Component { let path = self.components.rel_components_file(); let abs_path = self.components.prefix.abs_path(&path); let temp = tx.temp().new_file()?; - utils::filter_file("components", &abs_path, &temp, |l| (l != self.name))?; + utils::filter_file("components", &abs_path, &temp, |l| l != self.name)?; tx.modify_file(path)?; utils::rename_file("components", &temp, &abs_path, tx.notify_handler())?; @@ -303,7 +302,7 @@ impl Component { match &*part.0 { "file" => tx.remove_file(&self.name, part.1.clone())?, "dir" => tx.remove_dir(&self.name, part.1.clone())?, - _ => return Err(ErrorKind::CorruptComponent(self.name.clone()).into()), + _ => return Err(RustupError::CorruptComponent(self.name.clone()).into()), } pset.seen(part.1); } diff --git a/src/dist/component/package.rs b/src/dist/component/package.rs index 83e226619f..56a5a897b4 100644 --- a/src/dist/component/package.rs +++ b/src/dist/component/package.rs @@ -8,6 +8,7 @@ use std::io::{self, ErrorKind as IOErrorKind, Read}; use std::mem; use std::path::{Path, PathBuf}; +use anyhow::{anyhow, bail, Context, Result}; use tar::EntryType; use crate::diskio::{get_executor, CompletedIo, Executor, Item, Kind}; @@ -65,7 +66,7 @@ fn validate_installer_version(path: &Path) -> Result<()> { if v == INSTALLER_VERSION { Ok(()) } else { - Err(ErrorKind::BadInstallerVersion(v.to_owned()).into()) + Err(anyhow!(format!("unsupported installer version: {}", v))) } } @@ -100,7 +101,7 @@ impl Package for DirectoryPackage { for l in manifest.lines() { let part = ComponentPart::decode(l) - .ok_or_else(|| ErrorKind::CorruptComponent(name.to_owned()))?; + .ok_or_else(|| RustupError::CorruptComponent(name.to_owned()))?; let path = part.1; let src_path = root.join(&path); @@ -120,7 +121,7 @@ impl Package for DirectoryPackage { builder.move_dir(path.clone(), &src_path)? } } - _ => return Err(ErrorKind::CorruptComponent(name.to_owned()).into()), + _ => return Err(RustupError::CorruptComponent(name.to_owned()).into()), } } @@ -148,7 +149,8 @@ impl<'a> TarPackage<'a> { // The rust-installer packages unpack to a directory called // $pkgname-$version-$target. Skip that directory when // unpacking. - unpack_without_first_dir(&mut archive, &*temp_dir, notify_handler)?; + unpack_without_first_dir(&mut archive, &*temp_dir, notify_handler) + .context("failed to extract package (perhaps you ran out of disk space?)")?; Ok(TarPackage( DirectoryPackage::new(temp_dir.to_owned(), false)?, @@ -289,7 +291,7 @@ fn trigger_children( for mut item in io_executor.execute(pending_item).collect::>() { // TODO capture metrics budget.reclaim(&item); - filter_result(&mut item).chain_err(|| ErrorKind::ExtractingPackage)?; + filter_result(&mut item)?; result += trigger_children(io_executor, directories, budget, item)?; } } @@ -310,9 +312,7 @@ fn unpack_without_first_dir<'a, R: Read>( notify_handler: Option<&'a dyn Fn(Notification<'_>)>, ) -> Result<()> { let mut io_executor: Box = get_executor(notify_handler)?; - let entries = archive - .entries() - .chain_err(|| ErrorKind::ExtractingPackage)?; + let entries = archive.entries()?; const IO_CHUNK_SIZE: u64 = 16_777_216; let effective_max_ram = match effective_limits::memory_limit() { Ok(ram) => Some(ram as usize), @@ -336,14 +336,14 @@ fn unpack_without_first_dir<'a, R: Read>( for mut item in io_executor.completed().collect::>() { // TODO capture metrics budget.reclaim(&item); - filter_result(&mut item).chain_err(|| ErrorKind::ExtractingPackage)?; + filter_result(&mut item)?; trigger_children(&*io_executor, &mut directories, &mut budget, item)?; } - let mut entry = entry.chain_err(|| ErrorKind::ExtractingPackage)?; + let mut entry = entry?; let relpath = { let path = entry.path(); - let path = path.chain_err(|| ErrorKind::ExtractingPackage)?; + let path = path?; path.into_owned() }; // Reject path components that are not normal (..|/| etc) @@ -352,7 +352,7 @@ fn unpack_without_first_dir<'a, R: Read>( // Some very early rust tarballs include a "." segment which we have to // support, despite not liking it. std::path::Component::Normal(_) | std::path::Component::CurDir => {} - _ => return Err(ErrorKind::BadPath(relpath).into()), + _ => bail!(format!("tar path '{}' is not supported", relpath.display())), } } let mut components = relpath.components(); @@ -380,7 +380,7 @@ fn unpack_without_first_dir<'a, R: Read>( for mut op in io_executor.completed().collect::>() { // TODO capture metrics budget.reclaim(&op); - filter_result(&mut op).chain_err(|| ErrorKind::ExtractingPackage)?; + filter_result(&mut op)?; trigger_children(&*io_executor, &mut directories, &mut budget, op)?; } // Maybe stream a file incrementally @@ -394,10 +394,10 @@ fn unpack_without_first_dir<'a, R: Read>( v.resize(len, 0); budget.claim_chunk(len); if !sender(v) { - return Err(ErrorKind::DisconnectedChannel( - full_path.as_ref().to_path_buf(), - ) - .into()); + bail!(format!( + "IO receiver for '{}' disconnected", + full_path.as_ref().display() + )) } } } @@ -466,7 +466,7 @@ fn unpack_without_first_dir<'a, R: Read>( Item::write_file(full_path.clone(), v, mode) } } - _ => return Err(ErrorKind::UnsupportedKind(format!("{:?}", kind)).into()), + _ => bail!(format!("tar entry kind '{:?}' is not supported", kind)), }; budget.claim(&item); @@ -514,7 +514,7 @@ fn unpack_without_first_dir<'a, R: Read>( for mut item in io_executor.execute(item).collect::>() { // TODO capture metrics budget.reclaim(&item); - filter_result(&mut item).chain_err(|| ErrorKind::ExtractingPackage)?; + filter_result(&mut item)?; trigger_children(&*io_executor, &mut directories, &mut budget, item)?; } } @@ -538,7 +538,7 @@ fn unpack_without_first_dir<'a, R: Read>( // handle final IOs // TODO capture metrics budget.reclaim(&item); - filter_result(&mut item).chain_err(|| ErrorKind::ExtractingPackage)?; + filter_result(&mut item)?; triggered += trigger_children(&*io_executor, &mut directories, &mut budget, item)?; } if triggered == 0 { diff --git a/src/dist/component/transaction.rs b/src/dist/component/transaction.rs index 8a70d03871..828ae8da3c 100644 --- a/src/dist/component/transaction.rs +++ b/src/dist/component/transaction.rs @@ -9,15 +9,17 @@ //! FIXME: This uses ensure_dir_exists in some places but rollback //! does not remove any dirs created by it. +use std::fs::File; +use std::path::{Path, PathBuf}; + +use anyhow::{anyhow, Context, Result}; + use crate::dist::notifications::*; use crate::dist::prefix::InstallPrefix; use crate::dist::temp; use crate::errors::*; use crate::utils::utils; -use std::fs::File; -use std::path::{Path, PathBuf}; - /// A Transaction tracks changes to the file system, allowing them to /// be rolled back in case of an error. Instead of deleting or /// overwriting file, the old copies are moved to a temporary @@ -232,11 +234,10 @@ impl<'a> ChangedItem<'a> { fn dest_abs_path(prefix: &InstallPrefix, component: &str, relpath: &Path) -> Result { let abs_path = prefix.abs_path(relpath); if utils::path_exists(&abs_path) { - Err(ErrorKind::ComponentConflict { + Err(anyhow!(RustupError::ComponentConflict { name: component.to_owned(), path: relpath.to_path_buf(), - } - .into()) + })) } else { if let Some(p) = abs_path.parent() { utils::ensure_dir_exists("component", p, &|_: Notification<'_>| ())?; @@ -247,7 +248,7 @@ impl<'a> ChangedItem<'a> { fn add_file(prefix: &InstallPrefix, component: &str, relpath: PathBuf) -> Result<(Self, File)> { let abs_path = ChangedItem::dest_abs_path(prefix, component, &relpath)?; let file = File::create(&abs_path) - .chain_err(|| format!("error creating file '{}'", abs_path.display()))?; + .with_context(|| format!("error creating file '{}'", abs_path.display()))?; Ok((ChangedItem::AddedFile(relpath), file)) } fn copy_file( @@ -280,7 +281,7 @@ impl<'a> ChangedItem<'a> { let abs_path = prefix.abs_path(&relpath); let backup = temp_cfg.new_file()?; if !utils::path_exists(&abs_path) { - Err(ErrorKind::ComponentMissingFile { + Err(RustupError::ComponentMissingFile { name: component.to_owned(), path: relpath, } @@ -300,7 +301,7 @@ impl<'a> ChangedItem<'a> { let abs_path = prefix.abs_path(&relpath); let backup = temp_cfg.new_directory()?; if !utils::path_exists(&abs_path) { - Err(ErrorKind::ComponentMissingDir { + Err(RustupError::ComponentMissingDir { name: component.to_owned(), path: relpath, } diff --git a/src/dist/config.rs b/src/dist/config.rs index 8f37e143ee..ff10a7d825 100644 --- a/src/dist/config.rs +++ b/src/dist/config.rs @@ -1,3 +1,5 @@ +use anyhow::{bail, Context, Result}; + use super::manifest::Component; use crate::errors::*; use crate::utils::toml_utils::*; @@ -15,7 +17,7 @@ impl Config { pub fn from_toml(mut table: toml::value::Table, path: &str) -> Result { let config_version = get_string(&mut table, "config_version", path)?; if !SUPPORTED_CONFIG_VERSIONS.contains(&&*config_version) { - return Err(ErrorKind::UnsupportedVersion(config_version).into()); + bail!(RustupError::UnsupportedVersion(config_version)); } let components = get_array(&mut table, "components", path)?; @@ -41,7 +43,7 @@ impl Config { } pub fn parse(data: &str) -> Result { - let value = toml::from_str(data).map_err(ErrorKind::Parsing)?; + let value = toml::from_str(data).context("error parsing manifest")?; Self::from_toml(value, "") } diff --git a/src/dist/dist.rs b/src/dist/dist.rs index 4b0f486c30..95b5454499 100644 --- a/src/dist/dist.rs +++ b/src/dist/dist.rs @@ -1,21 +1,25 @@ +use std::collections::HashSet; use std::env; use std::fmt; +use std::io::Write; use std::ops::Deref; use std::path::Path; use std::str::FromStr; +use anyhow::{anyhow, bail, Context, Result}; use chrono::{Date, NaiveDate, TimeZone, Utc}; use lazy_static::lazy_static; use regex::Regex; +use thiserror::Error as ThisError; use crate::dist::download::DownloadCfg; -use crate::dist::manifest::Manifest as ManifestV2; +use crate::dist::manifest::{Component, Manifest as ManifestV2}; use crate::dist::manifestation::{Changes, Manifestation, UpdateStatus}; use crate::dist::notifications::*; use crate::dist::prefix::InstallPrefix; use crate::dist::temp; pub use crate::dist::triple::*; -use crate::errors::*; +use crate::errors::RustupError; use crate::process; use crate::utils::utils; @@ -33,6 +37,62 @@ static TOOLCHAIN_CHANNELS: &[&str] = &[ r"\d{1}\.\d{1,3}(?:\.\d{1,2})?", ]; +fn components_missing_msg(cs: &[Component], manifest: &ManifestV2, toolchain: &str) -> String { + assert!(!cs.is_empty()); + let mut buf = vec![]; + let suggestion = format!(" rustup toolchain add {} --profile minimal", toolchain); + let nightly_tips = "Sometimes not all components are available in any given nightly. "; + + if cs.len() == 1 { + let _ = writeln!( + buf, + "component {} is unavailable for download for channel '{}'", + &cs[0].description(manifest), + toolchain, + ); + + if toolchain.starts_with("nightly") { + let _ = write!(buf, "{}", nightly_tips.to_string()); + } + + let _ = write!( + buf, + "If you don't need the component, you could try a minimal installation with:\n\n{}", + suggestion + ); + } else { + let cs_str = cs + .iter() + .map(|c| c.description(manifest)) + .collect::>() + .join(", "); + let _ = write!( + buf, + "some components unavailable for download for channel '{}': {}", + toolchain, cs_str + ); + + if toolchain.starts_with("nightly") { + let _ = write!(buf, "{}", nightly_tips.to_string()); + } + let _ = write!( + buf, + "If you don't need the components, you could try a minimal installation with:\n\n{}", + suggestion + ); + } + + String::from_utf8(buf).unwrap() +} + +#[derive(Debug, ThisError)] +enum DistError { + #[error("{}", components_missing_msg(&.0, &.1, &.2))] + ToolchainComponentsMissing(Vec, ManifestV2, String), + #[error("no release found for '{0}'")] + MissingReleaseForToolchain(String), +} + #[derive(Debug, PartialEq)] struct ParsedToolchainDesc { channel: String, @@ -93,7 +153,7 @@ static TRIPLE_MIPS64_UNKNOWN_LINUX_GNUABI64: &str = "mips64-unknown-linux-gnuabi static TRIPLE_MIPS64_UNKNOWN_LINUX_GNUABI64: &str = "mips64el-unknown-linux-gnuabi64"; impl FromStr for ParsedToolchainDesc { - type Err = Error; + type Err = anyhow::Error; fn from_str(desc: &str) -> Result { lazy_static! { static ref TOOLCHAIN_CHANNEL_PATTERN: String = format!( @@ -124,7 +184,7 @@ impl FromStr for ParsedToolchainDesc { if let Some(d) = d { Ok(d) } else { - Err(ErrorKind::InvalidToolchainName(desc.to_string()).into()) + Err(RustupError::InvalidToolchainName(desc.to_string()).into()) } } } @@ -254,7 +314,7 @@ impl std::convert::TryFrom for TargetTriple { } impl FromStr for PartialToolchainDesc { - type Err = Error; + type Err = anyhow::Error; fn from_str(name: &str) -> Result { let parsed: ParsedToolchainDesc = name.parse()?; let target = PartialTargetTriple::new(parsed.target.as_deref().unwrap_or("")); @@ -265,29 +325,29 @@ impl FromStr for PartialToolchainDesc { date: parsed.date, target, }) - .ok_or_else(|| ErrorKind::InvalidToolchainName(name.to_string()).into()) + .ok_or_else(|| anyhow!(RustupError::InvalidToolchainName(name.to_string()))) } } impl PartialToolchainDesc { pub fn resolve(self, input_host: &TargetTriple) -> Result { let host = PartialTargetTriple::new(&input_host.0).ok_or_else(|| { - format!( + anyhow!(format!( "Provided host '{}' couldn't be converted to partial triple", input_host.0 - ) + )) })?; let host_arch = host.arch.ok_or_else(|| { - format!( + anyhow!(format!( "Provided host '{}' did not specify a CPU architecture", input_host.0 - ) + )) })?; let host_os = host.os.ok_or_else(|| { - format!( + anyhow!(format!( "Provided host '{}' did not specify an operating system", input_host.0 - ) + )) })?; let host_env = host.env; @@ -320,12 +380,12 @@ impl PartialToolchainDesc { } impl FromStr for ToolchainDesc { - type Err = Error; + type Err = anyhow::Error; fn from_str(name: &str) -> Result { let parsed: ParsedToolchainDesc = name.parse()?; if parsed.target.is_none() { - return Err(ErrorKind::InvalidToolchainName(name.to_string()).into()); + return Err(anyhow!(RustupError::InvalidToolchainName(name.to_string()))); } Ok(Self { @@ -390,7 +450,7 @@ impl ToolchainDesc { pub fn validate_channel_name(name: &str) -> Result<()> { let toolchain = PartialToolchainDesc::from_str(&name)?; if toolchain.has_triple() { - Err(format!("target triple in channel name '{}'", name).into()) + Err(anyhow!(format!("target triple in channel name '{}'", name))) } else { Ok(()) } @@ -399,24 +459,6 @@ pub fn validate_channel_name(name: &str) -> Result<()> { #[derive(Debug)] pub struct Manifest<'a>(temp::File<'a>, String); -impl<'a> Manifest<'a> { - pub fn package_url( - &self, - package: &str, - target_triple: &str, - ext: &str, - ) -> Result> { - let suffix = target_triple.to_owned() + ext; - utils::match_file("manifest", &self.0, |line| { - if line.starts_with(package) && line.ends_with(&suffix) { - Some(format!("{}/{}", &self.1, line)) - } else { - None - } - }) - } -} - #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] pub enum Profile { Minimal, @@ -425,14 +467,18 @@ pub enum Profile { } impl FromStr for Profile { - type Err = Error; + type Err = anyhow::Error; fn from_str(name: &str) -> Result { match name { "minimal" | "m" => Ok(Self::Minimal), "default" | "d" | "" => Ok(Self::Default), "complete" | "c" => Ok(Self::Complete), - _ => Err(ErrorKind::InvalidProfile(name.to_owned()).into()), + _ => Err(anyhow!(format!( + "invalid profile name: '{}'; valid names are: {}", + name, + valid_profile_names() + ))), } } } @@ -503,6 +549,14 @@ impl fmt::Display for Profile { } } +pub fn valid_profile_names() -> String { + Profile::names() + .iter() + .map(|s| format!("'{}'", s)) + .collect::>() + .join(", ") +} + // Installs or updates a toolchain from a dist server. If an initial // install then it will be installed with the default components. If // an upgrade then all the existing components will be upgraded. @@ -625,30 +679,33 @@ fn update_from_dist_<'a>( break Err(e); } - if let ErrorKind::ToolchainComponentsMissing(components, manifest, ..) = e.kind() { - (download.notify_handler)(Notification::SkippingNightlyMissingComponent( - &toolchain, - current_manifest.as_ref().unwrap_or(manifest), - components, - )); + let cause = e.downcast_ref::(); + match cause { + Some(DistError::ToolchainComponentsMissing(components, manifest, ..)) => { + (download.notify_handler)(Notification::SkippingNightlyMissingComponent( + &toolchain, + current_manifest.as_ref().unwrap_or(manifest), + components, + )); + + if first_err.is_none() { + first_err = Some(e); + } + // We decrement the backtrack count only on unavailable component errors + // so that the limit only applies to nightlies that were indeed available, + // and ignores missing ones. + backtrack_limit = backtrack_limit.map(|n| n - 1); + } - if first_err.is_none() { - first_err = Some(e); + Some(DistError::MissingReleaseForToolchain(..)) => { + // no need to even print anything for missing nightlies, + // since we don't really "skip" them } - // We decrement the backtrack count only on unavailable component errors - // so that the limit only applies to nightlies that were indeed available, - // and ignores missing ones. - backtrack_limit = backtrack_limit.map(|n| n - 1); - } else if let ErrorKind::MissingReleaseForToolchain(..) = e.kind() { - // no need to even print anything for missing nightlies, - // since we don't really "skip" them - } else if let Some(e) = first_err { - // if we fail to find a suitable nightly, we abort the search and give the - // original "components unavailable for download" error. - break Err(e); - } else { - break Err(e); - } + None => { + // All other errors break the loop + break Err(e); + } + }; if let Some(backtrack_limit) = backtrack_limit { if backtrack_limit < 1 { @@ -723,9 +780,6 @@ fn try_update_from_dist_<'a>( None => Vec::new(), }; - use crate::dist::manifest::Component; - use std::collections::HashSet; - let mut all_components: HashSet = profile_components.into_iter().collect(); let rust_package = m.get_package("rust")?; @@ -780,70 +834,100 @@ fn try_update_from_dist_<'a>( UpdateStatus::Unchanged => Ok(None), UpdateStatus::Changed => Ok(Some(hash)), }, - Err(err) => { - return if let ErrorKind::RequestedComponentsUnavailable( - cs, + Err(err) => match err.downcast_ref::() { + Some(RustupError::RequestedComponentsUnavailable { + components, manifest, - toolchain_str, - ) = err.kind() - { - Err(ErrorKind::ToolchainComponentsMissing( - cs.to_owned(), - manifest.to_owned(), - toolchain_str.to_owned(), - ) - .into()) - } else { - Err(err) - } - } + toolchain, + }) => Err(anyhow!(DistError::ToolchainComponentsMissing( + components.to_owned(), + manifest.to_owned(), + toolchain.to_owned(), + ))), + Some(_) | None => Err(err), + }, }; } Ok(None) => return Ok(None), - Err(Error(crate::ErrorKind::DownloadNotExists { .. }, _)) => { - // Proceed to try v1 as a fallback - (download.notify_handler)(Notification::DownloadingLegacyManifest); + Err(any) => { + enum Cases { + DNE, + CF, + Other, + } + let case = match any.downcast_ref::() { + Some(RustupError::ChecksumFailed { .. }) => Cases::CF, + Some(RustupError::DownloadNotExists { .. }) => Cases::DNE, + _ => Cases::Other, + }; + match case { + Cases::CF => return Ok(None), + Cases::DNE => { + // Proceed to try v1 as a fallback + (download.notify_handler)(Notification::DownloadingLegacyManifest); + } + Cases::Other => return Err(any), + } } - Err(Error(ErrorKind::ChecksumFailed { .. }, _)) => return Ok(None), - Err(e) => return Err(e), } // If the v2 manifest is not found then try v1 let manifest = match dl_v1_manifest(download, toolchain) { Ok(m) => m, - Err(Error(crate::ErrorKind::DownloadNotExists { .. }, _)) => { - return Err(Error::from(ErrorKind::MissingReleaseForToolchain( - toolchain.manifest_name(), - ))); - } - Err(e @ Error(ErrorKind::ChecksumFailed { .. }, _)) => { - return Err(e); - } - Err(e) => { - return Err(e).chain_err(|| { - format!( - "failed to download manifest for '{}'", - toolchain.manifest_name() - ) - }); + Err(any) => { + enum Cases { + DNE, + CF, + Other, + } + let case = match any.downcast_ref::() { + Some(RustupError::ChecksumFailed { .. }) => Cases::CF, + Some(RustupError::DownloadNotExists { .. }) => Cases::DNE, + _ => Cases::Other, + }; + match case { + Cases::DNE => { + bail!(DistError::MissingReleaseForToolchain( + toolchain.manifest_name() + )); + } + Cases::CF => return Err(any), + Cases::Other => { + return Err(any).with_context(|| { + format!( + "failed to download manifest for '{}'", + toolchain.manifest_name() + ) + }); + } + } } }; - match manifestation.update_v1( + let result = manifestation.update_v1( &manifest, update_hash, &download.temp_cfg, &download.notify_handler, &download.pgp_keys, - ) { - Ok(None) => Ok(None), - Ok(Some(hash)) => Ok(Some(hash)), - e @ Err(Error(crate::ErrorKind::DownloadNotExists { .. }, _)) => e.chain_err(|| { + ); + // inspect, determine what context to add, then process afterwards. + let mut download_not_exists = false; + match &result { + Ok(_) => (), + Err(e) => match e.downcast_ref::() { + Some(RustupError::DownloadNotExists { .. }) => download_not_exists = true, + _ => (), + }, + } + if download_not_exists { + result.with_context(|| { format!( "could not download nonexistent rust version `{}`", toolchain_str ) - }), - Err(e) => Err(e), + }) + } else { + result } } @@ -853,25 +937,31 @@ pub fn dl_v2_manifest<'a>( toolchain: &ToolchainDesc, ) -> Result> { let manifest_url = toolchain.manifest_v2_url(download.dist_root); - let manifest_dl_res = download.download_and_check(&manifest_url, update_hash, ".toml"); - - if let Ok(manifest_dl) = manifest_dl_res { - // Downloaded ok! - let (manifest_file, manifest_hash) = if let Some(m) = manifest_dl { - m - } else { - return Ok(None); - }; - let manifest_str = utils::read_file("manifest", &manifest_file)?; - let manifest = ManifestV2::parse(&manifest_str)?; + match download.download_and_check(&manifest_url, update_hash, ".toml") { + Ok(manifest_dl) => { + // Downloaded ok! + let (manifest_file, manifest_hash) = if let Some(m) = manifest_dl { + m + } else { + return Ok(None); + }; + let manifest_str = utils::read_file("manifest", &manifest_file)?; + let manifest = ManifestV2::parse(&manifest_str)?; - Ok(Some((manifest, manifest_hash))) - } else { - // Checksum failed - issue warning to try again later - if let ErrorKind::ChecksumFailed { .. } = manifest_dl_res.as_ref().unwrap_err().kind() { - (download.notify_handler)(Notification::ManifestChecksumFailedHack) + Ok(Some((manifest, manifest_hash))) + } + Err(any) => { + match any.downcast_ref::() { + Some(e) => { + // Checksum failed - issue warning to try again later + if let RustupError::ChecksumFailed { .. } = e { + (download.notify_handler)(Notification::ManifestChecksumFailedHack); + } + } + None => (), + } + Err(any) } - Err(manifest_dl_res.unwrap_err()) } } diff --git a/src/dist/download.rs b/src/dist/download.rs index e584f6a38a..d57ba846ba 100644 --- a/src/dist/download.rs +++ b/src/dist/download.rs @@ -1,16 +1,17 @@ +use std::fs; +use std::ops; +use std::path::{Path, PathBuf}; + +use anyhow::{anyhow, Context, Result}; +use sha2::{Digest, Sha256}; +use url::Url; + use crate::config::PgpPublicKey; use crate::dist::notifications::*; use crate::dist::temp; use crate::errors::*; use crate::utils::utils; -use sha2::{Digest, Sha256}; -use url::Url; - -use std::fs; -use std::ops; -use std::path::{Path, PathBuf}; - const UPDATE_HASH_LEN: usize = 20; #[derive(Copy, Clone)] @@ -55,7 +56,7 @@ impl<'a> DownloadCfg<'a> { return Ok(File { path: target_file }); } else { (self.notify_handler)(Notification::CachedFileChecksumFailed); - fs::remove_file(&target_file).chain_err(|| "cleaning up previous download")?; + fs::remove_file(&target_file).context("cleaning up previous download")?; } } @@ -79,10 +80,11 @@ impl<'a> DownloadCfg<'a> { true, &|n| (self.notify_handler)(n.into()), ) { + let err = Err(e); if partial_file_existed { - return Err(e).chain_err(|| ErrorKind::BrokenPartialFile); + return err.context(RustupError::BrokenPartialFile); } else { - return Err(e); + return err; } }; @@ -92,9 +94,9 @@ impl<'a> DownloadCfg<'a> { // Incorrect hash if partial_file_existed { self.clean(&[hash.to_string() + &".partial".to_string()])?; - Err(ErrorKind::BrokenPartialFile.into()) + Err(anyhow!(RustupError::BrokenPartialFile)) } else { - Err(ErrorKind::ChecksumFailed { + Err(RustupError::ChecksumFailed { url: url.to_string(), expected: hash.to_string(), calculated: actual_hash, @@ -118,7 +120,7 @@ impl<'a> DownloadCfg<'a> { for hash in hashes.iter() { let used_file = self.download_dir.join(hash); if self.download_dir.join(&used_file).exists() { - fs::remove_file(used_file).chain_err(|| "cleaning up cached downloads")?; + fs::remove_file(used_file).context("cleaning up cached downloads")?; } } Ok(()) @@ -152,14 +154,12 @@ impl<'a> DownloadCfg<'a> { "At least the builtin key must be present" ); - let signature = self.download_signature(url).map_err(|e| { - e.chain_err(|| ErrorKind::SignatureVerificationFailed { - url: url.to_owned(), - }) - })?; + let signature = self + .download_signature(url) + .with_context(|| format!("failed to download signature file {}", url))?; let file_path: &Path = &file; - let content = std::fs::File::open(file_path).chain_err(|| ErrorKind::ReadingFile { + let content = std::fs::File::open(file_path).with_context(|| RustupError::ReadingFile { name: "channel data", path: PathBuf::from(file_path), })?; @@ -170,10 +170,10 @@ impl<'a> DownloadCfg<'a> { let key = &self.pgp_keys[keyidx]; Ok(key) } else { - Err(ErrorKind::SignatureVerificationFailed { - url: url.to_owned(), - } - .into()) + Err(anyhow!(format!( + "signature verification failed for {}", + url + ))) } } @@ -217,7 +217,7 @@ impl<'a> DownloadCfg<'a> { if hash != actual_hash { // Incorrect hash - return Err(ErrorKind::ChecksumFailed { + return Err(RustupError::ChecksumFailed { url: url_str.to_owned(), expected: hash, calculated: actual_hash, diff --git a/src/dist/manifest.rs b/src/dist/manifest.rs index 3af5385a57..9419e73977 100644 --- a/src/dist/manifest.rs +++ b/src/dist/manifest.rs @@ -12,14 +12,16 @@ //! //! Docs: https://forge.rust-lang.org/infra/channel-layout.html -use crate::errors::*; -use crate::utils::toml_utils::*; - -use crate::dist::dist::{PartialTargetTriple, Profile, TargetTriple}; use std::collections::HashMap; use std::hash::{Hash, Hasher}; use std::str::FromStr; +use anyhow::{anyhow, bail, Context, Result}; + +use crate::dist::dist::{PartialTargetTriple, Profile, TargetTriple}; +use crate::errors::*; +use crate::utils::toml_utils::*; + pub const SUPPORTED_MANIFEST_VERSIONS: [&str; 1] = ["2"]; pub const DEFAULT_MANIFEST_VERSION: &str = "2"; @@ -109,7 +111,7 @@ impl Hash for Component { impl Manifest { pub fn parse(data: &str) -> Result { - let value = toml::from_str(data).map_err(ErrorKind::Parsing)?; + let value = toml::from_str(data).context("error parsing manifest")?; let manifest = Self::from_toml(value, "")?; manifest.validate()?; @@ -122,7 +124,7 @@ impl Manifest { pub fn from_toml(mut table: toml::value::Table, path: &str) -> Result { let version = get_string(&mut table, "manifest-version", path)?; if !SUPPORTED_MANIFEST_VERSIONS.contains(&&*version) { - return Err(ErrorKind::UnsupportedVersion(version).into()); + bail!(RustupError::UnsupportedVersion(version)); } let (renames, reverse_renames) = Self::table_to_renames(&mut table, path)?; Ok(Self { @@ -243,7 +245,7 @@ impl Manifest { pub fn get_package(&self, name: &str) -> Result<&Package> { self.packages .get(name) - .ok_or_else(|| format!("package not found: '{}'", name).into()) + .ok_or_else(|| anyhow!(format!("package not found: '{}'", name))) } pub fn get_rust_version(&self) -> Result<&str> { @@ -276,7 +278,7 @@ impl Manifest { let profile = self .profiles .get(&profile) - .ok_or_else(|| format!("profile not found: '{}'", profile))?; + .ok_or_else(|| anyhow!(format!("profile not found: '{}'", profile)))?; let rust_pkg = self.get_package("rust")?.get_target(Some(target))?; let result = profile @@ -299,10 +301,10 @@ impl Manifest { for c in tpkg.components.iter() { let cpkg = self .get_package(&c.pkg) - .chain_err(|| ErrorKind::MissingPackageForComponent(c.short_name(self)))?; + .with_context(|| RustupError::MissingPackageForComponent(c.short_name(self)))?; let _ctpkg = cpkg .get_target(c.target.as_ref()) - .chain_err(|| ErrorKind::MissingPackageForComponent(c.short_name(self)))?; + .with_context(|| RustupError::MissingPackageForComponent(c.short_name(self)))?; } Ok(()) } @@ -326,7 +328,10 @@ impl Manifest { // renames is unconstrained. for name in self.renames.values() { if !self.packages.contains_key(name) { - return Err(ErrorKind::MissingPackageForRename(name.clone()).into()); + bail!(format!( + "server sent a broken manifest: missing package for the target of a rename {}", + name + )); } } @@ -401,10 +406,10 @@ impl Package { if let Some(t) = target { tpkgs .get(t) - .ok_or_else(|| format!("target '{}' not found in channel. \ - Perhaps check https://doc.rust-lang.org/nightly/rustc/platform-support.html for available targets", t).into()) + .ok_or_else(|| anyhow!(format!("target '{}' not found in channel. \ + Perhaps check https://doc.rust-lang.org/nightly/rustc/platform-support.html for available targets", t))) } else { - Err("no target specified".into()) + Err(anyhow!("no target specified")) } } } diff --git a/src/dist/manifestation.rs b/src/dist/manifestation.rs index 5a9f369e0e..33f071b5da 100644 --- a/src/dist/manifestation.rs +++ b/src/dist/manifestation.rs @@ -3,6 +3,7 @@ use std::path::Path; +use anyhow::{anyhow, bail, Context, Result}; use retry::delay::NoDelay; use retry::{retry, OperationResult}; @@ -17,7 +18,7 @@ use crate::dist::manifest::{Component, CompressionKind, Manifest, TargetedPackag use crate::dist::notifications::*; use crate::dist::prefix::InstallPrefix; use crate::dist::temp; -use crate::errors::*; +use crate::errors::{OperationError, RustupError}; use crate::process; use crate::utils::utils; @@ -51,7 +52,7 @@ impl Changes { fn check_invariants(&self, config: &Option) -> Result<()> { for component_to_add in self.iter_add_components() { if self.remove_components.contains(component_to_add) { - return Err("can't both add and remove components".into()); + bail!("can't both add and remove components"); } } for component_to_remove in &self.remove_components { @@ -133,8 +134,10 @@ impl Manifestation { Ok(_) => {} Err(e) => { if force_update { - if let ErrorKind::RequestedComponentsUnavailable(components, _, _) = e.kind() { - for component in components { + if let Ok(RustupError::RequestedComponentsUnavailable { components, .. }) = + e.downcast::() + { + for component in &components { notify_handler(Notification::ForcingUnavailableComponent( component.name(new_manifest).as_str(), )); @@ -178,18 +181,24 @@ impl Manifestation { let downloaded_file = retry(NoDelay.take(max_retries), || { match download_cfg.download(&url_url, &hash) { Ok(f) => OperationResult::Ok(f), - Err(e) => match e.kind() { - // If there was a broken partial file, try again - ErrorKind::DownloadingFile { .. } | ErrorKind::BrokenPartialFile => { - notify_handler(Notification::RetryingDownload(&url)); - OperationResult::Retry(e) - } - - _ => OperationResult::Err(e), - }, + Err(e) => { + match e.downcast_ref::() { + Some(RustupError::BrokenPartialFile) => { + notify_handler(Notification::RetryingDownload(&url)); + return OperationResult::Retry(OperationError(e)); + } + Some(RustupError::DownloadingFile { .. }) => { + notify_handler(Notification::RetryingDownload(&url)); + return OperationResult::Retry(OperationError(e)); + } + Some(_) => return OperationResult::Err(OperationError(e)), + None => (), + }; + OperationResult::Err(OperationError(e)) + } } }) - .chain_err(|| ErrorKind::ComponentDownloadFailed(component.name(new_manifest)))?; + .with_context(|| RustupError::ComponentDownloadFailed(component.name(new_manifest)))?; things_downloaded.push(hash); @@ -261,7 +270,7 @@ impl Manifestation { // If the package doesn't contain the component that the // manifest says it does then somebody must be playing a joke on us. if !package.contains(&pkg_name, Some(&short_pkg_name)) { - return Err(ErrorKind::CorruptComponent(short_name).into()); + return Err(RustupError::CorruptComponent(short_name).into()); } tx = package.install(&self.installation, &pkg_name, Some(&short_pkg_name), tx)?; @@ -380,21 +389,19 @@ impl Manifestation { ) -> Result> { // If there's already a v2 installation then something has gone wrong if self.read_config()?.is_some() { - return Err( + return Err(anyhow!( "the server unexpectedly provided an obsolete version of the distribution manifest" - .into(), - ); + )); } let url = new_manifest .iter() .find(|u| u.contains(&format!("{}{}", self.target_triple, ".tar.gz"))); if url.is_none() { - return Err(format!( + return Err(anyhow!( "binary package was not provided for '{}'", self.target_triple.to_string() - ) - .into()); + )); } // Only replace once. The cost is inexpensive. let url = url @@ -655,12 +662,11 @@ impl Update { unavailable_components.extend_from_slice(&self.missing_components); if !unavailable_components.is_empty() { - return Err(ErrorKind::RequestedComponentsUnavailable( - unavailable_components, - new_manifest.clone(), - toolchain_str.to_owned(), - ) - .into()); + bail!(RustupError::RequestedComponentsUnavailable { + components: unavailable_components, + manifest: new_manifest.clone(), + toolchain: toolchain_str.to_owned(), + }); } Ok(()) diff --git a/src/dist/notifications.rs b/src/dist/notifications.rs index 267fe74260..21d8a074b6 100644 --- a/src/dist/notifications.rs +++ b/src/dist/notifications.rs @@ -2,7 +2,6 @@ use crate::config::PgpPublicKey; use crate::dist::dist::{TargetTriple, ToolchainDesc}; use crate::dist::manifest::Component; use crate::dist::temp; -use crate::errors::*; use crate::utils::notify::NotificationLevel; use std::fmt::{self, Display}; use std::path::Path; @@ -24,7 +23,7 @@ pub enum Notification<'a> { CachedFileChecksumFailed, RollingBack, ExtensionNotInstalled(&'a str), - NonFatalError(&'a Error), + NonFatalError(&'a anyhow::Error), MissingInstalledComponent(&'a str), DownloadingComponent(&'a str, &'a TargetTriple, Option<&'a TargetTriple>), InstallingComponent(&'a str, &'a TargetTriple, Option<&'a TargetTriple>), diff --git a/src/dist/signatures.rs b/src/dist/signatures.rs index 57efe5cb4c..0b3a0b927c 100644 --- a/src/dist/signatures.rs +++ b/src/dist/signatures.rs @@ -2,17 +2,13 @@ // TODO: Determine whether we want external keyring support +use std::io::Read; + +use anyhow::{Context, Result}; use pgp::types::KeyTrait; use pgp::{Deserializable, StandaloneSignature}; use crate::config::PgpPublicKey; -use crate::errors::*; - -use std::io::Read; - -fn squish_internal_err(err: E) -> Error { - ErrorKind::SignatureVerificationInternalError(format!("{}", err)).into() -} pub fn verify_signature( mut content: T, @@ -22,10 +18,10 @@ pub fn verify_signature( let mut content_buf = Vec::new(); content.read_to_end(&mut content_buf)?; let (signatures, _) = - StandaloneSignature::from_string_many(signature).map_err(squish_internal_err)?; + StandaloneSignature::from_string_many(signature).context("error verifying signature")?; for signature in signatures { - let signature = signature.map_err(squish_internal_err)?; + let signature = signature.context("error verifying signature")?; for (idx, key) in keys.iter().enumerate() { let actual_key = key.key(); diff --git a/src/dist/temp.rs b/src/dist/temp.rs index 8318c3e6e6..4e07fd2e1f 100644 --- a/src/dist/temp.rs +++ b/src/dist/temp.rs @@ -1,23 +1,26 @@ -use crate::utils::raw; -use crate::utils::utils; -use std::error; use std::fmt::{self, Display}; use std::fs; use std::io; use std::ops; use std::path::{Path, PathBuf}; +pub use anyhow::{Context, Result}; +use thiserror::Error as ThisError; + use crate::utils::notify::NotificationLevel; +use crate::utils::raw; +use crate::utils::utils; -#[derive(Debug)] +#[derive(Debug, ThisError)] pub enum Error { - CreatingRoot { path: PathBuf, error: io::Error }, - CreatingFile { path: PathBuf, error: io::Error }, - CreatingDirectory { path: PathBuf, error: io::Error }, + #[error("could not create temp root {}" ,.0.display())] + CreatingRoot(PathBuf), + #[error("could not create temp file {}",.0.display())] + CreatingFile(PathBuf), + #[error("could not create temp directory {}",.0.display())] + CreatingDirectory(PathBuf), } -pub type Result = std::result::Result; - #[derive(Debug)] pub enum Notification<'a> { CreatingRoot(&'a Path), @@ -86,43 +89,6 @@ impl<'a> Display for Notification<'a> { } } -impl error::Error for Error { - fn description(&self) -> &str { - use self::Error::*; - match self { - CreatingRoot { .. } => "could not create temp root", - CreatingFile { .. } => "could not create temp file", - CreatingDirectory { .. } => "could not create temp directory", - } - } - - fn cause(&self) -> Option<&dyn error::Error> { - use self::Error::*; - match self { - CreatingRoot { error, .. } - | CreatingFile { error, .. } - | CreatingDirectory { error, .. } => Some(error), - } - } -} - -impl Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> { - use self::Error::*; - match self { - CreatingRoot { path, .. } => { - write!(f, "could not create temp root: {}", path.display()) - } - CreatingFile { path, .. } => { - write!(f, "could not create temp file: {}", path.display()) - } - CreatingDirectory { path, .. } => { - write!(f, "could not create temp directory: {}", path.display()) - } - } - } -} - impl Cfg { pub fn new( root_directory: PathBuf, @@ -140,10 +106,7 @@ impl Cfg { raw::ensure_dir_exists(&self.root_directory, |p| { (self.notify_handler)(Notification::CreatingRoot(p)); }) - .map_err(|e| Error::CreatingRoot { - path: PathBuf::from(&self.root_directory), - error: e, - }) + .with_context(|| Error::CreatingRoot(PathBuf::from(&self.root_directory))) } pub fn new_directory(&self) -> Result> { @@ -158,10 +121,8 @@ impl Cfg { // random names at exactly the same time is... low. if !raw::path_exists(&temp_dir) { (self.notify_handler)(Notification::CreatingDirectory(&temp_dir)); - fs::create_dir(&temp_dir).map_err(|e| Error::CreatingDirectory { - path: PathBuf::from(&temp_dir), - error: e, - })?; + fs::create_dir(&temp_dir) + .with_context(|| Error::CreatingDirectory(PathBuf::from(&temp_dir)))?; return Ok(Dir { cfg: self, path: temp_dir, @@ -186,10 +147,8 @@ impl Cfg { // random names at exactly the same time is... low. if !raw::path_exists(&temp_file) { (self.notify_handler)(Notification::CreatingFile(&temp_file)); - fs::File::create(&temp_file).map_err(|e| Error::CreatingFile { - path: PathBuf::from(&temp_file), - error: e, - })?; + fs::File::create(&temp_file) + .with_context(|| Error::CreatingFile(PathBuf::from(&temp_file)))?; return Ok(File { cfg: self, path: temp_file, diff --git a/src/errors.rs b/src/errors.rs index b867eda33c..443b673cf7 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,19 +1,14 @@ #![allow(clippy::large_enum_variant)] -#![allow(deprecated)] // because of `Error::description` deprecation in `error_chain` use std::ffi::OsString; -use std::fmt::{self, Debug, Display}; +use std::fmt::Debug; use std::io::{self, Write}; use std::path::PathBuf; -use std::sync::{Arc, Mutex, MutexGuard, Weak}; -use error_chain::error_chain; +use thiserror::Error as ThisError; use url::Url; -use crate::dist::dist::Profile; use crate::dist::manifest::{Component, Manifest}; -use crate::dist::temp; -use crate::{component_for_bin, Toolchain}; pub const TOOLSTATE_MSG: &str = "If you require these components, please install and use the latest successful build version,\n\ @@ -23,539 +18,98 @@ pub const TOOLSTATE_MSG: &str = Then you can use the toolchain with commands such as:\n\n \ cargo +nightly-2018-12-27 build"; -error_chain! { - links { - Download(download::Error, download::ErrorKind); - } - - foreign_links { - Temp(temp::Error); - Io(io::Error); - Open(opener::OpenError); - Thread(std::sync::mpsc::RecvError); - } - - errors { - LocatingWorkingDir { - description("Unable to proceed. Could not locate working directory.") - } - ReadingFile { - name: &'static str, - path: PathBuf, - } { - description("could not read file") - display("could not read {} file: '{}'", name, path.display()) - } - ReadingDirectory { - name: &'static str, - path: PathBuf, - } { - description("could not read directory") - display("could not read {} directory: '{}'", name, path.display()) - } - WritingFile { - name: &'static str, - path: PathBuf, - } { - description("could not write file") - display("could not write {} file: '{}'", name, path.display()) - } - CreatingDirectory { - name: &'static str, - path: PathBuf, - } { - description("could not create directory") - display("could not create {} directory: '{}'", name, path.display()) - } - ExpectedType(t: &'static str, n: String) { - description("expected type") - display("expected type: '{}' for '{}'", t, n) - } - FilteringFile { - name: &'static str, - src: PathBuf, - dest: PathBuf, - } { - description("could not copy file") - display("could not copy {} file from '{}' to '{}'", name, src.display(), dest.display()) - } - RenamingFile { - name: &'static str, - src: PathBuf, - dest: PathBuf, - } { - description("could not rename file") - display("could not rename {} file from '{}' to '{}'", - name, src.display(), dest.display()) - } - RenamingDirectory { - name: &'static str, - src: PathBuf, - dest: PathBuf, - } { - description("could not rename directory") - display("could not rename {} directory from '{}' to '{}'", name, src.display(), dest.display()) - } - DownloadingFile { - url: Url, - path: PathBuf, - } { - description("could not download file") - display("could not download file from '{}' to '{}'", url, path.display()) - } - DownloadNotExists { - url: Url, - path: PathBuf, - } { - description("could not download file") - display("could not download file from '{}' to '{}'", url, path.display()) - } - DisconnectedChannel (v: PathBuf) { - description("IO channel disconnected") - display("IO receiver for '{}' disconnected", v.display()) - } - InvalidUrl { - url: String, - } { - description("invalid url") - display("invalid url: {}", url) - } - RunningCommand { - name: OsString, - } { - description("command failed") - display("command failed: '{}'", PathBuf::from(name).display()) - } - NotAFile { - path: PathBuf, - } { - description("not a file") - display("not a file: '{}'", path.display()) - } - NotADirectory { - path: PathBuf, - } { - description("not a directory") - display("not a directory: '{}'", path.display()) - } - LinkingFile { - src: PathBuf, - dest: PathBuf, - } { - description("could not link file") - display("could not create link from '{}' to '{}'", src.display(), dest.display()) - } - LinkingDirectory { - src: PathBuf, - dest: PathBuf, - } { - description("could not symlink directory") - display("could not create link from '{}' to '{}'", src.display(), dest.display()) - } - CopyingDirectory { - src: PathBuf, - dest: PathBuf, - } { - description("could not copy directory") - display("could not copy directory from '{}' to '{}'", src.display(), dest.display()) - } - CopyingFile { - src: PathBuf, - dest: PathBuf, - } { - description("could not copy file") - display("could not copy file from '{}' to '{}'", src.display(), dest.display()) - } - RemovingFile { - name: &'static str, - path: PathBuf, - } { - description("could not remove file") - display("could not remove '{}' file: '{}'", name, path.display()) - } - RemovingDirectory { - name: &'static str, - path: PathBuf, - } { - description("could not remove directory") - display("could not remove '{}' directory: '{}'", name, path.display()) - } - SettingPermissions { - path: PathBuf, - } { - description("failed to set permissions") - display("failed to set permissions for '{}'", path.display()) - } - CargoHome { - description("couldn't find value of CARGO_HOME") - } - RustupHome { - description("couldn't find value of RUSTUP_HOME") - } - InvalidToolchainName(t: String) { - description("invalid toolchain name") - display("invalid toolchain name: '{}'", t) - } - InvalidToolchainPath(p: PathBuf) { - description("invalid toolchain path"), - display("invalid toolchain path: '{}'", p.to_string_lossy()) - } - CannotSpecifyPathAndOptions(path: PathBuf) { - description("toolchain options are ignored for path toolchains"), - display("toolchain options are ignored for path toolchain ({})", path.display()) - } - CannotSpecifyChannelAndPath(channel: String, path: PathBuf) { - description("cannot specify channel and path simultaneously"), - display("cannot specify both channel ({}) and path ({}) simultaneously", channel, path.display()) - } - InvalidProfile(t: String) { - description("invalid profile name") - display("invalid profile name: '{}'; valid names are: {}", t, valid_profile_names()) - } - ChecksumFailed { - url: String, - expected: String, - calculated: String, - } { - description("checksum failed") - display("checksum failed, expected: '{}', calculated: '{}'", - expected, - calculated) - } - SignatureVerificationInternalError(msg: String) { - description("internal error verifying signature") - display("internal error verifying signature: {}", msg) - } - SignatureVerificationFailed { - url: String, - } { - description("signature verification failed") - display("signature verification failed for {}", url) - } - ComponentConflict { - name: String, - path: PathBuf, - } { - description("conflicting component") - display("failed to install component: '{}', detected conflict: '{:?}'", - name, - path) - } - ComponentMissingFile { - name: String, - path: PathBuf, - } { - description("missing file in component") - display("failure removing component '{}', directory does not exist: '{:?}'", - name, - path) - } - ComponentMissingDir { - name: String, - path: PathBuf, - } { - description("missing directory in component") - display("failure removing component '{}', directory does not exist: '{:?}'", - name, - path) - } - CorruptComponent(name: String) { - description("corrupt component manifest") - display("component manifest for '{}' is corrupt", name) - } - ExtractingPackage { - description("failed to extract package (perhaps you ran out of disk space?)") - } - BadInstallerVersion(v: String) { - description("unsupported installer version") - display("unsupported installer version: {}", v) - } - BadInstalledMetadataVersion(v: String) { - description("unsupported metadata version in existing installation") - display("unsupported metadata version in existing installation: {}", v) - } - ComponentDirPermissionsFailed { - description("I/O error walking directory during install") - } - ComponentFilePermissionsFailed { - description("error setting file permissions during install") - } - ComponentDownloadFailed(c: String) { - description("component download failed") - display("component download failed for {}", c) - } - Parsing(e: toml::de::Error) { - description("error parsing manifest") - } - UnsupportedVersion(v: String) { - description("unsupported manifest version") - display("manifest version '{}' is not supported", v) - } - MissingPackageForComponent(name: String) { - description("missing package for component") - display("server sent a broken manifest: missing package for component {}", name) - } - MissingPackageForRename(name: String) { - description("missing package for the target of a rename") - display("server sent a broken manifest: missing package for the target of a rename {}", name) - } - MissingReleaseForToolchain(name: String) { - description("missing release for a toolchain") - display("no release found for '{}'", name) - } - RequestedComponentsUnavailable(c: Vec, manifest: Manifest, toolchain: String) { - description("some requested components are unavailable to download") - display("{}", component_unavailable_msg(&c, &manifest, &toolchain)) - } - ToolchainComponentsMissing(c: Vec, manifest: Manifest,toolchain: String) { - description("at least one of the requested components is unavailable to download") - display("{}", components_missing_msg(&c,&manifest, &toolchain)) - } - UnknownMetadataVersion(v: String) { - description("unknown metadata version") - display("unknown metadata version: '{}'", v) - } - ToolchainNotInstalled(t: String) { - description("toolchain is not installed") - display("toolchain '{}' is not installed", t) - } - ToolchainNotInstallable(t: String) { - description("toolchain is not installable") - display("toolchain '{}' is not installable", t) - } - ToolchainNotSelected { - description("toolchain is not selected") - display("no override and no default toolchain set; run 'rustup default stable' to set the stable toolchain as default") - } - OverrideToolchainNotInstalled(t: String) { - description("override toolchain is not installed") - display("override toolchain '{}' is not installed", t) - } - BinaryNotFound(bin: String, t: String, is_default: bool) { - description("toolchain does not contain binary") - display("'{}' is not installed for the toolchain '{}'{}", bin, t, install_msg(bin, t, *is_default)) - } - BinaryProvidedByUnavailableComponent(component: String, bin: String, toolchain: String) { - description("binary is provided by a component which is not available in current toolchain") - display("the '{}' component which provides the command '{}' is not available for the '{}' toolchain", component, bin, toolchain) - } - BinaryNotProvidedByComponent(component: String, bin: String, toolchain: String) { - description("binary should be provided by component but isn't in current toolchain") - display("the '{}' binary, normally provided by the '{}' component, is not applicable to the '{}' toolchain", bin, component, toolchain) - } - NeedMetadataUpgrade { - description("rustup's metadata is out of date. run `rustup self upgrade-data`") - } - UpgradeIoError { - description("I/O error during upgrade") - } - BadInstallerType(s: String) { - description("invalid extension for installer") - display("invalid extension for installer: '{}'", s) - } - ComponentsUnsupported(t: String) { - description("toolchain does not support components") - display("toolchain '{}' does not support components", t) - } - UnknownComponent(t: String, c: String, s: Option) { - description("toolchain does not contain component") - display("toolchain '{}' does not contain component {}{}{}", t, c, if let Some(suggestion) = s { - format!("; did you mean '{}'?", suggestion) - } else { - "".to_string() - }, if c.contains("rust-std") { - format!("\nnote: not all platforms have the standard library pre-compiled: https://doc.rust-lang.org/nightly/rustc/platform-support.html{}", - if t.contains("nightly") { "\nhelp: consider using `cargo build -Z build-std` instead" } else { "" } - ) - } else { "".to_string() } - ) - } - UnknownProfile(p: String) { - description("unknown profile name") - display( - "unknown profile name: '{}'; valid profile names are {}", - p, - valid_profile_names(), - ) - } - AddingRequiredComponent(t: String, c: String) { - description("required component cannot be added") - display("component {} was automatically added because it is required for toolchain '{}'", - c, t) - } - ParsingFallbackSettings(e: toml::de::Error) { - description("error parsing settings") - } - ParsingSettings(e: toml::de::Error) { - description("error parsing settings") - } - NoExeName { - description("couldn't determine self executable name") - } - UnsupportedKind(v: String) { - description("unsupported tar entry") - display("tar entry kind '{}' is not supported", v) - } - BadPath(v: PathBuf) { - description("bad path in tar") - display("tar path '{}' is not supported", v.display()) - } - InvalidPgpKey(v: PathBuf, error: pgp::errors::Error) { - description("invalid PGP key"), - display("unable to read the PGP key '{}'", v.display()) - } - BrokenPartialFile { - description("partially downloaded file may have been damaged and was removed, please try again") - } - EmptyOverrideFile { - description("empty toolchain override file detected. Please remove it, or else specify the desired toolchain properties in the file") - } - InvalidOverrideFile { - description("missing toolchain properties in toolchain override file") - } - ParsingOverrideFile(e: toml::de::Error) { - description("error parsing override file") - } - } -} - -/// Inspired by failure::SyncFailure, but not identical. -/// -/// SyncError does not grant full safety: it will panic when errors are used -/// across threads (e.g. by threaded error logging libraries). This could be -/// fixed, but as we don't do that within rustup, it is not needed. If using -/// this code elsewhere, just hunt down and remove the unchecked unwraps. -pub struct SyncError { - inner: Arc>, - proxy: Option>, -} - -impl SyncError { - pub fn new(err: T) -> Self { - let arc = Arc::new(Mutex::new(err)); - let proxy = match arc.lock().unwrap().source() { - None => None, - Some(source) => Some(CauseProxy::new(source, Arc::downgrade(&arc), 0)), - }; - - SyncError { inner: arc, proxy } - } - - pub fn maybe(r: std::result::Result) -> std::result::Result { - match r { - Ok(v) => Ok(v), - Err(e) => Err(SyncError::new(e)), - } - } - - pub fn unwrap(self) -> T { - Arc::try_unwrap(self.inner).unwrap().into_inner().unwrap() - } - - pub fn as_ref(&self) -> MutexGuard<'_, T> { - Arc::as_ref(&self.inner).lock().unwrap() - } -} - -impl std::error::Error for SyncError { - #[cfg(backtrace)] - fn backtrace(&self) -> Option<&Backtrace> {} - - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - self.proxy.as_ref().map(|x| x as _) - } -} - -impl Display for SyncError -where - T: Display, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.inner.lock().unwrap().fmt(f) - } -} - -impl Debug for SyncError -where - T: Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.inner.lock().unwrap().fmt(f) - } -} - -struct CauseProxy { - inner: Weak>, - next: Option>>, - depth: u32, -} - -impl CauseProxy { - fn new(err: &dyn std::error::Error, weak: Weak>, depth: u32) -> Self { - // Can't allocate an object, or mutate the proxy safely during source(), - // so we just take the hit at construction, recursively. We can't hold - // references outside the mutex at all, so instead we remember how many - // steps to get to this proxy. And if some error chain plays tricks, the - // user gets both pieces. - CauseProxy { - inner: weak.clone(), - depth, - next: match err.source() { - None => None, - Some(source) => Some(Box::new(CauseProxy::new(source, weak, depth + 1))), - }, - } - } - - fn with_instance(&self, f: F) -> R - where - F: FnOnce(&(dyn std::error::Error + 'static)) -> R, - { - let arc = self.inner.upgrade().unwrap(); - { - let e = arc.lock().unwrap(); - let mut source = e.source().unwrap(); - for _ in 0..self.depth { - source = source.source().unwrap(); - } - f(source) - } - } -} - -impl std::error::Error for CauseProxy { - #[cfg(backtrace)] - fn backtrace(&self) -> Option<&Backtrace> {} - - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - self.next.as_ref().map(|x| x as _) - } -} - -impl Display for CauseProxy -where - T: Display + std::error::Error, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.with_instance(|i| std::fmt::Display::fmt(&i, f)) - } -} - -impl Debug for CauseProxy -where - T: Debug + std::error::Error, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.with_instance(|i| std::fmt::Debug::fmt(&i, f)) - } -} - -fn valid_profile_names() -> String { - Profile::names() - .iter() - .map(|s| format!("'{}'", s)) - .collect::>() - .join(", ") +/// A type erasing thunk for the retry crate to permit use with anyhow. See https://github.com/dtolnay/anyhow/issues/149 +#[derive(Debug, ThisError)] +#[error(transparent)] +pub struct OperationError(pub anyhow::Error); + +#[derive(ThisError, Debug)] +pub enum RustupError { + #[error("partially downloaded file may have been damaged and was removed, please try again")] + BrokenPartialFile, + #[error("component download failed for {0}")] + ComponentDownloadFailed(String), + #[error("failure removing component '{name}', directory does not exist: '{}'", .path.display())] + ComponentMissingDir { name: String, path: PathBuf }, + #[error("failure removing component '{name}', directory does not exist: '{}'", .path.display())] + ComponentMissingFile { name: String, path: PathBuf }, + #[error("could not create {name} directory: '{}'", .path.display())] + CreatingDirectory { name: &'static str, path: PathBuf }, + #[error("unable to read the PGP key '{}'", .path.display())] + InvalidPgpKey { + path: PathBuf, + source: pgp::errors::Error, + }, + #[error("invalid toolchain name: '{0}'")] + InvalidToolchainName(String), + #[error("could not create link from '{}' to '{}'", .src.display(), .dest.display())] + LinkingFile { src: PathBuf, dest: PathBuf }, + #[error("Unable to proceed. Could not locate working directory.")] + LocatingWorkingDir, + #[error("failed to set permissions for '{}'", .p.display())] + SettingPermissions { p: PathBuf, source: io::Error }, + #[error("checksum failed for '{url}', expected: '{expected}', calculated: '{calculated}'")] + ChecksumFailed { + url: String, + expected: String, + calculated: String, + }, + #[error("failed to install component: '{name}', detected conflict: '{}'", .path.display())] + ComponentConflict { name: String, path: PathBuf }, + #[error("toolchain '{0}' does not support components")] + ComponentsUnsupported(String), + #[error("component manifest for '{0}' is corrupt")] + CorruptComponent(String), + #[error("could not download file from '{url}' to '{}'", .path.display())] + DownloadingFile { url: Url, path: PathBuf }, + #[error("could not download file from '{url}' to '{}'", .path.display())] + DownloadNotExists { url: Url, path: PathBuf }, + #[error("Missing manifest in toolchain '{}'", .name)] + MissingManifest { name: String }, + #[error("server sent a broken manifest: missing package for component {0}")] + MissingPackageForComponent(String), + #[error("could not read {name} directory: '{}'", .path.display())] + ReadingDirectory { name: &'static str, path: PathBuf }, + #[error("could not read {name} file: '{}'", .path.display())] + ReadingFile { name: &'static str, path: PathBuf }, + #[error("could not remove '{}' directory: '{}'", .name, .path.display())] + RemovingDirectory { name: &'static str, path: PathBuf }, + #[error("could not remove '{name}' file: '{}'", .path.display())] + RemovingFile { name: &'static str, path: PathBuf }, + #[error("{}", component_unavailable_msg(&.components, &.manifest, &.toolchain))] + RequestedComponentsUnavailable { + components: Vec, + manifest: Manifest, + toolchain: String, + }, + #[error("command failed: '{}'", PathBuf::from(.name).display())] + RunningCommand { name: OsString }, + #[error("toolchain '{0}' is not installable")] + ToolchainNotInstallable(String), + #[error("toolchain '{0}' is not installed")] + ToolchainNotInstalled(String), + #[error("no override and no default toolchain set")] + ToolchainNotSelected, + #[error("toolchain '{}' does not contain component {}{}{}", .name, .component, if let Some(suggestion) = .suggestion { + format!("; did you mean '{}'?", suggestion) + } else { + "".to_string() + }, if .component.contains("rust-std") { + format!("\nnote: not all platforms have the standard library pre-compiled: https://doc.rust-lang.org/nightly/rustc/platform-support.html{}", + if name.contains("nightly") { "\nhelp: consider using `cargo build -Z build-std` instead" } else { "" } + ) + } else { "".to_string() })] + UnknownComponent { + name: String, + component: String, + suggestion: Option, + }, + #[error("unknown metadata version: '{0}'")] + UnknownMetadataVersion(String), + #[error("manifest version '{0}' is not supported")] + UnsupportedVersion(String), + #[error("could not write {name} file: '{}'", .path.display())] + WritingFile { name: &'static str, path: PathBuf }, } fn remove_component_msg(cs: &Component, manifest: &Manifest, toolchain: &str) -> String { @@ -638,68 +192,3 @@ fn component_unavailable_msg(cs: &[Component], manifest: &Manifest, toolchain: & String::from_utf8(buf).unwrap() } - -fn components_missing_msg(cs: &[Component], manifest: &Manifest, toolchain: &str) -> String { - assert!(!cs.is_empty()); - let mut buf = vec![]; - let suggestion = format!(" rustup toolchain add {} --profile minimal", toolchain); - let nightly_tips = "Sometimes not all components are available in any given nightly. "; - - if cs.len() == 1 { - let _ = writeln!( - buf, - "component {} is unavailable for download for channel '{}'", - &cs[0].description(manifest), - toolchain, - ); - - if toolchain.starts_with("nightly") { - let _ = write!(buf, "{}", nightly_tips.to_string()); - } - - let _ = write!( - buf, - "If you don't need the component, you could try a minimal installation with:\n\n{}", - suggestion - ); - } else { - let cs_str = cs - .iter() - .map(|c| c.description(manifest)) - .collect::>() - .join(", "); - let _ = write!( - buf, - "some components unavailable for download for channel '{}': {}", - toolchain, cs_str - ); - - if toolchain.starts_with("nightly") { - let _ = write!(buf, "{}", nightly_tips.to_string()); - } - let _ = write!( - buf, - "If you don't need the components, you could try a minimal installation with:\n\n{}", - suggestion - ); - } - - String::from_utf8(buf).unwrap() -} - -fn install_msg(bin: &str, toolchain: &str, is_default: bool) -> String { - if Toolchain::is_custom_name(toolchain) { - return "\nnote: this is a custom toolchain, which cannot use `rustup component add`\n\ - help: if you built this toolchain from source, and used `rustup toolchain link`, then you may be able to build the component with `x.py`".to_string(); - } - match component_for_bin(bin) { - Some(c) => format!("\nTo install, run `rustup component add {}{}`", c, { - if is_default { - String::new() - } else { - format!(" --toolchain {}", toolchain) - } - }), - None => String::new(), - } -} diff --git a/src/fallback_settings.rs b/src/fallback_settings.rs index 612dc35f0d..54ca0bf568 100644 --- a/src/fallback_settings.rs +++ b/src/fallback_settings.rs @@ -1,10 +1,11 @@ -use crate::errors::*; -use crate::utils::utils; use serde::Deserialize; -use std::error::Error; use std::io; use std::path::Path; +use anyhow::{Context, Result}; + +use crate::utils::utils; + #[derive(Clone, Debug, Deserialize, PartialEq)] pub struct FallbackSettings { pub default_toolchain: Option, @@ -25,16 +26,15 @@ impl FallbackSettings { // sorts of issues, logging messages about errors that should be fixed. Instead we trap some conservative errors // that hopefully won't lead to too many tickets. match utils::read_file("fallback settings", path.as_ref()) { - Err(e @ Error(ErrorKind::ReadingFile { .. }, _)) => { - let io_err = e.source().unwrap().downcast_ref::().unwrap(); - match io_err.kind() { + Err(e) => match e.downcast_ref::() { + Some(io_err) => match io_err.kind() { io::ErrorKind::NotFound | io::ErrorKind::PermissionDenied => Ok(None), _ => Err(e), - } - } - Err(e) => Err(e), + }, + None => Err(e), + }, Ok(file_contents) => Ok(Some( - toml::from_str(&file_contents).map_err(ErrorKind::ParsingFallbackSettings)?, + toml::from_str(&file_contents).context("error parsing settings")?, )), } } diff --git a/src/install.rs b/src/install.rs index e2057c2a97..38c74edcab 100644 --- a/src/install.rs +++ b/src/install.rs @@ -1,15 +1,17 @@ //! Installation and upgrade of both distribution-managed and local //! toolchains +use std::path::Path; + +use anyhow::Result; use crate::dist::dist; use crate::dist::download::DownloadCfg; use crate::dist::prefix::InstallPrefix; use crate::dist::Notification; -use crate::errors::{ErrorKind, Result}; +use crate::errors::RustupError; use crate::notifications::Notification as RootNotification; use crate::toolchain::{CustomToolchain, DistributableToolchain, Toolchain, UpdateStatus}; use crate::utils::utils; -use std::path::Path; #[derive(Copy, Clone)] pub enum InstallMethod<'a> { @@ -78,7 +80,7 @@ impl<'a> InstallMethod<'a> { // Final check, to ensure we're installed if !toolchain.exists() { - Err(ErrorKind::ToolchainNotInstallable(toolchain.name().to_string()).into()) + Err(RustupError::ToolchainNotInstallable(toolchain.name().to_string()).into()) } else { Ok(status) } diff --git a/src/notifications.rs b/src/notifications.rs index 1250a81498..c6021aaaf6 100644 --- a/src/notifications.rs +++ b/src/notifications.rs @@ -1,8 +1,6 @@ use std::fmt::{self, Display}; use std::path::{Path, PathBuf}; -use crate::errors::*; - use crate::dist::temp; use crate::utils::notify::NotificationLevel; @@ -29,7 +27,7 @@ pub enum Notification<'a> { MetadataUpgradeNotNeeded(&'a str), WritingMetadataVersion(&'a str), ReadMetadataVersion(&'a str), - NonFatalError(&'a Error), + NonFatalError(&'a anyhow::Error), UpgradeRemovesToolchains, MissingFileDuringSelfUninstall(PathBuf), PlainVerboseMessage(&'a str), diff --git a/src/settings.rs b/src/settings.rs index 0c2687d0e5..2576a8f077 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -1,10 +1,13 @@ +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::path::{Path, PathBuf}; + +use anyhow::{Context, Result}; + use crate::errors::*; use crate::notifications::*; use crate::toml_utils::*; use crate::utils::utils; -use std::cell::RefCell; -use std::collections::BTreeMap; -use std::path::{Path, PathBuf}; pub const SUPPORTED_METADATA_VERSIONS: [&str; 2] = ["2", "12"]; pub const DEFAULT_METADATA_VERSION: &str = "12"; @@ -126,7 +129,7 @@ impl Settings { } pub fn parse(data: &str) -> Result { - let value = toml::from_str(data).map_err(ErrorKind::ParsingSettings)?; + let value = toml::from_str(data).context("error parsing settings")?; Self::from_toml(value, "") } pub fn stringify(self) -> String { @@ -136,7 +139,7 @@ impl Settings { pub fn from_toml(mut table: toml::value::Table, path: &str) -> Result { let version = get_string(&mut table, "version", path)?; if !SUPPORTED_METADATA_VERSIONS.contains(&&*version) { - return Err(ErrorKind::UnknownMetadataVersion(version).into()); + return Err(RustupError::UnknownMetadataVersion(version).into()); } Ok(Self { version, diff --git a/src/test.rs b/src/test.rs index 7c87f36de2..219b4dcb9f 100644 --- a/src/test.rs +++ b/src/test.rs @@ -8,6 +8,8 @@ use std::io; use std::path::{Path, PathBuf}; use std::process::Command; +use anyhow::Result; + pub use crate::cli::self_update::test::{get_path, with_saved_path}; use crate::currentprocess; use crate::dist::dist::TargetTriple; @@ -166,9 +168,9 @@ impl fmt::Display for RustupHome { /// Create an isolated rustup home with no content, then call f with it, and /// delete it afterwards. -pub fn with_rustup_home(f: F) -> anyhow::Result<()> +pub fn with_rustup_home(f: F) -> Result<()> where - F: FnOnce(&RustupHome) -> anyhow::Result<()>, + F: FnOnce(&RustupHome) -> Result<()>, { let test_dir = test_dir()?; let rustup_home = RustupHome::new_in(test_dir)?; diff --git a/src/toolchain.rs b/src/toolchain.rs index 2bc0bd0ec1..a1a515e41d 100644 --- a/src/toolchain.rs +++ b/src/toolchain.rs @@ -8,6 +8,8 @@ use std::process::{Command, Stdio}; use std::str::FromStr; use std::time::Duration; +use anyhow::{anyhow, bail, Context, Result}; +use thiserror::Error as ThisError; use wait_timeout::ChildExt; use crate::component_for_bin; @@ -85,17 +87,21 @@ impl<'a> Toolchain<'a> { path.as_ref().to_path_buf() }; + #[derive(Debug, ThisError)] + #[error("invalid toolchain path: '{}'", .0.to_string_lossy())] + struct InvalidToolchainPath(PathBuf); + // Perform minimal validation; there should at least be a `bin/` that might // contain things for us to run. if !path.join("bin").is_dir() { - return Err(ErrorKind::InvalidToolchainPath(path).into()); + bail!(InvalidToolchainPath(path)); } Ok(Toolchain { cfg, name: utils::canonicalize_path(&path, cfg.notify_handler.as_ref()) .to_str() - .ok_or_else(|| ErrorKind::InvalidToolchainPath(path.clone()))? + .ok_or_else(|| anyhow!(InvalidToolchainPath(path.clone())))? .to_owned(), path, dist_handler: Box::new(move |n| (cfg.notify_handler)(n.into())), @@ -105,7 +111,7 @@ impl<'a> Toolchain<'a> { pub fn as_installed_common(&'a self) -> Result> { if !self.exists() { // Should be verify perhaps? - return Err(ErrorKind::ToolchainNotInstalled(self.name.to_owned()).into()); + return Err(RustupError::ToolchainNotInstalled(self.name.to_owned()).into()); } Ok(InstalledCommonToolchain(self)) } @@ -293,6 +299,22 @@ impl<'a> std::fmt::Debug for Toolchain<'a> { } } +fn install_msg(bin: &str, toolchain: &str, is_default: bool) -> String { + if Toolchain::is_custom_name(toolchain) { + return "\nnote: this is a custom toolchain, which cannot use `rustup component add`\n\ + help: if you built this toolchain from source, and used `rustup toolchain link`, then you may be able to build the component with `x.py`".to_string(); + } + match component_for_bin(bin) { + Some(c) => format!("\nTo install, run `rustup component add {}{}`", c, { + if is_default { + String::new() + } else { + format!(" --toolchain {}", toolchain) + } + }), + None => String::new(), + } +} /// Newtype hosting functions that apply to both custom and distributable toolchains that are installed. pub struct InstalledCommonToolchain<'a>(&'a Toolchain<'a>); @@ -334,30 +356,26 @@ impl<'a> InstalledCommonToolchain<'a> { panic!("component {} should be in the manifest", component_name) }); if !component_status.available { - return Err(ErrorKind::BinaryProvidedByUnavailableComponent( - component_status.component.short_name(&manifest), - binary_lossy, - self.0.name.clone(), - ) - .into()); + return Err(anyhow!(format!( + "the '{}' component which provides the command '{}' is not available for the '{}' toolchain", component_status.component.short_name(&manifest), binary_lossy, self.0.name))); } if component_status.installed { - return Err(ErrorKind::BinaryNotProvidedByComponent( - component_status.component.short_name(&manifest), - binary_lossy, - self.0.name.clone(), - ) - .into()); + return Err(anyhow!(format!( + "the '{}' binary, normally provided by the '{}' component, is not applicable to the '{}' toolchain", binary_lossy, component_status.component.short_name(&manifest), self.0.name))); } } } let defaults = self.0.cfg.get_default()?; - return Err(ErrorKind::BinaryNotFound( - binary.to_string_lossy().into(), - self.0.name.clone(), - Some(&self.0.name) == defaults.as_ref(), - ) - .into()); + return Err(anyhow!(format!( + "'{}' is not installed for the toolchain '{}'{}", + binary.to_string_lossy(), + self.0.name, + install_msg( + &binary.to_string_lossy(), + &self.0.name, + Some(&self.0.name) == defaults.as_ref() + ) + ))); } Path::new(&binary) }; @@ -448,7 +466,10 @@ impl<'a> CustomToolchain<'a> { if toolchain.is_custom() { Ok(CustomToolchain(&toolchain)) } else { - Err(format!("{} is not a custom toolchain", toolchain.name()).into()) + Err(anyhow!(format!( + "{} is not a custom toolchain", + toolchain.name() + ))) } } @@ -487,21 +508,31 @@ pub struct DistributableToolchain<'a>(&'a Toolchain<'a>); impl<'a> DistributableToolchain<'a> { pub fn new(toolchain: &'a Toolchain<'a>) -> Result> { if toolchain.is_custom() { - Err(format!("{} is a custom toolchain", toolchain.name()).into()) + Err(anyhow!(format!( + "{} is a custom toolchain", + toolchain.name() + ))) } else { Ok(DistributableToolchain(&toolchain)) } } + /// Temporary helper until we further split this into a newtype for + /// InstalledDistributableToolchain - one where the type can protect component operations. + pub fn new_for_components(toolchain: &'a Toolchain<'a>) -> Result> { + DistributableToolchain::new(toolchain).context(RustupError::ComponentsUnsupported( + toolchain.name().to_string(), + )) + } + // Installed only. pub fn add_component(&self, mut component: Component) -> Result<()> { if !self.0.exists() { - return Err(ErrorKind::ToolchainNotInstalled(self.0.name.to_owned()).into()); + return Err(RustupError::ToolchainNotInstalled(self.0.name.to_owned()).into()); } let toolchain = &self.0.name; - let toolchain = ToolchainDesc::from_str(toolchain) - .chain_err(|| ErrorKind::ComponentsUnsupported(self.0.name.to_string()))?; + let toolchain = ToolchainDesc::from_str(toolchain).expect("must be valid"); let prefix = InstallPrefix::from(self.0.path.to_owned()); let manifestation = Manifestation::open(prefix, toolchain.target.clone())?; @@ -527,11 +558,11 @@ impl<'a> DistributableToolchain<'a> { if targ_pkg.components.contains(&wildcard_component) { component = wildcard_component; } else { - return Err(ErrorKind::UnknownComponent( - self.0.name.to_string(), - component.description(&manifest), - self.get_component_suggestion(&component, &manifest, false), - ) + return Err(RustupError::UnknownComponent { + name: self.0.name.to_string(), + component: component.description(&manifest), + suggestion: self.get_component_suggestion(&component, &manifest, false), + } .into()); } } @@ -553,7 +584,10 @@ impl<'a> DistributableToolchain<'a> { Ok(()) } else { - Err(ErrorKind::ComponentsUnsupported(self.0.name.to_string()).into()) + Err(RustupError::MissingManifest { + name: self.0.name.to_string(), + } + .into()) } } @@ -569,7 +603,7 @@ impl<'a> DistributableToolchain<'a> { assert!(binary.as_ref() == "cargo" || binary.as_ref() == "cargo.exe"); if !self.0.exists() { - return Err(ErrorKind::ToolchainNotInstalled(self.0.name.to_owned()).into()); + return Err(RustupError::ToolchainNotInstalled(self.0.name.to_owned()).into()); } let installed_primary = primary_toolchain.as_installed_common()?; @@ -590,14 +624,12 @@ impl<'a> DistributableToolchain<'a> { use std::fs; let fallback_dir = self.0.cfg.rustup_dir.join("fallback"); fs::create_dir_all(&fallback_dir) - .chain_err(|| "unable to create dir to hold fallback exe")?; + .context("unable to create dir to hold fallback exe")?; let fallback_file = fallback_dir.join("cargo.exe"); if fallback_file.exists() { - fs::remove_file(&fallback_file) - .chain_err(|| "unable to unlink old fallback exe")?; + fs::remove_file(&fallback_file).context("unable to unlink old fallback exe")?; } - fs::hard_link(&src_file, &fallback_file) - .chain_err(|| "unable to hard link fallback exe")?; + fs::hard_link(&src_file, &fallback_file).context("unable to hard link fallback exe")?; fallback_file } else { src_file @@ -704,7 +736,7 @@ impl<'a> DistributableToolchain<'a> { // Installed only. pub fn get_manifest(&self) -> Result> { if !self.0.exists() { - return Err(ErrorKind::ToolchainNotInstalled(self.0.name().to_owned()).into()); + bail!(RustupError::ToolchainNotInstalled(self.0.name().to_owned())); } let toolchain = &self.0.name(); @@ -773,12 +805,12 @@ impl<'a> DistributableToolchain<'a> { // Installed only. pub fn list_components(&self) -> Result> { if !self.0.exists() { - return Err(ErrorKind::ToolchainNotInstalled(self.0.name.to_owned()).into()); + bail!(RustupError::ToolchainNotInstalled(self.0.name.to_owned())); } let toolchain = &self.0.name; let toolchain = ToolchainDesc::from_str(toolchain) - .chain_err(|| ErrorKind::ComponentsUnsupported(self.0.name.to_string()))?; + .context(RustupError::ComponentsUnsupported(self.0.name.to_string()))?; let prefix = InstallPrefix::from(self.0.path.to_owned()); let manifestation = Manifestation::open(prefix, toolchain.target.clone())?; @@ -833,7 +865,7 @@ impl<'a> DistributableToolchain<'a> { Ok(res) } else { - Err(ErrorKind::ComponentsUnsupported(self.0.name.to_string()).into()) + Err(RustupError::ComponentsUnsupported(self.0.name.to_string()).into()) } } @@ -841,12 +873,11 @@ impl<'a> DistributableToolchain<'a> { pub fn remove_component(&self, mut component: Component) -> Result<()> { // Overlapping code with get_manifest :/. if !self.0.exists() { - return Err(ErrorKind::ToolchainNotInstalled(self.0.name.to_owned()).into()); + return Err(RustupError::ToolchainNotInstalled(self.0.name.to_owned()).into()); } let toolchain = &self.0.name; - let toolchain = ToolchainDesc::from_str(toolchain) - .chain_err(|| ErrorKind::ComponentsUnsupported(self.0.name.to_string()))?; + let toolchain = ToolchainDesc::from_str(toolchain).expect("must be valid"); let prefix = InstallPrefix::from(self.0.path.to_owned()); let manifestation = Manifestation::open(prefix, toolchain.target.clone())?; @@ -863,11 +894,11 @@ impl<'a> DistributableToolchain<'a> { if dist_config.components.contains(&wildcard_component) { component = wildcard_component; } else { - return Err(ErrorKind::UnknownComponent( - self.0.name.to_string(), - component.description(&manifest), - self.get_component_suggestion(&component, &manifest, true), - ) + return Err(RustupError::UnknownComponent { + name: self.0.name.to_string(), + component: component.description(&manifest), + suggestion: self.get_component_suggestion(&component, &manifest, true), + } .into()); } } @@ -889,7 +920,10 @@ impl<'a> DistributableToolchain<'a> { Ok(()) } else { - Err(ErrorKind::ComponentsUnsupported(self.0.name.to_string()).into()) + Err(RustupError::MissingManifest { + name: self.0.name.to_string(), + } + .into()) } } diff --git a/src/utils/raw.rs b/src/utils/raw.rs index 6e330bae1c..47b1257740 100644 --- a/src/utils/raw.rs +++ b/src/utils/raw.rs @@ -1,7 +1,5 @@ use std::env; -use std::error; use std::ffi::{OsStr, OsString}; -use std::fmt; use std::fs; use std::io; use std::io::Write; @@ -9,6 +7,8 @@ use std::path::Path; use std::process::{Command, ExitStatus}; use std::str; +use thiserror::Error as ThisError; + use crate::process; pub fn ensure_dir_exists, F: FnOnce(&Path)>( @@ -92,21 +92,6 @@ pub fn filter_file bool>( Ok(removed) } -pub fn match_file Option>(src: &Path, mut f: F) -> io::Result> { - let src_file = fs::File::open(src)?; - - let mut reader = io::BufReader::new(src_file); - - for result in io::BufRead::lines(&mut reader) { - let line = result?; - if let Some(r) = f(&line) { - return Ok(Some(r)); - } - } - - Ok(None) -} - pub fn append_file(dest: &Path, line: &str) -> io::Result<()> { let mut dest_file = fs::OpenOptions::new() .write(true) @@ -121,23 +106,6 @@ pub fn append_file(dest: &Path, line: &str) -> io::Result<()> { Ok(()) } -pub fn tee_file(path: &Path, w: &mut W) -> io::Result<()> { - let mut file = fs::OpenOptions::new().read(true).open(path)?; - - let buffer_size = 0x10000; - let mut buffer = vec![0u8; buffer_size]; - - loop { - let bytes_read = io::Read::read(&mut file, &mut buffer)?; - - if bytes_read != 0 { - io::Write::write_all(w, &buffer[0..bytes_read])?; - } else { - return Ok(()); - } - } -} - pub fn symlink_dir(src: &Path, dest: &Path) -> io::Result<()> { #[cfg(windows)] fn symlink_dir_inner(src: &Path, dest: &Path) -> io::Result<()> { @@ -251,40 +219,15 @@ pub fn hardlink(src: &Path, dest: &Path) -> io::Result<()> { fs::hard_link(src, dest) } -#[derive(Debug)] +#[derive(Debug, ThisError)] pub enum CommandError { - Io(io::Error), + #[error("error running command")] + Io(#[source] io::Error), + #[error("command exited with unsuccessful status {0}")] Status(ExitStatus), } -pub type CommandResult = std::result::Result; - -impl error::Error for CommandError { - fn description(&self) -> &str { - use self::CommandError::*; - match self { - Io(_) => "could not execute command", - Status(_) => "command exited with unsuccessful status", - } - } - - fn cause(&self) -> Option<&dyn error::Error> { - use self::CommandError::*; - match self { - Io(e) => Some(e), - Status(_) => None, - } - } -} - -impl fmt::Display for CommandError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Io(e) => write!(f, "Io: {}", e), - Self::Status(s) => write!(f, "Status: {}", s), - } - } -} +pub type CommandResult = Result; pub fn cmd_status(cmd: &mut Command) -> CommandResult<()> { cmd.status().map_err(CommandError::Io).and_then(|s| { diff --git a/src/utils/toml_utils.rs b/src/utils/toml_utils.rs index a50b6e89f6..42296895aa 100644 --- a/src/utils/toml_utils.rs +++ b/src/utils/toml_utils.rs @@ -1,19 +1,22 @@ -use crate::errors::*; +use anyhow::{anyhow, Result}; +use thiserror::Error as ThisError; pub fn get_value(table: &mut toml::value::Table, key: &str, path: &str) -> Result { table .remove(key) - .ok_or_else(|| format!("missing key: '{}'", path.to_owned() + key).into()) + .ok_or_else(|| anyhow!(format!("missing key: '{}'", path.to_owned() + key))) } +#[derive(Debug, ThisError)] +#[error("expected type: '{0}' for '{1}'")] +struct ExpectedType(&'static str, String); + pub fn get_string(table: &mut toml::value::Table, key: &str, path: &str) -> Result { - get_value(table, key, path).and_then(|v| { - if let toml::Value::String(s) = v { - Ok(s) - } else { - Err(ErrorKind::ExpectedType("string", path.to_owned() + key).into()) - } - }) + if let toml::Value::String(s) = get_value(table, key, path)? { + Ok(s) + } else { + Err(ExpectedType("string", path.to_owned() + key).into()) + } } pub fn get_opt_string( @@ -25,7 +28,7 @@ pub fn get_opt_string( if let toml::Value::String(s) = v { Ok(Some(s)) } else { - Err(ErrorKind::ExpectedType("string", path.to_owned() + key).into()) + Err(ExpectedType("string", path.to_owned() + key).into()) } } else { Ok(None) @@ -37,7 +40,7 @@ pub fn get_bool(table: &mut toml::value::Table, key: &str, path: &str) -> Result if let toml::Value::Boolean(b) = v { Ok(b) } else { - Err(ErrorKind::ExpectedType("bool", path.to_owned() + key).into()) + Err(ExpectedType("bool", path.to_owned() + key).into()) } }) } @@ -51,7 +54,7 @@ pub fn get_table( if let toml::Value::Table(t) = v { Ok(t) } else { - Err(ErrorKind::ExpectedType("table", path.to_owned() + key).into()) + Err(ExpectedType("table", path.to_owned() + key).into()) } } else { Ok(toml::value::Table::new()) @@ -67,7 +70,7 @@ pub fn get_array( if let toml::Value::Array(s) = v { Ok(s) } else { - Err(ErrorKind::ExpectedType("array", path.to_owned() + key).into()) + Err(ExpectedType("array", path.to_owned() + key).into()) } } else { Ok(toml::value::Array::new()) diff --git a/src/utils/utils.rs b/src/utils/utils.rs index 74fc61262e..cec67e9dbe 100644 --- a/src/utils/utils.rs +++ b/src/utils/utils.rs @@ -5,6 +5,7 @@ use std::io::{self, BufReader, Write}; use std::path::{Path, PathBuf}; use std::process::Command; +use anyhow::{anyhow, bail, Context, Result}; use retry::delay::{jitter, Fibonacci}; use retry::{retry, OperationResult}; use sha2::Sha256; @@ -33,56 +34,49 @@ where raw::ensure_dir_exists(path, |_| { notify_handler(Notification::CreatingDirectory(name, path).into()) }) - .chain_err(|| ErrorKind::CreatingDirectory { + .with_context(|| RustupError::CreatingDirectory { name, path: PathBuf::from(path), }) } pub fn open_file(name: &'static str, path: &Path) -> Result { - File::open(path).chain_err(|| ErrorKind::ReadingFile { - name, - path: PathBuf::from(path), - }) -} - -pub fn read_file_bytes(name: &'static str, path: &Path) -> Result> { - fs::read(path).chain_err(|| ErrorKind::ReadingFile { + File::open(path).with_context(|| RustupError::ReadingFile { name, path: PathBuf::from(path), }) } pub fn read_file(name: &'static str, path: &Path) -> Result { - fs::read_to_string(path).chain_err(|| ErrorKind::ReadingFile { + fs::read_to_string(path).with_context(|| RustupError::ReadingFile { name, path: PathBuf::from(path), }) } pub fn write_file(name: &'static str, path: &Path, contents: &str) -> Result<()> { - raw::write_file(path, contents).chain_err(|| ErrorKind::WritingFile { + raw::write_file(path, contents).with_context(|| RustupError::WritingFile { name, path: PathBuf::from(path), }) } pub fn append_file(name: &'static str, path: &Path, line: &str) -> Result<()> { - raw::append_file(path, line).chain_err(|| ErrorKind::WritingFile { + raw::append_file(path, line).with_context(|| RustupError::WritingFile { name, path: PathBuf::from(path), }) } pub fn write_line(name: &'static str, file: &mut File, path: &Path, line: &str) -> Result<()> { - writeln!(file, "{}", line).chain_err(|| ErrorKind::WritingFile { + writeln!(file, "{}", line).with_context(|| RustupError::WritingFile { name, path: path.to_path_buf(), }) } pub fn write_str(name: &'static str, file: &mut File, path: &Path, s: &str) -> Result<()> { - write!(file, "{}", s).chain_err(|| ErrorKind::WritingFile { + write!(file, "{}", s).with_context(|| RustupError::WritingFile { name, path: path.to_path_buf(), }) @@ -118,22 +112,16 @@ pub fn filter_file bool>( dest: &Path, filter: F, ) -> Result { - raw::filter_file(src, dest, filter).chain_err(|| ErrorKind::FilteringFile { - name, - src: PathBuf::from(src), - dest: PathBuf::from(dest), - }) -} - -pub fn match_file Option>( - name: &'static str, - src: &Path, - f: F, -) -> Result> { - raw::match_file(src, f).chain_err(|| ErrorKind::ReadingFile { - name, - path: PathBuf::from(src), - }) + raw::filter_file(src, dest, filter) + .with_context(|| { + format!( + "could not copy {} file from '{}' to '{}'", + name, + src.display(), + dest.display() + ) + }) + .into() } pub fn canonicalize_path<'a, N>(path: &'a Path, notify_handler: &dyn Fn(N)) -> PathBuf @@ -146,13 +134,6 @@ where }) } -pub fn tee_file(name: &'static str, path: &Path, w: &mut W) -> Result<()> { - raw::tee_file(path, w).chain_err(|| ErrorKind::ReadingFile { - name, - path: PathBuf::from(path), - }) -} - pub fn download_file( url: &Url, path: &Path, @@ -169,26 +150,25 @@ pub fn download_file_with_resume( resume_from_partial: bool, notify_handler: &dyn Fn(Notification<'_>), ) -> Result<()> { - use download::ErrorKind as DEK; + use download::DownloadError as DEK; match download_file_(url, path, hasher, resume_from_partial, notify_handler) { Ok(_) => Ok(()), Err(e) => { - let is_client_error = match e.kind() { + let is_client_error = match e.downcast_ref::() { // Specifically treat the bad partial range error as not our // fault in case it was something odd which happened. - ErrorKind::Download(DEK::HttpStatus(416)) => false, - ErrorKind::Download(DEK::HttpStatus(400..=499)) => true, - ErrorKind::Download(DEK::FileNotFound) => true, + Some(DEK::HttpStatus(416)) => false, + Some(DEK::HttpStatus(400..=499)) | Some(DEK::FileNotFound) => true, _ => false, }; - Err(e).chain_err(|| { + Err(e).with_context(|| { if is_client_error { - ErrorKind::DownloadNotExists { + RustupError::DownloadNotExists { url: url.clone(), path: path.to_path_buf(), } } else { - ErrorKind::DownloadingFile { + RustupError::DownloadingFile { url: url.clone(), path: path.to_path_buf(), } @@ -266,27 +246,24 @@ fn download_file_( notify_handler(Notification::DownloadFinished); - res.map_err(|e| e.into()) + res } pub fn parse_url(url: &str) -> Result { - Url::parse(url).chain_err(|| format!("failed to parse url: {}", url)) + Url::parse(url).with_context(|| format!("failed to parse url: {}", url)) } pub fn cmd_status(name: &'static str, cmd: &mut Command) -> Result<()> { use std::ffi::OsString; - raw::cmd_status(cmd).chain_err(|| ErrorKind::RunningCommand { + raw::cmd_status(cmd).with_context(|| RustupError::RunningCommand { name: OsString::from(name), }) } pub fn assert_is_file(path: &Path) -> Result<()> { if !is_file(path) { - Err(ErrorKind::NotAFile { - path: PathBuf::from(path), - } - .into()) + Err(anyhow!(format!("not a file: '{}'", path.display()))) } else { Ok(()) } @@ -294,10 +271,7 @@ pub fn assert_is_file(path: &Path) -> Result<()> { pub fn assert_is_directory(path: &Path) -> Result<()> { if !is_directory(path) { - Err(ErrorKind::NotADirectory { - path: PathBuf::from(path), - } - .into()) + Err(anyhow!(format!("not a directory: '{}'", path.display()))) } else { Ok(()) } @@ -308,9 +282,12 @@ where N: From>, { notify_handler(Notification::LinkingDirectory(src, dest).into()); - raw::symlink_dir(src, dest).chain_err(|| ErrorKind::LinkingDirectory { - src: PathBuf::from(src), - dest: PathBuf::from(dest), + raw::symlink_dir(src, dest).with_context(|| { + format!( + "could not create link from '{}' to '{}'", + src.display(), + dest.display() + ) }) } @@ -322,7 +299,7 @@ pub fn hard_or_symlink_file(src: &Path, dest: &Path) -> Result<()> { } pub fn hardlink_file(src: &Path, dest: &Path) -> Result<()> { - raw::hardlink(src, dest).chain_err(|| ErrorKind::LinkingFile { + raw::hardlink(src, dest).with_context(|| RustupError::LinkingFile { src: PathBuf::from(src), dest: PathBuf::from(dest), }) @@ -330,7 +307,7 @@ pub fn hardlink_file(src: &Path, dest: &Path) -> Result<()> { #[cfg(unix)] pub fn symlink_file(src: &Path, dest: &Path) -> Result<()> { - std::os::unix::fs::symlink(src, dest).chain_err(|| ErrorKind::LinkingFile { + std::os::unix::fs::symlink(src, dest).with_context(|| RustupError::LinkingFile { src: PathBuf::from(src), dest: PathBuf::from(dest), }) @@ -339,11 +316,10 @@ pub fn symlink_file(src: &Path, dest: &Path) -> Result<()> { #[cfg(windows)] pub fn symlink_file(src: &Path, dest: &Path) -> Result<()> { // we are supposed to not use symlink on windows - Err(ErrorKind::LinkingFile { + Err(anyhow!(RustupError::LinkingFile { src: PathBuf::from(src), dest: PathBuf::from(dest), - } - .into()) + })) } pub fn copy_dir<'a, N>(src: &'a Path, dest: &'a Path, notify_handler: &dyn Fn(N)) -> Result<()> @@ -351,14 +327,17 @@ where N: From>, { notify_handler(Notification::CopyingDirectory(src, dest).into()); - raw::copy_dir(src, dest).chain_err(|| ErrorKind::CopyingDirectory { - src: PathBuf::from(src), - dest: PathBuf::from(dest), + raw::copy_dir(src, dest).with_context(|| { + format!( + "could not copy directory from '{}' to '{}'", + src.display(), + dest.display() + ) }) } pub fn copy_file(src: &Path, dest: &Path) -> Result<()> { - let metadata = fs::symlink_metadata(src).chain_err(|| ErrorKind::ReadingFile { + let metadata = fs::symlink_metadata(src).with_context(|| RustupError::ReadingFile { name: "metadata for", path: PathBuf::from(src), })?; @@ -366,9 +345,12 @@ pub fn copy_file(src: &Path, dest: &Path) -> Result<()> { symlink_file(&src, dest).map(|_| ()) } else { fs::copy(src, dest) - .chain_err(|| ErrorKind::CopyingFile { - src: PathBuf::from(src), - dest: PathBuf::from(dest), + .with_context(|| { + format!( + "could not copy file from '{}' to '{}'", + src.display(), + dest.display() + ) }) .map(|_| ()) } @@ -383,8 +365,8 @@ where N: From>, { notify_handler(Notification::RemovingDirectory(name, path).into()); - raw::remove_dir(path).chain_err(|| ErrorKind::RemovingDirectory { - name, + raw::remove_dir(path).with_context(|| RustupError::RemovingDirectory { + name: name, path: PathBuf::from(path), }) } @@ -405,7 +387,7 @@ pub fn remove_file(name: &'static str, path: &Path) -> Result<()> { }, }, ) - .chain_err(|| ErrorKind::RemovingFile { + .with_context(|| RustupError::RemovingFile { name, path: PathBuf::from(path), }) @@ -418,35 +400,40 @@ pub fn ensure_file_removed(name: &'static str, path: &Path) -> Result<()> { return Ok(()); } } - result.chain_err(|| ErrorKind::RemovingFile { + result.with_context(|| RustupError::RemovingFile { name, path: PathBuf::from(path), }) } pub fn read_dir(name: &'static str, path: &Path) -> Result { - fs::read_dir(path).chain_err(|| ErrorKind::ReadingDirectory { + fs::read_dir(path).with_context(|| RustupError::ReadingDirectory { name, path: PathBuf::from(path), }) } pub fn open_browser(path: &Path) -> Result<()> { - opener::open(path).chain_err(|| "couldn't open browser") + opener::open(path).context("couldn't open browser") } pub fn set_permissions(path: &Path, perms: fs::Permissions) -> Result<()> { - fs::set_permissions(path, perms).chain_err(|| ErrorKind::SettingPermissions { - path: PathBuf::from(path), + fs::set_permissions(path, perms).map_err(|e| { + RustupError::SettingPermissions { + p: PathBuf::from(path), + source: e, + } + .into() }) } pub fn file_size(path: &Path) -> Result { - let metadata = fs::metadata(path).chain_err(|| ErrorKind::ReadingFile { - name: "metadata for", - path: PathBuf::from(path), - })?; - Ok(metadata.len()) + Ok(fs::metadata(path) + .with_context(|| RustupError::ReadingFile { + name: "metadata for", + path: PathBuf::from(path), + })? + .len()) } pub fn make_executable(path: &Path) -> Result<()> { @@ -459,8 +446,9 @@ pub fn make_executable(path: &Path) -> Result<()> { fn inner(path: &Path) -> Result<()> { use std::os::unix::fs::PermissionsExt; - let metadata = fs::metadata(path).chain_err(|| ErrorKind::SettingPermissions { - path: PathBuf::from(path), + let metadata = fs::metadata(path).map_err(|e| RustupError::SettingPermissions { + p: PathBuf::from(path), + source: e, })?; let mut perms = metadata.permissions(); let mode = perms.mode(); @@ -481,11 +469,11 @@ pub fn make_executable(path: &Path) -> Result<()> { pub fn current_dir() -> Result { process() .current_dir() - .chain_err(|| ErrorKind::LocatingWorkingDir) + .context(RustupError::LocatingWorkingDir) } pub fn current_exe() -> Result { - env::current_exe().chain_err(|| ErrorKind::LocatingWorkingDir) + env::current_exe().context(RustupError::LocatingWorkingDir) } pub fn to_absolute>(path: P) -> Result { @@ -500,7 +488,7 @@ pub fn home_dir() -> Option { } pub fn cargo_home() -> Result { - home::cargo_home_from(&home_process()).map_err(|e| Error::from_kind(ErrorKind::Io(e))) + home::cargo_home_from(&home_process()).context("failed to determine cargo home") } // Creates a ~/.rustup folder @@ -512,7 +500,7 @@ pub fn create_rustup_home() -> Result<()> { } let home = rustup_home_in_user_dir()?; - fs::create_dir_all(&home).chain_err(|| "unable to create ~/.rustup")?; + fs::create_dir_all(&home).context("unable to create ~/.rustup")?; Ok(()) } @@ -522,11 +510,12 @@ fn dot_dir(name: &str) -> Option { } pub fn rustup_home_in_user_dir() -> Result { - dot_dir(".rustup").ok_or_else(|| ErrorKind::RustupHome.into()) + // XXX: This error message seems wrong/bogus. + dot_dir(".rustup").ok_or_else(|| anyhow::anyhow!("couldn't find value of RUSTUP_HOME")) } pub fn rustup_home() -> Result { - home::rustup_home_from(&home_process()).map_err(|e| Error::from_kind(ErrorKind::Io(e))) + home::rustup_home_from(&home_process()).context("failed to determine rustup home dir") } pub fn format_path_for_display(path: &str) -> String { @@ -586,8 +575,8 @@ where // This uses std::fs::copy() instead of the faster std::fs::rename() to // avoid cross-device link errors. if src.is_dir() { - copy_dir(src, dest, notify_handler).and(remove_dir_all::remove_dir_all(src).chain_err( - || ErrorKind::RemovingDirectory { + copy_dir(src, dest, notify_handler).and(remove_dir_all::remove_dir_all(src).with_context( + || RustupError::RemovingDirectory { name, path: PathBuf::from(src), }, @@ -635,10 +624,13 @@ where }, }, ) - .chain_err(|| ErrorKind::RenamingFile { - name, - src: PathBuf::from(src), - dest: PathBuf::from(dest), + .with_context(|| { + format!( + "could not rename {} file from '{}' to '{}'", + name, + src.display(), + dest.display() + ) }) } @@ -663,11 +655,10 @@ impl<'a> FileReaderWithProgress<'a> { let fh = match File::open(path) { Ok(fh) => fh, Err(_) => { - return Err(ErrorKind::ReadingFile { + bail!(RustupError::ReadingFile { name: "downloaded", path: path.to_path_buf(), - } - .into()) + }) } }; diff --git a/tests/cli-inst-interactive.rs b/tests/cli-inst-interactive.rs index d0b5cffaf1..3cb2048d9a 100644 --- a/tests/cli-inst-interactive.rs +++ b/tests/cli-inst-interactive.rs @@ -372,6 +372,8 @@ fn set_nightly_toolchain_and_unset() { &["rustup-init", "--no-modify-path"], "2\n\nnightly\n\n\n2\n\nbeta\n\n\n\n\n", ); + println!("{:?}", out.stderr); + println!("{:?}", out.stdout); assert!(out.ok); expect_stdout_ok(config, &["rustup", "show"], "beta"); diff --git a/tests/cli-v2.rs b/tests/cli-v2.rs index ad1e9b0060..d2d9284156 100644 --- a/tests/cli-v2.rs +++ b/tests/cli-v2.rs @@ -804,7 +804,7 @@ fn add_target_v1_toolchain() { clitools::CROSS_ARCH1, "--toolchain=nightly", ], - for_host!("toolchain 'nightly-{0}' does not support components"), + for_host!("Missing manifest in toolchain 'nightly-{0}'"), ); }); } @@ -946,7 +946,7 @@ fn remove_target_v1_toolchain() { clitools::CROSS_ARCH1, "--toolchain=nightly", ], - for_host!("toolchain 'nightly-{0}' does not support components"), + for_host!("Missing manifest in toolchain 'nightly-{0}'"), ); }); } diff --git a/tests/dist.rs b/tests/dist.rs index 83a751ed43..012b8c1706 100644 --- a/tests/dist.rs +++ b/tests/dist.rs @@ -11,6 +11,7 @@ use std::path::Path; use std::str::FromStr; use std::sync::Arc; +use anyhow::{anyhow, Result}; use url::Url; use rustup::currentprocess; @@ -21,10 +22,9 @@ use rustup::dist::manifestation::{Changes, Manifestation, UpdateStatus}; use rustup::dist::prefix::InstallPrefix; use rustup::dist::temp; use rustup::dist::Notification; -use rustup::errors::Result; +use rustup::errors::RustupError; use rustup::utils::raw as utils_raw; use rustup::utils::utils; -use rustup::ErrorKind; use rustup::PgpPublicKey; use crate::mock::dist::*; @@ -478,7 +478,7 @@ fn make_manifest_url(dist_server: &Url, toolchain: &ToolchainDesc) -> Result { - assert!(err.to_string().contains("rustup component remove --toolchain nightly --target x86_64-apple-darwin bonus")); + match err.downcast::() { + Ok(e @ RustupError::RequestedComponentsUnavailable { .. }) => { + assert!(e.to_string().contains("rustup component remove --toolchain nightly --target x86_64-apple-darwin bonus")); } _ => panic!(), } @@ -836,9 +836,9 @@ fn unavailable_component_from_profile() { false, ) .unwrap_err(); - match *err.kind() { - ErrorKind::RequestedComponentsUnavailable(..) => { - assert!(err.to_string().contains("rustup component remove --toolchain nightly --target x86_64-apple-darwin rustc")); + match err.downcast::() { + Ok(e @ RustupError::RequestedComponentsUnavailable { .. }) => { + assert!(e.to_string().contains("rustup component remove --toolchain nightly --target x86_64-apple-darwin rustc")); } _ => panic!(), } @@ -916,9 +916,9 @@ fn removed_component() { false, ) .unwrap_err(); - match *err.kind() { - ErrorKind::RequestedComponentsUnavailable(..) => { - assert!(err.to_string().contains("rustup component remove --toolchain nightly --target x86_64-apple-darwin bonus")); + match err.downcast::() { + Ok(e @ RustupError::RequestedComponentsUnavailable { .. }) => { + assert!(e.to_string().contains("rustup component remove --toolchain nightly --target x86_64-apple-darwin bonus")); } _ => panic!(), } @@ -995,9 +995,9 @@ fn unavailable_components_is_target() { false, ) .unwrap_err(); - match *err.kind() { - ErrorKind::RequestedComponentsUnavailable(..) => { - let err_str = err.to_string(); + match err.downcast::() { + Ok(e @ RustupError::RequestedComponentsUnavailable { .. }) => { + let err_str = e.to_string(); assert!(err_str .contains("rustup target remove --toolchain nightly i686-apple-darwin")); assert!(err_str.contains( @@ -1074,12 +1074,14 @@ fn unavailable_components_with_same_target() { false, ) .unwrap_err(); - match *err.kind() { - ErrorKind::RequestedComponentsUnavailable(..) => { - let err_str = err.to_string(); + match err.downcast::() { + Ok(e @ RustupError::RequestedComponentsUnavailable { .. }) => { + let err_str = e.to_string(); assert!(err_str .contains("rustup target remove --toolchain nightly x86_64-apple-darwin")); - assert!(err_str.contains("rustup component remove --toolchain nightly --target x86_64-apple-darwin rustc")); + assert!(err_str.contains( + "rustup component remove --toolchain nightly --target x86_64-apple-darwin rustc" + )); } _ => panic!(), } @@ -2018,8 +2020,8 @@ fn bad_component_hash() { ) .unwrap_err(); - match *err.kind() { - ErrorKind::ComponentDownloadFailed(_) => (), + match err.downcast::() { + Ok(RustupError::ComponentDownloadFailed(..)) => (), _ => panic!(), } }); @@ -2048,8 +2050,8 @@ fn unable_to_download_component() { ) .unwrap_err(); - match *err.kind() { - ErrorKind::ComponentDownloadFailed(..) => (), + match err.downcast::() { + Ok(RustupError::ComponentDownloadFailed(..)) => (), _ => panic!(), } }); diff --git a/tests/dist_install.rs b/tests/dist_install.rs index 9baf1ee9db..4f294c6cd4 100644 --- a/tests/dist_install.rs +++ b/tests/dist_install.rs @@ -9,7 +9,6 @@ use rustup::dist::prefix::InstallPrefix; use rustup::dist::temp; use rustup::dist::Notification; use rustup::utils::utils; -use rustup::ErrorKind; use std::fs::File; use std::io::Write; @@ -284,10 +283,10 @@ fn component_bad_version() { // Can't open components now let e = Components::open(prefix).unwrap_err(); - if let ErrorKind::BadInstalledMetadataVersion(_) = *e.kind() { - } else { - panic!() - } + assert_eq!( + "unsupported metadata version in existing installation: 100", + format!("{}", e) + ); } // Installing to a prefix that doesn't exist creates it automatically diff --git a/tests/dist_manifest.rs b/tests/dist_manifest.rs index 0b7e5ed07d..cbdaa99d9a 100644 --- a/tests/dist_manifest.rs +++ b/tests/dist_manifest.rs @@ -1,6 +1,6 @@ use rustup::dist::dist::TargetTriple; use rustup::dist::manifest::Manifest; -use rustup::ErrorKind; +use rustup::RustupError; // Example manifest from https://public.etherpad-mozilla.org/p/Rust-infra-work-week static EXAMPLE: &str = include_str!("channel-rust-nightly-example.toml"); @@ -94,8 +94,8 @@ date = "2015-10-10" let err = Manifest::parse(manifest).unwrap_err(); - match *err.kind() { - ErrorKind::MissingPackageForComponent(_) => {} + match err.downcast::().unwrap() { + RustupError::MissingPackageForComponent(_) => {} _ => panic!(), } } diff --git a/tests/dist_transactions.rs b/tests/dist_transactions.rs index 74aa128a99..e6a6a1d743 100644 --- a/tests/dist_transactions.rs +++ b/tests/dist_transactions.rs @@ -5,7 +5,7 @@ use rustup::dist::temp; use rustup::dist::Notification; use rustup::utils::raw as utils_raw; use rustup::utils::utils; -use rustup::ErrorKind; +use rustup::RustupError; use std::fs; use std::io::Write; use std::path::PathBuf; @@ -81,10 +81,10 @@ fn add_file_that_exists() { let err = tx.add_file("c", PathBuf::from("foo/bar")).unwrap_err(); - match err.0 { - ErrorKind::ComponentConflict { name, path } => { + match err.downcast_ref::() { + Some(RustupError::ComponentConflict { name, path }) => { assert_eq!(name, "c"); - assert_eq!(path, PathBuf::from("foo/bar")); + assert_eq!(path.clone(), PathBuf::from("foo/bar")); } _ => panic!(), } @@ -171,10 +171,10 @@ fn copy_file_that_exists() { .copy_file("c", PathBuf::from("foo/bar"), &srcpath) .unwrap_err(); - match err.0 { - ErrorKind::ComponentConflict { name, path } => { + match err.downcast_ref::() { + Some(RustupError::ComponentConflict { name, path }) => { assert_eq!(name, "c"); - assert_eq!(path, PathBuf::from("foo/bar")); + assert_eq!(path.clone(), PathBuf::from("foo/bar")); } _ => panic!(), } @@ -271,10 +271,10 @@ fn copy_dir_that_exists() { .copy_dir("c", PathBuf::from("a"), srcdir.path()) .unwrap_err(); - match err.0 { - ErrorKind::ComponentConflict { name, path } => { + match err.downcast_ref::() { + Some(RustupError::ComponentConflict { name, path }) => { assert_eq!(name, "c"); - assert_eq!(path, PathBuf::from("a")); + assert_eq!(path.clone(), PathBuf::from("a")); } _ => panic!(), } @@ -348,10 +348,10 @@ fn remove_file_that_not_exists() { let err = tx.remove_file("c", PathBuf::from("foo")).unwrap_err(); - match err.0 { - ErrorKind::ComponentMissingFile { name, path } => { + match err.downcast_ref::() { + Some(RustupError::ComponentMissingFile { name, path }) => { assert_eq!(name, "c"); - assert_eq!(path, PathBuf::from("foo")); + assert_eq!(path.clone(), PathBuf::from("foo")); } _ => panic!(), } @@ -427,10 +427,10 @@ fn remove_dir_that_not_exists() { let err = tx.remove_dir("c", PathBuf::from("foo")).unwrap_err(); - match err.0 { - ErrorKind::ComponentMissingDir { name, path } => { + match err.downcast_ref::() { + Some(RustupError::ComponentMissingDir { name, path }) => { assert_eq!(name, "c"); - assert_eq!(path, PathBuf::from("foo")); + assert_eq!(path.clone(), PathBuf::from("foo")); } _ => panic!(), } @@ -507,10 +507,10 @@ fn write_file_that_exists() { utils_raw::write_file(&prefix.path().join("a"), &content).unwrap(); let err = tx.write_file("c", PathBuf::from("a"), content).unwrap_err(); - match err.0 { - ErrorKind::ComponentConflict { name, path } => { + match err.downcast_ref::() { + Some(RustupError::ComponentConflict { name, path }) => { assert_eq!(name, "c"); - assert_eq!(path, PathBuf::from("a")); + assert_eq!(path.clone(), PathBuf::from("a")); } _ => panic!(), }