use crate::utils::paths::clean_path;
use anyhow::Context;
use git2::Repository;
use std::{
fmt,
path::{Path, PathBuf},
};
pub const GIT_REQUEST_NOT_FOUND: &str = "Git object doesn't exist";
pub struct Repo {
pub archive_path: String,
pub path: PathBuf,
pub org: String,
pub name: String,
pub repo: Repository,
}
impl fmt::Debug for Repo {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(
formatter,
"Repo for {}/{} in the archive at {}",
self.org, self.name, self.archive_path
)
}
}
#[allow(clippy::missing_trait_methods, clippy::unwrap_used)]
impl Clone for Repo {
fn clone(&self) -> Self {
Self {
archive_path: self.archive_path.clone(),
org: self.org.clone(),
name: self.name.clone(),
path: self.path.clone(),
repo: Repository::open(self.path.clone()).unwrap(),
}
}
}
impl Repo {
pub fn new(archive_path: &Path, org: &str, name: &str) -> anyhow::Result<Self> {
let archive_path_str = archive_path.to_string_lossy();
tracing::trace!(org, name, "Creating new Repo at {archive_path_str}");
let repo_path = format!("{archive_path_str}/{org}/{name}");
Ok(Self {
archive_path: archive_path_str.into(),
org: org.into(),
name: name.into(),
path: PathBuf::from(repo_path.clone()),
repo: Repository::open(repo_path)?,
})
}
pub fn find_blob(
archive_path: &Path,
namespace: &str,
name: &str,
remainder: &str,
commitish: &str,
) -> anyhow::Result<Vec<u8>> {
let repo = Self::new(archive_path, namespace, name)?;
let blob_path = clean_path(remainder);
let blob = repo.get_bytes_at_path(commitish, &blob_path)?;
Ok(blob)
}
pub fn get_bytes_at_path(&self, commitish: &str, path: &str) -> anyhow::Result<Vec<u8>> {
let base_revision = format!("{commitish}:{path}");
for postfix in ["", "/index.html", ".html", "index.html"] {
let query = &format!("{base_revision}{postfix}");
let blob = self.find(query);
if blob.is_ok() {
tracing::trace!(query, "Found Git object");
return blob;
}
}
tracing::debug!(base_revision, "Couldn't find requested Git object");
anyhow::bail!(GIT_REQUEST_NOT_FOUND)
}
fn find(&self, query: &str) -> anyhow::Result<Vec<u8>> {
tracing::trace!(query, "Git reverse parse search");
let obj = self.repo.revparse_single(query)?;
let blob = obj.as_blob().context("Couldn't cast Git object to blob")?;
Ok(blob.content().to_owned())
}
}