149 lines
4.0 KiB
Rust
149 lines
4.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 if input.starts_with("git@") {
|
|
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::<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))
|
|
}
|
|
|
|
/// Parses a github url in the format owner/repo.
|
|
fn parse_github_url(input: &str) -> Result<(String, String, String)> {
|
|
let split = input.split("/").collect::<Vec<_>>();
|
|
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<P: AsRef<Path>>(url: &str, place: P) -> Result<()> {
|
|
if place.as_ref().exists() {
|
|
return Err(Error::PathAlreadyExists);
|
|
}
|
|
|
|
match clone_dirty(url, &place) {
|
|
Ok(o) => Ok(o),
|
|
Err(e) => {
|
|
remove_dir_all(&place).ok();
|
|
Err(e)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Rewrites an url to a SSH url.
|
|
pub fn rewrite_url(url: &str) -> Result<String> {
|
|
let (server, owner, repo) = parse_url(url)?;
|
|
Ok(parse_to_url(&server, &owner, &repo))
|
|
}
|
|
|
|
/// Clones a git repository in the right place.
|
|
fn clone_dirty<P: AsRef<Path>>(url: &str, place: P) -> Result<()> {
|
|
let place = place.as_ref();
|
|
|
|
// Need to create the parent dir only if it exists
|
|
if let Some(parent) = place.parent() {
|
|
create_dir_all(parent)?;
|
|
}
|
|
|
|
let url = if env::var("GCLONE_FORCE_SSH") == Ok("true".to_owned()) {
|
|
rewrite_url(url)?
|
|
} else {
|
|
url.to_owned()
|
|
};
|
|
|
|
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(()),
|
|
}
|
|
}
|