JJ_MKVMaker/lib/core/app_hidden.dart

62 lines
2.3 KiB
Dart

import 'dart:io';
import 'package:path/path.dart' as p;
import 'file_ops.dart' show isSameOrInside, samePath;
import 'vfs.dart' show isDav, vExists, vJoin;
/// 약속 (E5c): 탐색기 "목록에서 숨기기" 의 목록 규칙 (파일은 그대로 - 앱 설정의 경로 목록).
/// - [isHidden]: 목록 · 경로 → 숨긴 것인지 (Windows 대소문자 무시, 같은 목록이면 열쇠를 다시 만들지 않음).
/// - [follow]: 옮기거나 이름을 바꾼 것 (원래 → 새 경로, 그 안의 것도) 은 새 경로로, 지운 것 (그 안의 것도 - 실제로 없어진
/// 것만, [exists] 로 확인) 은 목록에서 뺀다 (관리자 결정 10/18). 출력 = 새 목록, 바뀐 것이 없으면 null. 같은 경로는 한 번만.
/// - 다시 쓰는 곳: 탐색기 트리 · Rsync 트리 · 찾기 결과.
class HiddenList {
List<String>? _from;
Set<String> _keys = const {};
/// 숨긴 목록에서 같은 경로를 찾는 열쇠
static String keyOf(String x) {
if (isDav(x) || x.contains('://')) return x;
final n = p.normalize(x);
return Platform.isWindows ? n.toLowerCase() : n;
}
bool isHidden(List<String> list, String path) {
if (list.isEmpty) return false;
if (!identical(list, _from)) {
_from = list;
_keys = {for (final x in list) keyOf(x)};
}
return _keys.contains(keyOf(path));
}
static Future<List<String>?> follow(List<String> list,
{Iterable<(String, String)> moved = const [],
Iterable<String> gone = const [],
Future<bool> Function(String path) exists = vExists}) async {
if (list.isEmpty || (moved.isEmpty && gone.isEmpty)) return null;
final out = <String>[];
var changed = false;
for (final h in list) {
if (gone.any((g) => isSameOrInside(h, g)) && !await exists(h)) {
changed = true;
continue;
}
var now = h;
for (final (from, to) in moved) {
if (samePath(h, from)) {
now = to;
break;
}
if (isSameOrInside(h, from)) {
now = vJoin(to, isDav(h) ? h.substring(from.length).replaceAll('\\', '/').replaceFirst(RegExp('^/+'), '') : p.relative(h, from: from));
break;
}
}
if (now != h) changed = true;
if (!out.any((x) => keyOf(x) == keyOf(now))) out.add(now);
}
return changed ? out : null;
}
}