list method

All sessions in directory, sorted from oldest to newest (by last activity).

Non-regular directory entries, including symlinks, are ignored.

Implementation

Future<List<FileLogSession>> list() async {
  final dir = Directory(directory);
  if (!dir.existsSync()) return const [];

  final chunksById =
      <String, List<({int index, File file, FileStat stat})>>{};
  await for (final entity in dir.list(followLinks: false)) {
    final file = File(entity.path);
    final parsed = parseChunkName(file.uri.pathSegments.last);
    if (parsed == null) continue;
    if (!_isRegularFile(file)) continue;
    final stat = file.statSync();
    // The file may have been deleted between list() and statSync
    // (rotation or cleanup by another process) — otherwise the session
    // would get size -1 and a lastModified near the epoch.
    if (stat.type == FileSystemEntityType.notFound) continue;
    if (!_isRegularFile(file)) continue;
    chunksById
        .putIfAbsent(parsed.sessionId, () => [])
        .add((index: parsed.index, file: file, stat: stat));
  }

  final sessions = <FileLogSession>[];
  for (final MapEntry(key: id, value: chunks) in chunksById.entries) {
    chunks.sort((a, b) => a.index.compareTo(b.index));
    var size = 0;
    var lastModified = DateTime.fromMillisecondsSinceEpoch(0);
    for (final chunk in chunks) {
      size += chunk.stat.size;
      if (chunk.stat.modified.isAfter(lastModified)) {
        lastModified = chunk.stat.modified;
      }
    }
    sessions.add(
      FileLogSession._(
        id: id,
        files: List.unmodifiable(chunks.map((c) => c.file)),
        size: size,
        lastModified: lastModified,
      ),
    );
  }

  sessions.sort(
    (a, b) => switch (a.lastModified.compareTo(b.lastModified)) {
      0 => a.id.compareTo(b.id),
      final byTime => byTime,
    },
  );

  return sessions;
}