Skip to main content

ex_cli/git/
cache.rs

1use crate::error::MyResult;
2use crate::git::cache::TriOption::*;
3use crate::git::flags::GitFlags;
4use crate::git::repo::GitRepo;
5use std::cell::RefCell;
6use std::collections::HashMap;
7use std::ops::DerefMut;
8use std::path::{Path, PathBuf};
9use std::rc::Rc;
10
11enum TriOption<T> {
12    Yes(T),
13    No,
14    Maybe,
15}
16
17type GitRepoMap = HashMap<PathBuf, Option<Rc<RefCell<GitRepo>>>>;
18
19pub struct GitCache {
20    flags: GitFlags,
21    repos: RefCell<GitRepoMap>,
22}
23
24impl GitCache {
25    pub fn new(flags: &GitFlags) -> Self {
26        let flags = flags.clone();
27        let repos = RefCell::new(HashMap::new());
28        Self { flags, repos }
29    }
30
31    pub fn test_ignored(&self, path: &Path) -> bool {
32        // If a Git repo exists containing the parent directory, and the
33        // directory exists in its .gitignore file, ignore the directory.
34        if let Some(parent) = path.parent() {
35            if let Some(repo) = self.find_repository(parent) {
36                let repo = repo.borrow();
37                return repo.test_ignored(path).unwrap_or(true);
38            }
39        }
40        // Otherwise, allow the directory and recurse into it.
41        false
42    }
43
44    pub fn test_allowed(&self, path: &Path) -> MyResult<Option<GitFlags>> {
45        // If a Git repo exists containing the parent directory, test the
46        // Git status flags, and return the flags for display purposes.
47        if let Some(parent) = path.parent() {
48            if let Some(repo) = self.find_repository(parent) {
49                let mut repo = repo.borrow_mut();
50                let result = repo.test_allowed(&self.flags, path)?;
51                return Ok(result);
52            }
53        }
54        if self.flags.untracked {
55            let result = GitFlags::default().with_untracked(true);
56            return Ok(Some(result));
57        }
58        Ok(None)
59    }
60
61    fn find_repository(&self, path: &Path) -> Option<Rc<RefCell<GitRepo>>> {
62        let mut repos = self.repos.borrow_mut();
63        match Self::find_recursive(repos.deref_mut(), path) {
64            Yes(repo) => {
65                Some(repo)
66            }
67            No => {
68                None
69            }
70            Maybe => {
71                if let Some(repo) = GitRepo::open_repository(path, repos.keys()) {
72                    let root = repo.get_root().to_path_buf();
73                    let repo = Rc::new(RefCell::new(repo));
74                    repos.insert(root, Some(Rc::clone(&repo)));
75                    Some(repo)
76                } else {
77                    let path = PathBuf::from(path);
78                    repos.insert(path, None);
79                    None
80                }
81            }
82        }
83    }
84
85    fn find_recursive(repos: &GitRepoMap, path: &Path) -> TriOption<Rc<RefCell<GitRepo>>> {
86        if let Some(repo) = repos.get(path) {
87            if let Some(repo) = repo {
88                Yes(Rc::clone(repo))
89            } else {
90                No
91            }
92        } else if let Some(parent) = path.parent() {
93            if let Yes(repo) = Self::find_recursive(repos, parent) {
94                Yes(repo)
95            } else {
96                Maybe
97            }
98        } else {
99            Maybe
100        }
101    }
102}