use crate::config::{
DeploymentMode, GitVersionConfiguration, IncrementStrategy, SemanticVersionFormat,
};
use clap::{CommandFactory, Parser, ValueEnum};
use rust_i18n::t;
use std::path::PathBuf;
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub enum OutputFormat {
Json,
File,
DotEnv,
BuildServer,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub enum Verbosity {
Quiet,
Minimal,
Normal,
Verbose,
Diagnostic,
}
impl Verbosity {
pub fn to_level(self) -> log::LevelFilter {
match self {
Verbosity::Quiet => log::LevelFilter::Error,
Verbosity::Minimal => log::LevelFilter::Warn,
Verbosity::Normal => log::LevelFilter::Info,
Verbosity::Verbose => log::LevelFilter::Debug,
Verbosity::Diagnostic => log::LevelFilter::Trace,
}
}
}
#[derive(Debug, Parser)]
#[command(
name = "gitversion-rs",
version,
about = "Calculate a semantic version from Git history (GitVersion Rust port)"
)]
pub struct Cli {
#[arg(default_value = ".")]
pub path: PathBuf,
#[arg(long = "targetpath", value_name = "DIR")]
pub target_path: Option<PathBuf>,
#[arg(long)]
pub nofetch: bool,
#[arg(long)]
pub nonormalize: bool,
#[arg(long)]
pub nocache: bool,
#[arg(long)]
pub allowshallow: bool,
#[arg(long, value_enum, default_value = "json")]
pub output: Vec<OutputFormat>,
#[arg(long = "outputfile")]
pub output_file: Option<PathBuf>,
#[arg(long = "showvariable", short = 'v')]
pub show_variable: Option<String>,
#[arg(long)]
pub format: Option<String>,
#[arg(long)]
pub config: Option<PathBuf>,
#[arg(long = "showconfig")]
pub show_config: bool,
#[arg(long = "overrideconfig")]
pub override_config: Vec<String>,
#[arg(long, short = 'b')]
pub branch: Option<String>,
#[arg(long, value_name = "LANG")]
pub lang: Option<String>,
#[arg(long, value_enum, default_value = "normal")]
pub verbosity: Verbosity,
#[arg(long = "log", short = 'l', value_name = "FILE")]
pub log_file: Option<PathBuf>,
#[arg(long)]
pub diag: bool,
#[arg(long = "updateassemblyinfo", num_args = 0.., value_name = "FILE")]
pub update_assembly_info: Option<Vec<String>>,
#[arg(long = "ensureassemblyinfo")]
pub ensure_assembly_info: bool,
#[arg(long = "updateprojectfiles", num_args = 0.., value_name = "FILE")]
pub update_project_files: Option<Vec<String>>,
#[arg(long = "updatewixversionfile")]
pub update_wix_version_file: bool,
#[arg(long = "updatepackagefiles", num_args = 0.., value_name = "FILE")]
pub update_package_files: Option<Vec<String>>,
#[arg(long)]
pub url: Option<String>,
#[arg(long = "username", short = 'u')]
pub username: Option<String>,
#[arg(long = "password", short = 'p')]
pub password: Option<String>,
#[arg(long = "commit", short = 'c')]
pub commit: Option<String>,
#[arg(long = "dynamicRepoLocation")]
pub dynamic_repo_location: Option<PathBuf>,
#[arg(long)]
pub exec: Option<String>,
#[arg(long = "exec-version")]
pub exec_version: Option<String>,
#[arg(long = "dry-run")]
pub dry_run: bool,
#[arg(long)]
pub tui: bool,
}
pub fn localized_command() -> clap::Command {
Cli::command()
.about(t!("cli.about").to_string())
.mut_args(|arg| {
let key = format!("cli.help.{}", arg.get_id());
let val = t!(key.as_str()).to_string();
if val == key {
arg
} else {
arg.help(val)
}
})
}
pub fn apply_overrides(config: &mut GitVersionConfiguration, overrides: &[String]) {
for raw in overrides {
let Some((key, value)) = raw.split_once('=') else {
log::warn!("{}", t!("cli.override_invalid", entry = raw));
continue;
};
let key = key.trim();
let value = value.trim().to_string();
match key {
"tag-prefix" => config.tag_prefix = Some(value),
"next-version" => config.next_version = Some(value),
"label" => config.label = Some(value),
"commit-date-format" => config.commit_date_format = Some(value),
"major-version-bump-message" => config.major_version_bump_message = Some(value),
"minor-version-bump-message" => config.minor_version_bump_message = Some(value),
"patch-version-bump-message" => config.patch_version_bump_message = Some(value),
"no-bump-message" => config.no_bump_message = Some(value),
"tag-pre-release-weight" => {
if let Ok(n) = value.parse() {
config.tag_pre_release_weight = Some(n);
}
}
"update-build-number" => config.update_build_number = value.parse().ok(),
"increment" => config.increment = parse_increment(&value),
"mode" => config.mode = parse_mode(&value),
"semantic-version-format" => {
config.semantic_version_format = match value.to_lowercase().as_str() {
"loose" => Some(SemanticVersionFormat::Loose),
_ => Some(SemanticVersionFormat::Strict),
}
}
other => log::warn!("{}", t!("cli.override_unsupported", key = other)),
}
}
}
fn parse_increment(v: &str) -> Option<IncrementStrategy> {
Some(match v.to_lowercase().as_str() {
"major" => IncrementStrategy::Major,
"minor" => IncrementStrategy::Minor,
"patch" => IncrementStrategy::Patch,
"none" => IncrementStrategy::None,
"inherit" => IncrementStrategy::Inherit,
_ => return None,
})
}
fn parse_mode(v: &str) -> Option<DeploymentMode> {
Some(match v.to_lowercase().as_str() {
"continuousdelivery" => DeploymentMode::ContinuousDelivery,
"continuousdeployment" => DeploymentMode::ContinuousDeployment,
"manualdeployment" => DeploymentMode::ManualDeployment,
_ => return None,
})
}