1use std::path::PathBuf;
8
9use super::{Repo, RepoError};
10
11#[derive(Debug, Default, Clone, PartialEq, Eq)]
16pub struct RepoStatus {
17 pub staged: Vec<PathBuf>,
20 pub unstaged: Vec<PathBuf>,
23 pub untracked: Vec<PathBuf>,
25}
26
27impl Repo {
28 pub async fn status(&self) -> Result<RepoStatus, RepoError> {
36 let inner = self.thread_safe();
37 tokio::task::spawn_blocking(move || compute_status(&inner)).await.map_err(|join_err| {
38 RepoError::StatusFailed { cause: format!("spawn_blocking join error: {join_err}") }
39 })?
40 }
41}
42
43fn compute_status(inner: &gix::ThreadSafeRepository) -> Result<RepoStatus, RepoError> {
44 let repo = inner.to_thread_local();
45 let platform = repo
46 .status(gix::progress::Discard)
47 .map_err(|e| RepoError::StatusFailed { cause: e.to_string() })?
48 .untracked_files(gix::status::UntrackedFiles::Files);
49
50 let staged: Vec<PathBuf> = Vec::new();
52 let mut unstaged: Vec<PathBuf> = Vec::new();
53 let mut untracked: Vec<PathBuf> = Vec::new();
54
55 let iter = platform
56 .into_iter(std::iter::empty::<gix::bstr::BString>())
57 .map_err(|e| RepoError::StatusFailed { cause: e.to_string() })?;
58
59 for item in iter {
60 let item = item.map_err(|e| RepoError::StatusFailed { cause: e.to_string() })?;
61 match item {
62 gix::status::Item::IndexWorktree(iw) => match iw {
63 gix::status::index_worktree::Item::Modification { rela_path, .. } => {
64 unstaged.push(status_path(rela_path)?);
65 }
66 gix::status::index_worktree::Item::Rewrite { dirwalk_entry, .. } => {
67 unstaged.push(status_path(dirwalk_entry.rela_path)?);
72 }
73 gix::status::index_worktree::Item::DirectoryContents { entry, .. } => {
74 if matches!(entry.status, gix::dir::entry::Status::Untracked) {
75 untracked.push(status_path(entry.rela_path)?);
76 }
77 }
80 },
81 gix::status::Item::TreeIndex(_) => {
82 }
84 }
85 }
86
87 Ok(RepoStatus { staged, unstaged, untracked })
88}
89
90fn status_path(rela_path: gix::bstr::BString) -> Result<PathBuf, RepoError> {
91 gix::path::from_bstring(rela_path)
92 .map_err(|e| RepoError::StatusFailed { cause: format!("path encoding: {e}") })
93}