use super::tree_utils::{self as utils, PairIter};
use crate::{branch::BranchIter, files::Yield, Commit, HashId};
use git2::{ObjectType, Repository, TreeWalkMode, TreeWalkResult};
use std::sync::Arc;
pub struct FileTree {
repo: Arc<Repository>,
c: HashId,
}
impl FileTree {
pub(crate) fn new(repo: Arc<Repository>, c: HashId) -> Self {
Self { repo, c }
}
fn checkout(&self, c: HashId) -> Self {
Self::new(Arc::clone(&self.repo), c)
}
pub fn base_history(&self, iter: BranchIter, path: &str) -> Option<Vec<Commit>> {
let mut iter = iter.peekable();
let mut commits = vec![];
while let (Some(a), b) = iter.next_pair() {
let _a = a.commit().clone();
let a = self.checkout(a.commit().id.clone()).resolve(path);
let b = b.and_then(|c| self.checkout(c.commit().id.clone()).resolve(path));
match (a, b) {
(Some(a), Some(b)) if a != b => commits.push(_a),
(Some(_), None) => commits.push(_a),
(None, Some(_)) => commits.push(_a),
(_, _) => {}
}
}
Some(commits)
}
pub fn enumerate(&self, path: &str) -> Option<Vec<TreeEntry>> {
let tree = utils::open_tree(&self.repo, &self.c)?;
let target = utils::path_split(path);
let mut entries = vec![];
tree.walk(TreeWalkMode::PreOrder, |p, e| {
let path = utils::path_split(p);
if path == target {
entries.push(TreeEntry::new(p, &e));
TreeWalkResult::Ok
} else {
TreeWalkResult::Skip
}
})
.ok()?;
Some(entries)
}
pub fn resolve(&self, path: &str) -> Option<TreeEntry> {
let tree = utils::open_tree(&self.repo, &self.c)?;
let target = utils::path_split(path);
let mut entry = None;
tree.walk(TreeWalkMode::PreOrder, |p, e| {
if utils::path_cmp(&target, p, e.name().unwrap()) {
entry = Some(TreeEntry::new(p, &e));
TreeWalkResult::Ok
} else {
TreeWalkResult::Skip
}
})
.ok()?;
entry
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TreeEntry {
tt: EntryType,
id: HashId,
path: String,
name: Option<String>,
}
impl TreeEntry {
fn new(path: &str, entry: &git2::TreeEntry) -> Self {
let tt = match entry.kind() {
Some(ObjectType::Blob) => EntryType::File,
Some(ObjectType::Tree) => EntryType::Dir,
_ => unimplemented!(),
};
let id = entry.id().into();
let path = path.into();
let name = entry.name().map(|s| s.into());
Self { tt, id, path, name }
}
pub fn id(&self) -> HashId {
self.id.clone()
}
pub fn name(&self) -> Option<&String> {
self.name.as_ref()
}
pub fn resolve(&self) -> Yield {
todo!()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum EntryType {
File,
Dir,
}
#[test]
fn s_resolve() {
let path = env!("CARGO_MANIFEST_DIR").to_owned() + "/test-repo";
use crate::Repository as Repo;
eprintln!("Path: `{}`", path);
let r = Repo::open(&path).unwrap();
let b = r.branch("master".into()).unwrap();
let h = b.head();
let t = h.tree();
t.resolve("README").unwrap();
}
#[test]
fn s_enumerate() {
let path = env!("CARGO_MANIFEST_DIR").to_owned() + "/test-repo";
use crate::Repository as Repo;
eprintln!("Path: `{}`", path);
let r = Repo::open(&path).unwrap();
let b = r.branch("master".into()).unwrap();
let h = b.head();
let t = h.tree();
let entries = t.enumerate("").unwrap();
assert_eq!(
entries
.iter()
.filter_map(|e| e.name().map(|s| s.as_str()))
.collect::<Vec<_>>(),
vec!["README", "test.rs"]
);
}
#[test]
fn s_history() {
let path = env!("CARGO_MANIFEST_DIR").to_owned() + "/test-repo";
use crate::Repository as Repo;
eprintln!("Path: `{}`", path);
let r = Repo::open(&path).unwrap();
let b = r.branch("master".into()).unwrap();
let head = b.head();
let iter = b.get_all();
let tree = head.tree();
let history = tree.base_history(iter, "test.rs").unwrap();
dbg!(&history);
assert_eq!(history.len(), 1);
}