镜像自地址
https://github.com/lhly/cloud-drive-renamer.git
已同步 2026-10-20 13:47:47 +08:00
feat: add execution plan helper
这个提交包含在:
父节点
6d940e607e
当前提交
f3a89f28e6
75
src/core/execution-plan.ts
普通文件
75
src/core/execution-plan.ts
普通文件
@ -0,0 +1,75 @@
|
||||
import { FileItem } from '../types/platform';
|
||||
import { Task } from '../types/core';
|
||||
import {
|
||||
ConflictResolution,
|
||||
ConflictResult,
|
||||
resolveBatchConflicts,
|
||||
} from './conflict-detector';
|
||||
|
||||
export interface ExecutionPlanInput {
|
||||
files: FileItem[];
|
||||
newNames: string[];
|
||||
conflicts?: Map<string, ConflictResult>;
|
||||
resolution?: ConflictResolution | null;
|
||||
skipUnchanged?: boolean;
|
||||
}
|
||||
|
||||
export interface ExecutionPlanResult {
|
||||
resolvedNames: string[];
|
||||
tasks: Task[];
|
||||
skippedIndexes: number[];
|
||||
}
|
||||
|
||||
export function buildExecutionPlan({
|
||||
files,
|
||||
newNames,
|
||||
conflicts,
|
||||
resolution,
|
||||
skipUnchanged = true,
|
||||
}: ExecutionPlanInput): ExecutionPlanResult {
|
||||
if (files.length !== newNames.length) {
|
||||
throw new Error('Files and newNames length mismatch');
|
||||
}
|
||||
|
||||
const conflictMap = conflicts ?? new Map<string, ConflictResult>();
|
||||
|
||||
let resolvedNames = newNames.slice();
|
||||
|
||||
if (resolution && conflictMap.size > 0) {
|
||||
switch (resolution) {
|
||||
case ConflictResolution.AUTO_NUMBER:
|
||||
resolvedNames = resolveBatchConflicts(files, newNames, conflictMap);
|
||||
break;
|
||||
case ConflictResolution.SKIP:
|
||||
resolvedNames = newNames.map((name, index) => {
|
||||
const file = files[index];
|
||||
const conflict = conflictMap.get(file.id);
|
||||
return conflict?.hasConflict ? file.name : name;
|
||||
});
|
||||
break;
|
||||
case ConflictResolution.OVERWRITE:
|
||||
default:
|
||||
resolvedNames = newNames.slice();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const tasks: Task[] = [];
|
||||
const skippedIndexes: number[] = [];
|
||||
|
||||
resolvedNames.forEach((resolvedName, index) => {
|
||||
const file = files[index];
|
||||
if (skipUnchanged && resolvedName === file.name) {
|
||||
skippedIndexes.push(index);
|
||||
return;
|
||||
}
|
||||
|
||||
tasks.push({
|
||||
file,
|
||||
newName: resolvedName,
|
||||
index,
|
||||
});
|
||||
});
|
||||
|
||||
return { resolvedNames, tasks, skippedIndexes };
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { buildExecutionPlan } from '../../src/core/execution-plan';
|
||||
import { checkBatchConflicts, ConflictResolution } from '../../src/core/conflict-detector';
|
||||
|
||||
const files = [
|
||||
{ id: '1', name: 'a.txt', ext: '.txt', parentId: '0', size: 1, mtime: 1 },
|
||||
{ id: '2', name: 'b.txt', ext: '.txt', parentId: '0', size: 1, mtime: 1 },
|
||||
];
|
||||
|
||||
describe('buildExecutionPlan', () => {
|
||||
it('auto-number resolves duplicate names and skips unchanged', () => {
|
||||
const newNames = ['same.txt', 'same.txt'];
|
||||
const conflicts = checkBatchConflicts(files as any, newNames);
|
||||
|
||||
const plan = buildExecutionPlan({
|
||||
files: files as any,
|
||||
newNames,
|
||||
conflicts,
|
||||
resolution: ConflictResolution.AUTO_NUMBER,
|
||||
});
|
||||
|
||||
expect(plan.resolvedNames[0]).toMatch(/same\(1\)\.txt/);
|
||||
expect(plan.resolvedNames[1]).toMatch(/same\(2\)\.txt/);
|
||||
expect(plan.tasks.length).toBe(2);
|
||||
});
|
||||
|
||||
it('skip strategy preserves original names', () => {
|
||||
const newNames = ['same.txt', 'same.txt'];
|
||||
const conflicts = checkBatchConflicts(files as any, newNames);
|
||||
|
||||
const plan = buildExecutionPlan({
|
||||
files: files as any,
|
||||
newNames,
|
||||
conflicts,
|
||||
resolution: ConflictResolution.SKIP,
|
||||
});
|
||||
|
||||
expect(plan.resolvedNames).toEqual(['a.txt', 'b.txt']);
|
||||
});
|
||||
});
|
||||
正在加载...
在新工单中引用
屏蔽一个用户