mirror of
https://github.com/LHY0125/PathEditor.git
synced 2026-06-28 17:25:54 +08:00
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:
+102
@@ -2,3 +2,105 @@
|
||||
pub fn read_text_file(path: &str) -> Result<String, String> {
|
||||
std::fs::read_to_string(path).map_err(|e| format!("无法读取文件: {}", e))
|
||||
}
|
||||
|
||||
/// 导入路径文件(JSON / CSV / TXT),返回 (系统路径, 用户路径)
|
||||
pub fn import_paths(path: &str, content: &str) -> Result<(Vec<String>, Vec<String>), String> {
|
||||
let ext = std::path::Path::new(path)
|
||||
.extension()
|
||||
.map(|e| e.to_ascii_lowercase())
|
||||
.unwrap_or_default();
|
||||
let ext = ext.to_string_lossy();
|
||||
|
||||
match ext.as_ref() {
|
||||
"json" => import_json(content),
|
||||
"csv" => import_csv(content),
|
||||
"txt" => import_txt(content),
|
||||
_ => Err(format!("不支持的格式: .{}", ext)),
|
||||
}
|
||||
}
|
||||
|
||||
fn import_json(content: &str) -> Result<(Vec<String>, Vec<String>), String> {
|
||||
#[derive(serde::Deserialize)]
|
||||
struct ImportData {
|
||||
#[serde(default)]
|
||||
system: Vec<String>,
|
||||
#[serde(default)]
|
||||
user: Vec<String>,
|
||||
}
|
||||
let data: ImportData =
|
||||
serde_json::from_str(content).map_err(|e| format!("JSON 解析失败: {}", e))?;
|
||||
Ok((data.system, data.user))
|
||||
}
|
||||
|
||||
fn import_csv(content: &str) -> Result<(Vec<String>, Vec<String>), String> {
|
||||
let mut sys = Vec::new();
|
||||
let mut usr = Vec::new();
|
||||
for line in content.lines() {
|
||||
let fields: Vec<&str> = line.split(',').collect();
|
||||
if fields.len() >= 2 {
|
||||
match fields[0].trim() {
|
||||
"system" | "sys" => sys.push(fields[1].trim().to_string()),
|
||||
"user" | "usr" => usr.push(fields[1].trim().to_string()),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
if sys.is_empty() && usr.is_empty() {
|
||||
return Err("CSV 文件中未找到有效路径".into());
|
||||
}
|
||||
Ok((sys, usr))
|
||||
}
|
||||
|
||||
fn import_txt(content: &str) -> Result<(Vec<String>, Vec<String>), String> {
|
||||
let paths: Vec<String> = content
|
||||
.lines()
|
||||
.map(|l| l.trim().to_string())
|
||||
.filter(|l| !l.is_empty() && !l.starts_with('#'))
|
||||
.collect();
|
||||
if paths.is_empty() {
|
||||
return Err("TXT 文件中未找到路径".into());
|
||||
}
|
||||
// TXT 格式全部导入为用户路径
|
||||
Ok((vec![], paths))
|
||||
}
|
||||
|
||||
/// 导出 PATH 为指定格式字符串
|
||||
pub fn export_paths(sys: &[String], usr: &[String], format: &str) -> String {
|
||||
match format {
|
||||
"json" => {
|
||||
let data = serde_json::json!({
|
||||
"version": "5.0.0",
|
||||
"timestamp": chrono::Local::now().format("%Y-%m-%dT%H:%M:%S").to_string(),
|
||||
"system": sys,
|
||||
"user": usr,
|
||||
});
|
||||
serde_json::to_string_pretty(&data).unwrap_or_default()
|
||||
}
|
||||
"csv" => {
|
||||
let mut out = String::from("type,path\n");
|
||||
for p in sys {
|
||||
out.push_str(&format!("system,{}\n", p));
|
||||
}
|
||||
for p in usr {
|
||||
out.push_str(&format!("user,{}\n", p));
|
||||
}
|
||||
out
|
||||
}
|
||||
_ => {
|
||||
let mut out = String::new();
|
||||
if !sys.is_empty() {
|
||||
out.push_str(&format!("# 系统 PATH ({})\n", sys.len()));
|
||||
for p in sys {
|
||||
out.push_str(&format!("{}\n", p));
|
||||
}
|
||||
}
|
||||
if !usr.is_empty() {
|
||||
out.push_str(&format!("# 用户 PATH ({})\n", usr.len()));
|
||||
for p in usr {
|
||||
out.push_str(&format!("{}\n", p));
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +81,29 @@ fn join_path(paths: &[String]) -> String {
|
||||
.join(";")
|
||||
}
|
||||
|
||||
/// 清理路径列表:移除不存在的目录 + 重复路径(保留首次出现)
|
||||
/// 返回 (保留的路径, 被移除的路径)
|
||||
pub fn clean_paths(paths: Vec<String>) -> (Vec<String>, Vec<String>) {
|
||||
use std::collections::HashSet;
|
||||
let mut seen: HashSet<String> = HashSet::new();
|
||||
let mut kept = Vec::new();
|
||||
let mut removed = Vec::new();
|
||||
for p in paths {
|
||||
let key = p.trim().to_lowercase();
|
||||
if seen.contains(&key) {
|
||||
removed.push(p);
|
||||
continue;
|
||||
}
|
||||
seen.insert(key);
|
||||
if !p.contains('%') && !std::path::Path::new(&p).is_dir() {
|
||||
removed.push(p);
|
||||
continue;
|
||||
}
|
||||
kept.push(p);
|
||||
}
|
||||
(kept, removed)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
Reference in New Issue
Block a user