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
+6
-89
@@ -16,7 +16,8 @@
|
||||
//! 一个二进制两种用法(M82):`unlucky-launcher` 不带子命令时起 GUI;带子命令
|
||||
//! (`instances` / `install` / `launch` / `accounts` / `sessions` / `logs` / `diag` /
|
||||
//! `config` / `java` / `agent`)时直接转给 `unlucky_cli::run`,**不初始化任何 UI**。
|
||||
//! 子命令表不在这里抄一份——唯一真相源是 `crates/cli` 的解析器(见 [`is_cli_invocation`])。
|
||||
//! 子命令表不在这里抄一份——唯一真相源是 `crates/cli` 的解析器(见
|
||||
//! `unlucky_cli::cli::looks_like_cli`)。
|
||||
//! 零 UI 依赖的 `ull` 仍然保留:它连 fontconfig 都不需要,给服务器 / CI / Termux 用。
|
||||
//!
|
||||
//! 架构分层(本文件只有一个职责:分发 + 把窗口拉起来):
|
||||
@@ -101,8 +102,11 @@ fn run_gui() -> Result<(), slint::PlatformError> {
|
||||
/// 这条路径上**不构造窗口、不探后端、不扫字体、不依赖任何图形库**(这些库缺失时同一条命令照样
|
||||
/// 跑完,实测见 packaging/README.md §7.4)。`unlucky-launcher 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();
|
||||
@@ -113,30 +117,6 @@ fn run_cli_if_requested(args: &[String]) -> Option<ExitCode> {
|
||||
Some(ExitCode::from(u8::try_from(code).unwrap_or(2)))
|
||||
}
|
||||
|
||||
/// 这条命令行是 CLI 调用还是 GUI 启动?
|
||||
///
|
||||
/// 判据只有一条,**子命令表不在这里重复**:`crates/cli` 的解析器就是唯一真相源,
|
||||
/// 所以新增子命令不需要动这个文件(iced 版 `crates/app-iced/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`(没有子命令)、
|
||||
/// `--software-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,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Win7 老机 / 无可用 OpenGL 驱动时的软渲染兜底开关(01c §6 的 Slint 核心卖点)。
|
||||
fn apply_renderer_flag() {
|
||||
let software = std::env::args()
|
||||
@@ -146,66 +126,3 @@ fn apply_renderer_flag() {
|
||||
std::env::set_var("SLINT_BACKEND", "winit-software");
|
||||
}
|
||||
}
|
||||
|
||||
#[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(&["--screenshot-theme", "dark"]));
|
||||
assert!(!cli(&["--renderer=gpu", "--theme=dark"]));
|
||||
assert!(!cli(&["--diff", "a.png", "b.png"]));
|
||||
assert!(!cli(&["--mock"]));
|
||||
// 只有 --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() {
|
||||
// GUI 以前不认 --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