parent
4c4d16d58c
commit
eac4297914
@ -1,7 +1,146 @@ |
||||
use crate::{Branch, BranchIter, Commit, HashId}; |
||||
use git2::{ObjectType, TreeWalkMode, TreeWalkResult}; |
||||
use atomptr::AtomPtr; |
||||
use std::collections::BTreeMap; |
||||
use std::{path::PathBuf, sync::Arc}; |
||||
|
||||
pub type FileId = usize; |
||||
/// A tree of files
|
||||
pub struct FileTree { |
||||
repo: Arc<git2::Repository>, |
||||
tree: AtomPtr<BTreeMap<String, TreeEntry>>, |
||||
} |
||||
|
||||
impl FileTree { |
||||
/// Utility function to create a tree, and then parse it too
|
||||
pub(crate) fn new(repo: &Arc<git2::Repository>, commit: HashId) -> Arc<Self> { |
||||
Arc::new(Self { |
||||
repo: Arc::clone(repo), |
||||
tree: AtomPtr::new(BTreeMap::new()), |
||||
}) |
||||
.parse(commit) |
||||
} |
||||
|
||||
/// Parse a tree from a specific commit
|
||||
pub(crate) fn parse(self: Arc<Self>, commit: HashId) -> Arc<Self> { |
||||
let mut new_tree = BTreeMap::new(); |
||||
|
||||
let tree = (&self.repo) |
||||
.find_commit(commit.to_oid()) |
||||
.unwrap() |
||||
.tree() |
||||
.unwrap(); |
||||
|
||||
tree.walk(TreeWalkMode::PreOrder, |what, entry| { |
||||
let path_segs: Vec<_> = what.split("/").filter(|s| s != &"").collect(); |
||||
let path = if path_segs.len() == 0 { |
||||
None |
||||
} else { |
||||
Some(path_segs) |
||||
}; |
||||
|
||||
println!("{:?} {}", path, entry.name().unwrap()); |
||||
TreeWalkResult::Ok |
||||
}) |
||||
.unwrap(); |
||||
drop(tree); |
||||
|
||||
// Atomicly swap new tree into place
|
||||
self.tree.swap(new_tree); |
||||
|
||||
self |
||||
} |
||||
} |
||||
|
||||
/// An entry in a file tree
|
||||
///
|
||||
/// It's variants can either be a file (leaf), or a subtree, with it's
|
||||
/// own path handles, and children.
|
||||
pub enum TreeEntry { |
||||
/// A single file
|
||||
File(File), |
||||
/// A sub-tree
|
||||
Dir(Directory), |
||||
} |
||||
|
||||
impl TreeEntry { |
||||
/// Create a tree entry from a path and `git2::TreeEntry`
|
||||
fn generate(root: PathBuf, path_segments: Option<Vec<String>>, entry: git2::TreeEntry) -> Self { |
||||
let path = path_segments.map_or("".into(), |p| path_segs_join(p)); |
||||
|
||||
match entry.kind() { |
||||
Some(ObjectType::Blob) => Self::File(File::new(root, path)), |
||||
Some(ObjectType::Tree) => Self::Dir(Directory::new(root, path)), |
||||
_ => unimplemented!(), |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// A file to have ever existed in a git repo
|
||||
pub struct File { |
||||
id: FileId, |
||||
root: PathBuf, |
||||
path: String, |
||||
} |
||||
|
||||
impl File { |
||||
pub(crate) fn new(root: PathBuf, path: String) -> Self { |
||||
Self { root, path } |
||||
} |
||||
|
||||
/// Get the history of a file from a branch iterator
|
||||
pub fn get_history(&self, branch: BranchIter) -> Vec<Commit> { |
||||
todo!() |
||||
} |
||||
} |
||||
|
||||
/// A subdirectory in a file tree
|
||||
///
|
||||
/// A directory has a set of children, which can either be Files, or
|
||||
/// other directories. Many of the functions to retrieve metadata
|
||||
/// (such as the last commit, count, etc) will be deferred to the
|
||||
/// children of this directory.
|
||||
pub struct Directory { |
||||
root: PathBuf, |
||||
path: String, |
||||
} |
||||
|
||||
impl Directory { |
||||
pub(crate) fn new(root: PathBuf, path: String) -> Self { |
||||
Self { root, path } |
||||
} |
||||
} |
||||
|
||||
/// Take a vector of path segments, and turn it into a valid offset path
|
||||
///
|
||||
/// There are tests to make sure this function works properly.
|
||||
/// Following are some example transformations.
|
||||
///
|
||||
/// * vec![] -> ""
|
||||
/// * vec!["foo"] -> "foo"
|
||||
/// * vec!["foo", "bar", "baz"] -> "foo/bar/baz"
|
||||
fn path_segs_join(segments: Vec<String>) -> String { |
||||
segments |
||||
.into_iter() |
||||
.fold(PathBuf::new(), |buf, seg| buf.join(seg)) |
||||
.as_path() |
||||
.to_str() |
||||
.unwrap() |
||||
.to_owned() |
||||
} |
||||
|
||||
#[test] |
||||
fn empty_path() { |
||||
assert_eq!(path_segs_join(vec![]), String::from("")); |
||||
} |
||||
|
||||
#[test] |
||||
fn one_path() { |
||||
assert_eq!(path_segs_join(vec!["foo".into()]), String::from("foo")); |
||||
} |
||||
|
||||
#[test] |
||||
fn nested_path() { |
||||
assert_eq!( |
||||
path_segs_join(vec!["foo".into(), "bar".into(), "baz".into()]), |
||||
String::from("foo/bar/baz") |
||||
); |
||||
} |
||||
|
Loading…
Reference in new issue