refactor(cli): 判据收敛到 unlucky_cli::cli,banner 按被调用的二进制名自称(M82 复审)
去重:`looks_like_cli(args) -> bool` 落在 `unlucky_cli::cli`,两个 GUI 的 main.rs 只调用它, 判据从两份变一份。cli crate 里**不**出现 GUI 的 flag 表——语义仍是「首个 token 是裸词就接管; 首个 token 是 flag 时只有解析出真命令才接管,解析失败不接管」。原来两个 main.rs 里的 5+5 条 单测合并成 cli crate 的一份(用例集按复审要求原样搬,含 --screenshot/--renderer/--diff/ --mock/--force-state/--scale-factor、--help/-h/--version/-V、10 个已知子命令、--json 前置、 未知子命令),两个 main.rs 的旧测试已删除。 banner:新增 cli::program 模块,从 argv[0] 的 basename 取程序名(剥目录、剥 .exe、空值回落 ull),把帮助/版本/错误提示里独立出现的 `ull` 换成它——`unlucky-launcher --version` 不再自称 `ull`,也不会把人指去敲 `ull --help`。`run()` 签名未改;`ull` 自己跑时输出逐字不变(替换是 恒等),打包脚本的 `ull --version` 冒烟不受影响。
This commit is contained in:
1 parent
fecff989e7
commit
c9e6236a87
9 files changed
+275
-190
No files matched your search
@@ -5,7 +5,8 @@
|
||||
//! - 被子命令命中(`instances` / `install` / `launch` / `accounts` / `sessions` / `logs` /
|
||||
//! `diag` / `config` / `java` / `agent`,或 `--help` / `--version`)→ 直接转给
|
||||
//! `unlucky_cli::run`,**不初始化任何 UI**(不 probe 后端、不扫字体、不碰图形库)。
|
||||
//! 子命令表不在这里抄一份:唯一真相源是 `crates/cli` 的解析器(见 [`is_cli_invocation`])。
|
||||
//! 子命令表不在这里抄一份:唯一真相源是 `crates/cli` 的解析器
|
||||
//! (`unlucky_cli::cli::looks_like_cli`,本文件只调用)。
|
||||
//! - 其余参数(`--software-renderer` / `--renderer` / `--theme` / `--screenshot*` /
|
||||
//! `--diff` …):开窗跑骨架,`--screenshot` 出图后退出;`--diff <a> <b>` 只读两张 PNG
|
||||
//! 做逐像素对比(D30 的 GPU/CPU 几何一致性检查),不开窗。
|
||||
@@ -45,8 +46,11 @@ fn main() -> ExitCode {
|
||||
/// 这条路径上**不构造窗口、不探后端、不扫字体、不依赖任何图形库**(这些库缺失时同一条命令照样
|
||||
/// 跑完,实测见 packaging/README.md §7.4)。`unlucky-launcher-iced launch …` 的耗时因此与首帧无关——
|
||||
/// 这句是结构性判断,**没有重量**(原因见 §10-8)。
|
||||
///
|
||||
/// 「这条命令行算不算 CLI 调用」的判据不在这里重复:`unlucky_cli::cli::looks_like_cli` 是唯一
|
||||
/// 一处(M82 复审去重),两个 GUI 二进制都只调它,改语义不会漂。
|
||||
fn run_cli_if_requested(args: &[String]) -> Option<ExitCode> {
|
||||
if !is_cli_invocation(args) {
|
||||
if !unlucky_cli::cli::looks_like_cli(args) {
|
||||
return None;
|
||||
}
|
||||
let mut stdout = std::io::stdout();
|
||||
@@ -57,30 +61,6 @@ fn run_cli_if_requested(args: &[String]) -> Option<ExitCode> {
|
||||
Some(ExitCode::from(u8::try_from(code).unwrap_or(2)))
|
||||
}
|
||||
|
||||
/// 这条命令行是 CLI 调用还是 GUI 启动?
|
||||
///
|
||||
/// 判据只有一条,**子命令表不在这里重复**:`crates/cli` 的解析器就是唯一真相源,
|
||||
/// 所以新增子命令不需要动这个文件(Slint 版 `crates/app/src/main.rs` 是同一份判据)。
|
||||
///
|
||||
/// - 首个 token 是裸词(不以 `-` 开头)→ 一定是子命令位置:GUI 不接受位置参数,
|
||||
/// 而 `--diff` / `--screenshot` 的取值都在 flag 之后。未知子命令也走这条路,
|
||||
/// 由 CLI 自己报「未知命令 + `--help`」并以 1 退出(静默落到 GUI 会让脚本用户困惑)。
|
||||
/// - 首个 token 是 flag → 只有 CLI 自己解析出一条真命令才算 CLI 调用:`--help` / `--version` /
|
||||
/// `--json instances list` 走 CLI;`--data-dir X` 解析成 `Welcome`(没有子命令)、
|
||||
/// `--renderer` / `--screenshot` / `--diff` 等 GUI 参数干脆解析失败,一律仍走 GUI。
|
||||
/// 注意「解析失败」在这里是 GUI,不是 CLI:`--screenshot out.png` 也会解析失败,把它判成
|
||||
/// CLI 就会把截图脚本打死(这是本函数据以正确的那条边界)。
|
||||
fn is_cli_invocation(args: &[String]) -> bool {
|
||||
match args.first() {
|
||||
None => false,
|
||||
Some(first) if !first.starts_with('-') => true,
|
||||
Some(_) => match unlucky_cli::cli::parse(args) {
|
||||
Ok(invocation) => !matches!(invocation.command, unlucky_cli::cli::Command::Welcome),
|
||||
Err(_) => false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// 开窗路径:解析 GUI 参数 → (`--diff` 走比对)→ 起 UI。
|
||||
fn run_gui() -> Result<ExitCode, String> {
|
||||
let options = Options::from_env()?;
|
||||
@@ -111,66 +91,3 @@ fn run_gui() -> Result<ExitCode, String> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::is_cli_invocation;
|
||||
|
||||
fn cli(args: &[&str]) -> bool {
|
||||
is_cli_invocation(&args.iter().map(|arg| (*arg).to_owned()).collect::<Vec<_>>())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_args_starts_the_gui() {
|
||||
assert!(!cli(&[]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gui_flags_keep_working() {
|
||||
// CI 与验收脚本在用这些参数,一条都不能被当成子命令吃掉。
|
||||
assert!(!cli(&["--software-renderer"]));
|
||||
assert!(!cli(&["--screenshot", "out.png", "--data-dir", "/d/tmp/x"]));
|
||||
assert!(!cli(&["--renderer=gpu", "--theme=dark"]));
|
||||
assert!(!cli(&["--scale-factor", "1.0"]));
|
||||
assert!(!cli(&["--force-state", "hover"]));
|
||||
assert!(!cli(&["--diff", "a.png", "b.png"]));
|
||||
// 只有 --data-dir 时 CLI 解析成 Welcome(没有子命令),仍是 GUI 启动。
|
||||
assert!(!cli(&["--data-dir", "/d/tmp/x"]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn known_subcommands_go_to_the_cli() {
|
||||
for args in [
|
||||
vec!["instances", "list"],
|
||||
vec!["install", "1.20.1", "--dry-run"],
|
||||
vec!["launch", "1.20.1", "--dry-run"],
|
||||
vec!["accounts", "list"],
|
||||
vec!["sessions", "list"],
|
||||
vec!["logs", "abc", "--tail", "10"],
|
||||
vec!["diag", "abc"],
|
||||
vec!["config", "get"],
|
||||
vec!["java", "list"],
|
||||
vec!["agent", "config"],
|
||||
// 全局选项可以排在子命令前面
|
||||
vec!["--json", "instances", "list"],
|
||||
] {
|
||||
assert!(cli(&args), "{args:?} 应该交给 CLI");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn help_and_version_are_cli_too() {
|
||||
// iced 版以前不认 --version(会直接开窗),现在由 CLI 报版本并退出。
|
||||
assert!(cli(&["--version"]));
|
||||
assert!(cli(&["--help"]));
|
||||
assert!(cli(&["-h"]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_subcommand_is_left_to_the_cli_to_reject() {
|
||||
// 语义定案(README 两版同款):未知子命令 → CLI 报「未知命令 + ull --help」并以 1 退出,
|
||||
// 不静默落到 GUI(否则脚本用户会以为参数生效了)。
|
||||
assert!(cli(&["lst"]));
|
||||
assert!(cli(&["intsances", "list"]));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user