My personal project and infrastructure archive
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
nomicon/apps/servers/octopus/supergit/src/files.rs

146 lines
3.8 KiB

use crate::{Branch, BranchIter, Commit, HashId};
use git2::{ObjectType, TreeWalkMode, TreeWalkResult};
use atomptr::AtomPtr;
use std::collections::BTreeMap;
use std::{path::PathBuf, sync::Arc};
/// 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 {
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")
);
}