feat: CLI 补全至 GUI 功能 100% 对等 — 新增 9 条命令

新增: edit, move-up, move-down, clean, enable, disable, import, export, backup
core: registry.rs +clean_paths, fs.rs +import_paths +export_paths
CLI 特有增强: move-up/move-down 支持 --steps N 一次移动多格

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-28 23:43:05 +08:00
parent c181fe15d4
commit a553a16a64
3 changed files with 328 additions and 18 deletions
+203 -18
View File
@@ -28,18 +28,67 @@ enum Command {
index: usize,
#[arg(short, long)] system: bool,
},
/// 编辑指定位置的路径
Edit {
index: usize,
new_path: String,
#[arg(short, long)] system: bool,
},
/// 上移路径(--steps 指定移动格数,默认 1)
MoveUp {
index: usize,
#[arg(long, default_value = "1")] steps: usize,
#[arg(short, long)] system: bool,
},
/// 下移路径(--steps 指定移动格数,默认 1)
MoveDown {
index: usize,
#[arg(long, default_value = "1")] steps: usize,
#[arg(short, long)] system: bool,
},
/// 清理无效和重复路径
Clean {
#[arg(short, long)] system: bool,
#[arg(short, long)] user: bool,
#[arg(long)] dry_run: bool,
#[arg(long)] json: bool,
},
/// 启用指定位置的路径
Enable {
index: usize,
#[arg(short, long)] system: bool,
#[arg(short, long)] user: bool,
},
/// 禁用指定位置的路径
Disable {
index: usize,
#[arg(short, long)] system: bool,
#[arg(short, long)] user: bool,
},
/// 从文件导入 PATHJSON/CSV/TXT
Import {
file: String,
#[arg(long, default_value = "both")] target: String,
},
/// 导出 PATH 为文件
Export {
#[arg(long, default_value = "json")] format: String,
#[arg(short, long)] output: Option<String>,
},
/// 创建注册表备份
Backup,
/// 检测可执行文件冲突
Conflicts { #[arg(long)] json: bool },
/// 列出 PATH 可执行文件
/// 列出 PATH 目录中的可执行文件
Scan {
#[arg(long)] query: Option<String>,
#[arg(long)] json: bool,
},
/// 检查管理员权限
CheckAdmin { #[arg(long)] json: bool },
/// 管理配置文件
#[command(subcommand)]
Profile(ProfileCmd),
/// 检查管理员权限
CheckAdmin { #[arg(long)] json: bool },
}
#[derive(Subcommand)]
@@ -65,6 +114,21 @@ fn pick_target(system: bool, _user: bool) -> &'static str {
if system { "system" } else { "user" }
}
type SaveFn = fn(Vec<String>) -> Result<(), String>;
fn load_and_save(system: bool, f: impl FnOnce(Vec<String>) -> Vec<String>) {
let target = pick_target(system, false);
let (list, save): (Vec<String>, SaveFn) = if target == "system" {
(core::registry::load_system_paths().unwrap_or_else(|e| exit_err(&e)),
core::registry::save_system_paths)
} else {
(core::registry::load_user_paths().unwrap_or_else(|e| exit_err(&e)),
core::registry::save_user_paths)
};
let new_list = f(list);
save(new_list).unwrap_or_else(|e| exit_err(&e));
}
// ── 命令实现 ──
fn cmd_list(system: bool, user: bool, json_out: bool) {
@@ -93,34 +157,146 @@ fn cmd_list(system: bool, user: bool, json_out: bool) {
fn cmd_add(path: String, system: bool, user: bool) {
let target = pick_target(system, user);
let (mut list, save_fn): (Vec<String>, _) = if target == "system" {
(core::registry::load_system_paths().unwrap_or_else(|e| exit_err(&e)),
core::registry::save_system_paths as fn(Vec<String>) -> Result<(), String>)
} else {
(core::registry::load_user_paths().unwrap_or_else(|e| exit_err(&e)),
core::registry::save_user_paths as fn(Vec<String>) -> Result<(), String>)
};
list.push(path.clone());
save_fn(list).unwrap_or_else(|e| exit_err(&e));
load_and_save(system || false, |mut list| {
list.push(path.clone());
list
});
let label = if target == "system" { "系统" } else { "用户" };
println!("已添加到{} PATH: {path}", label);
}
fn cmd_remove(index: usize, system: bool) {
let target = pick_target(system, false);
let (mut list, save_fn): (Vec<String>, _) = if target == "system" {
(core::registry::load_system_paths().unwrap_or_else(|e| exit_err(&e)),
core::registry::save_system_paths as fn(Vec<String>) -> Result<(), String>)
let mut list = if target == "system" {
core::registry::load_system_paths().unwrap_or_else(|e| exit_err(&e))
} else {
(core::registry::load_user_paths().unwrap_or_else(|e| exit_err(&e)),
core::registry::save_user_paths as fn(Vec<String>) -> Result<(), String>)
core::registry::load_user_paths().unwrap_or_else(|e| exit_err(&e))
};
if index >= list.len() { exit_err(&format!("索引 {index} 超出范围 (共 {} 条)", list.len())); }
let removed = list.remove(index);
save_fn(list).unwrap_or_else(|e| exit_err(&e));
let save: SaveFn = if target == "system" { core::registry::save_system_paths } else { core::registry::save_user_paths };
save(list).unwrap_or_else(|e| exit_err(&e));
println!("已删除: {removed}");
}
fn cmd_edit(index: usize, new_path: String, system: bool) {
let target = pick_target(system, false);
let mut list = if target == "system" {
core::registry::load_system_paths().unwrap_or_else(|e| exit_err(&e))
} else {
core::registry::load_user_paths().unwrap_or_else(|e| exit_err(&e))
};
if index >= list.len() { exit_err(&format!("索引 {index} 超出范围 (共 {} 条)", list.len())); }
let old = std::mem::replace(&mut list[index], new_path.clone());
let save: SaveFn = if target == "system" { core::registry::save_system_paths } else { core::registry::save_user_paths };
save(list).unwrap_or_else(|e| exit_err(&e));
println!("已编辑: {old}{new_path}");
}
fn cmd_move(index: usize, steps: usize, system: bool, up: bool) {
load_and_save(system || false, |mut list| {
if index >= list.len() { exit_err(&format!("索引 {index} 超出范围 (共 {} 条)", list.len())); }
let end = if up {
if steps > index { 0 } else { index - steps }
} else {
let max = list.len() - 1;
if index + steps > max { max } else { index + steps }
};
let removed = list.remove(index);
list.insert(end, removed);
list
});
let dir = if up { "上移" } else { "下移" };
println!("{dir} {steps} 格完成");
}
fn cmd_clean(system: bool, user: bool, dry_run: bool, json_out: bool) {
let target = pick_target(system, user);
let list = if target == "system" {
core::registry::load_system_paths().unwrap_or_else(|e| exit_err(&e))
} else {
core::registry::load_user_paths().unwrap_or_else(|e| exit_err(&e))
};
let (kept, removed) = core::registry::clean_paths(list);
if json_out {
println!("{}", json!({ "kept": kept, "removed": removed, "kept_count": kept.len(), "removed_count": removed.len() }).to_string());
} else if dry_run {
println!("═══ 将被移除({} 条)═══", removed.len());
for r in &removed { println!("{}", r); }
println!("═══ 将保留({} 条)═══", kept.len());
for k in &kept { println!("{}", k); }
} else {
let kept_count = kept.len();
let save: SaveFn = if target == "system" { core::registry::save_system_paths } else { core::registry::save_user_paths };
save(kept).unwrap_or_else(|e| exit_err(&e));
println!("清理完成:移除 {} 条,保留 {}", removed.len(), kept_count);
if !removed.is_empty() {
for r in &removed { println!(" 已移除: {}", r); }
}
}
}
fn cmd_toggle(index: usize, system: bool, user: bool, enable: bool) {
let target = pick_target(system, user);
let list = if target == "system" {
core::registry::load_system_paths().unwrap_or_else(|e| exit_err(&e))
} else {
core::registry::load_user_paths().unwrap_or_else(|e| exit_err(&e))
};
if index >= list.len() { exit_err(&format!("索引 {index} 超出范围 (共 {} 条)", list.len())); }
let path = &list[index];
let (mut sys_dis, mut usr_dis) = core::disabled::load_disabled_state().unwrap_or_else(|_| (vec![], vec![]));
let target_list: &mut Vec<String> = if target == "system" { &mut sys_dis } else { &mut usr_dis };
if enable {
target_list.retain(|p| p != path);
} else if !target_list.contains(path) {
target_list.push(path.clone());
}
core::disabled::save_disabled_state(sys_dis, usr_dis).unwrap_or_else(|e| exit_err(&e));
let action = if enable { "启用" } else { "禁用" };
println!("{action}: {path}");
}
fn cmd_import(file: String, target: String) {
let content = core::fs::read_text_file(&file).unwrap_or_else(|e| exit_err(&e));
let (sys, usr) = core::fs::import_paths(&file, &content).unwrap_or_else(|e| exit_err(&e));
match target.as_str() {
"system" => {
core::registry::save_system_paths(sys).unwrap_or_else(|e| exit_err(&e));
println!("已导入到系统 PATH");
}
"user" => {
core::registry::save_user_paths(usr).unwrap_or_else(|e| exit_err(&e));
println!("已导入到用户 PATH");
}
_ => {
core::registry::save_system_paths(sys).unwrap_or_else(|e| exit_err(&e));
core::registry::save_user_paths(usr).unwrap_or_else(|e| exit_err(&e));
println!("已导入到系统 + 用户 PATH");
}
}
}
fn cmd_export(format: String, output: Option<String>) {
let sys = core::registry::load_system_paths().unwrap_or_else(|e| exit_err(&e));
let usr = core::registry::load_user_paths().unwrap_or_else(|e| exit_err(&e));
let content = core::fs::export_paths(&sys, &usr, &format);
if let Some(path) = output {
std::fs::write(&path, &content).unwrap_or_else(|e| exit_err(&format!("无法写入文件: {e}")));
println!("已导出到: {path}");
} else {
println!("{content}");
}
}
fn cmd_backup() {
let path = core::backup::backup_registry(None).unwrap_or_else(|e| exit_err(&e));
println!("备份已保存: {path}");
}
fn cmd_conflicts(json_out: bool) {
let mut paths: Vec<String> = vec![];
if let Ok(sys) = core::registry::load_system_paths() { paths.extend(sys); }
@@ -216,6 +392,15 @@ fn main() {
Command::List { system, user, json } => cmd_list(system, user, json),
Command::Add { path, system, user } => cmd_add(path, system, user),
Command::Remove { index, system } => cmd_remove(index, system),
Command::Edit { index, new_path, system } => cmd_edit(index, new_path, system),
Command::MoveUp { index, steps, system } => cmd_move(index, steps, system, true),
Command::MoveDown { index, steps, system } => cmd_move(index, steps, system, false),
Command::Clean { system, user, dry_run, json } => cmd_clean(system, user, dry_run, json),
Command::Enable { index, system, user } => cmd_toggle(index, system, user, true),
Command::Disable { index, system, user } => cmd_toggle(index, system, user, false),
Command::Import { file, target } => cmd_import(file, target),
Command::Export { format, output } => cmd_export(format, output),
Command::Backup => cmd_backup(),
Command::Conflicts { json } => cmd_conflicts(json),
Command::Scan { query, json } => cmd_scan(query, json),
Command::CheckAdmin { json } => cmd_check_admin(json),