diff options
Diffstat (limited to 'apps/servers/octopus/supergit/src/files')
-rw-r--r-- | apps/servers/octopus/supergit/src/files/explorer.rs | 9 | ||||
-rw-r--r-- | apps/servers/octopus/supergit/src/files/mod.rs | 15 | ||||
-rw-r--r-- | apps/servers/octopus/supergit/src/files/tree.rs | 104 | ||||
-rw-r--r-- | apps/servers/octopus/supergit/src/files/tree_utils.rs | 61 |
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") + ); +} |