gclone/src/git.rs

111 lines
3.0 KiB
Rust

//! 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 {
parse_ssh_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::<Vec<_>>();
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))
}
/// Clones a git repository in the right place.
///
/// If an error happens, it deletes the directory created.
pub fn clone<P: AsRef<Path>>(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<P: AsRef<Path>>(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", &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(()),
}
}