JJ_MKVMaker/lib/app/delete_ops.dart

119 lines
4.3 KiB
Dart

import 'dart:io';
import '../core/app_trash.dart';
import '../core/file_ops.dart' show FileOps;
import '../platform/windows/recycle_bin.dart';
/// 약속 (E5b): 지우기 · 되돌리기. 휴지통은 [RecycleBinBackend] (Windows, 처음 값 = 이 기기의 휴지통) · app_trash (Android) 를 부르기만 한다.
/// - [deletePaths]: 입력 = 경로들 · 영구인지 · 앱 휴지통 (있으면 그리로), 출력 = [DeleteReport] (실제로 어떻게 됐는지 하나씩).
/// 하나가 실패해도 나머지는 계속한다. 휴지통으로 못 보낸 것은 몰래 영구 삭제하지 않고 보고에 남긴다
/// (앱 휴지통 [DeleteReport.notTrashed] · 긴 경로 [DeleteReport.tooLong] - 영구 삭제는 부르는 쪽이 물은 뒤 [deleteForever]).
/// - [deleteForever]: 영구 삭제, 출력 = 못 지운 것 (경로, 예외).
/// - [undoTrash] · [undoRecycle]: 방금 휴지통으로 보낸 것을 원래 자리로, 출력 = 되살린 것 · 못 되살린 것 (이유).
/// - 다시 쓰는 곳: 탐색기 · 다운로드 화면 지우기 · 보관함 · 정리.
class DeleteReport {
DeleteReport(this.since);
/// 이 지우기를 시작한 때 (Windows 휴지통 되돌리기용 - 정보 파일 시각이 초 단위라 조금 앞)
final DateTime since;
/// Windows 휴지통으로 보낸 것 (되돌릴 수 있음)
final sent = <String>[];
/// 앱 휴지통으로 보낸 것 · 그 경로 (되돌릴 수 있음)
final trashed = <TrashItem>[];
final undoable = <String>[];
/// 앱 휴지통으로 옮기지 못한 것 (경로, 이유) - 다른 저장 장치 · 권한 등
final notTrashed = <(String, String)>[];
/// 경로가 너무 길어 휴지통이 받지 않은 것
final tooLong = <String>[];
/// 지우지 못한 것 (경로, 이유)
final failed = <(String, String)>[];
/// 휴지통으로 보낸 수 · Windows 가 묻고 영구 삭제한 수 · 취소한 수
var recycled = 0, nuked = 0, kept = 0;
}
/// 사람이 읽을 실패 이유 (OS 메시지가 있으면 그것)
String deleteFailReason(Object e) => e is FileSystemException ? (e.osError?.message ?? e.message) : '$e';
Future<DeleteReport> deletePaths(List<String> paths,
{required bool permanent, AppTrash? trash, RecycleBinBackend bin = const WindowsRecycleBin(), DateTime? now}) async {
final r = DeleteReport((now ?? DateTime.now()).subtract(const Duration(seconds: 2)));
for (final x in paths) {
if (!permanent && trash != null) {
try {
r.trashed.add(await trash.moveToTrash(x));
r.undoable.add(x);
r.recycled++;
} on TrashMoveException catch (e) {
r.notTrashed.add((x, '${e.cause}'));
}
continue;
}
try {
if (!permanent) {
switch (bin.move(x)) {
case RecycleResult.recycled:
r.recycled++;
r.sent.add(x);
case RecycleResult.deletedPermanently:
r.nuked++;
case RecycleResult.cancelled:
r.kept++;
}
} else {
await FileOps().delete([x]);
}
} on LongPathException {
r.tooLong.add(x);
} catch (e) {
r.failed.add((x, deleteFailReason(e)));
}
}
return r;
}
/// 영구 삭제 - 못 지운 것 (경로, 예외)
Future<List<(String, Object)>> deleteForever(Iterable<String> paths) async {
final failed = <(String, Object)>[];
for (final x in paths) {
try {
await FileOps().delete([x]);
} catch (e) {
failed.add((x, e));
}
}
return failed;
}
/// 앱 휴지통에서 되살림 - 되살린 경로 (못 되살린 것은 빠짐)
Future<List<String>> undoTrash(AppTrash trash, List<TrashItem> items) async {
final back = <String>[];
for (final i in items) {
try {
back.add(await trash.restore(i));
} catch (_) {}
}
return back;
}
/// Windows 휴지통에서 되살림 - 되살린 수 · 못 되살린 것 (경로, 이유)
({int back, List<(String, String)> failed}) undoRecycle(List<String> paths, DateTime since,
{RecycleBinBackend bin = const WindowsRecycleBin()}) {
final failed = <(String, String)>[];
var back = 0;
for (final x in paths) {
try {
bin.restore(x, since: since);
back++;
} catch (e) {
failed.add((x, e is FileSystemException ? e.message : '$e'));
}
}
return (back: back, failed: failed);
}