Use anyhow::Result in xtask, add contexts

This builds on #2231 but was actually done before that. You see, the
cause for #2231 was that I got this error message:

    Error: Error { kind: Io(Os { code: 2, kind: NotFound, message: "No such file or directory" }) }

Just switching to `anyhow::Result` got me stack traces (when setting
`RUST_LIB_BACKTRACE=1`) that at least showed

    stack backtrace:
      0: std::backtrace::Backtrace::create
      1: std::backtrace::Backtrace::capture
      2: anyhow::error::<impl core::convert::From<E> for anyhow::Error>::from
      3: xtask::install_server
      4: xtask::install
      5: xtask::main
      6: std::rt::lang_start::{{closure}}
      7: std::panicking::try::do_call
      8: __rust_maybe_catch_panic
      9: std::rt::lang_start_internal
      10: std::rt::lang_start
      11: main

With the added contexts (not at all exhaustive), the error became

    Error: install server

    Caused by:
        0: build AutoCfg with target directory
        1: No such file or directory (os error 2)

Since anyhow is such a small thing (no new transitive dependencies!),
and in general gives you `Result<T, Box<dyn Error>>` on steroids, I
think this a nice small change. The only slightly annoying thing was to
replace all the `Err(format!(…))?` calls (haven't even looked at whether
we can make it support wrapping strings though), but the `bail!` macro
is shorter anyway :)
This commit is contained in:
Pascal Hertleif 2019-11-13 20:51:57 +01:00
parent 5e3c1c2b5f
commit 5075c77957
7 changed files with 36 additions and 29 deletions

7
Cargo.lock generated
View file

