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
|
/// A file to have ever existed in a git repo
|
||||||
pub struct File { |
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