use std::fs;
use askama::Template;
use clap::Parser;
use eyre::Result;
use inquire::Select;
use thiserror::Error;
use crate::{config::config_file, hint, success, tracing::LogResult as _};
use super::helpers::ensure_in_git_worktree;
#[derive(Debug, Parser)]
pub struct Init {
#[arg(long, short = 'd')]
default: bool,
#[arg(long, short = 'f')]
force: bool,
}
#[derive(Debug, Error)]
pub enum InitError {
#[error("There is already a git-z.toml in the current repository")]
ExistingConfig,
}
#[derive(Debug, Default, Template)]
#[template(path = "git-z.toml.jinja", syntax = "template")]
struct Config {
scopes: Scopes,
ticket: Ticket,
}
#[derive(Debug)]
enum Scopes {
Ask {
accept: AcceptScopes,
},
DontAsk,
}
#[derive(Debug, Default)]
enum AcceptScopes {
#[default]
Any,
List,
}
#[derive(Debug)]
enum Ticket {
Ask {
required: bool,
},
DontAsk,
}
impl super::Command for Init {
#[tracing::instrument(name = "init", level = "trace", skip_all)]
fn run(&self) -> Result<()> {
tracing::info!(params = ?self, "running init");
ensure_in_git_worktree()?;
let config_file = config_file()?;
if !self.force && config_file.exists() {
Err(InitError::ExistingConfig).log_err()?;
}
let config = if self.default {
tracing::info!("using the default configuration");
Config::default()
} else {
tracing::info!("customising the configuration");
Config::run_wizard()?
};
tracing::info!(?config, "writing the configuration file");
fs::write(config_file, format!("{config}\n")).log_err()?;
success!("A git-z.toml has been created!");
hint!("You can now edit it to adjust the configuration.");
Ok(())
}
}
impl Config {
#[tracing::instrument(level = "trace")]
fn run_wizard() -> Result<Self> {
Ok(Self {
scopes: Scopes::run_wizard()?,
ticket: Ticket::run_wizard()?,
})
}
}
impl Scopes {
fn run_wizard() -> Result<Self> {
let options = vec![
"Ask for a scope, accept any",
"Ask for a scope in a list",
"Do not ask for a scope",
];
let choice = Select::new("Should git-z ask for a scope?", options)
.with_starting_cursor(0)
.prompt()
.log_err()?;
let scopes = match choice {
"Ask for a scope, accept any" => Self::Ask {
accept: AcceptScopes::Any,
},
"Ask for a scope in a list" => Self::Ask {
accept: AcceptScopes::List,
},
_ => Self::DontAsk,
};
tracing::debug!(?scopes);
Ok(scopes)
}
}
impl Ticket {
fn run_wizard() -> Result<Self> {
let options = vec![
"Require a ticket number",
"Ask for an optional ticket number",
"Do not ask for a ticket number",
];
let choice =
Select::new("Should git-z ask for a ticket number?", options)
.with_starting_cursor(1)
.prompt()
.log_err()?;
let ticket = match choice {
"Require a ticket number" => Self::Ask { required: true },
"Ask for an optional ticket number" => {
Self::Ask { required: false }
}
_ => Self::DontAsk,
};
tracing::debug!(?ticket);
Ok(ticket)
}
}
impl Default for Scopes {
fn default() -> Self {
Self::Ask {
accept: AcceptScopes::default(),
}
}
}
impl Default for Ticket {
fn default() -> Self {
Self::Ask { required: false }
}
}