use crate::Result;
use git2::{Repository, Status, StatusShow};
use serde::{Deserialize, Serialize};
use std::path::Path;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum StatusKind {
Modified,
Added,
Deleted,
Renamed,
Untracked,
Ignored,
Conflicted,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FileStatus {
pub path: String,
pub kind: StatusKind,
pub staged: bool,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct RepoStatus {
pub files: Vec<FileStatus>,
pub staged_count: usize,
pub modified_count: usize,
pub untracked_count: usize,
}
impl RepoStatus {
pub fn collect(repo: &Repository, _workdir: &Path) -> Result<Self> {
let mut files = Vec::new();
let mut staged_count = 0;
let mut modified_count = 0;
let mut untracked_count = 0;
let statuses = repo.statuses(Some(
git2::StatusOptions::new()
.show(StatusShow::IndexAndWorkdir)
.include_ignored(false)
.recurse_untracked_dirs(false),
))?;
for status in statuses.iter() {
let path = status.path().unwrap_or("").to_string();
let status_flags = status.status();
let is_staged = status_flags.intersects(
Status::INDEX_NEW
| Status::INDEX_MODIFIED
| Status::INDEX_DELETED
| Status::INDEX_RENAMED
| Status::INDEX_TYPECHANGE,
);
let kind = if status_flags.intersects(Status::CONFLICTED) {
StatusKind::Conflicted
} else if status_flags.intersects(Status::WT_NEW) {
StatusKind::Untracked
} else if status_flags.intersects(Status::WT_MODIFIED) {
StatusKind::Modified
} else if status_flags.intersects(Status::WT_DELETED) {
StatusKind::Deleted
} else if status_flags.intersects(Status::WT_RENAMED) {
StatusKind::Renamed
} else if status_flags.intersects(Status::INDEX_NEW) {
StatusKind::Added
} else if status_flags.intersects(Status::INDEX_MODIFIED) {
StatusKind::Modified
} else if status_flags.intersects(Status::INDEX_DELETED) {
StatusKind::Deleted
} else if status_flags.intersects(Status::INDEX_RENAMED) {
StatusKind::Renamed
} else if status_flags.intersects(Status::INDEX_TYPECHANGE) {
StatusKind::Modified
} else {
continue;
};
if is_staged {
staged_count += 1;
}
match kind {
StatusKind::Modified
| StatusKind::Deleted
| StatusKind::Renamed
| StatusKind::Conflicted => modified_count += 1,
StatusKind::Added | StatusKind::Untracked => untracked_count += 1,
StatusKind::Ignored => {}
}
files.push(FileStatus {
path,
kind,
staged: is_staged,
});
}
Ok(Self {
files,
staged_count,
modified_count,
untracked_count,
})
}
pub fn is_clean(&self) -> bool {
self.files.is_empty()
}
pub fn has_staged(&self) -> bool {
self.staged_count > 0
}
pub fn has_unstaged(&self) -> bool {
self.modified_count > 0 || self.untracked_count > 0
}
}