JJ_MKVMaker/lib/app/transfer_plan.dart

79 lines
3.3 KiB
Dart

import '../core/file_ops.dart' show outermostPaths, samePath, transferProblem;
import '../core/name_clash.dart';
import '../core/vfs.dart' show vBasename, vDirname, vJoin, vStat;
import '../l10n/tr.dart';
/// 약속 (E5b): 복사 · 이동을 시작하기 전 계획 ([planTransfer]) - CopyCenter.start 앞 단계.
/// 입력 = 원본들 · 대상 폴더 · 이동인지, 출력 = [TransferPlan] (실제로 보낼 것 · 건너뛸 것 · 무엇을 물을지 · 막는 문제).
/// 오류 없음 (막는 문제는 [TransferPlan.problem] 글로).
/// - 고른 폴더 안의 것은 한 번만 (바깥 것만).
/// - 135: 같은 폴더로 - 이동인데 일부만 이미 그 폴더에 있으면 그것만 건너뛰고 나머지를 옮긴다.
/// 모두 이미 그 폴더면 이동은 할 것이 없고 ([TransferPlan.nothingToMove]), 복사는 사본을 만들지 묻는다 ([TransferPlan.askCopyHere]).
/// - [findClashes]: 대상에 같은 이름이 있는 원본 (크기 · 바뀐 날 함께, 같은 폴더로 복사는 늘 새 이름이라 뺌).
/// - 다시 쓰는 곳: 탐색기 [복사] · [이동] · 끌어 놓기 · 붙여넣기 · 찾기 결과 복사.
class TransferPlan {
const TransferPlan({
required this.sources,
required this.here,
required this.skippedHere,
required this.nothingToMove,
required this.askCopyHere,
required this.problem,
});
/// 실제로 보낼 것
final List<String> sources;
/// 이미 대상 폴더에 있는 원본
final List<String> here;
/// 이동에서 이미 그 폴더에 있어 건너뛰는 것
final List<String> skippedHere;
final bool nothingToMove;
final bool askCopyHere;
/// 시작하지 않는 이유 (원본 · 대상이 없음, 폴더를 자기 안으로 등) - 없으면 null
final String? problem;
}
TransferPlan planTransfer(List<String> sources, String dest, {required bool move}) {
sources = outermostPaths(sources);
final here = [for (final x in sources) if (samePath(vDirname(x), dest)) x];
var skipped = const <String>[];
var rest = sources;
if (move && here.isNotEmpty && here.length < sources.length) {
skipped = here;
rest = [for (final x in sources) if (!here.contains(x)) x];
}
final all = here.isNotEmpty && skipped.isEmpty && (move || here.length == sources.length);
return TransferPlan(
sources: rest,
here: here,
skippedHere: skipped,
nothingToMove: all && move,
askCopyHere: all && !move,
problem: all && move ? null : transferProblem(rest, dest, move: move),
);
}
/// 48 · 172: 대상 폴더에 같은 이름이 있는 원본 - 두 파일의 크기 · 바뀐 날을 함께 (어느 쪽이 새것인지 보고 고르게)
Future<List<NameClash>> findClashes(List<String> sources, String dest) async {
final out = <NameClash>[];
for (final s in sources) {
if (samePath(vDirname(s), dest)) continue;
try {
final there = await vStat(vJoin(dest, vBasename(s)));
if (there == null) continue;
final here = await vStat(s);
out.add((src: s, here: here, there: there));
} catch (_) {}
}
return out;
}
/// 154: 항목 이름 (앞의 3개 + "외 n개")
String namesOf(List<String> paths) {
final names = [for (final x in paths.take(3)) vBasename(x)].join(', ');
return paths.length > 3 ? trf('{0} 외 {1}개', [names, paths.length - 3]) : names;
}