@ -8,6 +8,11 @@ dependencies = [
"memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "anyhow"
version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "arrayvec" name = "arrayvec"
version = "0.5.1" version = "0.5.1"
@ -1823,6 +1828,7 @@ dependencies = [
name = "xtask" name = "xtask"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)",
"autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"pico-args 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "pico-args 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1847,6 +1853,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata] [metadata]
"checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" "checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d"
"checksum anyhow 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)" = "57114fc2a6cc374bce195d3482057c846e706d252ff3604363449695684d7a0d"
"checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" "checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
"checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" "checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90"
"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" "checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"

View file

@ -13,3 +13,4 @@ quote = "1.0.2"
proc-macro2 = "1.0.1" proc-macro2 = "1.0.1"
ron = "0.5.1" ron = "0.5.1"
serde = { version = "1.0.0", features = ["derive"] } serde = { version = "1.0.0", features = ["derive"] }
anyhow = "1.0.19"

View file

@ -19,10 +19,10 @@ fn update_staged() -> Result<()> {
.current_dir(&root) .current_dir(&root)
.output()?; .output()?;
if !output.status.success() { if !output.status.success() {
Err(format!( anyhow::bail!(
"`git diff --diff-filter=MAR --name-only --cached` exited with {}", "`git diff --diff-filter=MAR --name-only --cached` exited with {}",
output.status output.status
))?; );
} }
for line in String::from_utf8(output.stdout)?.lines() { for line in String::from_utf8(output.stdout)?.lines() {
run(&format!("git update-index --add {}", root.join(line).to_string_lossy()), ".")?; run(&format!("git update-index --add {}", root.join(line).to_string_lossy()), ".")?;

View file

@ -52,7 +52,7 @@ fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> {
_ => (), _ => (),
} }
if mode == Mode::Verify { if mode == Mode::Verify {
Err(format!("`{}` is not up-to-date", path.display()))?; anyhow::bail!("`{}` is not up-to-date", path.display());
} }
eprintln!("updating {}", path.display()); eprintln!("updating {}", path.display());
fs::write(path, contents)?; fs::write(path, contents)?;
@ -101,10 +101,8 @@ fn do_extract_comment_blocks(text: &str, allow_blocks_with_empty_lins: bool) ->
let is_comment = line.starts_with(prefix); let is_comment = line.starts_with(prefix);
if is_comment { if is_comment {
block.push(line[prefix.len()..].to_string()); block.push(line[prefix.len()..].to_string());
} else { } else if !block.is_empty() {
if !block.is_empty() { res.push(mem::replace(&mut block, Vec::new()));
res.push(mem::replace(&mut block, Vec::new()))
}
} }
} }
if !block.is_empty() { if !block.is_empty() {

View file

@ -102,10 +102,10 @@ fn tests_from_dir(dir: &Path) -> Result<Tests> {
for test in collect_tests(&text) { for test in collect_tests(&text) {
if test.ok { if test.ok {
if let Some(old_test) = res.ok.insert(test.name.clone(), test) { if let Some(old_test) = res.ok.insert(test.name.clone(), test) {
return Err(format!("Duplicate test: {}", old_test.name).into()); anyhow::bail!("Duplicate test: {}", old_test.name);
} }
} else if let Some(old_test) = res.err.insert(test.name.clone(), test) { } else if let Some(old_test) = res.err.insert(test.name.clone(), test) {
return Err(format!("Duplicate test: {}", old_test.name).into()); anyhow::bail!("Duplicate test: {}", old_test.name);
} }
} }
Ok(()) Ok(())

View file

@ -2,10 +2,10 @@
pub mod codegen; pub mod codegen;
use anyhow::Context;
pub use anyhow::Result;
use std::{ use std::{
env, env, fs,
error::Error,
fs,
io::{Error as IoError, ErrorKind}, io::{Error as IoError, ErrorKind},
path::{Path, PathBuf}, path::{Path, PathBuf},
process::{Command, Output, Stdio}, process::{Command, Output, Stdio},
@ -13,8 +13,6 @@ use std::{
use crate::codegen::Mode; use crate::codegen::Mode;
pub type Result<T> = std::result::Result<T, Box<dyn Error>>;
const TOOLCHAIN: &str = "stable"; const TOOLCHAIN: &str = "stable";
pub fn project_root() -> PathBuf { pub fn project_root() -> PathBuf {
@ -69,7 +67,7 @@ pub fn run_rustfmt(mode: Mode) -> Result<()> {
.status() .status()
{ {
Ok(status) if status.success() => (), Ok(status) if status.success() => (),
_ => install_rustfmt()?, _ => install_rustfmt().context("install rustfmt")?,
}; };
if mode == Mode::Verify { if mode == Mode::Verify {
@ -112,7 +110,7 @@ pub fn run_clippy() -> Result<()> {
.status() .status()
{ {
Ok(status) if status.success() => (), Ok(status) if status.success() => (),
_ => install_clippy()?, _ => install_clippy().context("install clippy")?,
}; };
let allowed_lints = [ let allowed_lints = [
@ -162,9 +160,9 @@ where
let exec = args.next().unwrap(); let exec = args.next().unwrap();
let mut cmd = Command::new(exec); let mut cmd = Command::new(exec);
f(cmd.args(args).current_dir(proj_dir).stderr(Stdio::inherit())); f(cmd.args(args).current_dir(proj_dir).stderr(Stdio::inherit()));
let output = cmd.output()?; let output = cmd.output().with_context(|| format!("running `{}`", cmdline))?;
if !output.status.success() { if !output.status.success() {
Err(format!("`{}` exited with {}", cmdline, output.status))?; anyhow::bail!("`{}` exited with {}", cmdline, output.status);
} }
Ok(output) Ok(output)
} }

View file

@ -9,6 +9,7 @@
//! `.cargo/config`. //! `.cargo/config`.
mod help; mod help;
use anyhow::Context;
use autocfg; use autocfg;
use core::fmt::Write; use core::fmt::Write;
use core::str; use core::str;
@ -114,21 +115,21 @@ fn handle_extra_flags(e: pico_args::Error) -> Result<()> {
write!(&mut invalid_flags, "{}, ", flag)?; write!(&mut invalid_flags, "{}, ", flag)?;
} }
let (invalid_flags, _) = invalid_flags.split_at(invalid_flags.len() - 2); let (invalid_flags, _) = invalid_flags.split_at(invalid_flags.len() - 2);
Err(format!("Invalid flags: {}", invalid_flags).into()) anyhow::bail!("Invalid flags: {}", invalid_flags)
} else { } else {
Err(e.to_string().into()) anyhow::bail!(e.to_string())
} }
} }
fn install(opts: InstallOpt) -> Result<()> { fn install(opts: InstallOpt) -> Result<()> {
if cfg!(target_os = "macos") { if cfg!(target_os = "macos") {
fix_path_for_mac()? fix_path_for_mac().context("Fix path for mac")?
} }
if let Some(server) = opts.server { if let Some(server) = opts.server {
install_server(server)?; install_server(server).context("install server")?;
} }
if let Some(client) = opts.client { if let Some(client) = opts.client {
install_client(client)?; install_client(client).context("install client")?;
} }
Ok(()) Ok(())
} }
@ -140,7 +141,7 @@ fn fix_path_for_mac() -> Result<()> {
const ROOT_DIR: &str = ""; const ROOT_DIR: &str = "";
let home_dir = match env::var("HOME") { let home_dir = match env::var("HOME") {
Ok(home) => home, Ok(home) => home,
Err(e) => Err(format!("Failed getting HOME from environment with error: {}.", e))?, Err(e) => anyhow::bail!("Failed getting HOME from environment with error: {}.", e),
}; };
[ROOT_DIR, &home_dir] [ROOT_DIR, &home_dir]
@ -154,12 +155,12 @@ fn fix_path_for_mac() -> Result<()> {
if !vscode_path.is_empty() { if !vscode_path.is_empty() {
let vars = match env::var_os("PATH") { let vars = match env::var_os("PATH") {
Some(path) => path, Some(path) => path,
None => Err("Could not get PATH variable from env.")?, None => anyhow::bail!("Could not get PATH variable from env."),
}; };
let mut paths = env::split_paths(&vars).collect::<Vec<_>>(); let mut paths = env::split_paths(&vars).collect::<Vec<_>>();
paths.append(&mut vscode_path); paths.append(&mut vscode_path);
let new_paths = env::join_paths(paths)?; let new_paths = env::join_paths(paths).context("build env PATH")?;
env::set_var("PATH", &new_paths); env::set_var("PATH", &new_paths);
} }
@ -198,7 +199,7 @@ fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> {
let code_binary = match code_binary { let code_binary = match code_binary {
Some(it) => it, Some(it) => it,
None => Err("Can't execute `code --version`. Perhaps it is not in $PATH?")?, None => anyhow::bail!("Can't execute `code --version`. Perhaps it is not in $PATH?"),
}; };
Cmd { Cmd {
@ -219,8 +220,10 @@ fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> {
.run_with_output()?; .run_with_output()?;
if !str::from_utf8(&output.stdout)?.contains("ra-lsp") { if !str::from_utf8(&output.stdout)?.contains("ra-lsp") {
Err("Could not install the Visual Studio Code extension. \ anyhow::bail!(
Please make sure you have at least NodeJS 10.x installed and try again.")?; "Could not install the Visual Studio Code extension. \
Please make sure you have at least NodeJS 10.x installed and try again."
);
} }
Ok(()) Ok(())