use clap::{Parser, Subcommand};
use crate::cmd;
use crate::error::CliError;
#[derive(Parser, Debug)]
#[command(
name = "sz-rust",
bin_name = "sz-rust",
version,
about = "SZ-Rust 命令行工具 — 替代 PHP think 命令",
long_about = "SZ-Rust CLI 对齐 PHP ThinkPHP 6 think 命令体系,提供 make:migration / make:model / make:controller / migrate / route:list / cache:clear 等命令。"
)]
pub struct Cli {
#[command(subcommand)]
pub command: Option<Command>,
}
#[derive(Subcommand, Debug)]
pub enum Command {
#[command(name = "make")]
Make {
#[command(subcommand)]
make_command: cmd::make::MakeCommand,
},
#[command(name = "migrate")]
Migrate {
#[command(flatten)]
args: cmd::migrate::MigrateArgs,
},
#[command(name = "migrate:status")]
MigrateStatus {
#[arg(short = 'p', long, default_value = "migrations")]
path: String,
#[arg(long, default_value = "postgres")]
db_type: String,
#[arg(long)]
show_sql: bool,
#[arg(long)]
url: Option<String>,
},
#[command(name = "route:list")]
RouteList {
#[arg(short = 'f', long, default_value = "table")]
format: String,
},
#[command(name = "cache:clear")]
CacheClear {
#[arg(short = 's', long)]
store: Option<String>,
},
#[command(name = "db:seed")]
Seed {
#[arg(short = 'p', long, default_value = "seeds")]
path: String,
#[arg(long, default_value = "postgres")]
db_type: String,
#[arg(long)]
show_sql: bool,
#[arg(long)]
url: Option<String>,
#[arg(short = 'c', long)]
class: Option<String>,
},
#[command(name = "scheduler")]
Scheduler {
#[command(subcommand)]
scheduler_command: cmd::scheduler::SchedulerCommand,
},
#[command(name = "optimize:route")]
OptimizeRoute,
#[command(name = "optimize:config")]
OptimizeConfig,
#[command(name = "optimize:schema")]
OptimizeSchema,
#[command(name = "route:clear")]
RouteClear,
}
impl Cli {
pub fn execute(&self) -> Result<i32, CliError> {
match &self.command {
None => {
println!("SZ-Rust CLI — 使用 --help 查看可用命令");
Ok(0)
}
Some(Command::Make { make_command }) => cmd::make::execute(make_command).map(|_| 0),
Some(Command::Migrate { args }) => cmd::migrate::execute_migrate(args).map(|_| 0),
Some(Command::MigrateStatus {
path,
db_type,
show_sql,
url,
}) => cmd::migrate::execute_status_full(path, db_type, *show_sql, url.as_deref())
.map(|_| 0),
Some(Command::RouteList { format }) => {
cmd::route::execute_route_list(format).map(|_| 0)
}
Some(Command::CacheClear { store }) => {
cmd::cache::execute_cache_clear(store.as_deref()).map(|_| 0)
}
Some(Command::Seed {
path,
db_type,
show_sql,
url,
class,
}) => {
let args = cmd::seed::SeedArgs {
path: path.clone(),
db_type: db_type.clone(),
show_sql: *show_sql,
url: url.clone(),
class: class.clone(),
};
cmd::seed::execute_seed(&args).map(|_| 0)
}
Some(Command::Scheduler { scheduler_command }) => {
cmd::scheduler::execute(scheduler_command).map(|_| 0)
}
Some(Command::OptimizeRoute) => cmd::optimize::execute_optimize_route().map(|_| 0),
Some(Command::OptimizeConfig) => cmd::optimize::execute_optimize_config().map(|_| 0),
Some(Command::OptimizeSchema) => cmd::optimize::execute_optimize_schema().map(|_| 0),
Some(Command::RouteClear) => cmd::optimize::execute_route_clear().map(|_| 0),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use clap::Parser;
#[test]
fn test_parse_make_model() {
let cli = Cli::parse_from(["sz-rust", "make", "model", "User"]);
match cli.command {
Some(Command::Make { make_command }) => {
assert!(matches!(make_command, cmd::make::MakeCommand::Model { .. }));
}
_ => panic!("expected Make command"),
}
}
#[test]
fn test_parse_make_controller() {
let cli = Cli::parse_from(["sz-rust", "make", "controller", "User"]);
match cli.command {
Some(Command::Make { make_command }) => {
assert!(matches!(
make_command,
cmd::make::MakeCommand::Controller { .. }
));
}
_ => panic!("expected Make command"),
}
}
#[test]
fn test_parse_make_migration() {
let cli = Cli::parse_from(["sz-rust", "make", "migration", "create_users"]);
match cli.command {
Some(Command::Make { make_command }) => {
assert!(matches!(
make_command,
cmd::make::MakeCommand::Migration { .. }
));
}
_ => panic!("expected Make command"),
}
}
#[test]
fn test_parse_optimize_schema() {
let cli = Cli::parse_from(["sz-rust", "optimize:schema"]);
assert!(matches!(cli.command, Some(Command::OptimizeSchema)));
}
#[test]
fn test_parse_make_validate() {
let cli = Cli::parse_from(["sz-rust", "make", "validate", "User"]);
match cli.command {
Some(Command::Make { make_command }) => {
assert!(matches!(
make_command,
cmd::make::MakeCommand::Validate { .. }
));
}
_ => panic!("expected Make command"),
}
}
#[test]
fn test_parse_make_seeder() {
let cli = Cli::parse_from(["sz-rust", "make", "seeder", "001_users"]);
match cli.command {
Some(Command::Make { make_command }) => {
assert!(matches!(
make_command,
cmd::make::MakeCommand::Seeder { .. }
));
}
_ => panic!("expected Make command"),
}
}
#[test]
fn test_parse_migrate() {
let cli = Cli::parse_from(["sz-rust", "migrate"]);
assert!(matches!(cli.command, Some(Command::Migrate { .. })));
}
#[test]
fn test_parse_migrate_status() {
let cli = Cli::parse_from(["sz-rust", "migrate:status"]);
assert!(matches!(cli.command, Some(Command::MigrateStatus { .. })));
}
#[test]
fn test_parse_route_list() {
let cli = Cli::parse_from(["sz-rust", "route:list"]);
assert!(matches!(cli.command, Some(Command::RouteList { .. })));
}
#[test]
fn test_parse_cache_clear() {
let cli = Cli::parse_from(["sz-rust", "cache:clear"]);
assert!(matches!(cli.command, Some(Command::CacheClear { .. })));
}
#[test]
fn test_parse_cache_clear_with_store() {
let cli = Cli::parse_from(["sz-rust", "cache:clear", "--store", "redis"]);
match cli.command {
Some(Command::CacheClear { store }) => {
assert_eq!(store.as_deref(), Some("redis"));
}
_ => panic!("expected CacheClear command"),
}
}
#[test]
fn test_parse_scheduler() {
let cli = Cli::parse_from(["sz-rust", "scheduler", "list"]);
assert!(matches!(cli.command, Some(Command::Scheduler { .. })));
}
#[test]
fn test_parse_db_seed() {
let cli = Cli::parse_from(["sz-rust", "db:seed"]);
assert!(matches!(cli.command, Some(Command::Seed { .. })));
}
#[test]
fn test_parse_db_seed_with_options() {
let cli = Cli::parse_from([
"sz-rust",
"db:seed",
"--path",
"custom_seeds",
"--db-type",
"mysql",
"--show-sql",
"--url",
"mysql://user:pass@host:3306/db",
"--class",
"001_users",
]);
match cli.command {
Some(Command::Seed {
path,
db_type,
show_sql,
url,
class,
}) => {
assert_eq!(path, "custom_seeds");
assert_eq!(db_type, "mysql");
assert!(show_sql);
assert_eq!(url.as_deref(), Some("mysql://user:pass@host:3306/db"));
assert_eq!(class.as_deref(), Some("001_users"));
}
_ => panic!("expected Seed command"),
}
}
#[test]
fn test_execute_no_command_returns_ok() {
let cli = Cli { command: None };
let result = cli.execute();
assert!(result.is_ok());
assert_eq!(result.unwrap(), 0);
}
}