aboutsummaryrefslogtreecommitdiff
path: root/apps/servers/octopus/supergit/src/files
diff options
context:
space:
mode:
Diffstat (limited to 'apps/servers/octopus/supergit/src/files')
-rw-r--r--apps/servers/octopus/supergit/src/files/explorer.rs9
-rw-r--r--apps/servers/octopus/supergit/src/files/mod.rs15
-rw-r--r--apps/servers/octopus/supergit/src/files/tree.rs104
-rw-r--r--apps/servers/octopus/supergit/src/files/tree_utils.rs61
4 files changed, 189 insertions, 0 deletions
diff --git a/apps/servers/octopus/supergit/src/files/explorer.rs b/apps/servers/octopus/supergit/src/files/explorer.rs
new file mode 100644
index 000000000000..89ad461b8ade
--- /dev/null
+++ b/apps/servers/octopus/supergit/src/files/explorer.rs
@@ -0,0 +1,9 @@
+
+///
+pub struct Explorer {}
+
+///
+pub struct Yield {}
+
+///
+pub enum YieldType {}
diff --git a/apps/servers/octopus/supergit/src/files/mod.rs b/apps/servers/octopus/supergit/src/files/mod.rs
new file mode 100644
index 000000000000..d278818b23ab
--- /dev/null
+++ b/apps/servers/octopus/supergit/src/files/mod.rs
@@ -0,0 +1,15 @@
+//! File tree abstractions
+//!
+//! The supergit files API is split into two parts: `FileTree`, which
+//! is mostly used internally and only exposed to allow external tools
+//! to rely on the same indexing mechanism as supergit, and
+//! `Explorer`, which is a high-level API for loading trees for
+//! specific commits.
+//!
+
+pub(self) mod tree_utils;
+mod tree;
+pub use tree::{FileTree, TreeEntry, EntryType};
+
+mod explorer;
+pub use explorer::{Explorer, Yield, YieldType};
diff --git a/apps/servers/octopus/supergit/src/files/tree.rs b/apps/servers/octopus/supergit/src/files/tree.rs
new file mode 100644
index 000000000000..5f4fb6671aa5
--- /dev/null
+++ b/apps/servers/octopus/supergit/src/files/tree.rs
@@ -0,0 +1,104 @@
+//! Low-level abstraction over finding refs inside a commit tree
+
+use super::tree_utils as utils;
+use crate::HashId;
+use git2::{ObjectType, Repository, TreeWalkMode, TreeWalkResult};
+use std::sync::Arc;
+
+/// A git directory tree walker abstraction
+///
+/// This type is meant to be used ephemerally, and internally uses the
+/// libgit2 `Tree` abstraction to walk directory trees lazily to
+/// resolve paths to [`TreeEntry`](self::TreeEntry)'s.
+///
+/// Note: this type _may_ be removed in the future. For a more
+/// high-level (and stable) API, check
+/// [`Explorer`](crate::files::Explorer)
+pub struct FileTree {
+ repo: Arc<Repository>,
+ c: HashId,
+}
+
+impl FileTree {
+ /// Construct a new FileTree with a repository
+ pub(crate) fn new(repo: Arc<Repository>, c: HashId) -> Self {
+ Self { repo, c }
+ }
+
+ /// Resolve a path inside this file tree
+ ///
+ /// Will return `None` if there is no tree for the selected
+ /// commit, or the file inside the tree does not exist.
+ pub fn resolve(&self, path: &str) -> Option<TreeEntry> {
+ let tree = utils::open_tree(&self.repo, &self.c)?;
+ let target = utils::path_split(path);
+
+ // Initialise entry to None as a fallback
+ let mut entry = None;
+
+ // Walk over tree and swallor errors (which we use to
+ // terminace traversal to speed up indexing time)
+ let _ = 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
+ }
+ });
+
+ // Return whatever the entry is now
+ entry
+ }
+}
+
+/// An entry in a commit tree
+///
+/// This type is lazily loaded, and can represent either a Blob or a
+/// Directory. You can resolve its value by calling
+/// [`resolve()`](Self::resolve)
+pub struct TreeEntry {
+ tt: EntryType,
+ id: HashId,
+ path: 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();
+
+ Self { tt, id, path }
+ }
+
+ /// Resolve this type to a [`Yield`]()
+ pub fn resolve(&self) {}
+}
+
+/// Type of a TreeEntry
+pub enum EntryType {
+ /// A file that can be loaded
+ File,
+ /// A directory that can be indexed
+ Dir,
+}
+
+#[test]
+fn index_tree() {
+ 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".into()).unwrap();
+}
diff --git a/apps/servers/octopus/supergit/src/files/tree_utils.rs b/apps/servers/octopus/supergit/src/files/tree_utils.rs
new file mode 100644
index 000000000000..55a6c2e0ffab
--- /dev/null
+++ b/apps/servers/octopus/supergit/src/files/tree_utils.rs
@@ -0,0 +1,61 @@
+//! A set of tree utilities used internally in the tree.rs module
+
+use crate::HashId;
+use git2::{Repository, Tree};
+use std::{path::PathBuf, sync::Arc};
+
+/// 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"
+pub(super) fn path_segs_join(segments: Vec<&str>) -> String {
+ segments
+ .into_iter()
+ .fold(PathBuf::new(), |buf, seg| buf.join(seg))
+ .as_path()
+ .to_str()
+ .unwrap()
+ .to_owned()
+}
+
+/// Take an offset path inside the repository and split it into a vec
+pub(super) fn path_split(path: &str) -> Vec<&str> {
+ path.split("/").filter(|s| s != &"").collect()
+}
+
+/// Compare a path and entry to a target buffer
+pub(super) fn path_cmp(target: &Vec<&str>, path: &str, entry: &str) -> bool {
+ let mut buf = path_split(path);
+ buf.push(entry);
+
+ eprintln!("{:?}", buf);
+
+ target == &buf
+}
+
+/// Open a tree for a particular commit
+pub(super) fn open_tree<'r>(repo: &'r Arc<Repository>, c: &HashId) -> Option<Tree<'r>> {
+ repo.find_commit(c.to_oid()).unwrap().tree().ok()
+}
+
+#[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")
+ );
+}