ZIm/crates/git/src/commit.rs
Kirill Bulatov 16366cf9f2
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
2025-05-20 23:06:07 +00:00

111 lines
3.5 KiB
Rust
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use crate::{Oid, status::StatusCode};
use anyhow::{Context as _, Result};
use collections::HashMap;
use std::path::Path;
pub async fn get_messages(working_directory: &Path, shas: &[Oid]) -> Result<HashMap<Oid, String>> {
if shas.is_empty() {
return Ok(HashMap::default());
}
const MARKER: &str = "<MARKER>";
let output = util::command::new_smol_command("git")
.current_dir(working_directory)
.arg("show")
.arg("-s")
.arg(format!("--format=%B{}", MARKER))
.args(shas.iter().map(ToString::to_string))
.output()
.await
.context("starting git blame process")?;
anyhow::ensure!(
output.status.success(),
"'git show' failed with error {:?}",
output.status
);
Ok(shas
.iter()
.cloned()
.zip(
String::from_utf8_lossy(&output.stdout)
.trim()
.split_terminator(MARKER)
.map(|str| str.trim().replace("<", "&lt;").replace(">", "&gt;")),
)
.collect::<HashMap<Oid, String>>())
}
/// Parse the output of `git diff --name-status -z`
pub fn parse_git_diff_name_status(content: &str) -> impl Iterator<Item = (&Path, StatusCode)> {
let mut parts = content.split('\0');
std::iter::from_fn(move || {
loop {
let status_str = parts.next()?;
let path = parts.next()?;
let status = match status_str {
"M" => StatusCode::Modified,
"A" => StatusCode::Added,
"D" => StatusCode::Deleted,
_ => continue,
};
return Some((Path::new(path), status));
}
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_git_diff_name_status() {
let input = concat!(
"MCargo.lock",
"Mcrates/project/Cargo.toml",
"Mcrates/project/src/buffer_store.rs",
"Dcrates/project/src/git.rs",
"Acrates/project/src/git_store.rs",
"Acrates/project/src/git_store/git_traversal.rs",
"Mcrates/project/src/project.rs",
"Mcrates/project/src/worktree_store.rs",
"Mcrates/project_panel/src/project_panel.rs",
);
let output = parse_git_diff_name_status(input).collect::<Vec<_>>();
assert_eq!(
output,
&[
(Path::new("Cargo.lock"), StatusCode::Modified),
(Path::new("crates/project/Cargo.toml"), StatusCode::Modified),
(
Path::new("crates/project/src/buffer_store.rs"),
StatusCode::Modified
),
(Path::new("crates/project/src/git.rs"), StatusCode::Deleted),
(
Path::new("crates/project/src/git_store.rs"),
StatusCode::Added
),
(
Path::new("crates/project/src/git_store/git_traversal.rs"),
StatusCode::Added,
),
(
Path::new("crates/project/src/project.rs"),
StatusCode::Modified
),
(
Path::new("crates/project/src/worktree_store.rs"),
StatusCode::Modified
),
(
Path::new("crates/project_panel/src/project_panel.rs"),
StatusCode::Modified
),
]
);
}
}