use std::fmt::{Display, Formatter}; use git2::{Reference, Commit, Tree, ObjectType, Repository, Oid}; use std::ops::Add; pub struct GitRepo { git2 : Repository, } impl GitRepo { pub fn new(path : &str) -> Result<GitRepo, git2::Error> { Repository::open(path).map(|git2| GitRepo{git2}) } pub fn get_root_tree<'a, 'b> (&'a self, commit : &'b GitCommit) -> Result<GitBrowseDir<'a>, git2::Error> { let oid = Oid::from_str(&commit.0)?; let commit = self.git2.find_commit(oid)?; let tree = commit.tree()?; let fullname = "/".to_string(); Ok(GitBrowseDir{ fullname, tree }) } pub async fn browse<'a>(&'a self, dir: &GitBrowseDir<'a>) -> Result<Vec<GitBrowseEntry<'a>>, git2::Error> { let mut res = Vec::new(); for entry in dir.tree.iter() { match entry.kind() { None => {Err(git2::Error::from_str("each tree entry must be well defined"))?;} Some(kind) => match kind { ObjectType::Any => {Err(git2::Error::from_str("tree entry cannot be of kind Any"))?;} ObjectType::Commit => {Err(git2::Error::from_str("tree entry cannot be of kind Commit"))?;} ObjectType::Tree => { let name = entry.name().ok_or(git2::Error::from_str("entry must have valid utf8 name"))?; let fullname = dir.fullname.clone().add(name); let subtree = self.git2.find_tree(entry.id())?; res.push(GitBrowseEntry::EGitDir(GitBrowseDir{fullname, tree : subtree})); } ObjectType::Blob => { let name = entry.name().ok_or(git2::Error::from_str("entry must have valid utf8 name"))?; let fullname = dir.fullname.clone().add(name); res.push(GitBrowseEntry::EGitFile(GitBrowseFile(fullname))); } ObjectType::Tag => {Err(git2::Error::from_str("tree entry cannot be of kind tag"))?;} } }; } return Ok(res); } } #[derive(Debug)] pub struct GitCommit (String); impl GitCommit { pub fn new(s : String) -> GitCommit { GitCommit(s) } } impl Display for GitCommit { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0) } } #[derive(Debug)] pub struct GitBrowseFile (String); #[derive(Debug)] pub struct GitBrowseDir<'a>{ fullname: String, tree : git2::Tree<'a>, } impl Display for GitBrowseFile { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0) } } impl Display for GitBrowseDir<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.fullname) } } #[derive(Debug)] pub enum GitBrowseEntry<'a> { EGitFile(GitBrowseFile), EGitDir(GitBrowseDir<'a>) } impl Display for GitBrowseEntry<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { GitBrowseEntry::EGitFile(file) => {write!(f, "{}", file)} GitBrowseEntry::EGitDir(dir) => {write!(f, "{}", dir)} } } }