public StreamSubscription(
                IPublisher bus,
                string streamName,
                StreamRevision?startRevision,
                bool resolveLinks,
                ClaimsPrincipal user,
                bool requiresLeader,
                IReadIndex readIndex,
                CancellationToken cancellationToken)
            {
                if (bus == null)
                {
                    throw new ArgumentNullException(nameof(bus));
                }

                if (streamName == null)
                {
                    throw new ArgumentNullException(nameof(streamName));
                }

                if (readIndex == null)
                {
                    throw new ArgumentNullException(nameof(readIndex));
                }

                _subscriptionId      = Guid.NewGuid();
                _bus                 = bus;
                _streamName          = streamName;
                _resolveLinks        = resolveLinks;
                _user                = user;
                _requiresLeader      = requiresLeader;
                _readIndex           = readIndex;
                _cancellationToken   = cancellationToken;
                _subscriptionStarted = new TaskCompletionSource <bool>();

                _startRevision = startRevision == StreamRevision.End
                                        ? StreamRevision.FromInt64(readIndex.GetStreamLastEventNumber(_streamName) + 1)
                                        : startRevision + 1 ?? StreamRevision.Start;

                _inner = startRevision == StreamRevision.End
                                        ? (IStreamEnumerator) new LiveStreamSubscription(_subscriptionId, _bus, _streamName,
                                                                                         StreamRevision.FromInt64(readIndex.GetStreamLastEventNumber(_streamName) + 1), _resolveLinks,
                                                                                         _user, _requiresLeader, _subscriptionStarted, _cancellationToken)
                                        : new CatchupStreamSubscription(_subscriptionId, bus, streamName,
                                                                        startRevision + 1 ?? StreamRevision.Start, resolveLinks, user, _requiresLeader, readIndex,
                                                                        _subscriptionStarted, cancellationToken);
            }
            public async ValueTask <bool> MoveNextAsync()
            {
ReadLoop:
                if (await _inner.MoveNextAsync().ConfigureAwait(false))
                {
                    if (_inner.CurrentStreamRevision >= _startRevision)
                    {
                        return(true);
                    }
                    goto ReadLoop;
                }

                if (_cancellationToken.IsCancellationRequested)
                {
                    return(false);
                }

                await _inner.DisposeAsync().ConfigureAwait(false);

                var currentStreamRevision = _inner.CurrentStreamRevision;

                Log.Verbose(
                    "Subscription {subscriptionId} to {streamName} reached the end at {streamRevision}, switching...",
                    _subscriptionId, _streamName, currentStreamRevision);

                if (_inner is LiveStreamSubscription)
                {
                    _inner = new CatchupStreamSubscription(_subscriptionId, _bus, _streamName,
                                                           currentStreamRevision, _resolveLinks, _user, _requiresLeader, _readIndex, _subscriptionStarted,
                                                           _cancellationToken);
                }
                else
                {
                    _inner = new LiveStreamSubscription(_subscriptionId, _bus, _streamName, currentStreamRevision,
                                                        _resolveLinks, _user, _requiresLeader, _subscriptionStarted, _cancellationToken);
                }

                goto ReadLoop;
            }