mirror of
https://github.com/LHY0125/PathEditor.git
synced 2026-06-29 09:55:56 +08:00
refactor: 代码清理 — 删除 AppError、重命名 replacePaths、修正 detectExportFormat、统一 PATH 长度、优化 BOM 检查、添加同步注释
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -63,6 +63,8 @@ pub fn save_user_paths(paths: Vec<String>) -> Result<(), String> {
|
|||||||
save_paths(HKEY_CURRENT_USER, USER_REG_PATH, "用户", &paths)
|
save_paths(HKEY_CURRENT_USER, USER_REG_PATH, "用户", &paths)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 将分号分隔的 PATH 字符串拆分为数组。
|
||||||
|
/// 注意:TS 端 src/core/validation.ts 有相同逻辑的 split_path,修改时需同步两端。
|
||||||
pub(crate) fn split_path(raw: &str) -> Vec<String> {
|
pub(crate) fn split_path(raw: &str) -> Vec<String> {
|
||||||
raw.split(';')
|
raw.split(';')
|
||||||
.map(|s| s.trim().to_string())
|
.map(|s| s.trim().to_string())
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
/// 传给前端的统一错误类型(保留供未来迁移使用,届时所有命令改为返回 Result<T, AppError>)
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
pub struct AppError {
|
|
||||||
pub message: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for AppError {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "{}", self.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&str> for AppError {
|
|
||||||
fn from(s: &str) -> Self {
|
|
||||||
AppError {
|
|
||||||
message: s.to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<String> for AppError {
|
|
||||||
fn from(s: String) -> Self {
|
|
||||||
AppError { message: s }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<std::io::Error> for AppError {
|
|
||||||
fn from(e: std::io::Error) -> Self {
|
|
||||||
AppError {
|
|
||||||
message: format!("IO 错误: {}", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
mod commands;
|
mod commands;
|
||||||
mod error;
|
|
||||||
|
|
||||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||||
pub fn run() {
|
pub fn run() {
|
||||||
|
|||||||
@@ -12,8 +12,8 @@
|
|||||||
"dir": ""
|
"dir": ""
|
||||||
},
|
},
|
||||||
"path": {
|
"path": {
|
||||||
"maxSystemLength": 2048,
|
"maxSystemLength": 32767,
|
||||||
"maxUserLength": 2048,
|
"maxUserLength": 32767,
|
||||||
"maxCombinedLength": 8191
|
"maxCombinedLength": 32767
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* 导入导出模块 — 对应 C 版 import_export.c
|
* 导入导出模块 — 对应 C 版 import_export.c
|
||||||
* 支持 JSON、CSV、TXT 三种格式
|
* 支持 JSON、CSV、TXT 三种格式
|
||||||
*/
|
*/
|
||||||
export type ExportFormat = 'json' | 'csv';
|
export type ExportFormat = 'json' | 'csv' | 'txt';
|
||||||
|
|
||||||
export interface ExportData {
|
export interface ExportData {
|
||||||
system: string[];
|
system: string[];
|
||||||
@@ -11,7 +11,9 @@ export interface ExportData {
|
|||||||
|
|
||||||
/** 根据文件扩展名检测格式 */
|
/** 根据文件扩展名检测格式 */
|
||||||
export function detectExportFormat(filepath: string): ExportFormat {
|
export function detectExportFormat(filepath: string): ExportFormat {
|
||||||
if (filepath.toLowerCase().endsWith('.csv')) return 'csv';
|
const lower = filepath.toLowerCase();
|
||||||
|
if (lower.endsWith('.csv')) return 'csv';
|
||||||
|
if (lower.endsWith('.txt')) return 'txt';
|
||||||
return 'json';
|
return 'json';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,10 +67,10 @@ export function importFromCsv(content: string): ImportResult {
|
|||||||
|
|
||||||
let hasHeader = false;
|
let hasHeader = false;
|
||||||
|
|
||||||
for (const rawLine of lines) {
|
for (let i = 0; i < lines.length; i++) {
|
||||||
// 跳过 BOM
|
// 跳过 BOM(仅首行)
|
||||||
let line = rawLine;
|
let line = lines[i];
|
||||||
if (line.startsWith('')) {
|
if (i === 0 && line.startsWith('')) {
|
||||||
line = line.slice(1);
|
line = line.slice(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,9 +176,10 @@ export function importFromTxt(content: string): string[] {
|
|||||||
const paths: string[] = [];
|
const paths: string[] = [];
|
||||||
const lines = content.split(/\r?\n/);
|
const lines = content.split(/\r?\n/);
|
||||||
|
|
||||||
for (let line of lines) {
|
for (let i = 0; i < lines.length; i++) {
|
||||||
// 跳过 BOM
|
// 跳过 BOM(仅首行)
|
||||||
if (line.startsWith('')) line = line.slice(1);
|
let line = lines[i];
|
||||||
|
if (i === 0 && line.startsWith('')) line = line.slice(1);
|
||||||
|
|
||||||
const trimmed = line.trim();
|
const trimmed = line.trim();
|
||||||
if (trimmed.length === 0 || trimmed.startsWith('#')) continue;
|
if (trimmed.length === 0 || trimmed.startsWith('#')) continue;
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ export function join_path(paths: string[]): string {
|
|||||||
return paths.join(';');
|
return paths.join(';');
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 分割 PATH 字符串 */
|
/** 分割 PATH 字符串。
|
||||||
|
* 注意:Rust 端 src-tauri/src/commands/registry.rs 有相同逻辑的 split_path,修改时需同步两端。 */
|
||||||
export function split_path(raw: string): string[] {
|
export function split_path(raw: string): string[] {
|
||||||
return raw
|
return raw
|
||||||
.split(';')
|
.split(';')
|
||||||
|
|||||||
@@ -92,9 +92,9 @@ export function useAppActions(activeTab: TabId, dialogs: DialogState) {
|
|||||||
if (result.system.length > 0 && result.user.length > 0) {
|
if (result.system.length > 0 && result.user.length > 0) {
|
||||||
setImportDialog({ open: true, system: result.system, user: result.user });
|
setImportDialog({ open: true, system: result.system, user: result.user });
|
||||||
} else if (result.system.length > 0) {
|
} else if (result.system.length > 0) {
|
||||||
useAppStore.getState().importPaths(TargetType.SYSTEM, result.system);
|
useAppStore.getState().replacePaths(TargetType.SYSTEM, result.system);
|
||||||
} else if (result.user.length > 0) {
|
} else if (result.user.length > 0) {
|
||||||
useAppStore.getState().importPaths(TargetType.USER, result.user);
|
useAppStore.getState().replacePaths(TargetType.USER, result.user);
|
||||||
}
|
}
|
||||||
}, [setImportDialog]);
|
}, [setImportDialog]);
|
||||||
|
|
||||||
@@ -159,8 +159,8 @@ export function useAppActions(activeTab: TabId, dialogs: DialogState) {
|
|||||||
const handleImportSelect = useCallback((target: 'system' | 'user' | 'both') => {
|
const handleImportSelect = useCallback((target: 'system' | 'user' | 'both') => {
|
||||||
const { system, user } = dialogs.importDialog;
|
const { system, user } = dialogs.importDialog;
|
||||||
const flat = flattenImportResult({ system, user }, target);
|
const flat = flattenImportResult({ system, user }, target);
|
||||||
if (flat.system.length > 0) useAppStore.getState().importPaths(TargetType.SYSTEM, flat.system);
|
if (flat.system.length > 0) useAppStore.getState().replacePaths(TargetType.SYSTEM, flat.system);
|
||||||
if (flat.user.length > 0) useAppStore.getState().importPaths(TargetType.USER, flat.user);
|
if (flat.user.length > 0) useAppStore.getState().replacePaths(TargetType.USER, flat.user);
|
||||||
setImportDialog({ open: false, system: [], user: [] });
|
setImportDialog({ open: false, system: [], user: [] });
|
||||||
}, [dialogs.importDialog, setImportDialog]);
|
}, [dialogs.importDialog, setImportDialog]);
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ interface AppState {
|
|||||||
moveUp: (index: number, target: TargetType) => void;
|
moveUp: (index: number, target: TargetType) => void;
|
||||||
moveDown: (index: number, target: TargetType) => void;
|
moveDown: (index: number, target: TargetType) => void;
|
||||||
cleanPaths: (target: TargetType, validateFn: (p: string) => boolean) => string[];
|
cleanPaths: (target: TargetType, validateFn: (p: string) => boolean) => string[];
|
||||||
importPaths: (target: TargetType, importPaths: string[]) => void;
|
replacePaths: (target: TargetType, newPaths: string[]) => void;
|
||||||
clearPaths: (target: TargetType) => void;
|
clearPaths: (target: TargetType) => void;
|
||||||
|
|
||||||
undo: () => void;
|
undo: () => void;
|
||||||
@@ -169,18 +169,18 @@ export const useAppStore = create<AppState>((set, get) => ({
|
|||||||
return removed;
|
return removed;
|
||||||
},
|
},
|
||||||
|
|
||||||
importPaths: (target, importPaths) => {
|
replacePaths: (target, newPaths) => {
|
||||||
if (importPaths.length === 0) return;
|
if (newPaths.length === 0) return;
|
||||||
const state = get();
|
const state = get();
|
||||||
const list = target === TargetType.SYSTEM ? state.sysPaths : state.userPaths;
|
const list = target === TargetType.SYSTEM ? state.sysPaths : state.userPaths;
|
||||||
|
|
||||||
state.undoRedo.push({
|
state.undoRedo.push({
|
||||||
type: OperationType.IMPORT, target, index: 0, count: importPaths.length,
|
type: OperationType.IMPORT, target, index: 0, count: newPaths.length,
|
||||||
oldPaths: [...list], newPaths: [...importPaths],
|
oldPaths: [...list], newPaths: [...newPaths],
|
||||||
});
|
});
|
||||||
|
|
||||||
if (target === TargetType.SYSTEM) set({ sysPaths: [...importPaths], selectedIndices: [] });
|
if (target === TargetType.SYSTEM) set({ sysPaths: [...newPaths], selectedIndices: [] });
|
||||||
else set({ userPaths: [...importPaths], selectedIndices: [] });
|
else set({ userPaths: [...newPaths], selectedIndices: [] });
|
||||||
get()._markDirty();
|
get()._markDirty();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -140,11 +140,11 @@ describe('app-store CRUD', () => {
|
|||||||
expect(useAppStore.getState().sysPaths).toEqual(['C:\\valid']);
|
expect(useAppStore.getState().sysPaths).toEqual(['C:\\valid']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('importPaths 整体替换列表', () => {
|
it('replacePaths 整体替换列表', () => {
|
||||||
const store = useAppStore.getState();
|
const store = useAppStore.getState();
|
||||||
store.addPath('old1', TargetType.USER);
|
store.addPath('old1', TargetType.USER);
|
||||||
store.addPath('old2', TargetType.USER);
|
store.addPath('old2', TargetType.USER);
|
||||||
store.importPaths(TargetType.USER, ['new1', 'new2', 'new3']);
|
store.replacePaths(TargetType.USER, ['new1', 'new2', 'new3']);
|
||||||
expect(useAppStore.getState().userPaths).toEqual(['new1', 'new2', 'new3']);
|
expect(useAppStore.getState().userPaths).toEqual(['new1', 'new2', 'new3']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -116,10 +116,11 @@ describe('detectExportFormat', () => {
|
|||||||
it('.csv 检测为 CSV', () => {
|
it('.csv 检测为 CSV', () => {
|
||||||
expect(detectExportFormat('data.CSV')).toBe('csv');
|
expect(detectExportFormat('data.CSV')).toBe('csv');
|
||||||
});
|
});
|
||||||
|
it('.txt 检测为 TXT', () => {
|
||||||
|
expect(detectExportFormat('data.txt')).toBe('txt');
|
||||||
|
});
|
||||||
it('其他扩展名检测为 JSON', () => {
|
it('其他扩展名检测为 JSON', () => {
|
||||||
expect(detectExportFormat('data.json')).toBe('json');
|
expect(detectExportFormat('data.json')).toBe('json');
|
||||||
expect(detectExportFormat('data.txt')).toBe('json');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user