use crate::{ parser::{GitReceivePackArgs, GitUploadPackArgs}, ShackleError, }; use git2::{Repository, RepositoryInitMode, RepositoryInitOptions}; use std::{ path::{Path, PathBuf}, process::Command, }; use user_info::{get_user_groups, get_username}; pub struct GitInitResult { pub path: PathBuf, } fn personal_git_dir() -> Result { let username = get_username().ok_or(ShackleError::UserReadError)?; Ok(Path::new("git").join(username)) } fn group_git_dir(group: &str) -> Result { let groups = get_user_groups(); if !groups.iter().any(|g| g == group) { Err(ShackleError::InvalidGroup) } else { Ok(Path::new("git").join(group)) } } fn is_valid_personal_git_repo(path: &PathBuf) -> Result { let valid_dir = personal_git_dir()?; let relative_path = match path.strip_prefix(&valid_dir) { Ok(relative_path) => relative_path, Err(_) => { return Ok(false); } }; if relative_path.parent() != Some(Path::new("")) || relative_path.extension().map(|ext| ext == "git") != Some(true) { return Ok(false); } Ok(true) } pub fn init(repo_name: &str, group: &Option) -> Result { fn init_group(repo_name: &str, group: &str) -> Result { let path = group_git_dir(group)?.join(repo_name).with_extension("git"); Repository::init_opts( &path, &RepositoryInitOptions::new() .bare(true) .mode(RepositoryInitMode::SHARED_GROUP) .mkdir(true) .no_reinit(true), )?; Ok(GitInitResult { path }) } fn init_personal(repo_name: &str) -> Result { let path = personal_git_dir()?.join(repo_name).with_extension("git"); Repository::init_opts( &path, &RepositoryInitOptions::new() .bare(true) .mkdir(true) .no_reinit(true), )?; Ok(GitInitResult { path }) } match group { Some(group) => init_group(repo_name, group), None => init_personal(repo_name), } } pub fn upload_pack(upload_pack_args: &GitUploadPackArgs) -> Result<(), ShackleError> { if !is_valid_personal_git_repo(&upload_pack_args.directory)? { return Err(ShackleError::InvalidDirectory); } let mut command = Command::new("git-upload-pack"); if upload_pack_args.strict { command.arg("strict"); } if upload_pack_args.no_strict { command.arg("no-strict"); } if let Some(timeout) = upload_pack_args.timeout { command.args(["timeout", &timeout.to_string()]); } if upload_pack_args.stateless_rpc { command.arg("stateless-rpc"); } if upload_pack_args.advertise_refs { command.arg("advertise-refs"); } command.arg(&upload_pack_args.directory); command.spawn()?.wait()?; Ok(()) } pub fn receive_pack(receive_pack_args: &GitReceivePackArgs) -> Result<(), ShackleError> { if !is_valid_personal_git_repo(&receive_pack_args.directory)? { return Err(ShackleError::InvalidDirectory); } let mut command = Command::new("git-receive-pack"); if receive_pack_args.http_backend_info_refs { command.arg("--http-backend-info-refs"); } command.arg(&receive_pack_args.directory); command.spawn()?.wait()?; Ok(()) }