diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..53eaa21 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +**/*.rs.bk diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..cc4e1f3 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,23 @@ +[[package]] +name = "colored" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gclone" +version = "0.1.0" +dependencies = [ + "colored 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lazy_static" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum colored 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6e9a455e156a4271e12fd0246238c380b1e223e3736663c7a18ed8b6362028a9" +"checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..9850d4b --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "gclone" +version = "0.1.0" +authors = ["Thomas Forgione "] +edition = "2018" + +[dependencies] +colored = "1.7.0" diff --git a/LICENSE b/LICENSE index 8015026..7786c0c 100644 --- a/LICENSE +++ b/LICENSE @@ -581,9 +581,10 @@ them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. - +gclone: a program that clones a git repository and automatically puts it in the +right place -Copyright (C) +Copyright (C) 2019 Thomas Forgione This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..5e26da8 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,147 @@ +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!"); + +}