Files
PathEditor/src/core/import-export.ts
T

221 lines
5.4 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 导入导出模块 — 对应 C 版 import_export.c
* 支持 JSON、CSV、TXT 三种格式
*/
import type { PathEntry } from './path-entry';
export type ExportFormat = 'json' | 'csv' | 'txt';
export interface ExportData {
system: PathEntry[];
user: PathEntry[];
}
/** 根据文件扩展名检测格式 */
export function detectExportFormat(filepath: string): ExportFormat {
const lower = filepath.toLowerCase();
if (lower.endsWith('.csv')) return 'csv';
if (lower.endsWith('.txt')) return 'txt';
return 'json';
}
// ── JSON 导出 ──
export function exportToJson(data: ExportData): string {
const obj = {
version: '1.0',
type: 'PathEditor',
exported: new Date().toISOString(),
system: data.system.map(e => e.path),
user: data.user.map(e => e.path),
};
return JSON.stringify(obj, null, 2);
}
// ── CSV 导出 ──
export function exportToCsv(data: ExportData): string {
const lines: string[] = [];
// UTF-8 BOM
lines.push('type,path');
for (const entry of data.system) {
lines.push(`system,${escapeCsvField(entry.path)}`);
}
for (const entry of data.user) {
lines.push(`user,${escapeCsvField(entry.path)}`);
}
return lines.join('\n') + '\n';
}
function escapeCsvField(field: string): string {
if (field.includes(',') || field.includes('"') || field.includes('\n')) {
return `"${field.replace(/"/g, '""')}"`;
}
return field;
}
// ── CSV 导入 ──
export interface ImportResult {
system: PathEntry[];
user: PathEntry[];
}
export function importFromCsv(content: string): ImportResult {
const result: ImportResult = { system: [], user: [] };
const lines = content.split(/\r?\n/);
let hasHeader = false;
for (let i = 0; i < lines.length; i++) {
// 跳过 BOM(仅首行)
let line = lines[i];
if (i === 0 && line.startsWith('')) {
line = line.slice(1);
}
if (line.trim() === '') continue;
const fields = parseCsvLine(line);
if (fields.length < 2) continue;
// 检测头部行
if (!hasHeader && isHeaderRow(fields[0], fields[1])) {
hasHeader = true;
continue;
}
const type = fields[0].trim().toLowerCase();
const path = fields[1].trim();
if (path.length === 0) continue;
if (type === 'system') {
result.system.push({ path, enabled: true });
} else if (type === 'user') {
result.user.push({ path, enabled: true });
}
// 未知类型忽略
}
return result;
}
function isHeaderRow(col0: string, col1: string): boolean {
const c0 = col0.trim().toLowerCase();
const c1 = col1.trim().toLowerCase();
return c0 === 'type' && c1 === 'path';
}
function parseCsvLine(line: string): string[] {
const fields: string[] = [];
let current = '';
let inQuotes = false;
for (let i = 0; i < line.length; i++) {
const ch = line[i];
if (inQuotes) {
if (ch === '"') {
if (i + 1 < line.length && line[i + 1] === '"') {
current += '"';
i++; // 跳过转义引号
} else {
inQuotes = false;
}
} else {
current += ch;
}
} else {
if (ch === '"') {
inQuotes = true;
} else if (ch === ',') {
fields.push(current);
current = '';
} else {
current += ch;
}
}
}
fields.push(current);
return fields;
}
// ── JSON 导入 ──
export function importFromJson(content: string): ImportResult {
const result: ImportResult = { system: [], user: [] };
let obj: Record<string, unknown>;
try {
obj = JSON.parse(content);
} catch {
return result; // 无效 JSON 返回空结果,由调用方显示错误
}
if (typeof obj !== 'object' || obj === null) return result;
if (Array.isArray(obj.system)) {
result.system = obj.system
.filter((p: unknown) => typeof p === 'string' && p.trim().length > 0)
.map((p: string) => ({ path: p.trim(), enabled: true }));
}
if (Array.isArray(obj.user)) {
result.user = obj.user
.filter((p: unknown) => typeof p === 'string' && p.trim().length > 0)
.map((p: string) => ({ path: p.trim(), enabled: true }));
}
return result;
}
// ── TXT 导入 ──
export function importFromTxt(content: string): PathEntry[] {
const paths: PathEntry[] = [];
const lines = content.split(/\r?\n/);
for (let i = 0; i < lines.length; i++) {
// 跳过 BOM(仅首行)
let line = lines[i];
if (i === 0 && line.startsWith('')) line = line.slice(1);
const trimmed = line.trim();
if (trimmed.length === 0 || trimmed.startsWith('#')) continue;
paths.push({ path: trimmed, enabled: true });
}
return paths;
}
// ── 自动检测导入 ──
export function importFromContent(
content: string,
filepath: string,
): ImportResult {
const lower = filepath.toLowerCase();
if (lower.endsWith('.csv')) {
return importFromCsv(content);
} else if (lower.endsWith('.json')) {
return importFromJson(content);
} else {
// TXT 文件:所有路径放入 system(用户后续可选择目标)
return { system: importFromTxt(content), user: [] };
}
}
/** 将 ImportResult 合并为单个路径数组 */
export function flattenImportResult(
result: ImportResult,
target: 'system' | 'user' | 'both',
): ExportData {
if (target === 'system') return { system: result.system, user: [] };
if (target === 'user') return { system: [], user: result.user };
return result; // both
}