1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
//! Status

use std::collections::BTreeMap;

use git2::StatusOptions;

use crate::{error::Error, repo::GitRepository};

/// Status of a file
pub type Status = git2::Status;

/// Status show option
pub type StatusShow = git2::StatusShow;

/// Checks if the repo is clean, ie has no uncommited and untracked files
pub fn repo_status(
    repo: &GitRepository,
    show: StatusShow,
) -> Result<BTreeMap<String, git2::Status>, Error> {
    if repo.is_bare() {
        return Err(Error::msg("cannot report status on bare repository"));
    }

    let mut opts = StatusOptions::new();
    opts.show(show).include_untracked(true);
    let entries: BTreeMap<String, git2::Status> = repo
        .statuses(Some(&mut opts))?
        .into_iter()
        .map(|e| {
            (
                e.path().map(|p| p.to_string()).unwrap_or_default(),
                e.status(),
            )
        })
        .collect();

    Ok(entries)
}

#[cfg(test)]
mod tests {
    use crate::repo::discover_repo;

    // Note this useful idiom: importing names from outer (for mod tests) scope.
    use super::*;

    #[test]
    fn test_repo_status() {
        let cwd = std::env::current_dir().unwrap();
        let repo = discover_repo(&cwd).unwrap();
        let dirty_files = repo_status(&repo, StatusShow::IndexAndWorkdir).unwrap();
        for (file, status) in dirty_files {
            eprintln!("{:?}: {:?}", file, status);
        }
    }
}