use std::path::Path;
use std::process::Command;
use thiserror::Error;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GitInit {
Initialized,
SkippedInsideExistingRepo,
SkippedGitMissing,
}
#[derive(Debug, Error)]
pub enum GitError {
#[error("`git {command}` failed for the new application at `{path}`: {detail}")]
Command {
command: &'static str,
path: String,
detail: String,
},
}
pub fn init_repo(target: &Path) -> Result<GitInit, GitError> {
match inside_work_tree(target) {
InsideCheck::Missing => return Ok(GitInit::SkippedGitMissing),
InsideCheck::Inside => return Ok(GitInit::SkippedInsideExistingRepo),
InsideCheck::Outside => {}
}
run(target, "init", &["init", "-q"])?;
run(target, "add -A", &["add", "-A"])?;
run(target, "commit", &commit_args())?;
Ok(GitInit::Initialized)
}
enum InsideCheck {
Missing,
Inside,
Outside,
}
fn inside_work_tree(target: &Path) -> InsideCheck {
match Command::new("git")
.args(["rev-parse", "--is-inside-work-tree"])
.current_dir(target)
.output()
{
Err(_) => InsideCheck::Missing,
Ok(output) => {
if output.status.success() && String::from_utf8_lossy(&output.stdout).trim() == "true" {
InsideCheck::Inside
} else {
InsideCheck::Outside
}
}
}
}
fn commit_args() -> Vec<&'static str> {
let mut args = Vec::new();
if !identity_configured() {
args.extend_from_slice(&["-c", "user.name=Frame", "-c", "user.email=frame@localhost"]);
}
args.extend_from_slice(&["commit", "-q", "-m", "Initial commit (frame new)"]);
args
}
fn identity_configured() -> bool {
configured("user.name") && configured("user.email")
}
fn configured(key: &str) -> bool {
Command::new("git")
.args(["config", "--get", key])
.output()
.map(|output| output.status.success() && !output.stdout.is_empty())
.unwrap_or(false)
}
fn run(target: &Path, label: &'static str, args: &[&str]) -> Result<(), GitError> {
let output = Command::new("git")
.args(args)
.current_dir(target)
.output()
.map_err(|error| GitError::Command {
command: label,
path: target.display().to_string(),
detail: error.to_string(),
})?;
if output.status.success() {
Ok(())
} else {
Err(GitError::Command {
command: label,
path: target.display().to_string(),
detail: String::from_utf8_lossy(&output.stderr).trim().to_owned(),
})
}
}