dwFakeWindow<T extends DwDataObject> function

DwWindowResult<T> dwFakeWindow<T extends DwDataObject>(
  1. List<T> newestFirst,
  2. DwWindowRequest<T, Object, Object> request,
  3. DwPageQuery? page
)

The window of newestFirst a DwWindowRequest call asks for, with the cursors a DartWay server builds from the request's positionOf.

newestFirst must be ordered by that position, descending. Around an anchor the window holds the anchor row and the rows older than it, with up to half of the page newer than it.

Implementation

DwWindowResult<T> dwFakeWindow<T extends DwDataObject>(
  List<T> newestFirst,
  DwWindowRequest<T, Object, Object> request,
  DwPageQuery? page,
) {
  final query = page is DwWindowQuery ? page : const DwWindowQuery.newest();
  final size = request.servedPageSize(query.pageSize);
  DwWindowCursor keyOf(T row) {
    final (:sortValue, :id) = request.positionOf(row);
    return DwWindowCursor(sortValue, id);
  }

  // Positions in newest-first order: rows before `index` are newer.
  int firstNotNewerThan(DwWindowCursor cursor) {
    final index = newestFirst.indexWhere(
      (row) => _compare(keyOf(row), cursor) <= 0,
    );
    return index < 0 ? newestFirst.length : index;
  }

  final int start;
  final int end;
  switch ((query.direction, query.cursor)) {
    case (DwWindowDirection.around, null):
      start = 0;
      end = size;
    case (DwWindowDirection.around, final String anchor):
      final at = firstNotNewerThan(DwWindowCursor.decode(anchor));
      final newer = size ~/ 2;
      start = at - newer < 0 ? 0 : at - newer;
      end = start + size;
    case (DwWindowDirection.older, final String before):
      final cursor = DwWindowCursor.decode(before);
      final at = newestFirst.indexWhere(
        (row) => _compare(keyOf(row), cursor) < 0,
      );
      start = at < 0 ? newestFirst.length : at;
      end = start + size;
    case (DwWindowDirection.newer, final String after):
      final at = firstNotNewerThan(DwWindowCursor.decode(after));
      end = at;
      start = end - size < 0 ? 0 : end - size;
    default:
      throw ArgumentError('A window read past an end names its cursor');
  }
  final clampedEnd = end > newestFirst.length ? newestFirst.length : end;
  final rows = newestFirst.sublist(start, clampedEnd);
  if (rows.isEmpty) return DwWindowResult<T>(rows);
  return DwWindowResult<T>(
    rows,
    olderCursor: clampedEnd < newestFirst.length
        ? keyOf(rows.last).encoded
        : null,
    newerCursor: start > 0 ? keyOf(rows.first).encoded : null,
  );
}