use std::{
fs,
path::{Path, PathBuf},
};
use colored::Colorize;
use directories::ProjectDirs;
use git2::{Repository, ResetType};
pub fn get_cache_dir() -> Result<PathBuf, git2::Error> {
let project_dirs = ProjectDirs::from("com", "amirmo76", "bforge").ok_or(
git2::Error::from_str("Could not determine project directory"),
)?;
Ok(project_dirs.cache_dir().join("git"))
}
pub fn ensure_repo_cached(repo: &str, cache_dir: &Path) -> Result<PathBuf, git2::Error> {
let folder_name = sanitize_repo_name_to_folder(repo);
let repo_path = cache_dir.join(folder_name);
println!("Caching repo {} to {:?}", repo.dimmed(), repo_path);
if !cache_dir.exists() {
fs::create_dir_all(&cache_dir).map_err(|e| git2::Error::from_str(&e.to_string()))?;
}
if repo_path.exists() {
let repo = Repository::open(&repo_path)?;
update_repo(&repo)?;
Ok(repo_path)
} else {
let url = get_url(repo);
Repository::clone(&url, &repo_path)?;
Ok(repo_path)
}
}
fn update_repo(repo: &Repository) -> Result<(), git2::Error> {
let mut remote = repo.find_remote("origin")?;
remote.fetch(&["refs/heads/*:refs/heads/*"], None, None)?;
let head_ref = remote.default_branch()?;
let head_name = head_ref
.as_str()
.ok_or(git2::Error::from_str("Could not determine default branch"))?;
let object = repo.find_reference(head_name)?.peel_to_commit()?;
repo.reset(object.as_object(), ResetType::Hard, None)?;
Ok(())
}
fn get_url(repo: &str) -> String {
format!("https://github.com/{repo}.git")
}
fn sanitize_repo_name_to_folder(url: &str) -> String {
url.replace(':', "_").replace('/', "_").replace('.', "_")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_url() {
assert_eq!(get_url("user/repo"), "git@github.com:user/repo.git");
}
#[test]
fn test_sanitize_repo_name() {
assert_eq!(sanitize_repo_name_to_folder("user/repo"), "user_repo");
}
}