private static CursorPagingRange SliceRange(
            CursorPagingArguments arguments,
            int maxElementCount)
        {
            // [SPEC] if after is set then remove all elements of edges before and including
            // afterEdge.
            //
            // The cursor is increased by one so that the index points to the element after
            var startIndex = arguments.After is { } a
                ? IndexEdge <TEntity> .DeserializeCursor(a) + 1
                : 0;

            // [SPEC] if before is set then remove all elements of edges before and including
            // beforeEdge.
            var before = arguments.Before is { } b
                ? IndexEdge <TEntity> .DeserializeCursor(b)
                : maxElementCount;

            // if after is negative we have know how much of the offset was in the negative range.
            // The amount of positions that are in the negative range, have to be subtracted from
            // the take or we will fetch too many items.
            var startOffsetCorrection = 0;

            if (startIndex < 0)
            {
                startOffsetCorrection = Math.Abs(startIndex);
                startIndex            = 0;
            }

            CursorPagingRange range = new(startIndex, before);

            //[SPEC] If first is less than 0 throw an error
            ValidateFirst(arguments, out var first);

            if (first is not null)
            {
                first -= startOffsetCorrection;
                if (first < 0)
                {
                    first = 0;
                }
            }

            //[SPEC] Slice edges to be of length first by removing edges from the end of edges.
            range.Take(first);

            //[SPEC] if last is less than 0 throw an error
            ValidateLast(arguments, out var last);

            //[SPEC] Slice edges to be of length last by removing edges from the start of edges.
            range.TakeLast(last);

            return(range);
        }
        private async ValueTask <Connection> ResolveAsync(
            IQueryable <TEntity> source,
            CursorPagingArguments arguments     = default,
            CancellationToken cancellationToken = default)
        {
            var count = await Task.Run(source.Count, cancellationToken)
                        .ConfigureAwait(false);

            int?after = arguments.After is { } a
                ? (int?)IndexEdge <TEntity> .DeserializeCursor(a)
                : null;

            int?before = arguments.Before is { } b
                ? (int?)IndexEdge <TEntity> .DeserializeCursor(b)
                : null;

            IReadOnlyList <IndexEdge <TEntity> > selectedEdges =
                await GetSelectedEdgesAsync(
                    source, arguments.First, arguments.Last, after, before, cancellationToken)
                .ConfigureAwait(false);

            IndexEdge <TEntity>?firstEdge = selectedEdges.Count == 0
                ? null
                : selectedEdges[0];

            IndexEdge <TEntity>?lastEdge = selectedEdges.Count == 0
                ? null
                : selectedEdges[selectedEdges.Count - 1];

            var pageInfo = new ConnectionPageInfo(
                lastEdge?.Index < count - 1,
                firstEdge?.Index > 0,
                firstEdge?.Cursor,
                lastEdge?.Cursor,
                count);

            return(new Connection <TEntity>(
                       selectedEdges,
                       pageInfo,
                       ct => new ValueTask <int>(pageInfo.TotalCount ?? 0)));
        }
        protected virtual async ValueTask <IReadOnlyList <IndexEdge <TEntity> > > ExecuteQueryableAsync(
            IQueryable <TEntity> queryable,
            int offset,
            CancellationToken cancellationToken)
        {
            var list = new List <IndexEdge <TEntity> >();

            if (queryable is IAsyncEnumerable <TEntity> enumerable)
            {
                var index = offset;
                await foreach (TEntity item in enumerable.WithCancellation(cancellationToken)
                               .ConfigureAwait(false))
                {
                    list.Add(IndexEdge <TEntity> .Create(item, index++));
                }
            }
            else
            {
                await Task.Run(() =>
                {
                    var index = offset;
                    foreach (TEntity item in queryable)
                    {
                        if (cancellationToken.IsCancellationRequested)
                        {
                            break;
                        }
                        list.Add(IndexEdge <TEntity> .Create(item, index++));
                    }
                },
                               cancellationToken)
                .ConfigureAwait(false);
            }

            return(list);
        }