//! This module contains the utility functions to manage git repository. use std::env; use std::fs::{create_dir_all, remove_dir_all}; use std::path::Path; use std::process::{exit, Command, Stdio}; use lazy_static::lazy_static; use crate::{Error, Result}; lazy_static! { /// The directory in which the repositories will be cloned and searched. pub static ref GCLONE_PATH: String = { match env::var("GCLONE_PATH") { Ok(d) => d, Err(e) => { let e: Error = e.into(); error!("couldn't read environnement variable GCLONE_PATH: {}", e); exit(1); }, } }; } macro_rules! unwrap { ($e: expr) => { match $e { Some(e) => e, None => return Err(Error::GitUrlParseError), } }; } /// Transforms a git url to a (server, owner, repository) tuple. /// /// Returns none if it failed. pub fn parse_url(input: &str) -> Result<(String, String, String)> { if input.starts_with("http") { parse_http_url(input) } else if input.starts_with("ssh") { parse_ssh_url(input) } else { parse_github_url(input) } } /// Parses an HTTP url for a git repository. fn parse_http_url(input: &str) -> Result<(String, String, String)> { let split = input.split("/").collect::>(); if split.len() < 3 { return Err(Error::GitUrlParseError); } let repo = String::from(split[split.len() - 1]); let owner = String::from(split[split.len() - 2]); let server = split[2..split.len() - 2].join("/"); Ok((server, owner, repo)) } /// Parses an SSH url for a git repository. fn parse_ssh_url(input: &str) -> Result<(String, String, String)> { let url = unwrap!(input.split("@").nth(1)); let mut split = url.split(":"); let server = String::from(unwrap!(split.next())); let rest = unwrap!(split.next()); let mut resplit = rest.split("/"); let owner = String::from(unwrap!(resplit.next())); let repo = String::from(unwrap!(resplit.next())); Ok((server, owner, repo)) } /// Parses a github url in the format owner/repo. fn parse_github_url(input: &str) -> Result<(String, String, String)> { let split = input.split("/").collect::>(); if split.len() != 2 { return Err(Error::GitUrlParseError); } let server = String::from("github.com"); let owner = String::from(split[0]); let repo = String::from(split[1]); Ok((server, owner, repo)) } /// Converts the parse result of a repo into an url. pub fn parse_to_url(server: &str, owner: &str, repo: &str) -> String { format!("git@{}:{}/{}", server, owner, repo) } /// Clones a git repository in the right place. /// /// If an error happens, it deletes the directory created. pub fn clone>(url: &str, place: P) -> Result<()> { match clone_dirty(url, &place) { Ok(o) => Ok(o), Err(e) => { remove_dir_all(&place).ok(); Err(e) } } } /// Clones a git repository in the right place. fn clone_dirty>(url: &str, place: P) -> Result<()> { let place = place.as_ref(); if place.exists() { return Err(Error::PathAlreadyExists); } // Need to create the parent dir only if it exists if let Some(parent) = place.parent() { create_dir_all(parent)?; } let command = Command::new("git") .args(&[ "clone", "--recurse-submodules", &url, &place.display().to_string() ]) .stderr(Stdio::null()) .status(); match command { Err(e) => Err(Error::GitCloneError(Some(e))), Ok(o) if !o.success() => Err(Error::GitCloneError(None)), Ok(_) => Ok(()), } }