fix(cli): 程序名只替换模板占位符,不再改写用户数据(M82 r2 复审 P2)
r1 把 `rename` 套在**插值后的整条文本**上,于是用户数据被改坏:账户名恰好是 `ull` 会变成
`unlucky-launcher`,路径 `D:/tmp/ull/.ULL/...` 会变成 `D:/tmp/unlucky-launcher/.ULL/...`,
回显的子命令 `ull` 也会被改名。
改成模板占位符:文案里写 `%prog%`,**只在渲染时**(print_report / print_error / 帮助)替换成
argv[0] 的 basename。占位符只存在于静态模板里,插值进来的账户名 / 路径 / 子命令碰不到它。
用 `%prog%` 而不是 `{prog}`:这些模板要活在 `format!` 里,`{prog}` 会被 format! 当成参数名
编译失败。
同时补上 r1 漏的反方向:**stdout 的正常输出**(Report.human 与 warnings)现在也做替换,
以前只有 stderr 的错误做了,两边不一致。
回归测试:`user_data_that_says_ull_is_never_rewritten` 断言「账户名 `ull`」「路径 `\ull\`」
「未知命令 `ull`」在 fill 之后逐字不变。
占位符替换覆盖 21 个文件的 63 处文案;测试夹具里形如 `ull-mcp-diagnose-<pid>` 的临时目录名
已回滚为字面量(那不是文案)。
This commit is contained in:
1 parent
c9e6236a87
commit
2f8bbd1746
20 files changed
+99
-105
No files matched your search
@@ -28,8 +28,8 @@ pub fn list(ctx: &Ctx) -> Result<Report, CliError> {
|
||||
let mut human = Vec::new();
|
||||
if store.is_empty() {
|
||||
human.push("还没有账户。".to_string());
|
||||
human.push(" 离线:ull accounts add offline <玩家名>".to_string());
|
||||
human.push(" 微软:ull accounts add msa(设备码交互式登录)".to_string());
|
||||
human.push(" 离线:%prog% accounts add offline <玩家名>".to_string());
|
||||
human.push(" 微软:%prog% accounts add msa(设备码交互式登录)".to_string());
|
||||
} else {
|
||||
human.push(format!("账户({} 个):", store.accounts().len()));
|
||||
for meta in store.accounts() {
|
||||
|
||||
@@ -122,7 +122,7 @@ pub fn config_report(ctx: &Ctx, port: Option<u16>) -> Result<Report, CliError> {
|
||||
human.push("Cursor(.cursor/mcp.json):".to_string());
|
||||
human.extend(pretty_lines(&samples.cursor));
|
||||
human.push(
|
||||
"启动:ull agent serve(默认 Streamable HTTP) / ull agent serve --stdio".to_string(),
|
||||
"启动:%prog% agent serve(默认 Streamable HTTP) / %prog% agent serve --stdio".to_string(),
|
||||
);
|
||||
Ok(Report::new(data, human))
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ fn get_one(ctx: &Ctx, key: &str, show_secret: bool) -> Result<Report, CliError>
|
||||
let Some(value) = store.effective(key) else {
|
||||
return Err(
|
||||
CliError::user("unknown_key", format!("配置键 `{key}` 不在 schema 里"))
|
||||
.with_hint("`ull config get`(不带键)可以看到全部生效键"),
|
||||
.with_hint("`%prog% config get`(不带键)可以看到全部生效键"),
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ pub fn resolve(
|
||||
format!("有 {} 个账户,需要指定用哪个", many.len()),
|
||||
)
|
||||
.with_hint(format!(
|
||||
"ull launch <instance> --account <名字>;候选:{}",
|
||||
"%prog% launch <instance> --account <名字>;候选:{}",
|
||||
names_of(&store)
|
||||
)))
|
||||
}
|
||||
@@ -75,7 +75,7 @@ pub fn resolve(
|
||||
"已选中账户 {},`--username` 只用于没有账户时的离线占位",
|
||||
meta.display_name()
|
||||
))
|
||||
.with_hint("去掉 --username,或用 `ull accounts add offline <名字>` 建账户"));
|
||||
.with_hint("去掉 --username,或用 `%prog% accounts add offline <名字>` 建账户"));
|
||||
}
|
||||
|
||||
let mut notes = Vec::new();
|
||||
@@ -103,7 +103,7 @@ fn offline_fallback(username: Option<&str>) -> Identity {
|
||||
},
|
||||
account: None,
|
||||
notes: vec![
|
||||
"没有账户,按离线占位启动(ull accounts add offline <名字> 可建账户)".to_string(),
|
||||
"没有账户,按离线占位启动(%prog% accounts add offline <名字> 可建账户)".to_string(),
|
||||
],
|
||||
}
|
||||
}
|
||||
@@ -120,7 +120,7 @@ fn microsoft_identity(
|
||||
"account_credentials",
|
||||
format!("账户 {} 的微软凭据缺失或类型不符", meta.display_name()),
|
||||
)
|
||||
.with_hint("重新登录:ull accounts add msa"));
|
||||
.with_hint("重新登录:%prog% accounts add msa"));
|
||||
};
|
||||
|
||||
if dry_run {
|
||||
@@ -193,7 +193,7 @@ fn names_of(store: &AccountStore) -> String {
|
||||
|
||||
fn unknown_account(key: &str, store: &AccountStore) -> CliError {
|
||||
let hint = if store.is_empty() {
|
||||
"还没有账户:ull accounts add offline <名字> 或 ull accounts add msa".to_string()
|
||||
"还没有账户:%prog% accounts add offline <名字> 或 %prog% accounts add msa".to_string()
|
||||
} else {
|
||||
format!("可用账户:{}", names_of(store))
|
||||
};
|
||||
|
||||
@@ -44,7 +44,7 @@ pub(super) fn run(ctx: &Ctx, args: &InstallArgs, kind: LoaderKind) -> Result<Rep
|
||||
let version = args.version.trim().to_string();
|
||||
if version.is_empty() {
|
||||
return Err(CliError::usage(
|
||||
"install 需要版本 id:ull install <version> --loader fabric",
|
||||
"install 需要版本 id:%prog% install <version> --loader fabric",
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ pub(super) fn execute(
|
||||
),
|
||||
)
|
||||
.with_hint(format!(
|
||||
"`ull java install {}` 装一个托管 JDK,或把 JAVA_HOME 指向可用的 JDK(托管目录 {})",
|
||||
"`%prog% java install {}` 装一个托管 JDK,或把 JAVA_HOME 指向可用的 JDK(托管目录 {})",
|
||||
plan.java_requirement.minimum,
|
||||
managed.display()
|
||||
)));
|
||||
|
||||
@@ -113,7 +113,7 @@ pub(super) fn ensure_name_free(ctx: &Ctx, name: &str, version: &str) -> Result<(
|
||||
"instance_exists",
|
||||
format!("同名实例已存在:{name}(绑定版本 {})", profile.version_id),
|
||||
)
|
||||
.with_hint("用 --name 换个名字,或 `ull instances list` 看看现有实例")),
|
||||
.with_hint("用 --name 换个名字,或 `%prog% instances list` 看看现有实例")),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
@@ -145,7 +145,7 @@ pub(super) fn failure_error(report: &InstallReport, ctx: &Ctx) -> CliError {
|
||||
}
|
||||
}
|
||||
CliError::user(code, message).with_hint(format!(
|
||||
"重跑 `ull install` 会续传并跳过已就绪文件;游戏根 {}",
|
||||
"重跑 `%prog% install` 会续传并跳过已就绪文件;游戏根 {}",
|
||||
ctx.game().root().display()
|
||||
))
|
||||
}
|
||||
@@ -37,7 +37,7 @@ pub(super) fn run(ctx: &Ctx, args: &InstallArgs) -> Result<Report, CliError> {
|
||||
let version = args.version.trim().to_string();
|
||||
if version.is_empty() {
|
||||
return Err(CliError::usage(
|
||||
"install 需要版本 id:ull install <version>",
|
||||
"install 需要版本 id:%prog% install <version>",
|
||||
));
|
||||
}
|
||||
let name = args
|
||||
@@ -139,7 +139,7 @@ pub(super) fn plan_and_install(
|
||||
format!("版本 {version} 装不全:{reasons}"),
|
||||
)
|
||||
.with_hint(
|
||||
"旧版(pre-1.6)清单兜底待补;可先用官方启动器装好该版本再用 `ull launch`",
|
||||
"旧版(pre-1.6)清单兜底待补;可先用官方启动器装好该版本再用 `%prog% launch`",
|
||||
));
|
||||
}
|
||||
let sink = report::progress_sink(progress, json_progress, plan.file_count());
|
||||
|
||||
@@ -59,7 +59,7 @@ pub fn list(ctx: &Ctx) -> Result<Report, CliError> {
|
||||
ctx.data.launcher_home.display()
|
||||
)];
|
||||
if body.is_empty() {
|
||||
human.push(" (空)用 `ull install <version>` 登记第一个实例".to_string());
|
||||
human.push(" (空)用 `%prog% install <version>` 登记第一个实例".to_string());
|
||||
}
|
||||
human.extend(body);
|
||||
if installed.is_empty() {
|
||||
|
||||
@@ -111,7 +111,7 @@ pub fn list(ctx: &Ctx) -> Result<Report, CliError> {
|
||||
let mut human = vec![format!("Java {} 个", candidates.len())];
|
||||
if candidates.is_empty() {
|
||||
human.push(
|
||||
"(没找到可用 Java)用 `ull java install 21` 装一个,或设 JAVA_HOME / `ull launch --java <path>`"
|
||||
"(没找到可用 Java)用 `%prog% java install 21` 装一个,或设 JAVA_HOME / `%prog% launch --java <path>`"
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
@@ -209,7 +209,7 @@ pub fn install(ctx: &Ctx, major: u32, vendor: Option<JdkVendor>) -> Result<Repor
|
||||
for note in installed.chain.degradations() {
|
||||
human.push(format!("降级:{note}"));
|
||||
}
|
||||
human.push("`ull launch` 会自动优先用它".to_string());
|
||||
human.push("`%prog% launch` 会自动优先用它".to_string());
|
||||
Ok(Report::new(data, human))
|
||||
}
|
||||
|
||||
|
||||
@@ -330,7 +330,7 @@ fn map_launch_error(error: &LaunchError, ctx: &Ctx) -> CliError {
|
||||
|
||||
fn instance_hint(profiles: &ProfileList) -> String {
|
||||
if profiles.profiles.is_empty() {
|
||||
"用 `ull instances list` 查看实例,或 `ull install <version>` 登记一个".to_string()
|
||||
"用 `%prog% instances list` 查看实例,或 `%prog% install <version>` 登记一个".to_string()
|
||||
} else {
|
||||
let names: Vec<&str> = profiles
|
||||
.profiles
|
||||
|
||||
@@ -52,7 +52,7 @@ pub fn welcome() -> Report {
|
||||
#[must_use]
|
||||
pub fn help(topic: Option<&str>) -> Report {
|
||||
let text = match topic.and_then(help::topic) {
|
||||
Some(text) => program::rename(text, &program::name()),
|
||||
Some(text) => program::fill(text),
|
||||
None => general_help(),
|
||||
};
|
||||
let data = json!({
|
||||
@@ -64,5 +64,5 @@ pub fn help(topic: Option<&str>) -> Report {
|
||||
|
||||
/// 总帮助,按被调用的二进制名自述(`ull` 自己跑时逐字不变)。
|
||||
fn general_help() -> String {
|
||||
program::rename(help::general(), &program::name())
|
||||
program::fill(help::general())
|
||||
}
|
||||
@@ -75,14 +75,14 @@ pub(crate) fn map_auth_error(error: unlucky_core::auth::AuthError) -> CliError {
|
||||
let hint = match &error {
|
||||
AuthError::Canceled => Some("登录被取消,可以重跑一次".to_string()),
|
||||
AuthError::Timeout { .. } => {
|
||||
Some("重新执行 `ull accounts add msa` 拿新的用户码".to_string())
|
||||
Some("重新执行 `%prog% accounts add msa` 拿新的用户码".to_string())
|
||||
}
|
||||
AuthError::CredentialExpired => Some("重新登录:ull accounts add msa".to_string()),
|
||||
AuthError::CredentialExpired => Some("重新登录:%prog% accounts add msa".to_string()),
|
||||
AuthError::Unsupported(_) => {
|
||||
Some("微软登录需要设置 ULL_MSA_CLIENT_ID(Azure 公共客户端 id)".to_string())
|
||||
}
|
||||
AuthError::Conflict { .. } => Some("用 `ull accounts list` 查看已有账户".to_string()),
|
||||
AuthError::NotFound { .. } => Some("用 `ull accounts list` 查看账户名".to_string()),
|
||||
AuthError::Conflict { .. } => Some("用 `%prog% accounts list` 查看已有账户".to_string()),
|
||||
AuthError::NotFound { .. } => Some("用 `%prog% accounts list` 查看账户名".to_string()),
|
||||
AuthError::Denied { code, .. } if code == "no_profile" || code == "no_license" => {
|
||||
Some("去 minecraft.net 检查该账号的 Java 版拥有权与档案".to_string())
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ pub fn list(ctx: &Ctx, limit: usize) -> Result<Report, CliError> {
|
||||
index.display()
|
||||
)];
|
||||
if shown.is_empty() {
|
||||
human.push(" (空)先用 `ull launch <实例>` 跑一局".to_string());
|
||||
human.push(" (空)先用 `%prog% launch <实例>` 跑一局".to_string());
|
||||
}
|
||||
for entry in &shown {
|
||||
human.push(format!(
|
||||
|
||||
Reference in new issue
Block a user