Use anyhow more idiomatically (#31052)

https://github.com/zed-industries/zed/issues/30972 brought up another
case where our context is not enough to track the actual source of the
issue: we get a general top-level error without inner error.

The reason for this was `.ok_or_else(|| anyhow!("failed to read HEAD
SHA"))?; ` on the top level.

The PR finally reworks the way we use anyhow to reduce such issues (or
at least make it simpler to bubble them up later in a fix).
On top of that, uses a few more anyhow methods for better readability.

* `.ok_or_else(|| anyhow!("..."))`, `map_err` and other similar error
conversion/option reporting cases are replaced with `context` and
`with_context` calls
* in addition to that, various `anyhow!("failed to do ...")` are
stripped with `.context("Doing ...")` messages instead to remove the
parasitic `failed to` text
* `anyhow::ensure!` is used instead of `if ... { return Err(...); }`
calls
* `anyhow::bail!` is used instead of `return Err(anyhow!(...));`

Release Notes:

- N/A
This commit is contained in:
Kirill Bulatov 2025-05-21 02:06:07 +03:00 committed by GitHub
parent 1e51a7ac44
commit 16366cf9f2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
294 changed files with 2037 additions and 2610 deletions

View file

@ -1,6 +1,6 @@
use crate::commit::get_messages;
use crate::{GitRemote, Oid};
use anyhow::{Context as _, Result, anyhow};
use anyhow::{Context as _, Result};
use collections::{HashMap, HashSet};
use futures::AsyncWriteExt;
use gpui::SharedString;
@ -80,7 +80,7 @@ async fn run_git_blame(
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.map_err(|e| anyhow!("Failed to start git blame process: {}", e))?;
.context("starting git blame process")?;
let stdin = child
.stdin
@ -92,10 +92,7 @@ async fn run_git_blame(
}
stdin.flush().await?;
let output = child
.output()
.await
.map_err(|e| anyhow!("Failed to read git blame output: {}", e))?;
let output = child.output().await.context("reading git blame output")?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
@ -103,7 +100,7 @@ async fn run_git_blame(
if trimmed == GIT_BLAME_NO_COMMIT_ERROR || trimmed.contains(GIT_BLAME_NO_PATH) {
return Ok(String::new());
}
return Err(anyhow!("git blame process failed: {}", stderr));
anyhow::bail!("git blame process failed: {stderr}");
}
Ok(String::from_utf8(output.stdout)?)
@ -144,21 +141,21 @@ impl BlameEntry {
let sha = parts
.next()
.and_then(|line| line.parse::<Oid>().ok())
.ok_or_else(|| anyhow!("failed to parse sha"))?;
.context("parsing sha")?;
let original_line_number = parts
.next()
.and_then(|line| line.parse::<u32>().ok())
.ok_or_else(|| anyhow!("Failed to parse original line number"))?;
.context("parsing original line number")?;
let final_line_number = parts
.next()
.and_then(|line| line.parse::<u32>().ok())
.ok_or_else(|| anyhow!("Failed to parse final line number"))?;
.context("parsing final line number")?;
let line_count = parts
.next()
.and_then(|line| line.parse::<u32>().ok())
.ok_or_else(|| anyhow!("Failed to parse final line number"))?;
.context("parsing line count")?;
let start_line = final_line_number.saturating_sub(1);
let end_line = start_line + line_count;

Binary file not shown.

View file

@ -7,7 +7,7 @@ pub mod status;
pub use crate::hosting_provider::*;
pub use crate::remote::*;
use anyhow::{Context as _, Result, anyhow};
use anyhow::{Context as _, Result};
pub use git2 as libgit;
use gpui::action_with_deprecated_aliases;
use gpui::actions;
@ -99,7 +99,7 @@ impl FromStr for Oid {
fn from_str(s: &str) -> std::prelude::v1::Result<Self, Self::Err> {
libgit::Oid::from_str(s)
.map_err(|error| anyhow!("failed to parse git oid: {}", error))
.context("parsing git oid")
.map(Self)
}
}

View file

@ -477,7 +477,7 @@ impl GitRepository for RealGitRepository {
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.output()
.map_err(|e| anyhow!("Failed to start git show process: {e}"))?;
.context("starting git show process")?;
let show_stdout = String::from_utf8_lossy(&show_output.stdout);
let mut lines = show_stdout.split('\n');
@ -491,7 +491,7 @@ impl GitRepository for RealGitRepository {
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.map_err(|e| anyhow!("Failed to start git cat-file process: {e}"))?;
.context("starting git cat-file process")?;
use std::io::Write as _;
let mut files = Vec::<CommitFile>::new();
@ -578,12 +578,11 @@ impl GitRepository for RealGitRepository {
.args(["reset", mode_flag, &commit])
.output()
.await?;
if !output.status.success() {
return Err(anyhow!(
"Failed to reset:\n{}",
String::from_utf8_lossy(&output.stderr)
));
}
anyhow::ensure!(
output.status.success(),
"Failed to reset:\n{}",
String::from_utf8_lossy(&output.stderr),
);
Ok(())
}
.boxed()
@ -609,12 +608,11 @@ impl GitRepository for RealGitRepository {
.args(paths.iter().map(|path| path.as_ref()))
.output()
.await?;
if !output.status.success() {
return Err(anyhow!(
"Failed to checkout files:\n{}",
String::from_utf8_lossy(&output.stderr)
));
}
anyhow::ensure!(
output.status.success(),
"Failed to checkout files:\n{}",
String::from_utf8_lossy(&output.stderr),
);
Ok(())
}
.boxed()
@ -707,12 +705,11 @@ impl GitRepository for RealGitRepository {
.output()
.await?;
if !output.status.success() {
return Err(anyhow!(
"Failed to stage:\n{}",
String::from_utf8_lossy(&output.stderr)
));
}
anyhow::ensure!(
output.status.success(),
"Failed to stage:\n{}",
String::from_utf8_lossy(&output.stderr)
);
} else {
let output = new_smol_command(&git_binary_path)
.current_dir(&working_directory)
@ -721,13 +718,11 @@ impl GitRepository for RealGitRepository {
.arg(path.to_unix_style())
.output()
.await?;
if !output.status.success() {
return Err(anyhow!(
"Failed to unstage:\n{}",
String::from_utf8_lossy(&output.stderr)
));
}
anyhow::ensure!(
output.status.success(),
"Failed to unstage:\n{}",
String::from_utf8_lossy(&output.stderr)
);
}
Ok(())
@ -761,7 +756,7 @@ impl GitRepository for RealGitRepository {
let stdin = process
.stdin
.take()
.ok_or_else(|| anyhow!("no stdin for git cat-file subprocess"))?;
.context("no stdin for git cat-file subprocess")?;
let mut stdin = BufWriter::new(stdin);
for rev in &revs {
write!(&mut stdin, "{rev}\n")?;
@ -813,7 +808,7 @@ impl GitRepository for RealGitRepository {
stdout.parse()
} else {
let stderr = String::from_utf8_lossy(&output.stderr);
Err(anyhow!("git status failed: {}", stderr))
anyhow::bail!("git status failed: {stderr}");
}
})
.boxed()
@ -849,12 +844,11 @@ impl GitRepository for RealGitRepository {
.output()
.await?;
if !output.status.success() {
return Err(anyhow!(
"Failed to git git branches:\n{}",
String::from_utf8_lossy(&output.stderr)
));
}
anyhow::ensure!(
output.status.success(),
"Failed to git git branches:\n{}",
String::from_utf8_lossy(&output.stderr)
);
let input = String::from_utf8_lossy(&output.stdout);
@ -903,7 +897,7 @@ impl GitRepository for RealGitRepository {
branch.set_upstream(Some(&name))?;
branch
} else {
return Err(anyhow!("Branch not found"));
anyhow::bail!("Branch not found");
};
let revision = branch.get();
@ -912,7 +906,7 @@ impl GitRepository for RealGitRepository {
repo.set_head(
revision
.name()
.ok_or_else(|| anyhow!("Branch name could not be retrieved"))?,
.context("Branch name could not be retrieved")?,
)?;
Ok(())
})
@ -970,12 +964,11 @@ impl GitRepository for RealGitRepository {
.output()
.await?;
if !output.status.success() {
return Err(anyhow!(
"Failed to run git diff:\n{}",
String::from_utf8_lossy(&output.stderr)
));
}
anyhow::ensure!(
output.status.success(),
"Failed to run git diff:\n{}",
String::from_utf8_lossy(&output.stderr)
);
Ok(String::from_utf8_lossy(&output.stdout).to_string())
})
.boxed()
@ -998,13 +991,11 @@ impl GitRepository for RealGitRepository {
.args(paths.iter().map(|p| p.to_unix_style()))
.output()
.await?;
if !output.status.success() {
return Err(anyhow!(
"Failed to stage paths:\n{}",
String::from_utf8_lossy(&output.stderr)
));
}
anyhow::ensure!(
output.status.success(),
"Failed to stage paths:\n{}",
String::from_utf8_lossy(&output.stderr),
);
}
Ok(())
})
@ -1030,12 +1021,11 @@ impl GitRepository for RealGitRepository {
.output()
.await?;
if !output.status.success() {
return Err(anyhow!(
"Failed to unstage:\n{}",
String::from_utf8_lossy(&output.stderr)
));
}
anyhow::ensure!(
output.status.success(),
"Failed to unstage:\n{}",
String::from_utf8_lossy(&output.stderr),
);
}
Ok(())
})
@ -1069,12 +1059,11 @@ impl GitRepository for RealGitRepository {
let output = cmd.output().await?;
if !output.status.success() {
return Err(anyhow!(
"Failed to commit:\n{}",
String::from_utf8_lossy(&output.stderr)
));
}
anyhow::ensure!(
output.status.success(),
"Failed to commit:\n{}",
String::from_utf8_lossy(&output.stderr)
);
Ok(())
})
.boxed()
@ -1190,22 +1179,19 @@ impl GitRepository for RealGitRepository {
.output()
.await?;
if output.status.success() {
let remote_names = String::from_utf8_lossy(&output.stdout)
.split('\n')
.filter(|name| !name.is_empty())
.map(|name| Remote {
name: name.trim().to_string().into(),
})
.collect();
return Ok(remote_names);
} else {
return Err(anyhow!(
"Failed to get remotes:\n{}",
String::from_utf8_lossy(&output.stderr)
));
}
anyhow::ensure!(
output.status.success(),
"Failed to get remotes:\n{}",
String::from_utf8_lossy(&output.stderr)
);
let remote_names = String::from_utf8_lossy(&output.stdout)
.split('\n')
.filter(|name| !name.is_empty())
.map(|name| Remote {
name: name.trim().to_string().into(),
})
.collect();
Ok(remote_names)
})
.boxed()
}
@ -1222,11 +1208,11 @@ impl GitRepository for RealGitRepository {
.args(args)
.output()
.await?;
if output.status.success() {
Ok(String::from_utf8(output.stdout)?)
} else {
Err(anyhow!(String::from_utf8_lossy(&output.stderr).to_string()))
}
anyhow::ensure!(
output.status.success(),
String::from_utf8_lossy(&output.stderr).to_string()
);
Ok(String::from_utf8(output.stdout)?)
};
let head = git_cmd(&["rev-parse", "HEAD"])
@ -1504,14 +1490,14 @@ impl GitBinary {
{
let mut command = self.build_command(args);
let output = command.output().await?;
if output.status.success() {
Ok(String::from_utf8(output.stdout)?)
} else {
Err(anyhow!(GitBinaryCommandError {
anyhow::ensure!(
output.status.success(),
GitBinaryCommandError {
stdout: String::from_utf8_lossy(&output.stdout).to_string(),
status: output.status,
}))
}
}
);
Ok(String::from_utf8(output.stdout)?)
}
fn build_command<S>(&self, args: impl IntoIterator<Item = S>) -> smol::process::Command
@ -1545,14 +1531,15 @@ async fn run_git_command(
if env.contains_key("GIT_ASKPASS") {
let git_process = command.spawn()?;
let output = git_process.output().await?;
if !output.status.success() {
Err(anyhow!("{}", String::from_utf8_lossy(&output.stderr)))
} else {
Ok(RemoteCommandOutput {
stdout: String::from_utf8_lossy(&output.stdout).to_string(),
stderr: String::from_utf8_lossy(&output.stderr).to_string(),
})
}
anyhow::ensure!(
output.status.success(),
"{}",
String::from_utf8_lossy(&output.stderr)
);
Ok(RemoteCommandOutput {
stdout: String::from_utf8_lossy(&output.stdout).to_string(),
stderr: String::from_utf8_lossy(&output.stderr).to_string(),
})
} else {
let ask_pass = AskPassSession::new(executor, ask_pass).await?;
command
@ -1568,7 +1555,7 @@ async fn run_git_command(
async fn run_askpass_command(
mut ask_pass: AskPassSession,
git_process: smol::process::Child,
) -> std::result::Result<RemoteCommandOutput, anyhow::Error> {
) -> anyhow::Result<RemoteCommandOutput> {
select_biased! {
result = ask_pass.run().fuse() => {
match result {
@ -1582,17 +1569,15 @@ async fn run_askpass_command(
}
output = git_process.output().fuse() => {
let output = output?;
if !output.status.success() {
Err(anyhow!(
"{}",
String::from_utf8_lossy(&output.stderr)
))
} else {
Ok(RemoteCommandOutput {
stdout: String::from_utf8_lossy(&output.stdout).to_string(),
stderr: String::from_utf8_lossy(&output.stderr).to_string(),
})
}
anyhow::ensure!(
output.status.success(),
"{}",
String::from_utf8_lossy(&output.stderr)
);
Ok(RemoteCommandOutput {
stdout: String::from_utf8_lossy(&output.stdout).to_string(),
stderr: String::from_utf8_lossy(&output.stderr).to_string(),
})
}
}
}
@ -1752,12 +1737,8 @@ fn parse_upstream_track(upstream_track: &str) -> Result<UpstreamTracking> {
}));
}
let upstream_track = upstream_track
.strip_prefix("[")
.ok_or_else(|| anyhow!("missing ["))?;
let upstream_track = upstream_track
.strip_suffix("]")
.ok_or_else(|| anyhow!("missing ["))?;
let upstream_track = upstream_track.strip_prefix("[").context("missing [")?;
let upstream_track = upstream_track.strip_suffix("]").context("missing [")?;
let mut ahead: u32 = 0;
let mut behind: u32 = 0;
for component in upstream_track.split(", ") {

View file

@ -1,5 +1,5 @@
use crate::repository::RepoPath;
use anyhow::{Result, anyhow};
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::{path::Path, str::FromStr, sync::Arc};
use util::ResultExt;
@ -241,7 +241,7 @@ impl StatusCode {
b'R' => Ok(StatusCode::Renamed),
b'C' => Ok(StatusCode::Copied),
b' ' => Ok(StatusCode::Unmodified),
_ => Err(anyhow!("Invalid status code: {byte}")),
_ => anyhow::bail!("Invalid status code: {byte}"),
}
}
@ -286,7 +286,7 @@ impl UnmergedStatusCode {
b'A' => Ok(UnmergedStatusCode::Added),
b'D' => Ok(UnmergedStatusCode::Deleted),
b'U' => Ok(UnmergedStatusCode::Updated),
_ => Err(anyhow!("Invalid unmerged status code: {byte}")),
_ => anyhow::bail!("Invalid unmerged status code: {byte}"),
}
}
}