diff --git a/Cargo.lock b/Cargo.lock index da639c31b5..04716feafd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,19 +1,10 @@ # 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 = "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 +58,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" @@ -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" @@ -209,9 +186,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 +280,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 +340,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 +353,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 +364,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", @@ -519,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", ] @@ -591,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.2", -] - [[package]] name = "filetime" version = "0.2.14" @@ -658,30 +619,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 +652,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 +687,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]] @@ -754,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" @@ -782,17 +734,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 +751,6 @@ dependencies = [ "tokio", "tokio-util", "tracing", - "tracing-futures", ] [[package]] @@ -825,9 +770,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 +795,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 +818,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 +833,7 @@ dependencies = [ "httpdate", "itoa", "pin-project", - "socket2", + "socket2 0.4.0", "tokio", "tower-service", "tracing", @@ -941,9 +887,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 +901,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 +918,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 +942,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 +1009,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 +1024,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 +1034,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 +1047,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", ] @@ -1236,17 +1172,11 @@ 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.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 +1195,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 +1215,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 +1313,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 +1333,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 +1369,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 +1513,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 +1551,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", @@ -1663,9 +1592,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", ] @@ -1729,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" @@ -1771,7 +1694,6 @@ dependencies = [ "clap", "download", "effective-limits", - "error-chain", "flate2", "git-testament", "home", @@ -1796,6 +1718,7 @@ dependencies = [ "tar", "tempfile", "term", + "thiserror", "threadpool", "toml", "url", @@ -1856,9 +1779,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 +1792,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 +1820,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 +1840,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 +1939,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 +1991,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 +2086,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 +2107,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 +2122,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 +2170,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 +2199,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 +2217,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 +2245,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 +2261,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 +2332,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 +2347,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 +2380,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 +2392,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 +2407,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 +2419,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 +2429,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 +2442,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 +2567,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 +2586,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 336746e221..13e3b5012c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,9 +30,8 @@ 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" git-testament = "0.1.4" home = {git = "https://github.com/rbtcollins/home", rev = "a243ee2fbee6022c57d56f5aa79aefe194eabe53"} @@ -43,6 +42,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 +55,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" @@ -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"] 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 d9ee516e65..443b673cf7 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,16 +1,15 @@ #![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::Debug; use std::io::{self, Write}; use std::path::PathBuf; + +use thiserror::Error as ThisError; use url::Url; +use crate::dist::manifest::{Component, Manifest}; + 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 \ @@ -19,408 +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") - } - } -} - -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 { @@ -503,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!(), }