ex-cli 1.20.1

Command line tool to find, filter, sort and list files.
Documentation
use crate::error::MyResult;
use crate::git::cache::TriOption::*;
use crate::git::flags::GitFlags;
use crate::git::repo::GitRepo;
use std::cell::RefCell;
use std::collections::HashMap;
use std::ops::DerefMut;
use std::path::{Path, PathBuf};
use std::rc::Rc;

enum TriOption<T> {
    Yes(T),
    No,
    Maybe,
}

type GitRepoMap = HashMap<PathBuf, Option<Rc<RefCell<GitRepo>>>>;

pub struct GitCache {
    flags: GitFlags,
    repos: RefCell<GitRepoMap>,
}

impl GitCache {
    pub fn new(flags: &GitFlags) -> Self {
        let flags = flags.clone();
        let repos = RefCell::new(HashMap::new());
        Self { flags, repos }
    }

    pub fn test_ignored(&self, path: &Path) -> bool {
        // If a Git repo exists containing the parent directory, and the
        // directory exists in its .gitignore file, ignore the directory.
        if let Some(parent) = path.parent() {
            if let Some(repo) = self.find_repository(parent) {
                let repo = repo.borrow();
                return repo.test_ignored(path).unwrap_or(true);
            }
        }
        // Otherwise, allow the directory and recurse into it.
        false
    }

    pub fn test_allowed(&self, path: &Path) -> MyResult<Option<GitFlags>> {
        // If a Git repo exists containing the parent directory, test the
        // Git status flags, and return the flags for display purposes.
        if let Some(parent) = path.parent() {
            if let Some(repo) = self.find_repository(parent) {
                let mut repo = repo.borrow_mut();
                let result = repo.test_allowed(&self.flags, path)?;
                return Ok(result);
            }
        }
        if self.flags.untracked {
            let result = GitFlags::default().with_untracked(true);
            return Ok(Some(result));
        }
        Ok(None)
    }

    fn find_repository(&self, path: &Path) -> Option<Rc<RefCell<GitRepo>>> {
        let mut repos = self.repos.borrow_mut();
        match Self::find_recursive(repos.deref_mut(), path) {
            Yes(repo) => {
                Some(repo)
            }
            No => {
                None
            }
            Maybe => {
                if let Some(repo) = GitRepo::open_repository(path, repos.keys()) {
                    let root = repo.get_root().to_path_buf();
                    let repo = Rc::new(RefCell::new(repo));
                    repos.insert(root, Some(Rc::clone(&repo)));
                    Some(repo)
                } else {
                    let path = PathBuf::from(path);
                    repos.insert(path, None);
                    None
                }
            }
        }
    }

    fn find_recursive(repos: &GitRepoMap, path: &Path) -> TriOption<Rc<RefCell<GitRepo>>> {
        if let Some(repo) = repos.get(path) {
            if let Some(repo) = repo {
                Yes(Rc::clone(repo))
            } else {
                No
            }
        } else if let Some(parent) = path.parent() {
            if let Yes(repo) = Self::find_recursive(repos, parent) {
                Yes(repo)
            } else {
                Maybe
            }
        } else {
            Maybe
        }
    }
}