use std::env;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use tokio::fs;
use crate::errors::{Error, Result};
use crate::plumbing::{Object, Oid, Ref};
use crate::util;
pub struct Repository {
pub path: PathBuf,
}
impl Repository {
pub async fn open(path: impl AsRef<Path>) -> Result<Self> {
let path = path.as_ref().to_path_buf();
if !Repository::is_repo(&path).await? {
return Err(Error::PathIsNotRepository(path));
}
Ok(Repository { path })
}
pub async fn head(&self) -> Result<Ref> {
let head_path = self.path.join("HEAD");
let head_ref = util::file_to_string(head_path).await?;
Ok(Ref::parse(head_ref.trim())?)
}
pub async fn is_repo(path: impl AsRef<Path>) -> Result<bool> {
let path = path.as_ref();
if !path.is_dir() {
return Ok(false);
}
let head_path = path.join("HEAD");
if !head_path.exists() {
return Ok(false);
}
let contents = util::file_to_string(&head_path).await?;
if contents.starts_with("ref:") {
let rest = contents.trim_start_matches("ref:").trim();
if !rest.starts_with("refs/") {
return Ok(false);
}
} else {
let contents = contents.trim();
if !Oid::from_str(&contents).is_ok() {
return Ok(false);
}
if fs::read_link(&head_path).await.is_ok() {
return Ok(false);
}
}
let objects_path = match env::var("GIT_OBJECT_DIRECTORY") {
Ok(path) => PathBuf::from(path),
Err(_) => path.join("objects"),
};
if !objects_path.exists() {
return Ok(false);
}
let refs_path = path.join("refs");
if !refs_path.exists() {
return Ok(false);
}
Ok(true)
}
pub async fn find() -> Result<Option<PathBuf>> {
let mut cwd = env::current_dir()?;
loop {
let dot_git = cwd.join(".git");
if dot_git.exists() {
if dot_git.is_file() {
let contents = util::file_to_string(&dot_git).await?;
if contents.starts_with("gitdir:") {
let path = PathBuf::from(contents.trim_start_matches("gitdir:").trim());
if Repository::is_repo(&path).await? {
return Ok(Some(path));
}
}
} else if dot_git.is_dir() {
if Repository::is_repo(&dot_git).await? {
return Ok(Some(dot_git));
}
}
} else {
if Repository::is_repo(&cwd).await? {
return Ok(Some(cwd));
}
}
if Repository::is_repo(cwd.clone()).await? {
return Ok(Some(cwd));
}
cwd = match cwd.parent() {
Some(parent) => parent.to_path_buf(),
None => return Ok(None),
};
}
}
pub fn get_object(&self, id: Oid) -> Object {
Object {
repo_path: self.path.clone(),
id,
}
}
}