aboutsummaryrefslogtreecommitdiff
path: root/apps/servers/octopus/supergit/src/files.rs
diff options
context:
space:
mode:
Diffstat (limited to 'apps/servers/octopus/supergit/src/files.rs')
-rw-r--r--apps/servers/octopus/supergit/src/files.rs143
1 files changed, 141 insertions, 2 deletions
diff --git a/apps/servers/octopus/supergit/src/files.rs b/apps/servers/octopus/supergit/src/files.rs
index 681b8877256f..2a1b69a34562 100644
--- a/apps/servers/octopus/supergit/src/files.rs
+++ b/apps/servers/octopus/supergit/src/files.rs
@@ -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")
+ );
}