use std::env; use std::process::{Command, exit}; use std::fs::{create_dir_all, remove_dir_all}; use std::error::Error; use std::path::PathBuf; use colored::*; macro_rules! unwrap { ($e: expr) => { match $e { Some(e) => e, None => return None, } } } fn parse_http_url(input: &str) -> Option<(String, String, String)> { let split = input.split("/").collect::>(); if split.len() < 3 { return None; } let repo = String::from(split[split.len() - 1]); let owner = String::from(split[split.len() - 2]); let server = split[2 .. split.len() - 2].join("/"); Some((server, owner, repo)) } fn parse_ssh_url(input: &str) -> Option<(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())); Some((server, owner, repo)) } fn parse_url(input: &str) -> Option<(String, String, String)> { if input.starts_with("http") { parse_http_url(input) } else { parse_ssh_url(input) } } fn help() { } fn main() { let git_dir = match env::var("GCLONE_PATH") { Ok(path) => path, Err(e) => { eprintln!("{} {} {}", "error:".bold().red(), "environment variable GCLONE_PATH must be set:".bold(), e.description()); exit(1); }, }; // Parse args let url = match env::args().nth(1) { Some(arg) => arg, None => { eprintln!("{} {}", "error:".bold().red(), "gclone expects an argument".bold()); exit(1); }, }; if url == "-h" || url == "--help" { return help(); } let (server, owner, repo) = match parse_url(&url) { Some(parsed) => parsed, None => { eprintln!("{} {}", "error:".bold().red(), "couldn't guess server, owner and repo names from url".bold()); exit(1); }, }; // Build path let mut path = PathBuf::from(git_dir); path.push(&server); path.push(&owner); path.push(&repo); if path.exists() { eprintln!("{} {}", "error:".red().bold(), "the corresponding directory already exists".bold()); exit(1); } match create_dir_all(&path) { Ok(_) => (), Err(e) => { eprintln!("{} {} {}", "error:".red().bold(), "couldn't create the corresponding directory:".bold(), e.description()); remove_dir_all(&path).ok(); exit(1); }, } eprintln!("{} {} {}{}", "info:".bold(), "cloning", url, "..."); let command = Command::new("git") .args(&["clone", &url, &path.display().to_string()]) .output(); match command { Ok(repo) => repo, Err(e) => { eprintln!("{} {} {}", "error:".bold().red(), "couldn't clone repository:".bold(), e.description()); remove_dir_all(&path).ok(); exit(1); }, }; eprintln!("{} {}", "info:".bold(), "done!"); }