1use std::collections::{BTreeMap, BTreeSet};
4use std::path::PathBuf;
5
6use cadvm_store::ObjectId;
7
8use crate::error::Result;
9use crate::index::HashCache;
10use crate::model::Manifest;
11use crate::repo::Repository;
12use crate::worktree;
13
14#[derive(Debug, Clone, Default, PartialEq, Eq)]
16pub struct WorkingTreeStatus {
17 pub branch: Option<String>,
19 pub new: Vec<PathBuf>,
21 pub modified: Vec<PathBuf>,
23 pub deleted: Vec<PathBuf>,
25}
26
27impl WorkingTreeStatus {
28 pub fn is_clean(&self) -> bool {
30 self.new.is_empty() && self.modified.is_empty() && self.deleted.is_empty()
31 }
32}
33
34pub fn working_tree_status(repo: &Repository) -> Result<WorkingTreeStatus> {
36 let manifest = repo.head_manifest()?;
37 status_against(repo, &manifest)
38}
39
40pub fn status_against(repo: &Repository, manifest: &Manifest) -> Result<WorkingTreeStatus> {
42 let branch = repo.current_branch()?;
43
44 let mut cache = HashCache::load(repo);
47 let mut working: BTreeMap<PathBuf, ObjectId> = BTreeMap::new();
48 let mut seen: BTreeSet<PathBuf> = BTreeSet::new();
49 for rel in worktree::scan_step_files(repo)? {
50 let hash = cache.hash(repo, &rel)?;
51 seen.insert(rel.clone());
52 working.insert(rel, hash);
53 }
54 cache.retain(&seen);
55 let _ = cache.save(repo); let mut status = WorkingTreeStatus {
58 branch,
59 ..Default::default()
60 };
61
62 for (path, hash) in &working {
64 match manifest.files.get(path) {
65 None => status.new.push(path.clone()),
66 Some(entry) if &entry.raw_hash != hash => status.modified.push(path.clone()),
67 Some(_) => {}
68 }
69 }
70
71 for path in manifest.files.keys() {
73 if !working.contains_key(path) {
74 status.deleted.push(path.clone());
75 }
76 }
77
78 status.new.sort();
79 status.modified.sort();
80 status.deleted.sort();
81 Ok(status)
82}