//! Raw representation wrappers for libgit2 use crate::branch::{Branch, BranchCommit}; use git2::{self, Oid}; use std::{fmt, sync::Arc}; pub type GitResult = Result; /// The hex ID of a commit #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct HashId(String); impl fmt::Display for HashId { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) } } impl HashId { pub fn to_oid(&self) -> Oid { self.clone().into() } pub fn to_string(&self) -> String { self.0.clone() } } impl From for HashId { fn from(o: Oid) -> Self { Self(o.to_string()) } } impl From for Oid { fn from(hid: HashId) -> Self { Oid::from_str(hid.0.as_str()).expect(&format!( "Tried turning an invalid HashId variant into an Oid: {:?}", hid )) } } impl<'any> From<&'any HashId> for Oid { fn from(hid: &'any HashId) -> Self { Oid::from_str(hid.0.as_str()).expect(&format!( "Tried turning an invalid HashId variant into an Oid: {:?}", hid )) } } /// An error abstraction for raw git operations #[derive(Debug)] pub enum GitError { AllBad, } impl From for GitError { fn from(_: git2::Error) -> Self { Self::AllBad } } /// Represents a git repository with lazy data loading #[derive(Clone)] pub struct Repository { inner: Arc, } impl Repository { /// Open a repository read-only at a specific path pub fn open(path: &str) -> GitResult { Ok(Self { inner: Arc::new(git2::Repository::open(path)?), }) } /// Parse branch data from repository /// /// If you only care about a single branch, you can also use the /// convenience function `get_branch()`. /// /// ## Panics /// /// This function can panic when branch metadata is missing. pub fn branches(&self) -> GitResult> { Ok(self .inner .branches(None)? .into_iter() .filter_map(|e| e.ok()) .map(|(branch, _)| { let name = branch.name().unwrap().unwrap().into(); let head = branch.get().peel_to_commit().unwrap().id().into(); Branch::new(&self.inner, name, head) }) .collect()) } /// Get a single branch by name /// /// This function will enumerate all branches, and then select the /// desired one. If you want to make repeated queries onto the /// branch set, it's recommended you call `branches()`, and cache /// the data yourself. pub fn branch(&self, name: String) -> Option { self.branches().ok().and_then(|ok| { ok.into_iter() .filter(|b| b.name().is_some()) .find(|b| &b.name().unwrap() == &name) }) } }