use std::path::PathBuf;
use clap::{ArgAction, Args, Parser, Subcommand, ValueEnum};
#[derive(Parser, Debug)]
#[command(name = "pgevolve", version, about, long_about = None)]
pub struct Cli {
#[arg(long, global = true)]
pub config: Option<PathBuf>,
#[arg(long, value_enum, global = true, default_value_t = OutputFormat::Human)]
pub format: OutputFormat,
#[arg(short, long, global = true, action = ArgAction::Count)]
pub verbose: u8,
#[arg(short, long, global = true)]
pub quiet: bool,
#[command(subcommand)]
pub cmd: Command,
}
#[derive(Subcommand, Debug)]
pub enum Command {
Init(InitArgs),
Lint(LintArgs),
Validate(ValidateArgs),
Diff(DiffArgs),
Plan(PlanArgs),
Apply(ApplyArgs),
Status(StatusArgs),
Dump(DumpArgs),
Bootstrap(BootstrapArgs),
Doctor {
#[arg(long)]
db: String,
#[arg(long)]
url: Option<String>,
},
RewriteTable {
qname: String,
#[arg(long)]
db: String,
#[arg(long)]
url: Option<String>,
#[arg(long)]
confirm_rewrite: bool,
},
Cluster(ClusterArgs),
Graph {
#[arg(long = "graph-format", value_enum, default_value_t = GraphFormat::Dot)]
graph_format: GraphFormat,
#[arg(short = 'o', long)]
out: Option<PathBuf>,
#[arg(long)]
plan: Option<PathBuf>,
},
}
#[derive(Debug, Clone, Copy, clap::ValueEnum)]
pub enum GraphFormat {
Dot,
Mermaid,
}
#[derive(Debug, Clone, Copy, ValueEnum, PartialEq, Eq)]
pub enum OutputFormat {
Human,
Json,
Sql,
}
#[derive(Args, Debug)]
pub struct ClusterArgs {
#[arg(long, global = true)]
pub config: Option<PathBuf>,
#[command(subcommand)]
pub cmd: ClusterCommand,
}
#[derive(Subcommand, Debug)]
pub enum ClusterCommand {
Init {
path: Option<PathBuf>,
},
Diff,
Plan,
Apply {
plan_id: Option<String>,
},
Status,
}
#[derive(Args, Debug)]
pub struct InitArgs {
#[arg(long)]
pub dir: Option<PathBuf>,
#[arg(long)]
pub force: bool,
}
#[derive(Args, Debug)]
pub struct LintArgs {}
#[derive(Args, Debug)]
pub struct ValidateArgs {
#[arg(long)]
pub shadow: bool,
#[arg(long)]
pub shadow_validate: bool,
#[arg(long, requires = "shadow_validate")]
pub shadow_strict: bool,
}
#[derive(Args, Debug)]
pub struct DiffArgs {
#[arg(long)]
pub db: String,
#[arg(long)]
pub url: Option<String>,
#[arg(long)]
pub shadow_validate: bool,
#[arg(long, requires = "shadow_validate")]
pub shadow_strict: bool,
}
#[derive(Args, Debug)]
pub struct PlanArgs {
#[arg(long)]
pub db: String,
#[arg(long)]
pub url: Option<String>,
#[arg(short, long)]
pub output: Option<PathBuf>,
#[arg(long)]
pub shadow_validate: bool,
#[arg(long, requires = "shadow_validate")]
pub shadow_strict: bool,
}
#[derive(Args, Debug)]
pub struct ApplyArgs {
pub plan_dir: PathBuf,
#[arg(long)]
pub db: String,
#[arg(long)]
pub url: Option<String>,
#[arg(long)]
pub allow_different_target: bool,
#[arg(long)]
pub allow_drift: bool,
}
#[derive(Args, Debug)]
pub struct StatusArgs {
#[arg(long)]
pub db: String,
#[arg(long)]
pub url: Option<String>,
#[arg(long)]
pub apply_id: Option<String>,
#[arg(long, default_value_t = 10)]
pub limit: u32,
}
#[derive(Args, Debug)]
pub struct DumpArgs {
#[arg(long)]
pub db: String,
#[arg(long)]
pub url: Option<String>,
#[arg(short, long)]
pub output: PathBuf,
}
#[derive(Args, Debug)]
pub struct BootstrapArgs {
#[arg(long)]
pub db: String,
#[arg(long)]
pub url: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_diff_command() {
let cli = Cli::try_parse_from(["pgevolve", "diff", "--db", "dev"]).unwrap();
match cli.cmd {
Command::Diff(a) => assert_eq!(a.db, "dev"),
_ => panic!("expected diff"),
}
}
#[test]
fn parses_apply_with_plan_dir() {
let cli = Cli::try_parse_from(["pgevolve", "apply", "/tmp/plan", "--db", "dev"]).unwrap();
match cli.cmd {
Command::Apply(a) => {
assert_eq!(a.plan_dir, PathBuf::from("/tmp/plan"));
assert_eq!(a.db, "dev");
assert!(!a.allow_drift);
}
_ => panic!("expected apply"),
}
}
#[test]
fn rejects_missing_db_argument() {
assert!(Cli::try_parse_from(["pgevolve", "diff"]).is_err());
}
#[test]
fn parses_global_format_flag() {
let cli =
Cli::try_parse_from(["pgevolve", "--format", "json", "diff", "--db", "x"]).unwrap();
assert_eq!(cli.format, OutputFormat::Json);
}
#[test]
fn parses_verbosity_count() {
let cli = Cli::try_parse_from(["pgevolve", "-vv", "diff", "--db", "x"]).unwrap();
assert_eq!(cli.verbose, 2);
}
}