aboutsummaryrefslogtreecommitdiff
path: root/src/git/tree.rs
diff options
context:
space:
mode:
authorKatharina Fey <kookie@spacekookie.de>2020-06-22 06:23:04 +0200
committerKatharina Fey <kookie@spacekookie.de>2020-06-22 06:23:04 +0200
commite30713b84bc9e66f7a8e8d2f51e953472cac28e4 (patch)
tree3098b1c77a978dcad0c828f57386d1c8999aa26b /src/git/tree.rs
parent84a9a0ccee713e26a28ff5e54ea3776085d93b5f (diff)
Committing all the libgit2 progress before throwing it away
I don't think libgit2 is the way forward to make any of this work. There's so much work involved in parsing the git k-v store, and the library itself is essentially of zero help for most of the heavy lifting.
Diffstat (limited to 'src/git/tree.rs')
-rw-r--r--src/git/tree.rs144
1 files changed, 144 insertions, 0 deletions
diff --git a/src/git/tree.rs b/src/git/tree.rs
new file mode 100644
index 0000000..457eb40
--- /dev/null
+++ b/src/git/tree.rs
@@ -0,0 +1,144 @@
+//! Tree handling utilities
+//!
+//! The way that libgit2 handles trees is super low-level and overkill
+//! for what we need. In this module we knock it down a notch or two.
+//!
+//! This code takes a tree returned by
+//! `crate::git::repo::Repo::get_tree()`, and transforms it into a
+//! `TreeData` type that the template engine can render.
+
+use crate::templ_data::repo::{CommitData, FileData, TreeData};
+use git2::{self, ObjectType, TreeWalkMode};
+use std::collections::BTreeMap;
+
+/// A cache of a repository tree
+#[derive(Default, Debug, Clone)]
+pub(crate) struct Tree {
+ inner: BTreeMap<String, TreeNode>,
+}
+
+impl Tree {
+ /// Insert a node into a subtree with it's full path
+ fn insert_to_subtree(&mut self, mut path: Vec<String>, name: String, node: TreeNode) {
+ // If we are given a path, resolve it first
+ let curr = if path.len() > 0 {
+ let rest = path.split_off(1);
+ let mut curr = self.inner.get_mut(&path[0]).unwrap();
+
+ for dir in rest {
+ match curr {
+ TreeNode::Dir(ref mut d) => {
+ curr = d.children.inner.get_mut(&dir).unwrap();
+ }
+ _ => panic!("Not a tree!"),
+ }
+ }
+
+ match curr {
+ TreeNode::Dir(ref mut d) => &mut d.children,
+ TreeNode::File(_) => panic!("Not a tree!"),
+ }
+ } else {
+ // If no path was given, we assume the root is meant
+ self
+ };
+
+ curr.inner.insert(name, node);
+ }
+
+ /// Walk through the tree and only return filenode objects
+ pub(crate) fn flatten(&self) -> Vec<FileNode> {
+ self.inner.values().fold(vec![], |mut vec, node| {
+ match node {
+ TreeNode::File(f) => vec.push(f.clone()),
+ TreeNode::Dir(d) => vec.append(&mut d.children.flatten()),
+ }
+
+ vec
+ })
+ }
+}
+
+#[derive(Clone, Debug)]
+pub(crate) enum TreeNode {
+ File(FileNode),
+ Dir(DirNode),
+}
+
+impl TreeNode {
+ fn name(&self) -> String {
+ match self {
+ Self::File(f) => f.name.clone(),
+ Self::Dir(d) => d.name.clone(),
+ }
+ }
+}
+
+#[derive(Clone, Debug)]
+pub(crate) struct FileNode {
+ pub id: String,
+ pub path: Vec<String>,
+ pub name: String,
+}
+
+#[derive(Clone, Debug)]
+pub(crate) struct DirNode {
+ pub path: Vec<String>,
+ pub name: String,
+ pub children: Tree,
+}
+
+impl DirNode {
+ fn append(&mut self, node: TreeNode) {
+ self.children.inner.insert(node.name(), node);
+ }
+}
+
+/// Take a series of path-segments and render a tree at that location
+pub(crate) fn parse_tree(tree: git2::Tree, repo: &git2::Repository) -> Tree {
+ let mut root = Tree::default();
+
+ tree.walk(TreeWalkMode::PreOrder, |path, entry| {
+ let path: Vec<String> = path
+ .split("/")
+ .filter_map(|seg| match seg {
+ "" => None,
+ val => Some(val.into()),
+ })
+ .collect();
+ let name = entry.name().unwrap().to_string();
+
+ match entry.kind() {
+ // For every tree in the tree we create a new TreeNode with the path we know about
+ Some(ObjectType::Tree) => {
+ root.insert_to_subtree(
+ path.clone(),
+ name.clone(),
+ TreeNode::Dir(DirNode {
+ path,
+ name,
+ children: Tree::default(),
+ }),
+ );
+ }
+ // If we encounter a blob, this is a file that we can simply insert into the tree
+ Some(ObjectType::Blob) => {
+ root.insert_to_subtree(
+ path.clone(),
+ name.clone(),
+ TreeNode::File(FileNode {
+ id: format!("{}", entry.id()),
+ path,
+ name,
+ }),
+ );
+ }
+ _ => {}
+ }
+
+ 0
+ })
+ .unwrap();
+
+ root
+}