Skip to main content

sz_rust_cli/
cli.rs

1//! CLI 命令定义 — 基于 clap derive
2//!
3//! 对齐 PHP ThinkPHP 6 `think` 命令体系,借鉴 Laravel Artisan 风格。
4//!
5//! ## PHP 对齐
6//!
7//! PHP `think` 命令使用 Symfony Console 组件,通过 `configure()` 定义参数和选项。
8//! Rust 端使用 clap derive 宏,通过结构体字段定义参数和选项。
9//!
10//! ## 命令对照表
11//!
12//! | PHP | Rust | 说明 |
13//! |-----|------|------|
14//! | `php think make:model User` | `sz-rust make:model User` | 生成 Model |
15//! | `php think make:controller User` | `sz-rust make:controller User` | 生成 Controller |
16//! | `php think make:migration CreateUsers` | `sz-rust make:migration create_users` | 生成迁移文件 |
17//! | `php think migrate` | `sz-rust migrate` | 执行迁移 |
18//! | `php think migrate:rollback` | `sz-rust migrate --rollback` | 回滚迁移 |
19//! | `php think migrate:status` | `sz-rust migrate:status` | 迁移状态 |
20//! | `php think route:list` | `sz-rust route:list` | 路由列表 |
21//! | `php think cache:clear` | `sz-rust cache:clear` | 清空缓存 |
22
23use clap::{Parser, Subcommand};
24
25use crate::cmd;
26use crate::error::CliError;
27
28/// SZ-Rust 命令行工具
29///
30/// 替代 PHP `think` 命令,提供代码生成、数据库迁移、路由查看、缓存管理等功能。
31#[derive(Parser, Debug)]
32#[command(
33    name = "sz-rust",
34    bin_name = "sz-rust",
35    version,
36    about = "SZ-Rust 命令行工具 — 替代 PHP think 命令",
37    long_about = "SZ-Rust CLI 对齐 PHP ThinkPHP 6 think 命令体系,提供 make:migration / make:model / make:controller / migrate / route:list / cache:clear 等命令。"
38)]
39pub struct Cli {
40    /// 子命令
41    #[command(subcommand)]
42    pub command: Option<Command>,
43}
44
45/// 顶层命令枚举
46///
47/// 对齐 PHP `think` 的命令分组(make / migrate / route / cache)。
48#[derive(Subcommand, Debug)]
49pub enum Command {
50    /// 代码生成命令组(make:migration / make:model / make:controller / make:guard / make:scaffold)
51    #[command(name = "make")]
52    Make {
53        /// make 子命令
54        #[command(subcommand)]
55        make_command: cmd::make::MakeCommand,
56    },
57
58    /// 数据库迁移命令(migrate / migrate:status / migrate:rollback)
59    #[command(name = "migrate")]
60    Migrate {
61        /// 迁移子命令参数
62        #[command(flatten)]
63        args: cmd::migrate::MigrateArgs,
64    },
65
66    /// 迁移状态查询(对齐 PHP `php think migrate:status`)
67    #[command(name = "migrate:status")]
68    MigrateStatus {
69        /// 迁移目录(默认 `migrations`)
70        #[arg(short = 'p', long, default_value = "migrations")]
71        path: String,
72
73        /// 数据库类型(默认 `postgres`,对齐 sz-orm `DbType`)
74        #[arg(long, default_value = "postgres")]
75        db_type: String,
76
77        /// 打印每个迁移的 SQL 内容
78        #[arg(long)]
79        show_sql: bool,
80    },
81
82    /// 路由列表(对齐 PHP `php think route:list`)
83    #[command(name = "route:list")]
84    RouteList {
85        /// 输出格式(table / json)
86        #[arg(short = 'f', long, default_value = "table")]
87        format: String,
88    },
89
90    /// 清空缓存(对齐 PHP `php think cache:clear`)
91    #[command(name = "cache:clear")]
92    CacheClear {
93        /// 指定缓存存储名(默认清空所有)
94        #[arg(short = 's', long)]
95        store: Option<String>,
96    },
97
98    /// 调度器命令组(scheduler:list / scheduler:run / scheduler:start)
99    #[command(name = "scheduler")]
100    Scheduler {
101        /// scheduler 子命令
102        #[command(subcommand)]
103        scheduler_command: cmd::scheduler::SchedulerCommand,
104    },
105}
106
107impl Cli {
108    /// 执行命令
109    ///
110    /// 根据 `command` 字段分发到对应的命令处理器。
111    ///
112    /// # 返回
113    ///
114    /// - `Ok(0)`:成功
115    /// - `Ok(code)`:命令指定的退出码(非 0 表示部分失败)
116    /// - `Err(_)`:内部错误
117    pub fn execute(&self) -> Result<i32, CliError> {
118        match &self.command {
119            None => {
120                // 无子命令,打印帮助
121                println!("SZ-Rust CLI — 使用 --help 查看可用命令");
122                Ok(0)
123            }
124            Some(Command::Make { make_command }) => cmd::make::execute(make_command).map(|_| 0),
125            Some(Command::Migrate { args }) => cmd::migrate::execute_migrate(args).map(|_| 0),
126            Some(Command::MigrateStatus {
127                path,
128                db_type,
129                show_sql,
130            }) => cmd::migrate::execute_status_with(path, db_type, *show_sql).map(|_| 0),
131            Some(Command::RouteList { format }) => {
132                cmd::route::execute_route_list(format).map(|_| 0)
133            }
134            Some(Command::CacheClear { store }) => {
135                cmd::cache::execute_cache_clear(store.as_deref()).map(|_| 0)
136            }
137            Some(Command::Scheduler { scheduler_command }) => {
138                cmd::scheduler::execute(scheduler_command).map(|_| 0)
139            }
140        }
141    }
142}
143
144#[cfg(test)]
145mod tests {
146    use super::*;
147    use clap::Parser;
148
149    #[test]
150    fn test_parse_make_model() {
151        let cli = Cli::parse_from(["sz-rust", "make", "model", "User"]);
152        match cli.command {
153            Some(Command::Make { make_command }) => {
154                assert!(matches!(make_command, cmd::make::MakeCommand::Model { .. }));
155            }
156            _ => panic!("expected Make command"),
157        }
158    }
159
160    #[test]
161    fn test_parse_make_controller() {
162        let cli = Cli::parse_from(["sz-rust", "make", "controller", "User"]);
163        match cli.command {
164            Some(Command::Make { make_command }) => {
165                assert!(matches!(
166                    make_command,
167                    cmd::make::MakeCommand::Controller { .. }
168                ));
169            }
170            _ => panic!("expected Make command"),
171        }
172    }
173
174    #[test]
175    fn test_parse_make_migration() {
176        let cli = Cli::parse_from(["sz-rust", "make", "migration", "create_users"]);
177        match cli.command {
178            Some(Command::Make { make_command }) => {
179                assert!(matches!(
180                    make_command,
181                    cmd::make::MakeCommand::Migration { .. }
182                ));
183            }
184            _ => panic!("expected Make command"),
185        }
186    }
187
188    #[test]
189    fn test_parse_migrate() {
190        let cli = Cli::parse_from(["sz-rust", "migrate"]);
191        assert!(matches!(cli.command, Some(Command::Migrate { .. })));
192    }
193
194    #[test]
195    fn test_parse_migrate_status() {
196        let cli = Cli::parse_from(["sz-rust", "migrate:status"]);
197        assert!(matches!(cli.command, Some(Command::MigrateStatus { .. })));
198    }
199
200    #[test]
201    fn test_parse_route_list() {
202        let cli = Cli::parse_from(["sz-rust", "route:list"]);
203        assert!(matches!(cli.command, Some(Command::RouteList { .. })));
204    }
205
206    #[test]
207    fn test_parse_cache_clear() {
208        let cli = Cli::parse_from(["sz-rust", "cache:clear"]);
209        assert!(matches!(cli.command, Some(Command::CacheClear { .. })));
210    }
211
212    #[test]
213    fn test_parse_cache_clear_with_store() {
214        let cli = Cli::parse_from(["sz-rust", "cache:clear", "--store", "redis"]);
215        match cli.command {
216            Some(Command::CacheClear { store }) => {
217                assert_eq!(store.as_deref(), Some("redis"));
218            }
219            _ => panic!("expected CacheClear command"),
220        }
221    }
222
223    #[test]
224    fn test_parse_scheduler() {
225        let cli = Cli::parse_from(["sz-rust", "scheduler", "list"]);
226        assert!(matches!(cli.command, Some(Command::Scheduler { .. })));
227    }
228
229    #[test]
230    fn test_execute_no_command_returns_ok() {
231        let cli = Cli { command: None };
232        let result = cli.execute();
233        assert!(result.is_ok());
234        assert_eq!(result.unwrap(), 0);
235    }
236}